Refactor factory system & context

This commit is contained in:
LLytho 2022-06-19 17:44:37 +02:00
parent c6beabaf2e
commit e746cf95f4
25 changed files with 493 additions and 270 deletions

View file

@ -1,5 +1,6 @@
package com.almostreliable.unified;
import com.almostreliable.unified.handler.RecipeHandlerFactory;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@ -11,11 +12,6 @@ public class AlmostUnified {
@Nullable
private static AlmostUnifiedRuntime RUNTIME;
public AlmostUnified() {
// MANAGER.register(new ResourceLocation("minecraft:crafting_shaped"), new ShapedRecipeTransformer());
}
public static AlmostUnifiedRuntime getRuntime() {
if (RUNTIME == null) {
throw new IllegalStateException("AlmostUnifiedRuntime not initialized");
@ -24,6 +20,8 @@ public class AlmostUnified {
}
static void initializeRuntime() {
RUNTIME = new AlmostUnifiedRuntime();
RecipeHandlerFactory factory = new RecipeHandlerFactory();
// factory.registerForMod("immersiveengineering", new IERecipeTransformerFactory());
RUNTIME = new AlmostUnifiedRuntime(factory);
}
}

View file

@ -1,12 +1,7 @@
package com.almostreliable.unified;
import com.almostreliable.unified.api.RecipeTransformContext;
import com.almostreliable.unified.api.RecipeTransformer;
import com.almostreliable.unified.api.RecipeTransformerFactory;
import com.almostreliable.unified.api.RecipeTransformerRegistry;
import com.almostreliable.unified.transformer.GenericRecipeTransformerFactory;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import com.almostreliable.unified.api.RecipeHandler;
import com.almostreliable.unified.handler.RecipeHandlerFactory;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import net.minecraft.resources.ResourceLocation;
@ -23,72 +18,74 @@ import java.util.Map;
public class AlmostUnifiedRuntime {
protected final ModConfig config;
protected final RecipeTransformerFactory defaultFactory = new GenericRecipeTransformerFactory();
protected final RecipeHandlerFactory recipeHandlerFactory;
@Nullable protected TagManager tagManager;
public AlmostUnifiedRuntime() {
public AlmostUnifiedRuntime(RecipeHandlerFactory recipeHandlerFactory) {
this.recipeHandlerFactory = recipeHandlerFactory;
config = new ModConfig(BuildConfig.MOD_ID);
}
public void run(Map<ResourceLocation, JsonElement> recipes) {
config.load();
RecipeTransformContext context = createContext(config.getAllowedTags(), config.getModPriorities());
transformRecipes(recipes, context);
ReplacementMap replacementMap = createContext(config.getAllowedTags(), config.getModPriorities());
transformRecipes(recipes, replacementMap);
}
public void transformRecipes(Map<ResourceLocation, JsonElement> recipes, RecipeTransformContext helper) {
public void transformRecipes(Map<ResourceLocation, JsonElement> recipes, ReplacementMap replacementMap) {
int transformedRecipes = 0;
int transformedPropertiesInRecipes = 0;
long start = System.nanoTime();
for (var entry : recipes.entrySet()) {
if (entry.getValue() instanceof JsonObject json) {
int changes = transformRecipe(json, helper);
if (changes > 0) {
JsonObject transformedJson = transformRecipe(entry.getKey(), json, replacementMap);
if (transformedJson != null) {
transformedRecipes++;
transformedPropertiesInRecipes += changes;
entry.setValue(transformedJson);
}
}
}
long finish = System.nanoTime();
long timeElapsed = finish - start;
AlmostUnified.LOG.info("Transformed {}/{} recipes with {} changes in {}ms",
AlmostUnified.LOG.info("Transformed {}/{} recipes changes in {}ms",
transformedRecipes,
recipes.size(),
transformedPropertiesInRecipes,
timeElapsed / 1000_000D);
}
public int transformRecipe(JsonObject json, RecipeTransformContext helper) {
@Nullable
public JsonObject transformRecipe(ResourceLocation id, JsonObject json, ReplacementMap replacementMap) {
ResourceLocation recipeType = getRecipeType(json);
if (recipeType == null) {
return 0;
return null;
}
RecipeTransformerFactory factory = RecipeTransformerRegistry.getOrDefault(recipeType, defaultFactory);
int transformedProperties = 0;
for (var entry : json.entrySet()) {
String property = entry.getKey();
RecipeTransformer recipeTransformer = factory.create(recipeType, property);
if (recipeTransformer == null) {
continue;
RecipeContextImpl ctx = new RecipeContextImpl(recipeType, id, json, replacementMap);
// for (var entry : json.entrySet()) {
try {
RecipeHandler recipeHandler = recipeHandlerFactory.create(ctx);
if (recipeHandler == null) {
return null;
}
try {
JsonElement jsonValue = json.get(property);
JsonElement overriddenJson = recipeTransformer.transformRecipe(jsonValue.deepCopy(), helper);
if (overriddenJson != null && !jsonValue.equals(overriddenJson)) {
entry.setValue(overriddenJson);
transformedProperties++;
}
} catch (Exception e) {
AlmostUnified.LOG.warn("Error transforming recipe for type '{}' with property '{}': {}",
JsonObject copy = json.deepCopy();
recipeHandler.transformRecipe(copy, ctx);
if (!json.equals(copy)) {
AlmostUnified.LOG.info("Transformed recipe '{}' for type '{}' ========> {}",
id,
recipeType,
property,
e.getMessage());
e.printStackTrace();
copy);
return copy;
}
} catch (Exception e) {
AlmostUnified.LOG.warn("Error transforming recipe type '{}': {}",
recipeType,
e.getMessage());
e.printStackTrace();
}
return transformedProperties;
return null;
}
@ -102,7 +99,7 @@ public class AlmostUnifiedRuntime {
this.tagManager = tagManager;
}
protected RecipeTransformContext createContext(List<TagKey<Item>> allowedTags, List<String> modPriorities) {
protected ReplacementMap createContext(List<TagKey<Item>> allowedTags, List<String> modPriorities) {
if (tagManager == null) {
throw new IllegalStateException("Internal error. TagManager was not updated correctly");
}
@ -125,6 +122,6 @@ public class AlmostUnifiedRuntime {
}
}
return new RecipeTransformContextImpl(tagMap, itemToTagMapping, modPriorities);
return new ReplacementMap(tagMap, itemToTagMapping, modPriorities);
}
}

View file

@ -0,0 +1,107 @@
package com.almostreliable.unified;
import com.almostreliable.unified.api.RecipeContext;
import com.almostreliable.unified.utils.JsonUtils;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.tags.TagKey;
import net.minecraft.world.item.Item;
import javax.annotation.Nullable;
import java.util.concurrent.atomic.AtomicBoolean;
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;
private final ReplacementMap replacementMap;
public RecipeContextImpl(ResourceLocation type, ResourceLocation id, JsonObject currentRecipe, ReplacementMap replacementMap) {
this.type = type;
this.id = id;
this.currentRecipe = currentRecipe;
this.replacementMap = replacementMap;
}
@Nullable
@Override
public ResourceLocation getReplacementForItem(@Nullable ResourceLocation item) {
if (item == null) {
return null;
}
return replacementMap.getReplacementForItem(item);
}
@Nullable
@Override
public ResourceLocation getPreferredItemByTag(TagKey<Item> tag) {
return replacementMap.getPreferredItemByTag(tag);
}
@Nullable
@Override
public TagKey<Item> getPreferredTagByItem(@Nullable ResourceLocation item) {
if (item == null) {
return null;
}
return replacementMap.getPreferredTag(item);
}
@Override
public boolean replaceIngredient(JsonElement element) {
AtomicBoolean changed = new AtomicBoolean(false);
JsonUtils.arrayForEach(element, JsonObject.class, json -> {
if (json.get(ITEM) instanceof JsonPrimitive 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 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) {
ResourceLocation item = ResourceLocation.tryParse(primitive.getAsString());
ResourceLocation replacement = getReplacementForItem(item);
if (replacement != null) {
json.addProperty(ITEM, replacement.toString());
changed.set(true);
}
}
});
return changed.get();
}
@Override
public ResourceLocation getType() {
return type;
}
@Override
public ResourceLocation getId() {
return id;
}
@Override
public boolean hasProperty(String property) {
return currentRecipe.has(property);
}
}

View file

@ -1,93 +0,0 @@
package com.almostreliable.unified;
import com.almostreliable.unified.api.RecipeTransformContext;
import com.almostreliable.unified.api.ReplacementFallbackStrategy;
import com.almostreliable.unified.fallbacks.StoneStrataFallbackStrategy;
import net.minecraft.core.Registry;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.tags.TagKey;
import net.minecraft.world.item.Item;
import javax.annotation.Nullable;
import java.util.*;
public class RecipeTransformContextImpl implements RecipeTransformContext {
private final Collection<String> modPriorities;
private final TagMap tagMap;
private final Map<ResourceLocation, TagKey<Item>> itemToTagMapping;
/**
* Cache for replacements. Key is the item to replace, value is the replacement.
*/
private final Map<ResourceLocation, ResourceLocation> replacementCache = new HashMap<>();
private final Set<ResourceLocation> invalidCache = new HashSet<>();
// TODO - In the future this may be a list of multiple fallbacks.
private final ReplacementFallbackStrategy fallbackStrategy = new StoneStrataFallbackStrategy();
public RecipeTransformContextImpl(TagMap tagMap, Map<ResourceLocation, TagKey<Item>> itemToTagMapping, List<String> modPriorities) {
this.tagMap = tagMap;
this.itemToTagMapping = itemToTagMapping;
this.modPriorities = modPriorities;
}
@Nullable
@Override
public String findReplacement(String id) {
ResourceLocation asLocation = new ResourceLocation(id);
if(invalidCache.contains(asLocation)) {
return null;
}
ResourceLocation replacement = replacementCache.get(asLocation);
if(replacement != null) {
return replacement.toString();
}
TagKey<Item> tag = itemToTagMapping.get(asLocation);
if (!Registry.ITEM.containsKey(asLocation) || tag == null) {
return null;
}
ResourceLocation modReplacement = computeReplacement(asLocation, tag);
if(modReplacement == null || modReplacement.equals(asLocation)) {
invalidCache.add(asLocation);
return null;
}
replacementCache.put(asLocation, modReplacement);
AlmostUnified.LOG.info("########### {} -> {}", id, modReplacement.toString());
return modReplacement.toString();
}
@Nullable
public ResourceLocation computeReplacement(ResourceLocation item, TagKey<Item> tag) {
for (String mod : modPriorities) {
if(mod.equals(item.getNamespace())) {
return null;
}
List<ResourceLocation> sameModItems = tagMap
.getItems(tag)
.stream()
.filter(i -> i.getNamespace().equals(mod) && Registry.ITEM.containsKey(i))
.toList();
if(sameModItems.size() == 1) {
return sameModItems.get(0);
}
if(sameModItems.size() > 1) {
ResourceLocation fallback = fallbackStrategy.getFallback(item, tag, sameModItems, tagMap);
if(fallback != null) {
if(fallback.equals(item)) {
throw new IllegalStateException("Fallback for " + item + " is the same as the item itself. This is not allowed.");
}
return fallback;
}
}
}
return null;
}
}

View file

@ -0,0 +1,75 @@
package com.almostreliable.unified;
import com.almostreliable.unified.api.ReplacementFallbackStrategy;
import com.almostreliable.unified.fallbacks.StoneStrataFallbackStrategy;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.tags.TagKey;
import net.minecraft.world.item.Item;
import javax.annotation.Nullable;
import java.util.Collection;
import java.util.List;
import java.util.Map;
public class ReplacementMap {
private final Collection<String> modPriorities;
private final TagMap tagMap;
private final Map<ResourceLocation, TagKey<Item>> itemToTagMapping;
// TODO - In the future this may be a list of multiple fallbacks.
private final ReplacementFallbackStrategy fallbackStrategy = new StoneStrataFallbackStrategy();
public ReplacementMap(TagMap tagMap, Map<ResourceLocation, TagKey<Item>> itemToTagMapping, List<String> modPriorities) {
this.tagMap = tagMap;
this.itemToTagMapping = itemToTagMapping;
this.modPriorities = modPriorities;
}
@Nullable
public TagKey<Item> getPreferredTag(ResourceLocation item) {
return itemToTagMapping.get(item);
}
@Nullable
public ResourceLocation getReplacementForItem(ResourceLocation item) {
TagKey<Item> tag = getPreferredTag(item);
if (tag == null) {
return null;
}
ResourceLocation preferredItem = getPreferredItemByTag(tag, item.getNamespace());
if (item.equals(preferredItem)) {
return null;
}
return preferredItem;
}
@Nullable
public ResourceLocation getPreferredItemByTag(TagKey<Item> tag) {
return getPreferredItemByTag(tag, null);
}
@Nullable
public ResourceLocation getPreferredItemByTag(TagKey<Item> tag, @Nullable String ignoredNamespace) {
for (String mod : modPriorities) {
List<ResourceLocation> sameModItems = tagMap
.getItems(tag)
.stream()
.filter(i -> i.getNamespace().equals(mod))
.toList();
if (sameModItems.size() == 1) {
return sameModItems.get(0);
}
if (sameModItems.size() > 1 && !mod.equals(ignoredNamespace)) {
ResourceLocation fallback = fallbackStrategy.getFallback(tag, sameModItems, tagMap);
if (fallback != null) {
return fallback;
}
}
}
return null;
}
}

View file

@ -35,7 +35,11 @@ public class TagMap {
Tag<? extends Holder<?>> holderTag = entry.getValue();
for (Holder<?> holder : holderTag.getValues()) {
holder.unwrapKey().map(ResourceKey::location).ifPresent(itemId -> tagMap.put(tag, itemId));
holder
.unwrapKey()
.map(ResourceKey::location)
.filter(Registry.ITEM::containsKey)
.ifPresent(itemId -> tagMap.put(tag, itemId));
}
}

View file

@ -0,0 +1,36 @@
package com.almostreliable.unified.api;
import com.google.gson.JsonElement;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.tags.TagKey;
import net.minecraft.world.item.Item;
import javax.annotation.Nullable;
public interface RecipeContext {
@Nullable
ResourceLocation getReplacementForItem(@Nullable ResourceLocation item);
@Nullable
ResourceLocation getPreferredItemByTag(TagKey<Item> tag);
@Nullable
TagKey<Item> getPreferredTagByItem(@Nullable ResourceLocation item);
boolean replaceIngredient(JsonElement element);
boolean replaceResult(JsonElement element);
ResourceLocation getType();
ResourceLocation getId();
// String getIterateProperty();
boolean hasProperty(String property);
default String getModId() {
return getType().getNamespace();
}
}

View file

@ -0,0 +1,13 @@
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();
}
}

View file

@ -1,9 +0,0 @@
package com.almostreliable.unified.api;
import javax.annotation.Nullable;
public interface RecipeTransformContext {
@Nullable
String findReplacement(String id);
}

View file

@ -1,11 +0,0 @@
package com.almostreliable.unified.api;
import com.google.gson.JsonElement;
import javax.annotation.Nullable;
@FunctionalInterface
public interface RecipeTransformer {
@Nullable
JsonElement transformRecipe(JsonElement json, RecipeTransformContext context);
}

View file

@ -1,11 +0,0 @@
package com.almostreliable.unified.api;
import net.minecraft.resources.ResourceLocation;
import javax.annotation.Nullable;
import java.util.Collection;
public interface RecipeTransformerFactory {
@Nullable
RecipeTransformer create(ResourceLocation type, String property);
}

View file

@ -1,36 +0,0 @@
package com.almostreliable.unified.api;
import com.almostreliable.unified.AlmostUnified;
import com.almostreliable.unified.api.RecipeTransformerFactory;
import net.minecraft.resources.ResourceLocation;
import javax.annotation.Nullable;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
public class RecipeTransformerRegistry {
private static final Map<ResourceLocation, RecipeTransformerFactory> FACTORIES = new HashMap<>();
public static void registerFactory(ResourceLocation type, RecipeTransformerFactory factory) {
Objects.requireNonNull(type, "type cannot be null");
Objects.requireNonNull(factory, "factory cannot be null");
if (FACTORIES.containsKey(type)) {
AlmostUnified.LOG.warn("Overwriting transformer factory for {} with {}",
type,
factory.getClass().getName());
}
FACTORIES.put(type, factory);
}
@Nullable
public static RecipeTransformerFactory getFactory(ResourceLocation type) {
return FACTORIES.get(type);
}
public static RecipeTransformerFactory getOrDefault(ResourceLocation type, RecipeTransformerFactory defaultFactory) {
return FACTORIES.getOrDefault(type, defaultFactory);
}
}

View file

@ -12,7 +12,6 @@ public interface ReplacementFallbackStrategy {
/**
* Determine a fallback for the given item.
*
* @param lookupItem the item to lookup
* @param tag the tag to replace the item with
* @param potentialItems the potential items to replace with
* @param tags the tag map to use for lookup
@ -20,5 +19,5 @@ public interface ReplacementFallbackStrategy {
* @throws IllegalStateException if returning the lookupItem
*/
@Nullable
ResourceLocation getFallback(ResourceLocation lookupItem, TagKey<Item> tag, Collection<ResourceLocation> potentialItems, TagMap tags);
ResourceLocation getFallback(TagKey<Item> tag, Collection<ResourceLocation> potentialItems, TagMap tags);
}

View file

@ -12,7 +12,7 @@ import java.util.Comparator;
// TODO use config and not this :D
public class StoneStrataFallbackStrategy implements ReplacementFallbackStrategy {
@Override
public ResourceLocation getFallback(ResourceLocation lookupItem, TagKey<Item> tag, Collection<ResourceLocation> potentialItems, TagMap tags) {
public ResourceLocation getFallback(TagKey<Item> tag, Collection<ResourceLocation> potentialItems, TagMap tags) {
if (tag.location().getPath().contains("ores")) {
return potentialItems
.stream()

View file

@ -0,0 +1,59 @@
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 java.util.Set;
public class GenericRecipeHandler implements RecipeHandler {
public static final GenericRecipeHandler INSTANCE = new GenericRecipeHandler();
private final Set<String> inputKeys = Set.of(RecipeConstants.INPUT,
RecipeConstants.INGREDIENT,
RecipeConstants.INGREDIENTS);
private final Set<String> outputKeys = Set.of(RecipeConstants.OUTPUT,
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) {
for (String inputKey : inputKeys) {
if (json.has(inputKey)) {
context.replaceIngredient(json.get(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);
}
}
}
}

View file

@ -0,0 +1,12 @@
package com.almostreliable.unified.handler;
public class RecipeConstants {
public static final String ITEM = "item";
public static final String TAG = "tag";
public static final String INPUT = "input";
public static final String INGREDIENT = "ingredient";
public static final String INGREDIENTS = "ingredients";
public static final String OUTPUT = "output";
public static final String RESULT = "result";
public static final String RESULTS = "results";
}

View file

@ -0,0 +1,36 @@
package com.almostreliable.unified.handler;
import com.almostreliable.unified.api.RecipeContext;
import com.almostreliable.unified.api.RecipeHandler;
import net.minecraft.resources.ResourceLocation;
import javax.annotation.Nullable;
import java.util.HashMap;
import java.util.Map;
public class RecipeHandlerFactory {
private final Map<ResourceLocation, RecipeHandler> transformersByType = new HashMap<>();
@Nullable
public RecipeHandler create(RecipeContext context) {
RecipeHandler transformer = transformersByType.get(context.getType());
if (transformer != null) {
return transformer;
}
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 register(ResourceLocation type, RecipeHandler transformer) {
transformersByType.put(type, transformer);
}
}

View file

@ -0,0 +1,26 @@
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;
public class ShapedRecipeKeyHandler implements RecipeHandler {
public static final RecipeHandler INSTANCE = new ShapedRecipeKeyHandler();
public static final String PATTERN_PROPERTY = "pattern";
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");
if (result != null) {
context.replaceResult(result);
}
}
}

View file

@ -0,0 +1,14 @@
package com.almostreliable.unified.handler.ie;
import com.almostreliable.unified.api.RecipeContext;
import com.almostreliable.unified.api.RecipeHandler;
import com.google.gson.JsonObject;
public class IEBaseRecipeHandler implements RecipeHandler {
@Override
public void transformRecipe(JsonObject json, RecipeContext context) {
}
}

View file

@ -1,5 +1,5 @@
@ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault
package com.almostreliable.unified.transformer;
package com.almostreliable.unified.handler.ie;
import net.minecraft.MethodsReturnNonnullByDefault;

View file

@ -0,0 +1,6 @@
@ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault
package com.almostreliable.unified.handler;
import net.minecraft.MethodsReturnNonnullByDefault;
import javax.annotation.ParametersAreNonnullByDefault;

View file

@ -1,34 +0,0 @@
package com.almostreliable.unified.transformer;
import com.almostreliable.unified.api.RecipeTransformer;
import com.almostreliable.unified.api.RecipeTransformContext;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import javax.annotation.Nullable;
public class GenericRecipeTransformer implements RecipeTransformer {
protected void replaceItems(JsonElement element, RecipeTransformContext context) {
if (element instanceof JsonObject asObject) {
if (asObject.has("item")) {
String newItem = context.findReplacement(asObject.get("item").getAsString());
if (newItem != null) {
asObject.addProperty("item", newItem);
}
}
} else if (element instanceof JsonArray asArray) {
for (JsonElement innerElement : asArray) {
replaceItems(innerElement, context);
}
}
}
@Nullable
@Override
public JsonElement transformRecipe(JsonElement json, RecipeTransformContext context) {
// TODO refactor
replaceItems(json, context);
return json;
}
}

View file

@ -1,23 +0,0 @@
package com.almostreliable.unified.transformer;
import com.almostreliable.unified.api.RecipeTransformer;
import com.almostreliable.unified.api.RecipeTransformerFactory;
import net.minecraft.resources.ResourceLocation;
import java.util.Set;
public class GenericRecipeTransformerFactory implements RecipeTransformerFactory {
private final GenericRecipeTransformer transformer = new GenericRecipeTransformer();
private final Set<String> inputKeys = Set.of("input", "ingredient", "ingredients");
private final Set<String> outputKeys = Set.of("output", "result", "results");
@Override
public RecipeTransformer create(ResourceLocation type, String property) {
if (inputKeys.contains(property) || outputKeys.contains(property)) {
return transformer;
}
return null;
}
}

View file

@ -0,0 +1,31 @@
package com.almostreliable.unified.utils;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import javax.annotation.Nullable;
import java.util.function.Consumer;
public class JsonUtils {
public static JsonArray arrayOrSelf(@Nullable JsonElement element) {
if (element == null) {
return new JsonArray();
}
if (element.isJsonArray()) {
return element.getAsJsonArray();
}
JsonArray array = new JsonArray();
array.add(element);
return array;
}
public static <T extends JsonElement> void arrayForEach(@Nullable JsonElement element, Class<T> filter, Consumer<T> consumer) {
for (JsonElement e : arrayOrSelf(element)) {
if (filter.isInstance(e)) {
consumer.accept(filter.cast(e));
}
}
}
}

View file

@ -0,0 +1,27 @@
package com.almostreliable.unified.utils;
import com.almostreliable.unified.BuildConfig;
import net.minecraft.core.Registry;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.tags.TagKey;
import net.minecraft.world.item.Item;
import javax.annotation.Nullable;
public class Utils {
public static final ResourceLocation UNUSED_ID = new ResourceLocation(BuildConfig.MOD_ID, "unused_id");
public static final TagKey<Item> UNUSED_TAG = TagKey.create(Registry.ITEM_REGISTRY, UNUSED_ID);
public static TagKey<Item> toItemTag(@Nullable String tag) {
if (tag == null) {
return UNUSED_TAG;
}
ResourceLocation rl = ResourceLocation.tryParse(tag);
if (rl == null) {
return UNUSED_TAG;
}
return TagKey.create(Registry.ITEM_REGISTRY, rl);
}
}