diff --git a/gradle.properties b/gradle.properties index 13c6fd9..0e54afa 100644 --- a/gradle.properties +++ b/gradle.properties @@ -7,7 +7,7 @@ bta_version=7.1-pre1a loader_version=0.14.19-babric.3-bta # Mod -mod_version=3.1.4 +mod_version=3.2.0 mod_group=turniplabs mod_name=halplibe diff --git a/src/main/java/turniplabs/halplibe/HalpLibe.java b/src/main/java/turniplabs/halplibe/HalpLibe.java index 04a4726..83b1a73 100644 --- a/src/main/java/turniplabs/halplibe/HalpLibe.java +++ b/src/main/java/turniplabs/halplibe/HalpLibe.java @@ -23,6 +23,7 @@ public class HalpLibe implements ModInitializer, PreLaunchEntrypoint{ public static final String MOD_ID = "halplibe"; public static final Logger LOGGER = LoggerFactory.getLogger(MOD_ID); public static boolean sendModlist; + public static boolean exportRecipes; public static final TomlConfigHandler CONFIG; static { Toml toml = new Toml(); @@ -31,12 +32,15 @@ public class HalpLibe implements ModInitializer, PreLaunchEntrypoint{ toml.addEntry("Experimental.RequireTextures", "Require texture to exist on startup", false); toml.addCategory("Network"); toml.addEntry("Network.SendModlistPack", "This sends a modlist packet to clients that join the server when enabled, however it may cause issues if the clients do not have halplibe installed", true); + toml.addCategory("Debug"); + toml.addEntry("Debug.ExportRecipes", "Writes all the loaded game recipes to dumpRecipes after startup", false); CONFIG = new TomlConfigHandler(MOD_ID, toml); Global.TEXTURE_ATLAS_WIDTH_TILES = Math.max(32, CONFIG.getInt("Experimental.AtlasWidth")); sendModlist = CONFIG.getBoolean("Network.SendModlistPack"); + exportRecipes = CONFIG.getBoolean("Debug.ExportRecipes"); // Initialize Block and Item static fields try { diff --git a/src/main/java/turniplabs/halplibe/helper/RecipeBuilder.java b/src/main/java/turniplabs/halplibe/helper/RecipeBuilder.java new file mode 100644 index 0000000..daa833e --- /dev/null +++ b/src/main/java/turniplabs/halplibe/helper/RecipeBuilder.java @@ -0,0 +1,143 @@ +package turniplabs.halplibe.helper; + +import com.b100.utils.FileUtils; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.TypeAdapter; +import com.google.gson.reflect.TypeToken; +import net.minecraft.core.Global; +import net.minecraft.core.WeightedRandomBag; +import net.minecraft.core.WeightedRandomLootObject; +import net.minecraft.core.data.registry.Registries; +import net.minecraft.core.data.registry.recipe.HasJsonAdapter; +import net.minecraft.core.data.registry.recipe.RecipeEntryBase; +import net.minecraft.core.data.registry.recipe.RecipeGroup; +import net.minecraft.core.data.registry.recipe.RecipeNamespace; +import net.minecraft.core.data.registry.recipe.RecipeSymbol; +import net.minecraft.core.data.registry.recipe.adapter.ItemStackJsonAdapter; +import net.minecraft.core.data.registry.recipe.adapter.RecipeJsonAdapter; +import net.minecraft.core.data.registry.recipe.adapter.RecipeSymbolJsonAdapter; +import net.minecraft.core.data.registry.recipe.adapter.WeightedRandomBagJsonAdapter; +import net.minecraft.core.data.registry.recipe.adapter.WeightedRandomLootObjectJsonAdapter; +import net.minecraft.core.item.ItemStack; +import turniplabs.halplibe.helper.recipeBuilders.RecipeBuilderBlastFurnace; +import turniplabs.halplibe.helper.recipeBuilders.RecipeBuilderFurnace; +import turniplabs.halplibe.helper.recipeBuilders.RecipeBuilderShaped; +import turniplabs.halplibe.helper.recipeBuilders.RecipeBuilderShapeless; +import turniplabs.halplibe.helper.recipeBuilders.RecipeBuilderTrommel; +import turniplabs.halplibe.helper.recipeBuilders.modifiers.BlastFurnaceModifier; +import turniplabs.halplibe.helper.recipeBuilders.modifiers.FurnaceModifier; +import turniplabs.halplibe.helper.recipeBuilders.modifiers.TrommelModifier; +import turniplabs.halplibe.helper.recipeBuilders.modifiers.WorkbenchModifier; + +import javax.annotation.Nonnull; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.lang.reflect.Type; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +public final class RecipeBuilder { + @Nonnull + public static RecipeNamespace getRecipeNamespace(String modID){ + if (Registries.RECIPES.getItem(modID) != null){ + return Registries.RECIPES.getItem(modID); + } + RecipeNamespace modSpace = new RecipeNamespace(); + Registries.RECIPES.register(modID, modSpace); + return Objects.requireNonNull(modSpace); + } + @Nonnull + public static RecipeGroup getRecipeGroup(String modID, String key, RecipeSymbol symbol){ + return getRecipeGroup(getRecipeNamespace(modID), key, symbol); + } + @Nonnull + public static RecipeGroup getRecipeGroup(RecipeNamespace namespace, String key, RecipeSymbol symbol){ + if (namespace.getItem(key) != null){ + return namespace.getItem(key); + } + RecipeGroup group = new RecipeGroup<>(symbol); + namespace.register(key, group); + return Objects.requireNonNull(group); + } + public static RecipeBuilderShaped Shaped(String modID){ + return new RecipeBuilderShaped(modID); + } + public static RecipeBuilderShaped Shaped(String modID, String... shape){ + return new RecipeBuilderShaped(modID, shape); + } + public static RecipeBuilderShapeless Shapeless(String modID){ + return new RecipeBuilderShapeless(modID); + } + public static RecipeBuilderFurnace Furnace(String modID){ + return new RecipeBuilderFurnace(modID); + } + public static RecipeBuilderBlastFurnace BlastFurnace(String modID){ + return new RecipeBuilderBlastFurnace(modID); + } + public static RecipeBuilderTrommel Trommel(String modID){ + return new RecipeBuilderTrommel(modID); + } + public static TrommelModifier ModifyTrommel(String namespace, String key){ + return new TrommelModifier(namespace, key); + } + public static WorkbenchModifier ModifyWorkbench(String namespace){ + return new WorkbenchModifier(namespace); + } + public static FurnaceModifier ModifyFurnace(String namespace){ + return new FurnaceModifier(namespace); + } + public static BlastFurnaceModifier ModifyBlastFurnace(String namespace){ + return new BlastFurnaceModifier(namespace); + } + public static boolean isExporting = false; + public static void exportRecipes(){ + isExporting = true; + Path filePath = Paths.get(Global.accessor.getMinecraftDir() + "/" + "recipeDump"); + createDir(filePath); + String path = filePath + "/recipes.json"; + List> recipes = Registries.RECIPES.getAllSerializableRecipes(); + GsonBuilder builder = new GsonBuilder(); + builder.setPrettyPrinting(); + ArrayList usedAdapters = new ArrayList(); + for (RecipeEntryBase recipe : recipes) { + HasJsonAdapter hasJsonAdapter = (HasJsonAdapter) recipe; + RecipeJsonAdapter recipeJsonAdapter = hasJsonAdapter.getAdapter(); + if (usedAdapters.contains(recipeJsonAdapter)) continue; + builder.registerTypeAdapter(recipe.getClass(), recipeJsonAdapter); + usedAdapters.add(recipeJsonAdapter); + } + builder.registerTypeAdapter(ItemStack.class, new ItemStackJsonAdapter()); + builder.registerTypeAdapter(RecipeSymbol.class, new RecipeSymbolJsonAdapter()); + builder.registerTypeAdapter(new TypeToken>(){}.getType(), new WeightedRandomBagJsonAdapter()); + builder.registerTypeAdapter(WeightedRandomLootObject.class, new WeightedRandomLootObjectJsonAdapter()); + Gson gson = builder.create(); + JsonArray jsonArray = new JsonArray(); + for (RecipeEntryBase recipeEntryBase : recipes) { + TypeAdapter typeAdapter = (TypeAdapter) gson.getAdapter(recipeEntryBase.getClass()); + JsonElement json = typeAdapter.toJsonTree(recipeEntryBase); + jsonArray.add(json); + } + File file = FileUtils.createNewFile(new File(path)); + try (FileWriter fileWriter = new FileWriter(file)){ + gson.toJson(jsonArray, fileWriter); + } catch (IOException iOException) { + throw new RuntimeException(iOException); + } + isExporting = false; + } + private static void createDir(Path path){ + try { + Files.createDirectories(path); + } catch (IOException e) { + System.err.println("Failed to create directory!" + e.getMessage()); + } + } +} diff --git a/src/main/java/turniplabs/halplibe/helper/RecipeHelper.java b/src/main/java/turniplabs/halplibe/helper/RecipeHelper.java index 0fe3691..47b1ee6 100644 --- a/src/main/java/turniplabs/halplibe/helper/RecipeHelper.java +++ b/src/main/java/turniplabs/halplibe/helper/RecipeHelper.java @@ -5,11 +5,19 @@ import net.minecraft.core.crafting.legacy.recipe.IRecipe; import net.minecraft.core.crafting.legacy.recipe.RecipesBlastFurnace; import net.minecraft.core.crafting.legacy.recipe.RecipesFurnace; +import net.minecraft.core.data.registry.Registries; +import net.minecraft.core.data.registry.recipe.RecipeEntryBase; +import net.minecraft.core.data.registry.recipe.RecipeGroup; +import net.minecraft.core.data.registry.recipe.RecipeNamespace; +import net.minecraft.core.data.registry.recipe.RecipeSymbol; import net.minecraft.core.item.Item; import net.minecraft.core.item.ItemStack; +import javax.annotation.Nonnull; +import java.util.Objects; /** * @deprecated Class will be removed once the Legacy crafting manager is removed from BTA + * Replace by the RecipeBuilder */ @Deprecated public class RecipeHelper { diff --git a/src/main/java/turniplabs/halplibe/helper/recipeBuilders/RecipeBuilderBase.java b/src/main/java/turniplabs/halplibe/helper/recipeBuilders/RecipeBuilderBase.java new file mode 100644 index 0000000..295c334 --- /dev/null +++ b/src/main/java/turniplabs/halplibe/helper/recipeBuilders/RecipeBuilderBase.java @@ -0,0 +1,25 @@ +package turniplabs.halplibe.helper.recipeBuilders; + +import net.minecraft.core.item.ItemStack; + +import java.util.Objects; + +abstract class RecipeBuilderBase implements Cloneable { + protected String modID; + public RecipeBuilderBase(String modID){ + this.modID = Objects.requireNonNull(modID, "ModID must not be null!"); + } + public T clone(T object){ + return (T) clone(); + } + @Override + public RecipeBuilderBase clone() { + try { + // none of the fields are mutated so this should be fine + return (RecipeBuilderBase) super.clone(); + } catch (CloneNotSupportedException e) { + throw new AssertionError(); + } + } + protected abstract void create(String recipeID, ItemStack outputStack); +} diff --git a/src/main/java/turniplabs/halplibe/helper/recipeBuilders/RecipeBuilderBlastFurnace.java b/src/main/java/turniplabs/halplibe/helper/recipeBuilders/RecipeBuilderBlastFurnace.java new file mode 100644 index 0000000..e9a4938 --- /dev/null +++ b/src/main/java/turniplabs/halplibe/helper/recipeBuilders/RecipeBuilderBlastFurnace.java @@ -0,0 +1,19 @@ +package turniplabs.halplibe.helper.recipeBuilders; + +import net.minecraft.core.block.Block; +import net.minecraft.core.data.registry.recipe.RecipeGroup; +import net.minecraft.core.data.registry.recipe.RecipeSymbol; +import net.minecraft.core.data.registry.recipe.entry.RecipeEntryBlastFurnace; +import net.minecraft.core.item.ItemStack; +import turniplabs.halplibe.helper.RecipeBuilder; + +public class RecipeBuilderBlastFurnace extends RecipeBuilderFurnace{ + public RecipeBuilderBlastFurnace(String modID) { + super(modID); + } + @Override + public void create(String recipeID, ItemStack outputStack) { + ((RecipeGroup< RecipeEntryBlastFurnace>) RecipeBuilder.getRecipeGroup(modID, "blast_furnace", new RecipeSymbol(Block.furnaceStoneActive.getDefaultStack()))) + .register(recipeID, new RecipeEntryBlastFurnace(input, outputStack)); + } +} diff --git a/src/main/java/turniplabs/halplibe/helper/recipeBuilders/RecipeBuilderFurnace.java b/src/main/java/turniplabs/halplibe/helper/recipeBuilders/RecipeBuilderFurnace.java new file mode 100644 index 0000000..ed43709 --- /dev/null +++ b/src/main/java/turniplabs/halplibe/helper/recipeBuilders/RecipeBuilderFurnace.java @@ -0,0 +1,42 @@ +package turniplabs.halplibe.helper.recipeBuilders; + +import net.minecraft.core.block.Block; +import net.minecraft.core.data.registry.recipe.RecipeGroup; +import net.minecraft.core.data.registry.recipe.RecipeSymbol; +import net.minecraft.core.data.registry.recipe.entry.RecipeEntryFurnace; +import net.minecraft.core.item.IItemConvertible; +import net.minecraft.core.item.ItemStack; +import turniplabs.halplibe.helper.RecipeBuilder; + +import java.util.Objects; + +public class RecipeBuilderFurnace extends RecipeBuilderBase{ + protected RecipeSymbol input; + public RecipeBuilderFurnace(String modID) { + super(modID); + } + public RecipeBuilderFurnace setInput(IItemConvertible item){ + return setInput(item, 0); + } + public RecipeBuilderFurnace setInput(IItemConvertible item, int meta){ + return setInput(new ItemStack(item, 1, meta)); + } + public RecipeBuilderFurnace setInput(ItemStack input){ + return setInput(new RecipeSymbol(input)); + } + public RecipeBuilderFurnace setInput(String itemGroup){ + return setInput(new RecipeSymbol(itemGroup)); + } + public RecipeBuilderFurnace setInput(RecipeSymbol input){ + RecipeBuilderFurnace builder = this.clone(this); + builder.input = Objects.requireNonNull(input, "Input symbol must not be null!"); + return builder; + } + + @Override + public void create(String recipeID, ItemStack outputStack) { + Objects.requireNonNull(input, "Input symbol must not be null!"); + ((RecipeGroup) RecipeBuilder.getRecipeGroup(modID, "furnace", new RecipeSymbol(Block.furnaceStoneActive.getDefaultStack()))) + .register(recipeID, new RecipeEntryFurnace(input, outputStack)); + } +} diff --git a/src/main/java/turniplabs/halplibe/helper/recipeBuilders/RecipeBuilderShaped.java b/src/main/java/turniplabs/halplibe/helper/recipeBuilders/RecipeBuilderShaped.java new file mode 100644 index 0000000..3111201 --- /dev/null +++ b/src/main/java/turniplabs/halplibe/helper/recipeBuilders/RecipeBuilderShaped.java @@ -0,0 +1,95 @@ +package turniplabs.halplibe.helper.recipeBuilders; + +import net.minecraft.core.block.Block; +import net.minecraft.core.data.registry.recipe.RecipeGroup; +import net.minecraft.core.data.registry.recipe.RecipeSymbol; +import net.minecraft.core.data.registry.recipe.entry.RecipeEntryCrafting; +import net.minecraft.core.data.registry.recipe.entry.RecipeEntryCraftingShaped; +import net.minecraft.core.item.IItemConvertible; +import net.minecraft.core.item.ItemStack; +import turniplabs.halplibe.helper.RecipeBuilder; + +import java.util.Arrays; +import java.util.HashMap; + +public class RecipeBuilderShaped extends RecipeBuilderBase{ + protected String[] shape; // Only used for shaped recipes + protected int width; + protected int height; + protected boolean consumeContainer = false; // Only used for shapedRecipes + protected final HashMap symbolShapedMap = new HashMap<>(); + public RecipeBuilderShaped(String modID){ + super(modID); + } + public RecipeBuilderShaped(String modID, String... shape) { + super(modID); + setShapeLocal(shape); + } + public RecipeBuilderShaped setShape(String... shapeTemplate){ + RecipeBuilderShaped builder = this.clone(this); + builder.setShapeLocal(shapeTemplate); + return builder; + } + protected void setShapeLocal(String... shape){ + if (shape == null){ + throw new IllegalArgumentException("Shape Template cannot be set to null!"); + } + if (shape.length == 0){ + throw new IllegalArgumentException("Shape Template cannot have a size of 0!"); + } + if (shape.length > 3){ + throw new IllegalArgumentException("Shape Template height cannot exceed 3!\n" + Arrays.toString(shape)); + } + if (shape[0].length() > 3){ + throw new IllegalArgumentException("Shape Template width cannot exceed 3!\n" + Arrays.toString(shape)); + } + this.height = shape.length; + this.width = shape[0].length(); + this.shape = shape; + } + public RecipeBuilderShaped setConsumeContainer(boolean consumeContainer){ + RecipeBuilderShaped builder = this.clone(this); + builder.consumeContainer = consumeContainer; + return builder; + } + public RecipeBuilderShaped addInput(char templateSymbol, IItemConvertible stack){ + return addInput(templateSymbol, stack, 0); + } + public RecipeBuilderShaped addInput(char templateSymbol, IItemConvertible stack, int meta){ + ItemStack _stack = stack.getDefaultStack(); + _stack.setMetadata(meta); + return addInput(templateSymbol, _stack); + } + public RecipeBuilderShaped addInput(char templateSymbol, ItemStack stack){ + return addInput(templateSymbol, new RecipeSymbol(stack)); + } + public RecipeBuilderShaped addInput(char templateSymbol, String itemGroup) { + return addInput(templateSymbol, new RecipeSymbol(itemGroup)); + } + public RecipeBuilderShaped addInput(char templateSymbol, RecipeSymbol symbol){ + RecipeBuilderShaped builder = this.clone(this); + symbolShapedMap.put(templateSymbol, symbol); + return builder; + } + public void create(String recipeID, ItemStack outputStack) { + if (shape == null) throw new RuntimeException("Shaped recipe: " + recipeID + " attempted to build without a assigned shape!!"); + RecipeSymbol[] recipe = new RecipeSymbol[height * width]; + for (int x = 0; x < width; x++) { + for (int y = 0; y < height; y++) { + Character cha = null; + if (shape[y].length() > x) { + cha = shape[y].charAt(x); + } + RecipeSymbol tempplate = symbolShapedMap.get(cha); + if (tempplate == null){ + recipe[x + y * 3] = null; + } else { + recipe[x + y * 3] = new RecipeSymbol(cha == null ? ' ' : cha, tempplate.getStack(), tempplate.getItemGroup()); + } + + } + } + ((RecipeGroup>) RecipeBuilder.getRecipeGroup(modID, "workbench", new RecipeSymbol(Block.workbench.getDefaultStack()))) + .register(recipeID, new RecipeEntryCraftingShaped(width, height, recipe, outputStack, consumeContainer)); + } +} diff --git a/src/main/java/turniplabs/halplibe/helper/recipeBuilders/RecipeBuilderShapeless.java b/src/main/java/turniplabs/halplibe/helper/recipeBuilders/RecipeBuilderShapeless.java new file mode 100644 index 0000000..bccaa23 --- /dev/null +++ b/src/main/java/turniplabs/halplibe/helper/recipeBuilders/RecipeBuilderShapeless.java @@ -0,0 +1,44 @@ +package turniplabs.halplibe.helper.recipeBuilders; + +import net.minecraft.core.block.Block; +import net.minecraft.core.data.registry.recipe.RecipeGroup; +import net.minecraft.core.data.registry.recipe.RecipeSymbol; +import net.minecraft.core.data.registry.recipe.entry.RecipeEntryCrafting; +import net.minecraft.core.data.registry.recipe.entry.RecipeEntryCraftingShapeless; +import net.minecraft.core.item.IItemConvertible; +import net.minecraft.core.item.ItemStack; +import turniplabs.halplibe.helper.RecipeBuilder; + +import java.util.ArrayList; +import java.util.List; + +public class RecipeBuilderShapeless extends RecipeBuilderBase{ + private final List symbolShapelessList = new ArrayList<>(); + public RecipeBuilderShapeless(String modID) { + super(modID); + } + public RecipeBuilderShapeless addInput(IItemConvertible stack){ + return addInput(stack, 0); + } + public RecipeBuilderShapeless addInput(IItemConvertible stack, int meta){ + ItemStack _stack = stack.getDefaultStack(); + _stack.setMetadata(meta); + return addInput(_stack); + } + public RecipeBuilderShapeless addInput(String itemGroup){ + return addInput(new RecipeSymbol(itemGroup)); + } + public RecipeBuilderShapeless addInput(ItemStack stack){ + return addInput(new RecipeSymbol(stack)); + } + public RecipeBuilderShapeless addInput(RecipeSymbol symbol){ + RecipeBuilderShapeless builder = this.clone(this); + symbolShapelessList.add(symbol); + return builder; + } + @Override + public void create(String recipeID, ItemStack outputStack) { + ((RecipeGroup>) RecipeBuilder.getRecipeGroup(modID, "workbench", new RecipeSymbol(Block.workbench.getDefaultStack()))) + .register(recipeID, new RecipeEntryCraftingShapeless(symbolShapelessList, outputStack)); + } +} diff --git a/src/main/java/turniplabs/halplibe/helper/recipeBuilders/RecipeBuilderTrommel.java b/src/main/java/turniplabs/halplibe/helper/recipeBuilders/RecipeBuilderTrommel.java new file mode 100644 index 0000000..d8c108c --- /dev/null +++ b/src/main/java/turniplabs/halplibe/helper/recipeBuilders/RecipeBuilderTrommel.java @@ -0,0 +1,55 @@ +package turniplabs.halplibe.helper.recipeBuilders; + +import net.minecraft.core.WeightedRandomBag; +import net.minecraft.core.WeightedRandomLootObject; +import net.minecraft.core.block.Block; +import net.minecraft.core.data.registry.recipe.RecipeGroup; +import net.minecraft.core.data.registry.recipe.RecipeSymbol; +import net.minecraft.core.data.registry.recipe.entry.RecipeEntryTrommel; +import net.minecraft.core.item.IItemConvertible; +import net.minecraft.core.item.ItemStack; +import turniplabs.halplibe.helper.RecipeBuilder; + +import java.util.Objects; + +public class RecipeBuilderTrommel extends RecipeBuilderBase{ + protected RecipeSymbol input; + protected WeightedRandomBag bag = new WeightedRandomBag<>(); + public RecipeBuilderTrommel(String modID) { + super(modID); + } + + public RecipeBuilderTrommel setInput(IItemConvertible item){ + return setInput(item, 0); + } + public RecipeBuilderTrommel setInput(IItemConvertible item, int meta){ + return setInput(new ItemStack(item, 1, meta)); + } + public RecipeBuilderTrommel setInput(ItemStack input){ + return setInput(new RecipeSymbol(input)); + } + public RecipeBuilderTrommel setInput(String itemGroup){ + return setInput(new RecipeSymbol(itemGroup)); + } + public RecipeBuilderTrommel setInput(RecipeSymbol input){ + RecipeBuilderTrommel builder = this.clone(this); + builder.input = Objects.requireNonNull(input, "Input symbol must not be null!"); + return builder; + } + public RecipeBuilderTrommel addEntry(WeightedRandomLootObject lootObject, double weight){ + RecipeBuilderTrommel builder = this.clone(this); + builder.bag.addEntry(lootObject, weight); + return builder; + } + + public void create(String recipeID) { + Objects.requireNonNull(input, "Input symbol must not be null!"); + Objects.requireNonNull(bag, "Weighted Bag must not be null!"); + ((RecipeGroup) RecipeBuilder.getRecipeGroup(modID, "trommel", new RecipeSymbol(Block.trommelActive.getDefaultStack()))) + .register(recipeID, new RecipeEntryTrommel(input, bag)); + } + @Override + protected void create(String recipeID, ItemStack outputStack) { + + } +} diff --git a/src/main/java/turniplabs/halplibe/helper/recipeBuilders/modifiers/BlastFurnaceModifier.java b/src/main/java/turniplabs/halplibe/helper/recipeBuilders/modifiers/BlastFurnaceModifier.java new file mode 100644 index 0000000..b6d8fa6 --- /dev/null +++ b/src/main/java/turniplabs/halplibe/helper/recipeBuilders/modifiers/BlastFurnaceModifier.java @@ -0,0 +1,19 @@ +package turniplabs.halplibe.helper.recipeBuilders.modifiers; + +import net.minecraft.core.block.Block; +import net.minecraft.core.data.registry.recipe.RecipeGroup; +import net.minecraft.core.data.registry.recipe.RecipeSymbol; +import net.minecraft.core.data.registry.recipe.entry.RecipeEntryBlastFurnace; +import turniplabs.halplibe.helper.RecipeBuilder; +import turniplabs.halplibe.util.IUnregister; + +public class BlastFurnaceModifier { + protected RecipeGroup recipeGroup; + public BlastFurnaceModifier(String namespace){ + recipeGroup = (RecipeGroup) RecipeBuilder.getRecipeGroup(namespace, "blast_furnace", new RecipeSymbol(Block.furnaceBlastActive.getDefaultStack())); + } + public BlastFurnaceModifier removeRecipe(String recipeID){ + ((IUnregister)recipeGroup).unregister(recipeID); + return this; + } +} diff --git a/src/main/java/turniplabs/halplibe/helper/recipeBuilders/modifiers/FurnaceModifier.java b/src/main/java/turniplabs/halplibe/helper/recipeBuilders/modifiers/FurnaceModifier.java new file mode 100644 index 0000000..005f7ea --- /dev/null +++ b/src/main/java/turniplabs/halplibe/helper/recipeBuilders/modifiers/FurnaceModifier.java @@ -0,0 +1,19 @@ +package turniplabs.halplibe.helper.recipeBuilders.modifiers; + +import net.minecraft.core.block.Block; +import net.minecraft.core.data.registry.recipe.RecipeGroup; +import net.minecraft.core.data.registry.recipe.RecipeSymbol; +import net.minecraft.core.data.registry.recipe.entry.RecipeEntryFurnace; +import turniplabs.halplibe.helper.RecipeBuilder; +import turniplabs.halplibe.util.IUnregister; + +public class FurnaceModifier { + protected RecipeGroup recipeGroup; + public FurnaceModifier(String namespace){ + recipeGroup = (RecipeGroup) RecipeBuilder.getRecipeGroup(namespace, "furnace", new RecipeSymbol(Block.furnaceStoneActive.getDefaultStack())); + } + public FurnaceModifier removeRecipe(String recipeID){ + ((IUnregister)recipeGroup).unregister(recipeID); + return this; + } +} diff --git a/src/main/java/turniplabs/halplibe/helper/recipeBuilders/modifiers/TrommelModifier.java b/src/main/java/turniplabs/halplibe/helper/recipeBuilders/modifiers/TrommelModifier.java new file mode 100644 index 0000000..db5cd0e --- /dev/null +++ b/src/main/java/turniplabs/halplibe/helper/recipeBuilders/modifiers/TrommelModifier.java @@ -0,0 +1,97 @@ +package turniplabs.halplibe.helper.recipeBuilders.modifiers; + +import net.minecraft.core.WeightedRandomBag; +import net.minecraft.core.WeightedRandomLootObject; +import net.minecraft.core.block.Block; +import net.minecraft.core.data.registry.recipe.RecipeSymbol; +import net.minecraft.core.item.ItemStack; +import turniplabs.halplibe.helper.RecipeBuilder; +import turniplabs.halplibe.mixin.accessors.WeightedRandomBagAccessor; +import turniplabs.halplibe.mixin.accessors.WeightedRandomBagEntryAccessor; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +public class TrommelModifier { + private final WeightedRandomBag trommelEntry; + public TrommelModifier(String namespace, String key){ + trommelEntry = (WeightedRandomBag) Objects.requireNonNull(RecipeBuilder.getRecipeGroup(namespace, "trommel", new RecipeSymbol(Block.trommelActive.getDefaultStack())).getItem(key), "Requested recipe " + (namespace + ":trommel/" + key) + " does not exist!").getOutput(); + } + public TrommelModifier addEntry(WeightedRandomLootObject lootObject, double weight){ + trommelEntry.addEntry(lootObject, weight); + return this; + } + + /** + * @param outputStack The stack the entries to be removed use + * Deletes all entries matching the provided stack. + */ + public TrommelModifier removeEntries(ItemStack outputStack){ + List.Entry> outputs = new ArrayList<>(trommelEntry.getEntriesWithWeights()); + for (WeightedRandomBag.Entry object : outputs){ + WeightedRandomLootObject weightedObject = (WeightedRandomLootObject) object.getObject(); + if (weightedObject.getItemStack().isItemEqual(outputStack)){ + ((WeightedRandomBagAccessor)trommelEntry).getRawEntries().remove(object); + } + } + recalculateWeights(); + return this; + } + /** + * @param outputStack The stack the entry to be removed uses + * Deletes the first entry matching the provided stack and weight. + */ + public TrommelModifier removeEntry(ItemStack outputStack, double weight){ + List.Entry> outputs = new ArrayList<>(trommelEntry.getEntriesWithWeights()); + for (WeightedRandomBag.Entry object : outputs){ + WeightedRandomLootObject weightedObject = (WeightedRandomLootObject) object.getObject(); + if (weightedObject.getItemStack().isItemEqual(outputStack) && weight == object.getWeight()){ + ((WeightedRandomBagAccessor)trommelEntry).getRawEntries().remove(object); + break; + } + } + recalculateWeights(); + return this; + } + + /** + * @param outputStack The stack the entry to be modified uses + * @param oldWeight The weight the bag currently uses + * @param newWeight The weight to replace the old weight with + */ + public TrommelModifier setWeight(ItemStack outputStack, double oldWeight, double newWeight){ + List.Entry> outputs = new ArrayList<>(trommelEntry.getEntriesWithWeights()); + for (WeightedRandomBag.Entry object : outputs){ + WeightedRandomLootObject weightedObject = (WeightedRandomLootObject) object.getObject(); + if (weightedObject.getItemStack().isItemEqual(outputStack) && oldWeight == object.getWeight()){ + ((WeightedRandomBagEntryAccessor)object).setWeight(newWeight); + break; + } + } + recalculateWeights(); + return this; + } + /** + * @param outputStack The stack the entries to be modified uses + * @param newWeight The weight to replace the old weight with + */ + public TrommelModifier setWeights(ItemStack outputStack, double newWeight){ + List.Entry> outputs = new ArrayList<>(trommelEntry.getEntriesWithWeights()); + for (WeightedRandomBag.Entry object : outputs){ + WeightedRandomLootObject weightedObject = (WeightedRandomLootObject) object.getObject(); + if (weightedObject.getItemStack().isItemEqual(outputStack)){ + ((WeightedRandomBagEntryAccessor)object).setWeight(newWeight); + } + } + recalculateWeights(); + return this; + } + protected void recalculateWeights(){ + double weight = 0; + for (WeightedRandomBag.Entry object : trommelEntry.getEntriesWithWeights()){ + weight += object.getWeight(); + } + ((WeightedRandomBagAccessor)trommelEntry).setAccumulatedWeight(weight); + } +} diff --git a/src/main/java/turniplabs/halplibe/helper/recipeBuilders/modifiers/WorkbenchModifier.java b/src/main/java/turniplabs/halplibe/helper/recipeBuilders/modifiers/WorkbenchModifier.java new file mode 100644 index 0000000..de90d6f --- /dev/null +++ b/src/main/java/turniplabs/halplibe/helper/recipeBuilders/modifiers/WorkbenchModifier.java @@ -0,0 +1,19 @@ +package turniplabs.halplibe.helper.recipeBuilders.modifiers; + +import net.minecraft.core.block.Block; +import net.minecraft.core.data.registry.recipe.RecipeGroup; +import net.minecraft.core.data.registry.recipe.RecipeSymbol; +import net.minecraft.core.data.registry.recipe.entry.RecipeEntryCrafting; +import turniplabs.halplibe.helper.RecipeBuilder; +import turniplabs.halplibe.util.IUnregister; + +public class WorkbenchModifier { + protected RecipeGroup> recipeGroup; + public WorkbenchModifier(String namespace){ + recipeGroup = (RecipeGroup>) RecipeBuilder.getRecipeGroup(namespace, "workbench", new RecipeSymbol(Block.workbench.getDefaultStack())); + } + public WorkbenchModifier removeRecipe(String recipeID){ + ((IUnregister>)recipeGroup).unregister(recipeID); + return this; + } +} diff --git a/src/main/java/turniplabs/halplibe/mixin/accessors/WeightedRandomBagAccessor.java b/src/main/java/turniplabs/halplibe/mixin/accessors/WeightedRandomBagAccessor.java new file mode 100644 index 0000000..6f813a9 --- /dev/null +++ b/src/main/java/turniplabs/halplibe/mixin/accessors/WeightedRandomBagAccessor.java @@ -0,0 +1,17 @@ +package turniplabs.halplibe.mixin.accessors; + +import net.minecraft.core.WeightedRandomBag; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +import java.util.List; + +@Mixin(WeightedRandomBag.class) +public interface WeightedRandomBagAccessor { + @Accessor("entries") + List getRawEntries(); + @Accessor + double getAccumulatedWeight(); + @Accessor("accumulatedWeight") + void setAccumulatedWeight(double weight); +} diff --git a/src/main/java/turniplabs/halplibe/mixin/accessors/WeightedRandomBagEntryAccessor.java b/src/main/java/turniplabs/halplibe/mixin/accessors/WeightedRandomBagEntryAccessor.java new file mode 100644 index 0000000..da81bd2 --- /dev/null +++ b/src/main/java/turniplabs/halplibe/mixin/accessors/WeightedRandomBagEntryAccessor.java @@ -0,0 +1,11 @@ +package turniplabs.halplibe.mixin.accessors; + +import net.minecraft.core.WeightedRandomBag; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(WeightedRandomBag.Entry.class) +public interface WeightedRandomBagEntryAccessor { + @Accessor("weight") + void setWeight(double weight); +} diff --git a/src/main/java/turniplabs/halplibe/mixin/mixins/ItemStackJsonAdapterMixin.java b/src/main/java/turniplabs/halplibe/mixin/mixins/ItemStackJsonAdapterMixin.java index 431b64c..06d3a99 100644 --- a/src/main/java/turniplabs/halplibe/mixin/mixins/ItemStackJsonAdapterMixin.java +++ b/src/main/java/turniplabs/halplibe/mixin/mixins/ItemStackJsonAdapterMixin.java @@ -10,8 +10,10 @@ import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import turniplabs.halplibe.HalpLibe; +import turniplabs.halplibe.helper.RecipeBuilder; import java.lang.reflect.Type; @@ -28,5 +30,14 @@ protected void deserializeKey(JsonElement json, Type typeOfT, JsonDeserializatio cir.setReturnValue(stack); } } + @Redirect(method = "serialize(Lnet/minecraft/core/item/ItemStack;Ljava/lang/reflect/Type;Lcom/google/gson/JsonSerializationContext;)Lcom/google/gson/JsonElement;", + at = @At(value = "INVOKE", target = "Lcom/google/gson/JsonObject;addProperty(Ljava/lang/String;Ljava/lang/Number;)V", ordinal = 0)) + private void useKeyForSerializing(JsonObject instance, String property, Number value){ + if (RecipeBuilder.isExporting){ + instance.addProperty("key", Item.itemsList[(int) value].getKey()); + return; + } + instance.addProperty(property, value); + } } diff --git a/src/main/java/turniplabs/halplibe/mixin/mixins/MinecraftMixin.java b/src/main/java/turniplabs/halplibe/mixin/mixins/MinecraftMixin.java index c9f6fc7..0333db5 100644 --- a/src/main/java/turniplabs/halplibe/mixin/mixins/MinecraftMixin.java +++ b/src/main/java/turniplabs/halplibe/mixin/mixins/MinecraftMixin.java @@ -2,10 +2,17 @@ import net.fabricmc.loader.api.FabricLoader; import net.minecraft.client.Minecraft; +import net.minecraft.core.WeightedRandomLootObject; +import net.minecraft.core.block.Block; +import net.minecraft.core.item.Item; +import net.minecraft.core.item.ItemStack; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import turniplabs.halplibe.HalpLibe; +import turniplabs.halplibe.helper.RecipeBuilder; +import turniplabs.halplibe.helper.recipeBuilders.RecipeBuilderShaped; import turniplabs.halplibe.util.ClientStartEntrypoint; import turniplabs.halplibe.util.GameStartEntrypoint; import turniplabs.halplibe.util.RecipeEntrypoint; @@ -32,5 +39,8 @@ public void beforeGameStartEntrypoint(CallbackInfo ci){ public void afterGameStartEntrypoint(CallbackInfo ci){ FabricLoader.getInstance().getEntrypoints("afterGameStart", GameStartEntrypoint.class).forEach(GameStartEntrypoint::afterGameStart); FabricLoader.getInstance().getEntrypoints("afterClientStart", ClientStartEntrypoint.class).forEach(ClientStartEntrypoint::afterClientStart); + if (HalpLibe.exportRecipes){ + RecipeBuilder.exportRecipes(); + } } } diff --git a/src/main/java/turniplabs/halplibe/mixin/mixins/MinecraftServerMixin.java b/src/main/java/turniplabs/halplibe/mixin/mixins/MinecraftServerMixin.java index b1910ab..dd8339e 100644 --- a/src/main/java/turniplabs/halplibe/mixin/mixins/MinecraftServerMixin.java +++ b/src/main/java/turniplabs/halplibe/mixin/mixins/MinecraftServerMixin.java @@ -2,12 +2,18 @@ import net.fabricmc.loader.api.FabricLoader; import net.minecraft.core.Global; +import net.minecraft.core.WeightedRandomLootObject; +import net.minecraft.core.block.Block; +import net.minecraft.core.item.Item; import net.minecraft.server.MinecraftServer; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import turniplabs.halplibe.HalpLibe; +import turniplabs.halplibe.helper.RecipeBuilder; +import turniplabs.halplibe.helper.recipeBuilders.RecipeBuilderShaped; import turniplabs.halplibe.util.GameStartEntrypoint; import turniplabs.halplibe.util.RecipeEntrypoint; @@ -29,5 +35,8 @@ public void beforeGameStartEntrypoint(CallbackInfoReturnable cir){ @Inject(method = "startServer", at = @At("TAIL")) public void afterGameStartEntrypoint(CallbackInfoReturnable cir){ FabricLoader.getInstance().getEntrypoints("afterGameStart", GameStartEntrypoint.class).forEach(GameStartEntrypoint::afterGameStart); + if (HalpLibe.exportRecipes){ + RecipeBuilder.exportRecipes(); + } } } diff --git a/src/main/java/turniplabs/halplibe/mixin/mixins/RegistryMixin.java b/src/main/java/turniplabs/halplibe/mixin/mixins/RegistryMixin.java new file mode 100644 index 0000000..e2f56b2 --- /dev/null +++ b/src/main/java/turniplabs/halplibe/mixin/mixins/RegistryMixin.java @@ -0,0 +1,34 @@ +package turniplabs.halplibe.mixin.mixins; + +import net.minecraft.core.data.registry.Registry; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; +import turniplabs.halplibe.util.IUnregister; + +import java.util.List; +import java.util.Map; + +@Mixin(value = Registry.class, remap = false) +public abstract class RegistryMixin implements IUnregister { + @Shadow @Final private Map keyItemMap; + + @Shadow public abstract T getItem(String key); + + @Shadow @Final private List items; + + @Shadow @Final private Map itemKeyMap; + + @Unique + public void unregister(String key){ + T object = getItem(key); + items.remove(object); + keyItemMap.remove(key); + itemKeyMap.remove(object); + } + @Unique + public void unregister(T object){ + unregister(itemKeyMap.get(object)); + } +} diff --git a/src/main/java/turniplabs/halplibe/mixin/mixins/TileEntityBlastFurnaceMixin.java b/src/main/java/turniplabs/halplibe/mixin/mixins/TileEntityBlastFurnaceMixin.java new file mode 100644 index 0000000..b908c5b --- /dev/null +++ b/src/main/java/turniplabs/halplibe/mixin/mixins/TileEntityBlastFurnaceMixin.java @@ -0,0 +1,52 @@ +package turniplabs.halplibe.mixin.mixins; + +import net.minecraft.core.block.entity.TileEntityBlastFurnace; +import net.minecraft.core.block.entity.TileEntityFurnace; +import net.minecraft.core.data.registry.Registries; +import net.minecraft.core.data.registry.recipe.RecipeGroup; +import net.minecraft.core.data.registry.recipe.RecipeRegistry; +import net.minecraft.core.data.registry.recipe.entry.RecipeEntryBlastFurnace; +import net.minecraft.core.data.registry.recipe.entry.RecipeEntryFurnace; +import net.minecraft.core.item.ItemStack; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +import java.util.List; + +@Mixin(value = TileEntityBlastFurnace.class, remap = false) +public class TileEntityBlastFurnaceMixin extends TileEntityFurnace { + @Redirect(method = "canSmelt()Z", at = @At(value = "INVOKE", target = "Lnet/minecraft/core/data/registry/recipe/RecipeRegistry;getGroupFromKey(Ljava/lang/String;)Lnet/minecraft/core/data/registry/recipe/RecipeGroup;")) + private RecipeGroup canSmeltFake(RecipeRegistry instance, String e){ + RecipeGroup group = groupEmulator(); + if (group != null) return groupEmulator(); + return instance.getGroupFromKey(e); + } + @Redirect(method = "smeltItem()V", at = @At(value = "INVOKE", target = "Lnet/minecraft/core/data/registry/recipe/RecipeRegistry;getGroupFromKey(Ljava/lang/String;)Lnet/minecraft/core/data/registry/recipe/RecipeGroup;")) + private RecipeGroup doSmeltFake(RecipeRegistry instance, String e){ + RecipeGroup group = groupEmulator(); + if (group != null) return groupEmulator(); + return instance.getGroupFromKey(e); + } + + @Unique + private RecipeGroup groupEmulator(){ + RecipeGroup group = new RecipeGroup<>(null); + RecipeEntryBlastFurnace entryFurnace = getMatchingRecipe(); + if (entryFurnace == null) return null; + group.register("don'tCareLol", getMatchingRecipe()); + return group; + } + @Unique + private RecipeEntryBlastFurnace getMatchingRecipe(){ + List recipes = Registries.RECIPES.getAllBlastFurnaceRecipes(); + for (RecipeEntryBlastFurnace recipeEntryFurnace : recipes){ + if (recipeEntryFurnace.matches(furnaceItemStacks[0])){ + return recipeEntryFurnace; + } + } + return null; + } +} diff --git a/src/main/java/turniplabs/halplibe/mixin/mixins/TileEntityFurnaceMixin.java b/src/main/java/turniplabs/halplibe/mixin/mixins/TileEntityFurnaceMixin.java new file mode 100644 index 0000000..c537f93 --- /dev/null +++ b/src/main/java/turniplabs/halplibe/mixin/mixins/TileEntityFurnaceMixin.java @@ -0,0 +1,51 @@ +package turniplabs.halplibe.mixin.mixins; + +import net.minecraft.core.block.entity.TileEntityFurnace; +import net.minecraft.core.data.registry.Registries; +import net.minecraft.core.data.registry.recipe.RecipeGroup; +import net.minecraft.core.data.registry.recipe.RecipeRegistry; +import net.minecraft.core.data.registry.recipe.entry.RecipeEntryFurnace; +import net.minecraft.core.item.ItemStack; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +import java.util.List; + +@Mixin(value = TileEntityFurnace.class, remap = false) +public class TileEntityFurnaceMixin { + @Shadow protected ItemStack[] furnaceItemStacks; + @Redirect(method = "canSmelt()Z", at = @At(value = "INVOKE", target = "Lnet/minecraft/core/data/registry/recipe/RecipeRegistry;getGroupFromKey(Ljava/lang/String;)Lnet/minecraft/core/data/registry/recipe/RecipeGroup;")) + private RecipeGroup canSmeltFake(RecipeRegistry instance, String e){ + RecipeGroup group = groupEmulator(); + if (group != null) return groupEmulator(); + return instance.getGroupFromKey(e); + } + @Redirect(method = "smeltItem()V", at = @At(value = "INVOKE", target = "Lnet/minecraft/core/data/registry/recipe/RecipeRegistry;getGroupFromKey(Ljava/lang/String;)Lnet/minecraft/core/data/registry/recipe/RecipeGroup;")) + private RecipeGroup doSmeltFake(RecipeRegistry instance, String e){ + RecipeGroup group = groupEmulator(); + if (group != null) return groupEmulator(); + return instance.getGroupFromKey(e); + } + + @Unique + private RecipeGroup groupEmulator(){ + RecipeGroup group = new RecipeGroup<>(null); + RecipeEntryFurnace entryFurnace = getMatchingRecipe(); + if (entryFurnace == null) return null; + group.register("don'tCareLol", getMatchingRecipe()); + return group; + } + @Unique + private RecipeEntryFurnace getMatchingRecipe(){ + List recipes = Registries.RECIPES.getAllFurnaceRecipes(); + for (RecipeEntryFurnace recipeEntryFurnace : recipes){ + if (recipeEntryFurnace.matches(furnaceItemStacks[0])){ + return recipeEntryFurnace; + } + } + return null; + } +} diff --git a/src/main/java/turniplabs/halplibe/util/IUnregister.java b/src/main/java/turniplabs/halplibe/util/IUnregister.java new file mode 100644 index 0000000..5950041 --- /dev/null +++ b/src/main/java/turniplabs/halplibe/util/IUnregister.java @@ -0,0 +1,8 @@ +package turniplabs.halplibe.util; + +import org.spongepowered.asm.mixin.Unique; + +public interface IUnregister { + void unregister(String key); + void unregister(T object); +} diff --git a/src/main/resources/halplibe.mixins.json b/src/main/resources/halplibe.mixins.json index 750a1e7..22e9652 100644 --- a/src/main/resources/halplibe.mixins.json +++ b/src/main/resources/halplibe.mixins.json @@ -8,10 +8,15 @@ "accessors.EntityListAccessor", "accessors.LanguageAccessor", "accessors.TileEntityAccessor", - "mixins.BlockSoundDispatcherMixin", + "accessors.WeightedRandomBagAccessor", + "accessors.WeightedRandomBagEntryAccessor", "mixins.BlockFireMixin", + "mixins.BlockSoundDispatcherMixin", "mixins.I18nMixin", "mixins.ItemStackJsonAdapterMixin", + "mixins.RegistryMixin", + "mixins.TileEntityBlastFurnaceMixin", + "mixins.TileEntityFurnaceMixin", "mixins.network.EntityTrackerEntryMixin", "mixins.network.NetworkManagerMixin", "mixins.network.PacketMixin",