Implement forge:conditionals type unify

This commit is contained in:
LLytho 2022-07-31 16:20:42 +02:00
parent b36227dc2b
commit 7fe1f6254f
4 changed files with 94 additions and 55 deletions

View file

@ -5,15 +5,20 @@ import com.almostreliable.unified.config.DuplicationConfig;
import com.almostreliable.unified.config.UnifyConfig;
import com.almostreliable.unified.recipe.unifier.RecipeHandlerFactory;
import com.almostreliable.unified.utils.JsonCompare;
import com.almostreliable.unified.utils.JsonQuery;
import com.almostreliable.unified.utils.ReplacementMap;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
import net.minecraft.resources.ResourceLocation;
import javax.annotation.Nullable;
import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.stream.Collectors;
public class RecipeTransformer {
@ -50,29 +55,67 @@ public class RecipeTransformer {
Result result = new Result();
Map<ResourceLocation, List<RecipeLink>> byType = groupRecipesByType(recipes);
byType.forEach((type, recipeLinks) -> {
Set<RecipeLink.DuplicateLink> duplicates = new HashSet<>(recipeLinks.size());
for (RecipeLink curRecipe : recipeLinks) {
unifyRecipe(curRecipe);
if (curRecipe.isUnified()) {
recipes.put(curRecipe.getId(), curRecipe.getUnified());
if (handleDuplicate(curRecipe, recipeLinks)) {
duplicates.add(curRecipe.getDuplicateLink());
}
}
result.add(curRecipe);
}
for (RecipeLink.DuplicateLink link : duplicates) {
link.getRecipes().forEach(recipe -> recipes.remove(recipe.getId()));
recipes.put(link.createNewRecipeId(), link.getMaster().getActual());
ResourceLocation fcLocation = new ResourceLocation("forge:conditional");
byType.forEach((type, recipeLinks) -> {
if (type.equals(fcLocation)) {
recipeLinks.forEach(recipeLink -> handleForgeConditionals(recipeLink).ifPresent(json -> recipes.put(
recipeLink.getId(),
json)));
} else {
transformRecipes(recipeLinks, recipes::put, recipes::remove);
}
});
byType.values().stream().flatMap(Collection::stream).forEach(result::add);
AlmostUnified.LOG.warn("Recipe counts afterwards: " + recipes.size());
return result;
}
private Optional<JsonObject> handleForgeConditionals(RecipeLink recipeLink) {
JsonObject copy = recipeLink.getOriginal().deepCopy();
if (copy.get("recipes") instanceof JsonArray recipes) {
for (JsonElement element : recipes) {
JsonQuery
.of(element, "recipe")
.asObject()
.map(jsonObject -> new RecipeLink(recipeLink.getId(), jsonObject))
.ifPresent(temporaryLink -> {
unifyRecipe(temporaryLink);
if (temporaryLink.isUnified()) {
element.getAsJsonObject().add("recipe", temporaryLink.getUnified());
}
});
}
if (!copy.equals(recipeLink.getOriginal())) {
recipeLink.setUnified(copy);
return Optional.of(copy);
}
}
return Optional.empty();
}
private void transformRecipes(List<RecipeLink> recipeLinks, BiConsumer<ResourceLocation, JsonElement> onAdd, Consumer<ResourceLocation> onRemove) {
Set<RecipeLink.DuplicateLink> duplicates = new HashSet<>(recipeLinks.size());
for (RecipeLink curRecipe : recipeLinks) {
unifyRecipe(curRecipe);
if (curRecipe.isUnified()) {
onAdd.accept(curRecipe.getId(), curRecipe.getUnified());
if (handleDuplicate(curRecipe, recipeLinks)) {
duplicates.add(curRecipe.getDuplicateLink());
}
}
}
for (RecipeLink.DuplicateLink link : duplicates) {
link.getRecipes().forEach(recipe -> onRemove.accept(recipe.getId()));
onAdd.accept(link.createNewRecipeId(), link.getMaster().getActual());
}
}
public Map<ResourceLocation, List<RecipeLink>> groupRecipesByType(Map<ResourceLocation, JsonElement> recipes) {
return recipes
.entrySet()
@ -83,8 +126,7 @@ public class RecipeTransformer {
}
private boolean includeRecipe(ResourceLocation recipe, JsonElement json) {
return unifyConfig.includeRecipe(recipe) && json.isJsonObject() &&
hasValidType(json.getAsJsonObject());
return unifyConfig.includeRecipe(recipe) && json.isJsonObject() && hasValidType(json.getAsJsonObject());
}
private boolean handleDuplicate(RecipeLink curRecipe, List<RecipeLink> recipes) {
@ -132,9 +174,7 @@ public class RecipeTransformer {
recipe.setUnified(result);
}
} catch (Exception e) {
AlmostUnified.LOG.warn("Error unifying recipe '{}': {}",
recipe.getId(),
e.getMessage());
AlmostUnified.LOG.warn("Error unifying recipe '{}': {}", recipe.getId(), e.getMessage());
e.printStackTrace();
}
}

View file

@ -5,14 +5,21 @@ import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import org.apache.commons.lang3.StringUtils;
import javax.annotation.Nullable;
import java.util.Optional;
public class JsonQuery {
private final JsonElement element;
@Nullable private final JsonElement element;
JsonQuery(JsonElement element) {
JsonQuery(@Nullable JsonElement element) {
this.element = element;
}
JsonQuery() {
this.element = null;
}
public static JsonQuery of(JsonElement element) {
return new JsonQuery(element);
}
@ -31,56 +38,45 @@ public class JsonQuery {
}
public JsonQuery get(String identifier) {
if (!element.isJsonObject()) {
throw new IllegalArgumentException("Expected JsonObject, got " + element.getClass());
if (element instanceof JsonObject json) {
JsonElement child = json.get(identifier);
if (child != null) {
return new JsonQuery(child);
}
}
JsonElement child = element.getAsJsonObject().get(identifier);
if (child == null) {
return null;
}
return new JsonQuery(child);
return new JsonQuery();
}
public JsonQuery get(int index) {
if (!element.isJsonArray()) {
throw new IllegalArgumentException("Expected JsonArray, got " + element.getClass());
if (element instanceof JsonArray json && 0 <= index && index < json.size()) {
JsonElement child = json.get(index);
if (child != null) {
return new JsonQuery(child);
}
}
JsonElement child = element.getAsJsonArray().get(index);
if (child == null) {
return null;
}
return new JsonQuery(child);
return new JsonQuery();
}
public JsonQuery get(String identifier, int index) {
return get(identifier).get(index);
}
public JsonObject asObject() {
return element.getAsJsonObject();
public Optional<JsonElement> asElement() {
return Optional.ofNullable(element);
}
public JsonArray asArray() {
return element.getAsJsonArray();
public Optional<JsonObject> asObject() {
return asElement().filter(JsonObject.class::isInstance).map(JsonObject.class::cast);
}
public String asString() {
return element.getAsString();
public Optional<JsonArray> asArray() {
return asElement().filter(JsonArray.class::isInstance).map(JsonArray.class::cast);
}
public int asInt() {
return element.getAsInt();
}
public boolean asBoolean() {
return element.getAsBoolean();
}
public float asFloat() {
return element.getAsFloat();
public Optional<String> asString() {
return asElement().filter(JsonElement::isJsonPrimitive).map(JsonElement::getAsString);
}
}

View file

@ -8,6 +8,7 @@ import net.minecraft.world.item.Item;
import javax.annotation.Nullable;
import java.util.Collection;
import java.util.Comparator;
import java.util.List;
import java.util.function.Predicate;
@ -84,6 +85,8 @@ public class ReplacementMap {
.getItems(tag)
.stream()
.filter(itemFilter)
// Helps us to get the clean stone variant first in case of a stone strata tag
.sorted(Comparator.comparingInt(value -> value.toString().length()))
.toList();
for (String modPriority : unifyConfig.getModPriorities()) {

View file

@ -80,7 +80,7 @@ public class TestUtils {
ReplacementMap map = new ReplacementMap(TagMapTests.testTagMap(), DEFAULT_UNIFY_CONFIG);
RecipeHandlerFactory factory = new RecipeHandlerFactory();
consumer.accept(factory);
return new RecipeTransformer(factory, map, DEFAULT_UNIFY_CONFIG, duplicationConfig);
return new RecipeTransformer(factory, map, DEFAULT_UNIFY_CONFIG, null);
}
public static JsonObject json(String json) {