mirror of
https://github.com/AlmostReliable/almostunified.git
synced 2024-11-28 10:35:38 -05:00
Implement RecipeTransformations
This commit is contained in:
parent
328a193421
commit
a2d51836c6
10 changed files with 217 additions and 138 deletions
|
@ -1,7 +1,8 @@
|
|||
package com.almostreliable.unified;
|
||||
|
||||
import com.almostreliable.unified.api.RecipeHandler;
|
||||
import com.almostreliable.unified.handler.RecipeHandlerFactory;
|
||||
import com.google.common.collect.HashMultimap;
|
||||
import com.google.common.collect.Multimap;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
@ -34,7 +35,7 @@ public abstract class AlmostUnifiedRuntime {
|
|||
}
|
||||
|
||||
public void transformRecipes(Map<ResourceLocation, JsonElement> recipes, ReplacementMap replacementMap) {
|
||||
Map<ResourceLocation, Integer> typeCount = new HashMap<>();
|
||||
Multimap<ResourceLocation, ResourceLocation> typeCount = HashMultimap.create();
|
||||
|
||||
int transformedRecipes = 0;
|
||||
long start = System.nanoTime();
|
||||
|
@ -47,7 +48,7 @@ public abstract class AlmostUnifiedRuntime {
|
|||
|
||||
// TODO for debugging remove this later
|
||||
ResourceLocation recipeType = getRecipeType(json);
|
||||
typeCount.compute(recipeType, (rl, old) -> old == null ? 1 : old + 1);
|
||||
typeCount.put(recipeType, entry.getKey());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -57,8 +58,12 @@ public abstract class AlmostUnifiedRuntime {
|
|||
transformedRecipes,
|
||||
recipes.size(),
|
||||
timeElapsed / 1000_000D);
|
||||
typeCount.entrySet().stream().sorted(Comparator.comparing(o -> o.getKey().toString())).forEach(entry -> {
|
||||
AlmostUnified.LOG.info("{}: {}", StringUtils.leftPad(entry.getKey().toString(), 50), entry.getValue());
|
||||
// TODO Pls remove this on release
|
||||
typeCount.asMap().entrySet().stream().sorted(Comparator.comparing(o -> o.getKey().toString())).forEach((e) -> {
|
||||
AlmostUnified.LOG.info("{}: {} | {}",
|
||||
StringUtils.leftPad(e.getKey().toString(), 40),
|
||||
StringUtils.leftPad(String.valueOf(e.getValue().size()), 4),
|
||||
e.getValue().toString());
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -69,16 +74,12 @@ public abstract class AlmostUnifiedRuntime {
|
|||
return null;
|
||||
}
|
||||
|
||||
RecipeContextImpl ctx = new RecipeContextImpl(recipeType, id, json, replacementMap);
|
||||
|
||||
try {
|
||||
RecipeHandler recipeHandler = recipeHandlerFactory.create(ctx);
|
||||
if (recipeHandler == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
RecipeContextImpl ctx = new RecipeContextImpl(recipeType, id, json, replacementMap);
|
||||
RecipeTransformationsImpl builder = new RecipeTransformationsImpl();
|
||||
recipeHandlerFactory.create(builder, ctx);
|
||||
JsonObject copy = json.deepCopy();
|
||||
recipeHandler.transformRecipe(copy, ctx);
|
||||
builder.transform(copy, ctx);
|
||||
if (!json.equals(copy)) {
|
||||
return copy;
|
||||
}
|
||||
|
@ -92,7 +93,6 @@ public abstract class AlmostUnifiedRuntime {
|
|||
return null;
|
||||
}
|
||||
|
||||
|
||||
@Nullable
|
||||
protected ResourceLocation getRecipeType(JsonObject recipeJson) {
|
||||
String type = recipeJson.get("type").getAsString();
|
||||
|
@ -108,7 +108,6 @@ public abstract class AlmostUnifiedRuntime {
|
|||
throw new IllegalStateException("Internal error. TagManager was not updated correctly");
|
||||
}
|
||||
|
||||
|
||||
TagMap tagMap = TagMap.create(tagManager);
|
||||
Map<ResourceLocation, TagKey<Item>> itemToTagMapping = new HashMap<>(allowedTags.size());
|
||||
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
package com.almostreliable.unified;
|
||||
|
||||
import com.almostreliable.unified.api.RecipeContext;
|
||||
import com.almostreliable.unified.utils.JsonUtils;
|
||||
import com.almostreliable.unified.handler.RecipeConstants;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonPrimitive;
|
||||
|
@ -10,11 +11,10 @@ import net.minecraft.tags.TagKey;
|
|||
import net.minecraft.world.item.Item;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.function.UnaryOperator;
|
||||
|
||||
@SuppressWarnings("SameParameterValue")
|
||||
public class RecipeContextImpl implements RecipeContext {
|
||||
public static final String TAG = "tag";
|
||||
public static final String ITEM = "item";
|
||||
private final ResourceLocation type;
|
||||
private final ResourceLocation id;
|
||||
private final JsonObject currentRecipe;
|
||||
|
@ -53,41 +53,55 @@ public class RecipeContextImpl implements RecipeContext {
|
|||
return replacementMap.getPreferredTag(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean replaceIngredient(JsonElement element) {
|
||||
AtomicBoolean changed = new AtomicBoolean(false);
|
||||
@Nullable
|
||||
protected JsonElement depthReplace(JsonElement element, String potentialFrom, String potentialTo, UnaryOperator<JsonPrimitive> primitiveCallback) {
|
||||
if (element instanceof JsonPrimitive primitive) {
|
||||
return primitiveCallback.apply(primitive);
|
||||
}
|
||||
|
||||
JsonUtils.arrayForEach(element, JsonObject.class, json -> {
|
||||
if (json.get(ITEM) instanceof JsonPrimitive primitive) {
|
||||
if (element instanceof JsonObject object) {
|
||||
JsonElement replace = depthReplace(object.get(potentialFrom), potentialFrom, potentialTo, primitiveCallback);
|
||||
if (replace != null) {
|
||||
object.remove(potentialFrom);
|
||||
object.add(potentialTo, replace);
|
||||
}
|
||||
}
|
||||
|
||||
if (element instanceof JsonArray array) {
|
||||
for (int i = 0; i < array.size(); i++) {
|
||||
JsonElement replace = depthReplace(array.get(i), potentialFrom, potentialTo, primitiveCallback);
|
||||
if (replace != null) {
|
||||
array.set(i, replace);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public JsonElement replaceIngredient(JsonElement element) {
|
||||
return depthReplace(element, RecipeConstants.ITEM, RecipeConstants.TAG, primitive -> {
|
||||
ResourceLocation item = ResourceLocation.tryParse(primitive.getAsString());
|
||||
TagKey<Item> tag = getPreferredTagByItem(item);
|
||||
if (tag != null) {
|
||||
json.remove(ITEM);
|
||||
json.addProperty(TAG, tag.location().toString());
|
||||
changed.set(true);
|
||||
}
|
||||
return new JsonPrimitive(tag.location().toString());
|
||||
}
|
||||
return null;
|
||||
});
|
||||
|
||||
return changed.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean replaceResult(JsonElement element) {
|
||||
AtomicBoolean changed = new AtomicBoolean(false);
|
||||
|
||||
JsonUtils.arrayForEach(element, JsonObject.class, json -> {
|
||||
if (json.get(ITEM) instanceof JsonPrimitive primitive) {
|
||||
public JsonElement replaceResult(JsonElement element) {
|
||||
return depthReplace(element, RecipeConstants.ITEM, RecipeConstants.ITEM, primitive -> {
|
||||
ResourceLocation item = ResourceLocation.tryParse(primitive.getAsString());
|
||||
ResourceLocation replacement = getReplacementForItem(item);
|
||||
if (replacement != null) {
|
||||
json.addProperty(ITEM, replacement.toString());
|
||||
changed.set(true);
|
||||
}
|
||||
return new JsonPrimitive(replacement.toString());
|
||||
}
|
||||
return null;
|
||||
});
|
||||
|
||||
return changed.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
package com.almostreliable.unified;
|
||||
|
||||
import com.almostreliable.unified.api.RecipeContext;
|
||||
import com.almostreliable.unified.api.RecipeTransformations;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
public class RecipeTransformationsImpl implements RecipeTransformations {
|
||||
private final Map<String, Entry<?>> consumers = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public void forEachObject(String property, BiFunction<JsonObject, RecipeContext, JsonObject> consumer) {
|
||||
BiFunction<JsonArray, RecipeContext, JsonArray> arrayConsumer = (array, ctx) -> {
|
||||
for (int i = 0; i < array.size(); i++) {
|
||||
JsonElement element = array.get(i);
|
||||
if (element instanceof JsonObject obj) {
|
||||
JsonObject result = consumer.apply(obj, ctx);
|
||||
if (result != null) {
|
||||
array.set(i, result);
|
||||
}
|
||||
}
|
||||
}
|
||||
return array;
|
||||
};
|
||||
|
||||
put(property, JsonArray.class, arrayConsumer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void replaceIngredient(String property) {
|
||||
put(property, JsonElement.class, (jsonElement, context) -> context.replaceIngredient(jsonElement));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void replaceResult(String property) {
|
||||
put(property, JsonElement.class, (jsonElement, context) -> context.replaceResult(jsonElement));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void put(String property, BiFunction<JsonElement, RecipeContext, JsonElement> consumer) {
|
||||
consumers.put(property, new Entry<>(JsonElement.class, consumer));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends JsonElement> void put(String property, Class<T> type, BiFunction<T, RecipeContext, T> consumer) {
|
||||
consumers.put(property, new Entry<>(type, consumer));
|
||||
}
|
||||
|
||||
public void transform(JsonObject json, RecipeContext context) {
|
||||
for (var e : json.entrySet()) {
|
||||
Entry<?> consumer = consumers.get(e.getKey());
|
||||
if (consumer != null) {
|
||||
JsonElement result = consumer.apply(e.getValue(), context);
|
||||
if (result != null) {
|
||||
e.setValue(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private record Entry<T extends JsonElement>(Class<T> expectedType,
|
||||
BiFunction<T, RecipeContext, T> func) {
|
||||
@Nullable
|
||||
T apply(JsonElement json, RecipeContext context) {
|
||||
if (expectedType.isInstance(json)) {
|
||||
return func.apply(expectedType.cast(json), context);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -18,16 +18,14 @@ public interface RecipeContext {
|
|||
@Nullable
|
||||
TagKey<Item> getPreferredTagByItem(@Nullable ResourceLocation item);
|
||||
|
||||
boolean replaceIngredient(JsonElement element);
|
||||
JsonElement replaceIngredient(JsonElement element);
|
||||
|
||||
boolean replaceResult(JsonElement element);
|
||||
JsonElement replaceResult(JsonElement element);
|
||||
|
||||
ResourceLocation getType();
|
||||
|
||||
ResourceLocation getId();
|
||||
|
||||
// String getIterateProperty();
|
||||
|
||||
boolean hasProperty(String property);
|
||||
|
||||
default String getModId() {
|
||||
|
|
|
@ -1,13 +1,5 @@
|
|||
package com.almostreliable.unified.api;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface RecipeHandler {
|
||||
|
||||
void transformRecipe(JsonObject json, RecipeContext context);
|
||||
|
||||
default String getName() {
|
||||
return getClass().getSimpleName();
|
||||
}
|
||||
void collectTransformations(RecipeTransformations builder);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
package com.almostreliable.unified.api;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
public interface RecipeTransformations {
|
||||
void forEachObject(String property, BiFunction<JsonObject, RecipeContext, JsonObject> consumer);
|
||||
|
||||
void replaceIngredient(String property);
|
||||
|
||||
void replaceResult(String property);
|
||||
|
||||
void put(String property, BiFunction<JsonElement, RecipeContext, JsonElement> consumer);
|
||||
|
||||
<T extends JsonElement> void put(String property, Class<T> type, BiFunction<T, RecipeContext, T> consumer);
|
||||
}
|
|
@ -1,11 +1,7 @@
|
|||
package com.almostreliable.unified.handler;
|
||||
|
||||
import com.almostreliable.unified.api.RecipeContext;
|
||||
import com.almostreliable.unified.api.RecipeHandler;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonPrimitive;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import com.almostreliable.unified.api.RecipeTransformations;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
|
@ -18,42 +14,14 @@ public class GenericRecipeHandler implements RecipeHandler {
|
|||
RecipeConstants.RESULT,
|
||||
RecipeConstants.RESULTS);
|
||||
|
||||
public boolean hasInputOrOutputProperty(RecipeContext property) {
|
||||
for (String inputKey : inputKeys) {
|
||||
if (property.hasProperty(inputKey)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
for (String outputKey : outputKeys) {
|
||||
if (property.hasProperty(outputKey)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void transformRecipe(JsonObject json, RecipeContext context) {
|
||||
public void collectTransformations(RecipeTransformations builder) {
|
||||
for (String inputKey : inputKeys) {
|
||||
if (json.has(inputKey)) {
|
||||
context.replaceIngredient(json.get(inputKey));
|
||||
}
|
||||
builder.replaceIngredient(inputKey);
|
||||
}
|
||||
|
||||
for (String outputKey : outputKeys) {
|
||||
JsonElement jsonElement = json.get(outputKey);
|
||||
if (jsonElement instanceof JsonPrimitive) {
|
||||
ResourceLocation item = context.getReplacementForItem(ResourceLocation.tryParse(jsonElement.getAsString()));
|
||||
if (item != null) {
|
||||
json.addProperty(outputKey, item.toString());
|
||||
}
|
||||
} else if (jsonElement != null) {
|
||||
// Forge patched recipe results to also allow JsonObjects. See SimpleCookingSerializer::fromJson as an example.
|
||||
context.replaceResult(jsonElement);
|
||||
}
|
||||
|
||||
builder.replaceResult(outputKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,9 +2,9 @@ package com.almostreliable.unified.handler;
|
|||
|
||||
import com.almostreliable.unified.api.RecipeContext;
|
||||
import com.almostreliable.unified.api.RecipeHandler;
|
||||
import com.almostreliable.unified.api.RecipeTransformations;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
|
@ -12,32 +12,23 @@ public class RecipeHandlerFactory {
|
|||
private final Map<ResourceLocation, RecipeHandler> transformersByType = new HashMap<>();
|
||||
private final Map<String, RecipeHandler> transformersByModId = new HashMap<>();
|
||||
|
||||
@Nullable
|
||||
public RecipeHandler create(RecipeContext context) {
|
||||
public void create(RecipeTransformations builder, RecipeContext context) {
|
||||
GenericRecipeHandler.INSTANCE.collectTransformations(builder);
|
||||
|
||||
if (context.hasProperty(ShapedRecipeKeyHandler.PATTERN_PROPERTY) &&
|
||||
context.hasProperty(ShapedRecipeKeyHandler.KEY_PROPERTY)) {
|
||||
ShapedRecipeKeyHandler.INSTANCE.collectTransformations(builder);
|
||||
}
|
||||
|
||||
RecipeHandler byType = transformersByType.get(context.getType());
|
||||
if (byType != null) {
|
||||
return byType;
|
||||
byType.collectTransformations(builder);
|
||||
}
|
||||
|
||||
RecipeHandler byMod = transformersByModId.get(context.getModId());
|
||||
if (byMod != null) {
|
||||
return byMod;
|
||||
byMod.collectTransformations(builder);
|
||||
}
|
||||
|
||||
if (context.hasProperty(ShapedRecipeKeyHandler.PATTERN_PROPERTY) &&
|
||||
context.hasProperty(ShapedRecipeKeyHandler.KEY_PROPERTY)) {
|
||||
return ShapedRecipeKeyHandler.INSTANCE;
|
||||
}
|
||||
|
||||
if (GenericRecipeHandler.INSTANCE.hasInputOrOutputProperty(context)) {
|
||||
return GenericRecipeHandler.INSTANCE;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public void registerForType(String type, RecipeHandler transformer) {
|
||||
transformersByType.put(new ResourceLocation(type), transformer);
|
||||
}
|
||||
|
||||
public void registerForType(ResourceLocation type, RecipeHandler transformer) {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package com.almostreliable.unified.handler;
|
||||
|
||||
import com.almostreliable.unified.api.RecipeContext;
|
||||
import com.almostreliable.unified.api.RecipeHandler;
|
||||
import com.almostreliable.unified.api.RecipeTransformations;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
|
@ -11,16 +11,15 @@ public class ShapedRecipeKeyHandler implements RecipeHandler {
|
|||
public static final String KEY_PROPERTY = "key";
|
||||
|
||||
@Override
|
||||
public void transformRecipe(JsonObject json, RecipeContext context) {
|
||||
if (json.get(KEY_PROPERTY) instanceof JsonObject object) {
|
||||
for (var entry : object.entrySet()) {
|
||||
context.replaceIngredient(entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
JsonElement result = json.get("result");
|
||||
public void collectTransformations(RecipeTransformations builder) {
|
||||
builder.put(KEY_PROPERTY, JsonObject.class, (json, context) -> {
|
||||
for (var entry : json.entrySet()) {
|
||||
JsonElement result = context.replaceIngredient(entry.getValue());
|
||||
if (result != null) {
|
||||
context.replaceResult(result);
|
||||
entry.setValue(result);
|
||||
}
|
||||
}
|
||||
return json;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package com.almostreliable.unified.compat.ie;
|
|||
|
||||
import com.almostreliable.unified.api.RecipeContext;
|
||||
import com.almostreliable.unified.api.RecipeHandler;
|
||||
import com.almostreliable.unified.api.RecipeTransformations;
|
||||
import com.almostreliable.unified.handler.RecipeConstants;
|
||||
import com.almostreliable.unified.utils.JsonUtils;
|
||||
import com.almostreliable.unified.utils.Utils;
|
||||
|
@ -16,24 +17,45 @@ public class IERecipeHandler implements RecipeHandler {
|
|||
// From IE
|
||||
protected static final String BASE_KEY = "base_ingredient";
|
||||
|
||||
// TODO make it cleaner
|
||||
@Override
|
||||
public void transformRecipe(JsonObject json, RecipeContext context) {
|
||||
public void collectTransformations(RecipeTransformations builder) {
|
||||
builder.put("input0", (json, context) -> {
|
||||
replaceIEIngredient(json, context);
|
||||
return json;
|
||||
}); // alloy recipes, refinery
|
||||
builder.put("input1", (json, context) -> {
|
||||
replaceIEIngredient(json, context);
|
||||
return json;
|
||||
}); // alloy recipes, refinery
|
||||
builder.put(RecipeConstants.INPUT, (json, context) -> {
|
||||
replaceIEIngredient(json, context);
|
||||
return json;
|
||||
}); // arc furnace, squeezer, cloche, coke oven, fermenter, fertilizer, metal_press
|
||||
builder.put("additives", (json, context) -> {
|
||||
replaceIEIngredient(json, context);
|
||||
return json;
|
||||
}); // arc furnace
|
||||
builder.put(RecipeConstants.INPUTS, (json, context) -> {
|
||||
replaceIEIngredient(json, context);
|
||||
return json;
|
||||
}); // blueprint, mixer
|
||||
|
||||
replaceIEIngredient(json.get("input0"), context); // alloy recipes, refinery
|
||||
replaceIEIngredient(json.get("input1"), context); // alloy recipes, refinery
|
||||
replaceIEIngredient(json.get(RecipeConstants.INPUT),
|
||||
context); // arc furnace, squeezer, cloche, coke oven, fermenter, fertilizer, metal_press
|
||||
replaceIEIngredient(json.get("additives"), context); // arc furnace
|
||||
replaceIEIngredient(json.get(RecipeConstants.INPUTS), context); // blueprint, mixer
|
||||
|
||||
replaceIEResult(json.get("secondaries"), context); // alloy recipes, crusher
|
||||
|
||||
JsonUtils.arrayForEach(json.get("secondaries"), JsonObject.class, secondary -> {
|
||||
replaceIEResult(secondary.get(RecipeConstants.OUTPUT), context);
|
||||
// alloy recipes, crusher
|
||||
builder.forEachObject("secondaries", (jsonObject, context) -> {
|
||||
replaceIEResult(jsonObject.get(RecipeConstants.OUTPUT), context);
|
||||
return jsonObject;
|
||||
});
|
||||
|
||||
replaceIEResult(json.get(RecipeConstants.RESULT), context);
|
||||
replaceIEResult(json.get(RecipeConstants.RESULTS), context);
|
||||
builder.put(RecipeConstants.RESULT, (json, context) -> {
|
||||
replaceIEResult(json, context);
|
||||
return json;
|
||||
});
|
||||
|
||||
builder.put(RecipeConstants.RESULTS, (json, context) -> {
|
||||
replaceIEResult(json, context);
|
||||
return json;
|
||||
});
|
||||
}
|
||||
|
||||
protected void replaceIEResult(@Nullable JsonElement element, RecipeContext context) {
|
||||
|
|
Loading…
Reference in a new issue