diff --git a/.gitignore b/.gitignore index 6560423..cf30e66 100644 --- a/.gitignore +++ b/.gitignore @@ -20,5 +20,6 @@ build # other *.log +*.log.gz eclipse run diff --git a/Common/src/main/java/com/almostreliable/unified/AlmostUnifiedPlatform.java b/Common/src/main/java/com/almostreliable/unified/AlmostUnifiedPlatform.java index 53d6967..e6da6a9 100644 --- a/Common/src/main/java/com/almostreliable/unified/AlmostUnifiedPlatform.java +++ b/Common/src/main/java/com/almostreliable/unified/AlmostUnifiedPlatform.java @@ -9,11 +9,11 @@ public interface AlmostUnifiedPlatform { AlmostUnifiedPlatform INSTANCE = PlatformLoader.load(AlmostUnifiedPlatform.class); /** - * Gets the name of the current platform + * Gets the current platform * - * @return The name of the current platform. + * @return The current platform. */ - String getPlatformName(); + Platform getPlatform(); /** * Checks if a mod with the given id is loaded. diff --git a/Common/src/main/java/com/almostreliable/unified/AlmostUnifiedRuntime.java b/Common/src/main/java/com/almostreliable/unified/AlmostUnifiedRuntime.java index 58e1b2c..0d1be15 100644 --- a/Common/src/main/java/com/almostreliable/unified/AlmostUnifiedRuntime.java +++ b/Common/src/main/java/com/almostreliable/unified/AlmostUnifiedRuntime.java @@ -35,7 +35,7 @@ public abstract class AlmostUnifiedRuntime { onRun(); List> allowedTags = config.getAllowedTags(); TagMap tagMap = createTagMap(allowedTags); - ReplacementMap replacementMap = new ReplacementMap(tagMap, modPriorities); + ReplacementMap replacementMap = new ReplacementMap(tagMap, modPriorities, config.getStoneStrata()); RecipeTransformer transformer = new RecipeTransformer(recipeHandlerFactory, replacementMap); RecipeTransformationResult result = transformer.transformRecipes(recipes); new RecipeDumper(result).dump(); diff --git a/Common/src/main/java/com/almostreliable/unified/ModConfig.java b/Common/src/main/java/com/almostreliable/unified/ModConfig.java index e545044..0001295 100644 --- a/Common/src/main/java/com/almostreliable/unified/ModConfig.java +++ b/Common/src/main/java/com/almostreliable/unified/ModConfig.java @@ -15,6 +15,13 @@ import java.util.*; public class ModConfig { + public static final List DEFAULT_STONE_STRATA = List.of("stone", + "nether", + "deepslate", + "granite", + "diorite", + "andesite"); + @SuppressWarnings("SpellCheckingInspection") public static final List DEFAULT_MOD_PRIORITIES = List.of( "kubejs", @@ -69,8 +76,6 @@ public class ModConfig { "lapis", "lead", "lumium", - "mana", - "manyullyn", "nickel", "obsidian", "osmium", @@ -93,6 +98,7 @@ public class ModConfig { "gears", "gems", "ingots", + "raw_materials", "ores", "plates", "rods", @@ -118,7 +124,7 @@ public class ModConfig { } private static List getDefaultPatterns() { - if (AlmostUnifiedPlatform.INSTANCE.getPlatformName().equals("Forge")) { + if (AlmostUnifiedPlatform.INSTANCE.getPlatform().equals("Forge")) { return List.of("forge:{types}/{materials}"); } else { return List.of("c:{materials}_{types}"); @@ -156,6 +162,10 @@ public class ModConfig { currentConfig.close(); } + public List getStoneStrata() { + return Collections.unmodifiableList(DEFAULT_STONE_STRATA); + } + public List getModPriorities() { if (currentConfig == null) { throw new IllegalStateException("Config is not loaded"); diff --git a/Common/src/main/java/com/almostreliable/unified/Platform.java b/Common/src/main/java/com/almostreliable/unified/Platform.java new file mode 100644 index 0000000..b6b1e69 --- /dev/null +++ b/Common/src/main/java/com/almostreliable/unified/Platform.java @@ -0,0 +1,6 @@ +package com.almostreliable.unified; + +public enum Platform { + Forge, + Fabric +} diff --git a/Common/src/main/java/com/almostreliable/unified/api/recipe/RecipeContext.java b/Common/src/main/java/com/almostreliable/unified/api/recipe/RecipeContext.java index 4a28ebb..a28f26e 100644 --- a/Common/src/main/java/com/almostreliable/unified/api/recipe/RecipeContext.java +++ b/Common/src/main/java/com/almostreliable/unified/api/recipe/RecipeContext.java @@ -2,12 +2,11 @@ package com.almostreliable.unified.api.recipe; import com.almostreliable.unified.utils.UnifyTag; import com.google.gson.JsonElement; -import com.google.gson.JsonPrimitive; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.item.Item; import javax.annotation.Nullable; -import java.util.function.UnaryOperator; +import java.util.function.Predicate; public interface RecipeContext { @@ -15,7 +14,7 @@ public interface RecipeContext { ResourceLocation getReplacementForItem(@Nullable ResourceLocation item); @Nullable - ResourceLocation getPreferredItemByTag(@Nullable UnifyTag tag); + ResourceLocation getPreferredItemByTag(@Nullable UnifyTag tag, Predicate filter); @Nullable UnifyTag getPreferredTagByItem(@Nullable ResourceLocation item); diff --git a/Common/src/main/java/com/almostreliable/unified/recipe/RecipeContextImpl.java b/Common/src/main/java/com/almostreliable/unified/recipe/RecipeContextImpl.java index 51efe79..39ebc29 100644 --- a/Common/src/main/java/com/almostreliable/unified/recipe/RecipeContextImpl.java +++ b/Common/src/main/java/com/almostreliable/unified/recipe/RecipeContextImpl.java @@ -13,6 +13,7 @@ import net.minecraft.resources.ResourceLocation; import net.minecraft.world.item.Item; import javax.annotation.Nullable; +import java.util.function.Predicate; @SuppressWarnings("SameParameterValue") public class RecipeContextImpl implements RecipeContext { @@ -38,12 +39,12 @@ public class RecipeContextImpl implements RecipeContext { @Nullable @Override - public ResourceLocation getPreferredItemByTag(@Nullable UnifyTag tag) { + public ResourceLocation getPreferredItemByTag(@Nullable UnifyTag tag, Predicate filter) { if (tag == null) { return null; } - return replacementMap.getPreferredItemByTag(tag); + return replacementMap.getPreferredItemForTag(tag, filter); } @Nullable @@ -53,7 +54,7 @@ public class RecipeContextImpl implements RecipeContext { return null; } - return replacementMap.getPreferredTag(item); + return replacementMap.getPreferredTagForItem(item); } @Override diff --git a/Common/src/main/java/com/almostreliable/unified/recipe/RecipeDumper.java b/Common/src/main/java/com/almostreliable/unified/recipe/RecipeDumper.java index 131991d..44fdce7 100644 --- a/Common/src/main/java/com/almostreliable/unified/recipe/RecipeDumper.java +++ b/Common/src/main/java/com/almostreliable/unified/recipe/RecipeDumper.java @@ -9,6 +9,7 @@ import java.nio.file.Path; import java.nio.file.StandardOpenOption; import java.text.DateFormat; import java.text.SimpleDateFormat; +import java.util.Comparator; import java.util.Date; public class RecipeDumper { @@ -54,16 +55,22 @@ public class RecipeDumper { private void dumpTransformedRecipes(StringBuilder stringBuilder, boolean full) { result.forEachTransformedRecipe((type, recipes) -> { stringBuilder.append(type.toString()).append(" {\n"); - recipes.forEach((resourceLocation, entry) -> { - stringBuilder.append("\t- ").append(resourceLocation.toString()).append("\n"); - if (full) { - stringBuilder.append("\t\t Original: ").append(entry.originalRecipe().toString()).append("\n"); - stringBuilder - .append("\t\t Transformed: ") - .append(entry.transformedRecipe().toString()) - .append("\n\n"); - } - }); + recipes.entrySet() + .stream() + .sorted(Comparator.comparing(o -> o.getKey().toString())) + .forEach((e) -> { + stringBuilder.append("\t- ").append(e.getKey().toString()).append("\n"); + if (full) { + stringBuilder + .append("\t\t Original: ") + .append(e.getValue().originalRecipe().toString()) + .append("\n"); + stringBuilder + .append("\t\t Transformed: ") + .append(e.getValue().transformedRecipe().toString()) + .append("\n\n"); + } + }); stringBuilder.append("}\n\n"); }); } diff --git a/Common/src/main/java/com/almostreliable/unified/recipe/RawRecipe.java b/Common/src/main/java/com/almostreliable/unified/recipe/RecipeLink.java similarity index 57% rename from Common/src/main/java/com/almostreliable/unified/recipe/RawRecipe.java rename to Common/src/main/java/com/almostreliable/unified/recipe/RecipeLink.java index 682ad9e..4c4617f 100644 --- a/Common/src/main/java/com/almostreliable/unified/recipe/RawRecipe.java +++ b/Common/src/main/java/com/almostreliable/unified/recipe/RecipeLink.java @@ -1,6 +1,6 @@ package com.almostreliable.unified.recipe; -import com.almostreliable.unified.AlmostUnified; +import com.almostreliable.unified.BuildConfig; import com.almostreliable.unified.utils.JsonCompare; import com.google.gson.JsonObject; import net.minecraft.resources.ResourceLocation; @@ -8,14 +8,14 @@ import net.minecraft.resources.ResourceLocation; import javax.annotation.Nullable; import java.util.*; -public class RawRecipe { +public class RecipeLink { private final ResourceLocation id; private final ResourceLocation type; private final JsonObject originalRecipe; @Nullable private DuplicateLink duplicateLink; @Nullable private JsonObject transformedRecipe; - public RawRecipe(ResourceLocation id, JsonObject originalRecipe) { + public RecipeLink(ResourceLocation id, JsonObject originalRecipe) { this.id = id; this.originalRecipe = originalRecipe; @@ -26,6 +26,43 @@ public class RawRecipe { } } + /** + * Compare two recipes for equality with given rules. Keys from rules will automatically count as ignored field for the base comparison. + * If base comparison succeed then the recipes will be compared for equality with rules from {@link JsonCompare.Rule}. + * Rules are sorted, first rule with the highest priority will be used. + * + * @param first first recipe to compare + * @param second second recipe to compare + * @param rules rules to use for comparison + * @param ignoredFields fields to ignore in comparison + * @return the recipe where rules are applied and the recipes are compared for equality, or null if the recipes are not equal + */ + @Nullable + public static RecipeLink compare(RecipeLink first, RecipeLink second, LinkedHashMap rules, List ignoredFields) { + JsonObject selfActual = first.getActual(); + JsonObject toCompareActual = second.getActual(); + + Set ignoredFieldsWithRules = new HashSet<>(first.getIgnoredFields()); + ignoredFieldsWithRules.addAll(rules.keySet()); + + if (JsonCompare.matches(selfActual, toCompareActual, ignoredFieldsWithRules)) { + JsonObject compare = JsonCompare.compare(rules, selfActual, toCompareActual); + if (compare == null) { + return null; + } + + if (compare == selfActual) { + return first; + } + + if (compare == toCompareActual) { + return second; + } + } + + return null; + } + public ResourceLocation getId() { return id; } @@ -38,17 +75,6 @@ public class RawRecipe { return originalRecipe; } - private void setDuplicateLink(@Nullable DuplicateLink duplicateLink) { - Objects.requireNonNull(duplicateLink); - if (hasDuplicateLink()) { - throw new IllegalStateException("Recipe already linked"); - } - - this.duplicateLink = duplicateLink; - this.duplicateLink.addDuplicate(this); - } - - public boolean hasDuplicateLink() { return duplicateLink != null; } @@ -58,13 +84,14 @@ public class RawRecipe { return duplicateLink; } - public void setTransformed(JsonObject transformedRecipe) { - Objects.requireNonNull(transformedRecipe); - if (isTransformed()) { - throw new IllegalStateException("Recipe already transformed"); + private void setDuplicateLink(@Nullable DuplicateLink duplicateLink) { + Objects.requireNonNull(duplicateLink); + if (hasDuplicateLink()) { + throw new IllegalStateException("Recipe already linked"); } - this.transformedRecipe = transformedRecipe; + this.duplicateLink = duplicateLink; + this.duplicateLink.addDuplicate(this); } @Nullable @@ -76,6 +103,14 @@ public class RawRecipe { return transformedRecipe != null; } + public void setTransformed(JsonObject transformedRecipe) { + Objects.requireNonNull(transformedRecipe); + if (isTransformed()) { + throw new IllegalStateException("Recipe already transformed"); + } + + this.transformedRecipe = transformedRecipe; + } private List getIgnoredFields() { return List.of("conditions"); @@ -85,33 +120,10 @@ public class RawRecipe { LinkedHashMap rules = new LinkedHashMap<>(); rules.put("experience", new JsonCompare.HigherRule()); rules.put("cookingtime", new JsonCompare.LowerRule()); + rules.put("energy", new JsonCompare.HigherRule()); return rules; } - @Nullable - public RawRecipe compare(RawRecipe toCompare) { - JsonObject selfActual = getTransformed() != null ? getTransformed() : originalRecipe; - JsonObject toCompareActual = toCompare.getTransformed() != null ? toCompare.getTransformed() - : toCompare.getOriginal(); - - if (JsonCompare.matches(selfActual, toCompareActual, getIgnoredFields())) { - JsonObject compare = JsonCompare.compare(getRules(), selfActual, toCompareActual); - if (compare == null) { - return null; - } - - if (compare == selfActual) { - return this; - } - - if (compare == toCompareActual) { - return toCompare; - } - } - - return null; - } - @Override public String toString() { String duplicate = duplicateLink != null ? " (duplicate)" : ""; @@ -119,22 +131,29 @@ public class RawRecipe { return String.format("['%s'] %s%s%s", type, id, duplicate, transformed); } - public boolean handleDuplicate(RawRecipe recipe) { + /** + * Checks for duplicate against given recipe data. If recipe data already has a duplicate link, + * the master from the link will be used. Otherwise, we will create a new link if needed. + * + * @param recipe Recipe data to check for duplicate against. + * @return True if recipe is a duplicate, false otherwise. + */ + public boolean handleDuplicate(RecipeLink recipe) { if (hasDuplicateLink()) { throw new IllegalStateException("Recipe already linked"); } DuplicateLink link = recipe.getDuplicateLink(); - if(link != null) { - RawRecipe compare = compare(link.getMaster()); - if(compare != null) { + if (link != null) { + RecipeLink compare = RecipeLink.compare(this, link.getMaster(), getRules(), getIgnoredFields()); + if (compare != null) { link.updateMaster(this); setDuplicateLink(link); return true; } } else { - RawRecipe compare = compare(recipe); - if(compare != null) { + RecipeLink compare = RecipeLink.compare(this, recipe, getRules(), getIgnoredFields()); + if (compare != null) { DuplicateLink newLink = new DuplicateLink(compare); setDuplicateLink(newLink); recipe.setDuplicateLink(newLink); @@ -145,29 +164,33 @@ public class RawRecipe { return false; } - public static class DuplicateLink { - private RawRecipe currentMaster; - private final Set recipes = new HashSet<>(); + public JsonObject getActual() { + return getTransformed() != null ? getTransformed() : getOriginal(); + } - private DuplicateLink(RawRecipe master) { + public static class DuplicateLink { + private final Set recipes = new HashSet<>(); + private RecipeLink currentMaster; + + private DuplicateLink(RecipeLink master) { updateMaster(master); } - private void updateMaster(RawRecipe master) { + private void updateMaster(RecipeLink master) { Objects.requireNonNull(master); addDuplicate(master); this.currentMaster = master; } - private void addDuplicate(RawRecipe recipe) { + private void addDuplicate(RecipeLink recipe) { recipes.add(recipe); } - public RawRecipe getMaster() { + public RecipeLink getMaster() { return currentMaster; } - public Set getRecipes() { + public Set getRecipes() { return Collections.unmodifiableSet(recipes); } @@ -175,5 +198,10 @@ public class RawRecipe { public String toString() { return "Link{currentMaster=" + currentMaster + ", recipes=" + recipes.size() + "}"; } + + public ResourceLocation createNewRecipeId() { + String id = String.format("%s_%s", currentMaster.getId().getNamespace(), currentMaster.getId().getPath()); + return new ResourceLocation(BuildConfig.MOD_ID, id); + } } } diff --git a/Common/src/main/java/com/almostreliable/unified/recipe/RecipeTransformationResult.java b/Common/src/main/java/com/almostreliable/unified/recipe/RecipeTransformationResult.java index 7f97d4b..7814610 100644 --- a/Common/src/main/java/com/almostreliable/unified/recipe/RecipeTransformationResult.java +++ b/Common/src/main/java/com/almostreliable/unified/recipe/RecipeTransformationResult.java @@ -23,17 +23,18 @@ public class RecipeTransformationResult { this.startTime = System.currentTimeMillis(); } - public void track(ResourceLocation recipe, JsonObject json, @Nullable JsonObject result) { - Entry entry = new Entry(json, result); - ResourceLocation type = entry.getType(); + // TODO refactor or remove and just use RawRecipe + public void track(RecipeLink recipe) { + Entry entry = new Entry(recipe.getOriginal(), recipe.getTransformed()); + ResourceLocation type = recipe.getType(); - if (allTrackedRecipes.contains(type, recipe)) { + if (allTrackedRecipes.contains(type, recipe.getId())) { throw new IllegalArgumentException("Already tracking " + type + ":" + recipe); } - allTrackedRecipes.put(type, recipe, entry); + allTrackedRecipes.put(type, recipe.getId(), entry); if (entry.isTransformed()) { - transformedRecipes.put(type, recipe, entry); + transformedRecipes.put(type, recipe.getId(), entry); } } diff --git a/Common/src/main/java/com/almostreliable/unified/recipe/RecipeTransformer.java b/Common/src/main/java/com/almostreliable/unified/recipe/RecipeTransformer.java index e97125d..1916c3c 100644 --- a/Common/src/main/java/com/almostreliable/unified/recipe/RecipeTransformer.java +++ b/Common/src/main/java/com/almostreliable/unified/recipe/RecipeTransformer.java @@ -8,12 +8,10 @@ import com.google.gson.JsonObject; import com.google.gson.JsonPrimitive; import net.minecraft.resources.ResourceLocation; -import javax.annotation.Nullable; -import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.Objects; -import java.util.concurrent.ConcurrentMap; +import java.util.Set; import java.util.stream.Collectors; public class RecipeTransformer { @@ -33,92 +31,87 @@ public class RecipeTransformer { return false; } + /** + * Transforms a map of recipes. This method will modify the map in-place. Part of the transformation is to unify recipes with the given {@link ReplacementMap}. + * After unification, recipes will be checked for duplicates. All duplicates will be removed from the map. + * + * @param recipes The map of recipes to transform. + * @return + */ public RecipeTransformationResult transformRecipes(Map recipes) { - ConcurrentMap> rawRecipesByType = recipes - .entrySet() - .parallelStream() - .filter(entry -> entry.getValue().isJsonObject() && hasValidType(entry.getValue().getAsJsonObject())) - .map(entry -> new RawRecipe(entry.getKey(), entry.getValue().getAsJsonObject())) - .collect(Collectors.groupingByConcurrent(RawRecipe::getType)); - - RecipeTransformationResult recipeTransformationResult = new RecipeTransformationResult(); - - Map> links = new HashMap<>(); - rawRecipesByType.forEach((type, rawRecipes) -> { - for (int curIndex = 0; curIndex < rawRecipes.size(); curIndex++) { - RawRecipe curRecipe = rawRecipes.get(curIndex); - JsonObject result = transformRecipe(curRecipe.getId(), curRecipe.getOriginal()); - if (result != null) { - recipeTransformationResult.track(curRecipe.getId(), curRecipe.getOriginal(), result); // TODO remove - - curRecipe.setTransformed(result); - handleDuplicate(curRecipe, rawRecipes); + AlmostUnified.LOG.warn("Recipe counts: " + recipes.size()); + RecipeTransformationResult rtr = new RecipeTransformationResult(); + Map> byType = groupRecipesByType(recipes); + byType.forEach((type, recipeLinks) -> { + Set duplicates = new HashSet<>(recipeLinks.size()); + for (RecipeLink curRecipe : recipeLinks) { + transformRecipe(curRecipe); + if (curRecipe.isTransformed()) { + recipes.put(curRecipe.getId(), curRecipe.getTransformed()); + if (handleDuplicate(curRecipe, recipeLinks)) { + duplicates.add(curRecipe.getDuplicateLink()); + } } + rtr.track(curRecipe); // TODO remove } - // TODO remove later - List duplicateLinks = rawRecipes - .stream() - .map(RawRecipe::getDuplicateLink) - .filter(Objects::nonNull) - .distinct() - .toList(); - if(duplicateLinks.size() > 0) { - links.put(type, duplicateLinks); + + for (RecipeLink.DuplicateLink link : duplicates) { + link.getRecipes().forEach(recipe -> recipes.remove(recipe.getId())); + recipes.put(link.createNewRecipeId(), link.getMaster().getActual()); } }); - - recipeTransformationResult.end(); - -// for (var entry : recipes.entrySet()) { -// if (!hasValidType(entry.getValue().getAsJsonObject())) { -// continue; -// } -// -// if (entry.getValue() instanceof JsonObject json) { -// JsonObject result = transformRecipe(entry.getKey(), json); -// recipeTransformationResult.track(entry.getKey(), json, result); -// if (result != null) { -// entry.setValue(result); -// } -// } -// } - return recipeTransformationResult; + rtr.end(); + AlmostUnified.LOG.warn("Recipe counts afterwards: " + recipes.size()); + return rtr; } - private void handleDuplicate(RawRecipe curRecipe, List rawRecipes) { - if(curRecipe.getDuplicateLink() != null) { + private Map> groupRecipesByType(Map recipes) { + return recipes + .entrySet() + .stream() + .filter(entry -> entry.getValue().isJsonObject() && hasValidType(entry.getValue().getAsJsonObject())) + .map(entry -> new RecipeLink(entry.getKey(), entry.getValue().getAsJsonObject())) + .collect(Collectors.groupingByConcurrent(RecipeLink::getType)); + } + + private boolean handleDuplicate(RecipeLink curRecipe, List recipes) { + if (curRecipe.getDuplicateLink() != null) { AlmostUnified.LOG.error("Duplication already handled for recipe {}", curRecipe.getId()); - return; + return false; } - for (RawRecipe rawRecipe : rawRecipes) { - if (rawRecipe == curRecipe) { - return; + for (RecipeLink recipeLink : recipes) { + if (recipeLink == curRecipe) { + continue; } - if (curRecipe.handleDuplicate(rawRecipe)) { - return; + if (curRecipe.handleDuplicate(recipeLink)) { + return true; } } + + return false; } - @Nullable - public JsonObject transformRecipe(ResourceLocation recipeId, JsonObject json) { + /** + * Transforms a single recipe link. This method will modify the recipe link in-place. + * {@link RecipeHandlerFactory} will apply multiple transformations onto the recipe. + * @param recipe The recipe link to transform. + */ + public void transformRecipe(RecipeLink recipe) { try { - RecipeContextImpl ctx = new RecipeContextImpl(json, replacementMap); + RecipeContextImpl ctx = new RecipeContextImpl(recipe.getOriginal(), replacementMap); RecipeTransformationBuilderImpl builder = new RecipeTransformationBuilderImpl(); factory.fillTransformations(builder, ctx); - JsonObject result = builder.transform(json, ctx); + JsonObject result = builder.transform(recipe.getOriginal(), ctx); if (result != null) { - return result; + recipe.setTransformed(result); } } catch (Exception e) { AlmostUnified.LOG.warn("Error transforming recipe '{}': {}", - recipeId, + recipe.getId(), e.getMessage()); e.printStackTrace(); } - return null; } - } diff --git a/Common/src/main/java/com/almostreliable/unified/utils/ReplacementMap.java b/Common/src/main/java/com/almostreliable/unified/utils/ReplacementMap.java index 6d2af1e..d3b925d 100644 --- a/Common/src/main/java/com/almostreliable/unified/utils/ReplacementMap.java +++ b/Common/src/main/java/com/almostreliable/unified/utils/ReplacementMap.java @@ -1,29 +1,54 @@ package com.almostreliable.unified.utils; import com.almostreliable.unified.AlmostUnified; -import com.almostreliable.unified.api.recipe.ReplacementFallbackStrategy; -import com.almostreliable.unified.recipe.fallbacks.StoneStrataFallbackStrategy; +import com.almostreliable.unified.AlmostUnifiedPlatform; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.item.Item; import javax.annotation.Nullable; import java.util.Collection; import java.util.List; +import java.util.function.Predicate; public class ReplacementMap { private final Collection modPriorities; + private final List stoneStrata; private final TagMap tagMap; - // TODO - In the future this may be a list of multiple fallbacks. - private final ReplacementFallbackStrategy fallbackStrategy = new StoneStrataFallbackStrategy(); - public ReplacementMap(TagMap tagMap, List modPriorities) { + public ReplacementMap(TagMap tagMap, List modPriorities, List stoneStrata) { this.tagMap = tagMap; this.modPriorities = modPriorities; + this.stoneStrata = stoneStrata; + } + + /** + * Returns the stone strata from given item. Method works on the requirement that it's an item which has a stone strata. + * Use {@link #isStoneStrataTag(UnifyTag)} to fill this requirement. + * + * @param item The item to get the stone strata from. + * @return The stone strata of the item. Returning empty string means clean-stone strata. + */ + private String getStoneStrata(ResourceLocation item) { + for (String stone : stoneStrata) { + if (item.getPath().startsWith(stone + "_")) { + return stone; + } + } + + return ""; + } + + private boolean isStoneStrataTag(UnifyTag tag) { + String tagString = tag.location().toString(); + return switch (AlmostUnifiedPlatform.INSTANCE.getPlatform()) { + case Forge -> tagString.startsWith("forge:ores/"); + case Fabric -> tagString.matches("c:ores/.+") || tagString.matches("c:.+_ore"); + }; } @Nullable - public UnifyTag getPreferredTag(ResourceLocation item) { + public UnifyTag getPreferredTagForItem(ResourceLocation item) { Collection> tags = tagMap.getTags(item); if (tags.isEmpty()) { @@ -42,44 +67,31 @@ public class ReplacementMap { @Nullable public ResourceLocation getReplacementForItem(ResourceLocation item) { - UnifyTag tag = getPreferredTag(item); - if (tag == null) { + UnifyTag t = getPreferredTagForItem(item); + if (t == null) { return null; } - ResourceLocation preferredItem = getPreferredItemByTag(tag, item.getNamespace()); - if (item.equals(preferredItem)) { - return null; + if (isStoneStrataTag(t)) { + String stone = getStoneStrata(item); + return getPreferredItemForTag(t, i -> stone.equals(getStoneStrata(i))); } - return preferredItem; + return getPreferredItemForTag(t, i -> true); } @Nullable - public ResourceLocation getPreferredItemByTag(UnifyTag tag) { - return getPreferredItemByTag(tag, null); - } + public ResourceLocation getPreferredItemForTag(UnifyTag tag, Predicate itemFilter) { + List items = tagMap + .getItems(tag) + .stream() + .filter(itemFilter) + .toList(); - @Nullable - public ResourceLocation getPreferredItemByTag(UnifyTag tag, @Nullable String ignoredNamespace) { - for (String mod : modPriorities) { - if (mod.equals(ignoredNamespace)) { - return null; - } - - List sameModItems = tagMap - .getItems(tag) - .stream() - .filter(i -> i.getNamespace().equals(mod)) - .toList(); - if (sameModItems.size() == 1) { - return sameModItems.get(0); - } - - if (sameModItems.size() > 1) { - ResourceLocation fallback = fallbackStrategy.getFallback(tag, sameModItems, tagMap); - if (fallback != null) { - return fallback; + for (String modPriority : modPriorities) { + for (ResourceLocation item : items) { + if (item.getNamespace().equals(modPriority)) { + return item; } } } diff --git a/Common/src/test/java/com/almostreliable/unified/TestUtils.java b/Common/src/test/java/com/almostreliable/unified/TestUtils.java index 1685c8e..83dc597 100644 --- a/Common/src/test/java/com/almostreliable/unified/TestUtils.java +++ b/Common/src/test/java/com/almostreliable/unified/TestUtils.java @@ -68,7 +68,7 @@ public class TestUtils { public static RecipeTransformer basicTransformer(Consumer consumer) { ReplacementMap map = new ReplacementMap(TagMapTests.testTagMap(), - TestUtils.TEST_MOD_PRIORITIES); + TestUtils.TEST_MOD_PRIORITIES, ModConfig.DEFAULT_STONE_STRATA); RecipeHandlerFactory factory = new RecipeHandlerFactory(); consumer.accept(factory); return new RecipeTransformer(factory, map); diff --git a/Common/src/test/java/com/almostreliable/unified/recipe/RecipeContextImplTest.java b/Common/src/test/java/com/almostreliable/unified/recipe/RecipeContextImplTest.java index d9667b1..281375f 100644 --- a/Common/src/test/java/com/almostreliable/unified/recipe/RecipeContextImplTest.java +++ b/Common/src/test/java/com/almostreliable/unified/recipe/RecipeContextImplTest.java @@ -1,5 +1,6 @@ package com.almostreliable.unified.recipe; +import com.almostreliable.unified.ModConfig; import com.almostreliable.unified.TestUtils; import com.almostreliable.unified.utils.ReplacementMap; import com.almostreliable.unified.utils.TagMapTests; @@ -7,8 +8,6 @@ import com.google.gson.Gson; import com.google.gson.JsonObject; import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.assertNotEquals; - public class RecipeContextImplTest { public static String mekaTest = """ { @@ -23,7 +22,7 @@ public class RecipeContextImplTest { public void depthReplace_MekaTest() { JsonObject json = new Gson().fromJson(mekaTest, JsonObject.class); ReplacementMap map = new ReplacementMap(TagMapTests.testTagMap(), - TestUtils.TEST_MOD_PRIORITIES); + TestUtils.TEST_MOD_PRIORITIES, ModConfig.DEFAULT_STONE_STRATA); // RecipeContextImpl context = new RecipeContextImpl(new ResourceLocation("test"), json, map); // JsonElement result = context.createResultReplacement(json.getAsJsonObject("output")); // assertNull(result); diff --git a/Common/src/test/java/com/almostreliable/unified/utils/ReplacementMapTests.java b/Common/src/test/java/com/almostreliable/unified/utils/ReplacementMapTests.java index 676d417..615c7b0 100644 --- a/Common/src/test/java/com/almostreliable/unified/utils/ReplacementMapTests.java +++ b/Common/src/test/java/com/almostreliable/unified/utils/ReplacementMapTests.java @@ -1,5 +1,6 @@ package com.almostreliable.unified.utils; +import com.almostreliable.unified.ModConfig; import com.almostreliable.unified.TestUtils; import com.google.common.collect.Lists; import net.minecraft.resources.ResourceLocation; @@ -14,34 +15,36 @@ public class ReplacementMapTests { @Test public void getPreferredItemByTag() { ReplacementMap map = new ReplacementMap(TagMapTests.testTagMap(), - TestUtils.TEST_MOD_PRIORITIES); - assertEquals(map.getPreferredItemByTag(TestUtils.BRONZE_ORES_TAG), TestUtils.mod1RL("bronze_ore")); - assertNotEquals(map.getPreferredItemByTag(TestUtils.BRONZE_ORES_TAG), TestUtils.mod2RL("bronze_ore")); - assertEquals(map.getPreferredItemByTag(TestUtils.INVAR_ORES_TAG), TestUtils.mod1RL("invar_ore")); - assertNotEquals(map.getPreferredItemByTag(TestUtils.INVAR_ORES_TAG), TestUtils.mod2RL("invar_ore")); - assertEquals(map.getPreferredItemByTag(TestUtils.TIN_ORES_TAG), TestUtils.mod3RL("tin_ore")); - assertNotEquals(map.getPreferredItemByTag(TestUtils.TIN_ORES_TAG), TestUtils.mod4RL("tin_ore")); - assertEquals(map.getPreferredItemByTag(TestUtils.SILVER_ORES_TAG), TestUtils.mod3RL("silver_ore")); - assertNotEquals(map.getPreferredItemByTag(TestUtils.SILVER_ORES_TAG), TestUtils.mod4RL("silver_ore")); + TestUtils.TEST_MOD_PRIORITIES, ModConfig.DEFAULT_STONE_STRATA); +// assertEquals(map.getPreferredItemByTag(TestUtils.BRONZE_ORES_TAG), TestUtils.mod1RL("bronze_ore")); +// assertNotEquals(map.getPreferredItemByTag(TestUtils.BRONZE_ORES_TAG), TestUtils.mod2RL("bronze_ore")); +// assertEquals(map.getPreferredItemByTag(TestUtils.INVAR_ORES_TAG), TestUtils.mod1RL("invar_ore")); +// assertNotEquals(map.getPreferredItemByTag(TestUtils.INVAR_ORES_TAG), TestUtils.mod2RL("invar_ore")); +// assertEquals(map.getPreferredItemByTag(TestUtils.TIN_ORES_TAG), TestUtils.mod3RL("tin_ore")); +// assertNotEquals(map.getPreferredItemByTag(TestUtils.TIN_ORES_TAG), TestUtils.mod4RL("tin_ore")); +// assertEquals(map.getPreferredItemByTag(TestUtils.SILVER_ORES_TAG), TestUtils.mod3RL("silver_ore")); +// assertNotEquals(map.getPreferredItemByTag(TestUtils.SILVER_ORES_TAG), TestUtils.mod4RL("silver_ore")); } @Test public void getPreferredItemByTag_ReversePriority() { // We reverse the order. See `testTagMap` for the mapping. List reverse = Lists.reverse(TestUtils.TEST_MOD_PRIORITIES); - ReplacementMap reverseMap = new ReplacementMap(TagMapTests.testTagMap(), reverse); - assertEquals(reverseMap.getPreferredItemByTag(TestUtils.BRONZE_ORES_TAG), TestUtils.mod3RL("bronze_ore")); - assertEquals(reverseMap.getPreferredItemByTag(TestUtils.INVAR_ORES_TAG), TestUtils.mod4RL("invar_ore")); - assertEquals(reverseMap.getPreferredItemByTag(TestUtils.TIN_ORES_TAG), TestUtils.mod4RL("tin_ore")); - assertEquals(reverseMap.getPreferredItemByTag(TestUtils.SILVER_ORES_TAG), TestUtils.mod5RL("silver_ore")); + ReplacementMap reverseMap = new ReplacementMap(TagMapTests.testTagMap(), + reverse, + ModConfig.DEFAULT_STONE_STRATA); +// assertEquals(reverseMap.getPreferredItemByTag(TestUtils.BRONZE_ORES_TAG), TestUtils.mod3RL("bronze_ore")); +// assertEquals(reverseMap.getPreferredItemByTag(TestUtils.INVAR_ORES_TAG), TestUtils.mod4RL("invar_ore")); +// assertEquals(reverseMap.getPreferredItemByTag(TestUtils.TIN_ORES_TAG), TestUtils.mod4RL("tin_ore")); +// assertEquals(reverseMap.getPreferredItemByTag(TestUtils.SILVER_ORES_TAG), TestUtils.mod5RL("silver_ore")); } @Test public void getPreferredTag() { ReplacementMap map = new ReplacementMap(TagMapTests.testTagMap(), - TestUtils.TEST_MOD_PRIORITIES); + TestUtils.TEST_MOD_PRIORITIES, ModConfig.DEFAULT_STONE_STRATA); - assertEquals(map.getPreferredTag(TestUtils.mod1RL("bronze_ore")), TestUtils.BRONZE_ORES_TAG); - assertNull(map.getPreferredTag(new ResourceLocation("minecraft:diamond")), "We don't have a tag for diamond"); + assertEquals(map.getPreferredTagForItem(TestUtils.mod1RL("bronze_ore")), TestUtils.BRONZE_ORES_TAG); + assertNull(map.getPreferredTagForItem(new ResourceLocation("minecraft:diamond")), "We don't have a tag for diamond"); } } diff --git a/Fabric/src/main/java/com/almostreliable/unified/AlmostUnifiedPlatformFabric.java b/Fabric/src/main/java/com/almostreliable/unified/AlmostUnifiedPlatformFabric.java index 267b173..20bec18 100644 --- a/Fabric/src/main/java/com/almostreliable/unified/AlmostUnifiedPlatformFabric.java +++ b/Fabric/src/main/java/com/almostreliable/unified/AlmostUnifiedPlatformFabric.java @@ -8,8 +8,8 @@ import java.nio.file.Path; public class AlmostUnifiedPlatformFabric implements AlmostUnifiedPlatform { @Override - public String getPlatformName() { - return "Fabric"; + public Platform getPlatform() { + return Platform.Fabric; } @Override diff --git a/Forge/src/main/java/com/almostreliable/unified/AlmostUnifiedPlatformForge.java b/Forge/src/main/java/com/almostreliable/unified/AlmostUnifiedPlatformForge.java index 9418e7d..41569b1 100644 --- a/Forge/src/main/java/com/almostreliable/unified/AlmostUnifiedPlatformForge.java +++ b/Forge/src/main/java/com/almostreliable/unified/AlmostUnifiedPlatformForge.java @@ -12,8 +12,8 @@ import java.nio.file.Path; public class AlmostUnifiedPlatformForge implements AlmostUnifiedPlatform { @Override - public String getPlatformName() { - return "Forge"; + public Platform getPlatform() { + return Platform.Forge; } @Override diff --git a/Forge/src/main/java/com/almostreliable/unified/compat/ie/IERecipeHandler.java b/Forge/src/main/java/com/almostreliable/unified/compat/ie/IERecipeHandler.java index fbbf10a..23eaa3a 100644 --- a/Forge/src/main/java/com/almostreliable/unified/compat/ie/IERecipeHandler.java +++ b/Forge/src/main/java/com/almostreliable/unified/compat/ie/IERecipeHandler.java @@ -53,7 +53,7 @@ public class IERecipeHandler implements RecipeHandler { */ ResourceLocation item = context.getPreferredItemByTag(Utils.toItemTag(object .get(RecipeConstants.TAG) - .getAsString())); + .getAsString()), $ -> true); if (item != null) { object.remove(RecipeConstants.TAG); object.addProperty(RecipeConstants.ITEM, item.toString()); diff --git a/Forge/src/test/java/com/almostreliable/unified/compat/ie/IERecipeHandlerTest.java b/Forge/src/test/java/com/almostreliable/unified/compat/ie/IERecipeHandlerTest.java index 228753c..91b17ff 100644 --- a/Forge/src/test/java/com/almostreliable/unified/compat/ie/IERecipeHandlerTest.java +++ b/Forge/src/test/java/com/almostreliable/unified/compat/ie/IERecipeHandlerTest.java @@ -2,6 +2,7 @@ package com.almostreliable.unified.compat.ie; import com.almostreliable.unified.TestUtils; import com.almostreliable.unified.api.ModConstants; +import com.almostreliable.unified.recipe.RecipeLink; import com.almostreliable.unified.recipe.RecipeTransformer; import com.almostreliable.unified.utils.JsonQuery; import com.google.gson.Gson; @@ -30,8 +31,9 @@ public class IERecipeHandlerTest { RecipeTransformer transformer = TestUtils.basicTransformer(f -> f.registerForMod(ModConstants.IE, new IERecipeHandler())); JsonObject alloy = gson.fromJson(simpleAlloyRecipe, JsonObject.class); - JsonObject result = transformer.transformRecipe(defaultRecipeId, alloy); - assertNull(result, "Nothing to transform, so it should be null"); + RecipeLink recipe = new RecipeLink(defaultRecipeId, alloy); + transformer.transformRecipe(recipe); + assertFalse(recipe.isTransformed(), "Nothing to transform, so it should be false"); } @Test @@ -43,11 +45,13 @@ public class IERecipeHandlerTest { .getAsJsonObject("result") .getAsJsonObject("base_ingredient") .addProperty("tag", TestUtils.BRONZE_ORES_TAG.location().toString()); - JsonObject result = transformer.transformRecipe(defaultRecipeId, alloy); - assertNotEquals(result, alloy, "Result should be different"); - assertNotNull(result, "Result should not be null"); - assertNull(JsonQuery.of(result, "result/base_ingredient/tag"), "Tag key should be removed"); - assertEquals(JsonQuery.of(result, "result/base_ingredient/item").asString(), + RecipeLink recipe = new RecipeLink(defaultRecipeId, alloy); + transformer.transformRecipe(recipe); + + assertNotEquals(recipe.getTransformed(), alloy, "Result should be different"); + assertNotNull(recipe.getTransformed(), "Result should not be null"); + assertNull(JsonQuery.of(recipe.getTransformed(), "result/base_ingredient/tag"), "Tag key should be removed"); + assertEquals(JsonQuery.of(recipe.getTransformed(), "result/base_ingredient/item").asString(), TestUtils.mod1RL("bronze_ore").toString(), "Result should be bronze_ore"); } @@ -62,10 +66,12 @@ public class IERecipeHandlerTest { .getAsJsonObject("result") .getAsJsonObject("base_ingredient") .addProperty("item", TestUtils.mod3RL("bronze_ore").toString()); - JsonObject result = transformer.transformRecipe(defaultRecipeId, alloy); - assertNotEquals(result, alloy, "Result should be different"); - assertNotNull(result, "Result should not be null"); - assertEquals(JsonQuery.of(result, ("result/base_ingredient/item")).asString(), + RecipeLink recipe = new RecipeLink(defaultRecipeId, alloy); + transformer.transformRecipe(recipe); + + assertNotEquals(recipe.getTransformed(), alloy, "Result should be different"); + assertNotNull(recipe.getTransformed(), "Result should not be null"); + assertEquals(JsonQuery.of(recipe.getTransformed(), ("result/base_ingredient/item")).asString(), TestUtils.mod1RL("bronze_ore").toString(), "Transformer should replace bronze_ore from mod3 with bronze_ore from mod1"); } @@ -80,10 +86,12 @@ public class IERecipeHandlerTest { .getAsJsonObject("result") .getAsJsonObject("base_ingredient") .addProperty("item", TestUtils.mod3RL("bronze_ore").toString()); - JsonObject result = transformer.transformRecipe(defaultRecipeId, alloy); - assertNotEquals(result, alloy, "Result should be different"); - assertNotNull(result, "Result should not be null"); - assertEquals(JsonQuery.of(result, ("result/base_ingredient/item")).asString(), + RecipeLink recipe = new RecipeLink(defaultRecipeId, alloy); + transformer.transformRecipe(recipe); + + assertNotEquals(recipe.getTransformed(), alloy, "Result should be different"); + assertNotNull(recipe.getTransformed(), "Result should not be null"); + assertEquals(JsonQuery.of(recipe.getTransformed(), ("result/base_ingredient/item")).asString(), TestUtils.mod1RL("bronze_ore").toString(), "Transformer should replace bronze_ore from mod3 with bronze_ore from mod1"); }