From 632772b666bc2e734941d15206074298820295db Mon Sep 17 00:00:00 2001 From: Lucas Vas Date: Mon, 8 Sep 2025 23:38:11 -0400 Subject: [PATCH] feat: implement basic crafting block This block still needs a lot of polish and real crafting mechanics, but it serves as a relatively complex idea of how to create a crafting recipe type and a block/BlockEntity to go along with it. --- .../uncreativebunch/magicmod/MagicMod.java | 2 + .../block/crafting/MagicalCrafterBlock.java | 34 ++++- .../entity/MagicalCrafterBlockEntity.java | 84 ++++++++-- .../magicmod/datagen/DataGenerator.java | 2 +- .../magicmod/datagen/MagicRecipeProvider.java | 44 ++++++ .../datagen/StickTagRecipeProvider.java | 42 ----- .../ImplementedInventory.java | 2 +- .../magicmod/inventory/OutputInventory.java | 25 +++ .../magicmod/item/ModItems.java | 2 +- .../magicmod/recipe/MagicShapedInput.java | 4 +- .../magicmod/recipe/MagicShapedRecipe.java | 144 ++++++++++-------- .../magicmod/recipe/ModRecipeSerializers.java | 16 +- .../magicmod/recipe/ModRecipeTypes.java | 5 +- .../screen/MagicalCraftingScreenHandler.java | 40 ++--- 14 files changed, 298 insertions(+), 148 deletions(-) create mode 100644 src/main/java/com/uncreativebunch/magicmod/datagen/MagicRecipeProvider.java delete mode 100644 src/main/java/com/uncreativebunch/magicmod/datagen/StickTagRecipeProvider.java rename src/main/java/com/uncreativebunch/magicmod/{util => inventory}/ImplementedInventory.java (97%) create mode 100644 src/main/java/com/uncreativebunch/magicmod/inventory/OutputInventory.java diff --git a/src/main/java/com/uncreativebunch/magicmod/MagicMod.java b/src/main/java/com/uncreativebunch/magicmod/MagicMod.java index c2c1efb..6be6486 100644 --- a/src/main/java/com/uncreativebunch/magicmod/MagicMod.java +++ b/src/main/java/com/uncreativebunch/magicmod/MagicMod.java @@ -4,6 +4,7 @@ import com.uncreativebunch.magicmod.block.ModBlocks; import com.uncreativebunch.magicmod.block.entity.ModBlockEntityTypes; import com.uncreativebunch.magicmod.item.ModItemGroups; import com.uncreativebunch.magicmod.item.ModItems; +import com.uncreativebunch.magicmod.recipe.ModRecipeSerializers; import com.uncreativebunch.magicmod.recipe.ModRecipeTypes; import com.uncreativebunch.magicmod.screen.ModScreenHandlers; import net.fabricmc.api.ModInitializer; @@ -33,6 +34,7 @@ public class MagicMod implements ModInitializer { ModItems.init(); ModItemGroups.init(); ModScreenHandlers.init(); + ModRecipeSerializers.init(); ModRecipeTypes.init(); ModBlockEntityTypes.init(); LOGGER.info("Finished Magic Mod initialization!"); diff --git a/src/main/java/com/uncreativebunch/magicmod/block/crafting/MagicalCrafterBlock.java b/src/main/java/com/uncreativebunch/magicmod/block/crafting/MagicalCrafterBlock.java index a36744a..36616df 100644 --- a/src/main/java/com/uncreativebunch/magicmod/block/crafting/MagicalCrafterBlock.java +++ b/src/main/java/com/uncreativebunch/magicmod/block/crafting/MagicalCrafterBlock.java @@ -2,10 +2,14 @@ package com.uncreativebunch.magicmod.block.crafting; import com.mojang.serialization.MapCodec; import com.uncreativebunch.magicmod.MagicMod; +import com.uncreativebunch.magicmod.block.ModBlocks; import com.uncreativebunch.magicmod.block.entity.MagicalCrafterBlockEntity; +import com.uncreativebunch.magicmod.block.entity.ModBlockEntityTypes; import com.uncreativebunch.magicmod.screen.MagicalCraftingScreenHandler; import net.minecraft.block.*; import net.minecraft.block.entity.BlockEntity; +import net.minecraft.block.entity.BlockEntityTicker; +import net.minecraft.block.entity.BlockEntityType; import net.minecraft.block.enums.SlabType; import net.minecraft.entity.EntityType; import net.minecraft.entity.LightningEntity; @@ -80,14 +84,14 @@ public class MagicalCrafterBlock extends BlockWithEntity { return ActionResult.SUCCESS; } - @Override - public NamedScreenHandlerFactory createScreenHandlerFactory(BlockState state, World world, BlockPos pos) { - return new SimpleNamedScreenHandlerFactory( - ((syncId, playerInventory, player) -> - new MagicalCraftingScreenHandler(syncId, playerInventory, ScreenHandlerContext.create(world, pos))), - Text.translatable("container.magical_crafting_table") - ); - } +// @Override +// public NamedScreenHandlerFactory createScreenHandlerFactory(BlockState state, World world, BlockPos pos) { +// return new SimpleNamedScreenHandlerFactory( +// ((syncId, playerInventory, player) -> +// new MagicalCraftingScreenHandler(syncId, playerInventory, ScreenHandlerContext.create(world, pos))), +// Text.translatable("container.magical_crafting_table") +// ); +// } @Override public BlockEntity createBlockEntity(BlockPos pos, BlockState state) { @@ -102,4 +106,18 @@ public class MagicalCrafterBlock extends BlockWithEntity { protected void appendProperties(StateManager.Builder builder) { builder.add(SlabBlock.TYPE, SlabBlock.WATERLOGGED); } + + /** + * Creates a {@link BlockEntityTicker} that runs the specified "tick" function from the + * given {@link BlockEntityType}. + * @param world The {@link World} instance. + * @param state The {@link BlockState} of the block. + * @param type The {@link BlockEntityType} to run the tick function from. + * @return A new {@link BlockEntityTicker} that runs the specified tick function. + * @param The {@link BlockEntity} to use. + */ + @Override + public BlockEntityTicker getTicker(World world, BlockState state, BlockEntityType type) { + return validateTicker(type, ModBlockEntityTypes.MAGICAL_CRAFTING_TABLE, MagicalCrafterBlockEntity::tick); + } } diff --git a/src/main/java/com/uncreativebunch/magicmod/block/entity/MagicalCrafterBlockEntity.java b/src/main/java/com/uncreativebunch/magicmod/block/entity/MagicalCrafterBlockEntity.java index 2c2c75d..257bc4b 100644 --- a/src/main/java/com/uncreativebunch/magicmod/block/entity/MagicalCrafterBlockEntity.java +++ b/src/main/java/com/uncreativebunch/magicmod/block/entity/MagicalCrafterBlockEntity.java @@ -1,21 +1,87 @@ package com.uncreativebunch.magicmod.block.entity; -import com.uncreativebunch.magicmod.util.ImplementedInventory; +import com.uncreativebunch.magicmod.inventory.OutputInventory; +import com.uncreativebunch.magicmod.recipe.MagicShapedInput; +import com.uncreativebunch.magicmod.recipe.ModRecipeTypes; +import com.uncreativebunch.magicmod.recipe.MagicShapedRecipe; +import com.uncreativebunch.magicmod.screen.MagicalCraftingScreenHandler; import net.minecraft.block.BlockState; import net.minecraft.block.entity.BlockEntity; -import net.minecraft.item.ItemStack; -import net.minecraft.util.collection.DefaultedList; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.entity.player.PlayerInventory; +import net.minecraft.inventory.SimpleInventory; +import net.minecraft.recipe.RecipeEntry; +import net.minecraft.recipe.ServerRecipeManager; +import net.minecraft.screen.NamedScreenHandlerFactory; +import net.minecraft.screen.ScreenHandler; +import net.minecraft.server.world.ServerWorld; +import net.minecraft.text.Text; import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; -public class MagicalCrafterBlockEntity extends BlockEntity implements ImplementedInventory { - protected DefaultedList items = DefaultedList.ofSize(3, ItemStack.EMPTY); +import java.util.Optional; - @Override - public DefaultedList getItems() { - return items; - } +public class MagicalCrafterBlockEntity extends BlockEntity implements NamedScreenHandlerFactory { + private final ServerRecipeManager.MatchGetter recipeGetter; + + /** + * The input inventory for crafting ingredients. It goes in the order core, material, and grip. + */ + private final SimpleInventory inputInventory = new SimpleInventory(3); + /** + * The output inventory for crafting ingredients. + */ + private final OutputInventory outputInventory = new OutputInventory(1); public MagicalCrafterBlockEntity(BlockPos pos, BlockState state) { super(ModBlockEntityTypes.MAGICAL_CRAFTING_TABLE, pos, state); + this.recipeGetter = ServerRecipeManager.createCachedMatchGetter(ModRecipeTypes.MAGIC_SHAPED); + } + + /** + * Gets the input inventory for this block entity. + * @return A {@link SimpleInventory} that represents the input inventory. + */ + public SimpleInventory getInputInventory() { return inputInventory; } + + /** + * Gets the output inventory for this block entity. + * @return An {@link OutputInventory} that represents the output slot. + */ + public OutputInventory getOutputInventory() { return outputInventory; } + + /** + * The ticking logic. This will check if the block's inventory contains the correct items to craft + * a {@link MagicShapedRecipe}. + * @param world The {@link World} instance. + * @param pos The {@link BlockPos} positional data. + * @param state The {@link BlockState} of the block. + * @param blockEntity An instance of this type. + */ + public static void tick(World world, BlockPos pos, BlockState state, MagicalCrafterBlockEntity blockEntity) { + // Check if the recipe within the crafting inventory matches any recipes. Must happen on server side. + if (world instanceof ServerWorld serverWorld) { + Optional> matched = blockEntity.recipeGetter + .getFirstMatch( + new MagicShapedInput(blockEntity.inputInventory.getHeldStacks()), + serverWorld + ); + + if (matched.isPresent()) { + // Work with the RecipeEntry here + MagicShapedRecipe entry = matched.get().value(); + blockEntity.outputInventory.heldStacks.set(0, entry.output()); + } + } + } + + @Override + public ScreenHandler createMenu(int syncId, PlayerInventory playerInventory, PlayerEntity player) { + return new MagicalCraftingScreenHandler(syncId, playerInventory, inputInventory, outputInventory); + } + + @Override + public Text getDisplayName() { + return Text.translatable(getCachedState().getBlock().getTranslationKey()); } } diff --git a/src/main/java/com/uncreativebunch/magicmod/datagen/DataGenerator.java b/src/main/java/com/uncreativebunch/magicmod/datagen/DataGenerator.java index f5e2a80..8f01978 100644 --- a/src/main/java/com/uncreativebunch/magicmod/datagen/DataGenerator.java +++ b/src/main/java/com/uncreativebunch/magicmod/datagen/DataGenerator.java @@ -13,6 +13,6 @@ public class DataGenerator implements DataGeneratorEntrypoint { MagicMod.LOGGER.info("Hello from the data generator!"); // Add providers to the pack here. - pack.addProvider(StickTagRecipeProvider::new); + pack.addProvider(MagicRecipeProvider::new); } } diff --git a/src/main/java/com/uncreativebunch/magicmod/datagen/MagicRecipeProvider.java b/src/main/java/com/uncreativebunch/magicmod/datagen/MagicRecipeProvider.java new file mode 100644 index 0000000..0a80ffd --- /dev/null +++ b/src/main/java/com/uncreativebunch/magicmod/datagen/MagicRecipeProvider.java @@ -0,0 +1,44 @@ +package com.uncreativebunch.magicmod.datagen; + +import com.uncreativebunch.magicmod.recipe.MagicShapedRecipe; +import net.fabricmc.fabric.api.datagen.v1.FabricDataOutput; +import net.fabricmc.fabric.api.datagen.v1.provider.FabricRecipeProvider; +import net.minecraft.data.recipe.RecipeExporter; +import net.minecraft.data.recipe.RecipeGenerator; +import net.minecraft.item.Item; +import net.minecraft.item.Items; +import net.minecraft.recipe.Ingredient; +import net.minecraft.registry.RegistryKeys; +import net.minecraft.registry.RegistryWrapper; +import net.minecraft.util.Identifier; + +import java.util.concurrent.CompletableFuture; + +public class MagicRecipeProvider extends FabricRecipeProvider { + public MagicRecipeProvider(FabricDataOutput output, CompletableFuture registriesFuture) { + super(output, registriesFuture); + } + + @Override + protected RecipeGenerator getRecipeGenerator(RegistryWrapper.WrapperLookup registryLookup, RecipeExporter exporter) { + return new RecipeGenerator(registryLookup, exporter) { + @Override + public void generate() { + RegistryWrapper.Impl itemLookup = registries.getOrThrow(RegistryKeys.ITEM); + + // Recipes are added here. We only want to create MagicShapedRecipes here. + MagicShapedRecipe.Builder.build( + Ingredient.ofItem(Items.STICK), + Ingredient.ofItem(Items.STICK), + Ingredient.ofItem(Items.STICK), + Items.REDSTONE.getDefaultStack() + ).offerTo(exporter, Identifier.of("magic_mod", "magic_testing_recipe")); + } + }; + } + + @Override + public String getName() { + return "MagicRecipeProvider"; + } +} diff --git a/src/main/java/com/uncreativebunch/magicmod/datagen/StickTagRecipeProvider.java b/src/main/java/com/uncreativebunch/magicmod/datagen/StickTagRecipeProvider.java deleted file mode 100644 index 7689ba8..0000000 --- a/src/main/java/com/uncreativebunch/magicmod/datagen/StickTagRecipeProvider.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.uncreativebunch.magicmod.datagen; - -import com.uncreativebunch.magicmod.MagicMod; -import net.fabricmc.fabric.api.datagen.v1.FabricDataOutput; -import net.fabricmc.fabric.api.datagen.v1.provider.FabricRecipeProvider; -import net.minecraft.data.recipe.RecipeExporter; -import net.minecraft.data.recipe.RecipeGenerator; -import net.minecraft.registry.RegistryWrapper; - -import java.util.concurrent.CompletableFuture; - -public class StickTagRecipeProvider extends FabricRecipeProvider { - public StickTagRecipeProvider(FabricDataOutput output, CompletableFuture registriesFuture) { - super(output, registriesFuture); - } - - @Override - protected RecipeGenerator getRecipeGenerator(RegistryWrapper.WrapperLookup lookup, RecipeExporter exporter) { - return new RecipeGenerator(lookup, exporter) { - @Override - public void generate() { - MagicMod.LOGGER.info("Hello from the recipe provider!"); - MagicMod.LOGGER.info("This cannot scan recipes - apparently they don't exist in this context."); - - // Create a builder for our custom recipe types. This will allow for type-safe creation of recipes. - // This builder should likely be defined as a static class or function within the main Recipe class. - // RecipeBuilder.create( - // Ingredient.fromTag(ItemTags.MAGIC_MATERIALS), - // Ingredient.fromTag(ItemTags.MAGIC_CORES), - // Ingredient.fromTag(ItemTags.MAGIC_GRIPS), - // ModItems.BASIC_WAND.getDefaultStack(), - // new Identifier("magic_mod", "basic_wand_crafting") - // ); - } - }; - } - - @Override - public String getName() { - return "StickTagRecipeProvider"; - } -} diff --git a/src/main/java/com/uncreativebunch/magicmod/util/ImplementedInventory.java b/src/main/java/com/uncreativebunch/magicmod/inventory/ImplementedInventory.java similarity index 97% rename from src/main/java/com/uncreativebunch/magicmod/util/ImplementedInventory.java rename to src/main/java/com/uncreativebunch/magicmod/inventory/ImplementedInventory.java index b594e5a..77b4cfd 100644 --- a/src/main/java/com/uncreativebunch/magicmod/util/ImplementedInventory.java +++ b/src/main/java/com/uncreativebunch/magicmod/inventory/ImplementedInventory.java @@ -1,4 +1,4 @@ -package com.uncreativebunch.magicmod.util; +package com.uncreativebunch.magicmod.inventory; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.inventory.Inventories; diff --git a/src/main/java/com/uncreativebunch/magicmod/inventory/OutputInventory.java b/src/main/java/com/uncreativebunch/magicmod/inventory/OutputInventory.java new file mode 100644 index 0000000..4c83e2d --- /dev/null +++ b/src/main/java/com/uncreativebunch/magicmod/inventory/OutputInventory.java @@ -0,0 +1,25 @@ +package com.uncreativebunch.magicmod.inventory; + +import net.minecraft.inventory.SimpleInventory; +import net.minecraft.item.ItemStack; +import net.minecraft.util.collection.DefaultedList; + +public class OutputInventory extends SimpleInventory { + + public OutputInventory(int size) { + super(size); + } + + public OutputInventory(ItemStack... items) { + super(items); + } + + @Override + public boolean canInsert(ItemStack stack) { + return false; + } + + public boolean canAddStack(ItemStack stack) { + return super.canInsert(stack); + } +} diff --git a/src/main/java/com/uncreativebunch/magicmod/item/ModItems.java b/src/main/java/com/uncreativebunch/magicmod/item/ModItems.java index 4df44c7..8fcb99a 100644 --- a/src/main/java/com/uncreativebunch/magicmod/item/ModItems.java +++ b/src/main/java/com/uncreativebunch/magicmod/item/ModItems.java @@ -93,7 +93,7 @@ public class ModItems { Item::new, new Item.Settings() ); - + public static RegistryKey keyOf(String id) { return RegistryKey.of(RegistryKeys.ITEM, Identifier.of(MagicMod.MOD_ID, id)); } diff --git a/src/main/java/com/uncreativebunch/magicmod/recipe/MagicShapedInput.java b/src/main/java/com/uncreativebunch/magicmod/recipe/MagicShapedInput.java index 0ec7ada..3f463ea 100644 --- a/src/main/java/com/uncreativebunch/magicmod/recipe/MagicShapedInput.java +++ b/src/main/java/com/uncreativebunch/magicmod/recipe/MagicShapedInput.java @@ -7,8 +7,8 @@ import java.util.List; public record MagicShapedInput(List stacks) implements RecipeInput { @Override - public ItemStack getStackInSlot(int slot) { - return stacks.get(slot); + public ItemStack getStackInSlot(int index) { + return stacks.get(index); } @Override diff --git a/src/main/java/com/uncreativebunch/magicmod/recipe/MagicShapedRecipe.java b/src/main/java/com/uncreativebunch/magicmod/recipe/MagicShapedRecipe.java index f829c3f..eb2d3c4 100644 --- a/src/main/java/com/uncreativebunch/magicmod/recipe/MagicShapedRecipe.java +++ b/src/main/java/com/uncreativebunch/magicmod/recipe/MagicShapedRecipe.java @@ -1,50 +1,26 @@ package com.uncreativebunch.magicmod.recipe; -import com.mojang.serialization.Codec; import com.mojang.serialization.MapCodec; import com.mojang.serialization.codecs.RecordCodecBuilder; +import com.uncreativebunch.magicmod.MagicMod; +import net.minecraft.data.recipe.RecipeExporter; import net.minecraft.item.ItemStack; import net.minecraft.network.RegistryByteBuf; import net.minecraft.network.codec.PacketCodec; -import net.minecraft.network.codec.PacketCodecs; import net.minecraft.recipe.*; import net.minecraft.recipe.book.RecipeBookCategory; import net.minecraft.recipe.display.RecipeDisplay; +import net.minecraft.registry.RegistryKey; +import net.minecraft.registry.RegistryKeys; import net.minecraft.registry.RegistryWrapper; import net.minecraft.util.Identifier; import net.minecraft.world.World; -import java.util.ArrayList; import java.util.List; +import java.util.Optional; -public class MagicShapedRecipe implements Recipe { - private final Ingredient inputA; - private final Ingredient inputB; - private final ItemStack output; - private final Identifier id; - - public MagicShapedRecipe(Ingredient inputA, Ingredient inputB, ItemStack output, Identifier id) { - this.inputA = inputA; - this.inputB = inputB; - this.output = output; - this.id = id; - } - - public Ingredient getInputA() { - return this.inputA; - } - - public Ingredient getInputB() { - return inputB; - } - - public ItemStack getOutput() { - return this.output; - } - - public Identifier getId() { - return this.id; - } +public record MagicShapedRecipe(Ingredient coreIngredient, Ingredient materialIngredient, Ingredient gripIngredient, + ItemStack output, Identifier id) implements Recipe { public boolean fits(int width, int height) { return true; @@ -53,7 +29,9 @@ public class MagicShapedRecipe implements Recipe { @Override public boolean matches(MagicShapedInput input, World world) { if (input.stacks().size() < 2) return false; - return inputA.test(input.stacks().getFirst()) && inputB.test(input.stacks().get(1)); + return coreIngredient.test(input.stacks().getFirst()) + && materialIngredient.test(input.stacks().get(1)) + && gripIngredient.test(input.stacks().get(2)); } @Override @@ -78,32 +56,14 @@ public class MagicShapedRecipe implements Recipe { @Override public RecipeSerializer> getSerializer() { - return null; + return ModRecipeSerializers.MAGIC_SHAPED; } - // Implement the recipe's type. - public static class Type implements RecipeType { - private Type() { } - public static final Type INSTANCE = new Type(); - public static final String ID = "magic_crafting"; - } - - /** - * {@return the type of this recipe} - * - *

The {@code type} in the recipe JSON format is the {@linkplain - * #getSerializer() serializer} instead. - */ @Override public RecipeType> getType() { return ModRecipeTypes.MAGIC_SHAPED; } - @Override - public IngredientPlacement getIngredientPlacement() { - return null; - } - @Override public List getDisplays() { return Recipe.super.getDisplays(); @@ -114,31 +74,54 @@ public class MagicShapedRecipe implements Recipe { return null; } + @Override + public IngredientPlacement getIngredientPlacement() { + List> ingredients = List.of( + Optional.of(coreIngredient), + Optional.of(materialIngredient), + Optional.of(gripIngredient) + ); + + return IngredientPlacement.forMultipleSlots(ingredients); + } + + // Type definition + public static class Type implements RecipeType { + private Type() { + } + + public static final Type INSTANCE = new Type(); + public static final String ID = "magic_crafting"; + } + // Serializer definition public static class Serializer implements RecipeSerializer { public static final Serializer INSTANCE = new Serializer(); private static final MapCodec CODEC = RecordCodecBuilder.mapCodec( - // This function essentially creates a way to deserialize from a JSON using the constructor's - // parameters, as defined above. - instance -> instance.group( - Ingredient.CODEC.fieldOf("inputA").forGetter(MagicShapedRecipe::getInputA), - Ingredient.CODEC.fieldOf("inputB").forGetter(MagicShapedRecipe::getInputB), - ItemStack.CODEC.fieldOf("output").forGetter(MagicShapedRecipe::getOutput), - Identifier.CODEC.fieldOf("id").forGetter(MagicShapedRecipe::getId) - ).apply(instance, MagicShapedRecipe::new) + // This function essentially creates a way to deserialize from a JSON using the constructor's + // parameters, as defined above. + instance -> instance.group( + Ingredient.CODEC.fieldOf("coreIngredient").forGetter(MagicShapedRecipe::coreIngredient), + Ingredient.CODEC.fieldOf("materialIngredient").forGetter(MagicShapedRecipe::materialIngredient), + Ingredient.CODEC.fieldOf("gripIngredient").forGetter(MagicShapedRecipe::gripIngredient), + ItemStack.CODEC.fieldOf("output").forGetter(MagicShapedRecipe::output), + Identifier.CODEC.fieldOf("id").forGetter(MagicShapedRecipe::id) + ).apply(instance, MagicShapedRecipe::new) ); private static final PacketCodec PACKET_CODEC = PacketCodec.tuple( - Ingredient.PACKET_CODEC, MagicShapedRecipe::getInputA, - Ingredient.PACKET_CODEC, MagicShapedRecipe::getInputB, - ItemStack.PACKET_CODEC, MagicShapedRecipe::getOutput, - Identifier.PACKET_CODEC, MagicShapedRecipe::getId, - MagicShapedRecipe::new + Ingredient.PACKET_CODEC, MagicShapedRecipe::coreIngredient, + Ingredient.PACKET_CODEC, MagicShapedRecipe::materialIngredient, + Ingredient.PACKET_CODEC, MagicShapedRecipe::gripIngredient, + ItemStack.PACKET_CODEC, MagicShapedRecipe::output, + Identifier.PACKET_CODEC, MagicShapedRecipe::id, + MagicShapedRecipe::new ); /** * Gets the codec for this serializer. This determines how the recipe is parsed from JSON. + * * @return The codec for this serializer. */ @Override @@ -148,6 +131,7 @@ public class MagicShapedRecipe implements Recipe { /** * Gets the packet codec for this serializer. This determines how the recipe is parsed from packets. + * * @return The packet codec for this serializer. */ @Override @@ -155,4 +139,36 @@ public class MagicShapedRecipe implements Recipe { return PACKET_CODEC; } } + + // Builder definition + public record Builder(Ingredient core, Ingredient material, Ingredient grip, ItemStack output) { + /** + * Creates a new {@link Builder}. + * @param core The core {@link Ingredient}. + * @param material The material {@link Ingredient}. + * @param grip The grip {@link Ingredient}. + * @param output The output {@link ItemStack} + * @return A new {@link Builder} that can be offered to a {@link RecipeExporter}. + */ + public static Builder build( + Ingredient core, + Ingredient material, + Ingredient grip, + ItemStack output + ) { + return new Builder(core, material, grip, output); + } + + /** + * Offers the contents of this builder to the given {@link RecipeExporter}. + * @param exporter The {@link RecipeExporter} to use. + * @param recipeId The {@link Identifier} of the recipe to create. + */ + public void offerTo(RecipeExporter exporter, Identifier recipeId) { + RegistryKey> key = RegistryKey.of(RegistryKeys.RECIPE, recipeId); + MagicShapedRecipe recipe = new MagicShapedRecipe(core, material, grip, output, recipeId); + exporter.accept(key, recipe, null); + MagicMod.LOGGER.info("Created recipe with id {}", recipeId.toString()); + } + } } diff --git a/src/main/java/com/uncreativebunch/magicmod/recipe/ModRecipeSerializers.java b/src/main/java/com/uncreativebunch/magicmod/recipe/ModRecipeSerializers.java index 564ff2f..524055f 100644 --- a/src/main/java/com/uncreativebunch/magicmod/recipe/ModRecipeSerializers.java +++ b/src/main/java/com/uncreativebunch/magicmod/recipe/ModRecipeSerializers.java @@ -1,15 +1,27 @@ package com.uncreativebunch.magicmod.recipe; +import com.uncreativebunch.magicmod.MagicMod; import net.minecraft.recipe.Recipe; import net.minecraft.recipe.RecipeSerializer; import net.minecraft.registry.Registries; import net.minecraft.registry.Registry; +import net.minecraft.util.Identifier; public class ModRecipeSerializers { - public static RecipeSerializer MAGIC_SHAPED = new MagicShapedRecipe.Serializer(); + public static RecipeSerializer MAGIC_SHAPED = register( + "magic_shaped", + MagicShapedRecipe.Serializer.INSTANCE + ); + /** + * Registers a {@link RecipeSerializer} with Minecraft's internal registry. + * @param id The {@link net.minecraft.util.Identifier} of the serializer. + * @param type An instance of the {@link RecipeSerializer} to register. + * @return The registered instance of the {@link RecipeSerializer}. + * @param The type of the {@link Recipe} that is serialized. + */ public static > RecipeSerializer register(String id, RecipeSerializer type) { - return Registry.register(Registries.RECIPE_SERIALIZER, id, type); + return Registry.register(Registries.RECIPE_SERIALIZER, Identifier.of(MagicMod.MOD_ID, id), type); } public static void init() {} diff --git a/src/main/java/com/uncreativebunch/magicmod/recipe/ModRecipeTypes.java b/src/main/java/com/uncreativebunch/magicmod/recipe/ModRecipeTypes.java index 15fc56e..986ebe0 100644 --- a/src/main/java/com/uncreativebunch/magicmod/recipe/ModRecipeTypes.java +++ b/src/main/java/com/uncreativebunch/magicmod/recipe/ModRecipeTypes.java @@ -1,15 +1,18 @@ package com.uncreativebunch.magicmod.recipe; +import com.uncreativebunch.magicmod.MagicMod; import net.minecraft.recipe.Recipe; import net.minecraft.recipe.RecipeType; +import net.minecraft.recipe.input.RecipeInput; import net.minecraft.registry.Registries; import net.minecraft.registry.Registry; +import net.minecraft.util.Identifier; public class ModRecipeTypes { public static final RecipeType MAGIC_SHAPED = register("magic_shaped", MagicShapedRecipe.Type.INSTANCE); public static > RecipeType register(String id, RecipeType type) { - return Registry.register(Registries.RECIPE_TYPE, id, type); + return Registry.register(Registries.RECIPE_TYPE, Identifier.of(MagicMod.MOD_ID, id), type); } public static void init() {} diff --git a/src/main/java/com/uncreativebunch/magicmod/screen/MagicalCraftingScreenHandler.java b/src/main/java/com/uncreativebunch/magicmod/screen/MagicalCraftingScreenHandler.java index 1ed50a7..a2e2805 100644 --- a/src/main/java/com/uncreativebunch/magicmod/screen/MagicalCraftingScreenHandler.java +++ b/src/main/java/com/uncreativebunch/magicmod/screen/MagicalCraftingScreenHandler.java @@ -1,8 +1,10 @@ package com.uncreativebunch.magicmod.screen; +import com.uncreativebunch.magicmod.inventory.OutputInventory; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerInventory; import net.minecraft.inventory.CraftingInventory; +import net.minecraft.inventory.SimpleInventory; import net.minecraft.item.ItemStack; import net.minecraft.screen.ScreenHandler; import net.minecraft.screen.ScreenHandlerContext; @@ -10,8 +12,8 @@ import net.minecraft.screen.slot.Slot; public class MagicalCraftingScreenHandler extends ScreenHandler { - private final ScreenHandlerContext context; - private final CraftingInventory inventory; + private final SimpleInventory inputInventory; + private final OutputInventory outputInventory; /** * Client-side constructor. @@ -19,7 +21,7 @@ public class MagicalCraftingScreenHandler extends ScreenHandler { * @param playerInventory The player's inventory. */ public MagicalCraftingScreenHandler(int syncId, PlayerInventory playerInventory) { - this(syncId, playerInventory, ScreenHandlerContext.EMPTY); + this(syncId, playerInventory, new SimpleInventory(3), new OutputInventory(1)); } /** @@ -29,28 +31,32 @@ public class MagicalCraftingScreenHandler extends ScreenHandler { * @param playerInventory The player's inventory. * @param screenHandlerContext The context of the screen handler. Not sure if this is needed anymore. */ - public MagicalCraftingScreenHandler(int syncId, PlayerInventory playerInventory, ScreenHandlerContext screenHandlerContext) { + public MagicalCraftingScreenHandler(int syncId, PlayerInventory playerInventory, SimpleInventory inputInventory, + OutputInventory outputInventory) { super(ModScreenHandlers.MAGICAL_CRAFTING_SCREEN_HANDLER, syncId); - this.context = screenHandlerContext; - this.inventory = new CraftingInventory(this, 3, 3); + this.inputInventory = inputInventory; + this.outputInventory = outputInventory; // Ensure that the crafting inventory is the correct size. - checkSize(inventory, 9); + checkSize(inputInventory, 3); + checkSize(outputInventory, 1); // Ensure that the inventory's custom logic runs (if any) - inventory.onOpen(playerInventory.player); + inputInventory.onOpen(playerInventory.player); // Place all inventory slots within the screen. This piece doesn't actually render the background of the slots, // as this is handled by the client-side application (the Screen), but it does tell the screen where to render. + // We do assign locations - each slot is 18 units wide/tall. int m, l; - for (m = 0; m < 3; m++) { - for (l = 0; l < 3; l++) { - // Create the crafting slots. This is good for a 3x3 grid of crafting slots. It registers them to a - // specific inventory and location on the client's screen, relative to the screen's size and origin. - this.addSlot(new Slot(inventory, l + m * 3, 62 + l * 18, 17 + m * 18)); - } + for (l = 0; l < 3; l++) { + // Create the crafting slots. This is good for a 3x3 grid of crafting slots. It registers them to a + // specific inventory and location on the client's screen, relative to the screen's size and origin. + this.addSlot(new Slot(inputInventory, l, 62 + l * 18, 17)); } + // TODO I don't actually know where the output slot should be located. Get correct coords. + this.addSlot(new Slot(outputInventory, 0, 80, 35)); + // Add all player inventory slots. Start with the main inventory, then the hotbar. for (m = 0; m < 3; m++) { for (l = 0; l < 9; l++) { @@ -68,7 +74,7 @@ public class MagicalCraftingScreenHandler extends ScreenHandler { * @return True if the slot index is within the crafting inventory, false otherwise. */ protected boolean isCraftingInventorySlot(int slotIndex) { - return slotIndex < this.inventory.size(); + return slotIndex < this.inputInventory.size(); } @Override @@ -83,7 +89,7 @@ public class MagicalCraftingScreenHandler extends ScreenHandler { newStack = stack.copy(); if (isCraftingInventorySlot(slotIndex)) { // Attempt to move the stack into the player's inventory. - if (!this.insertItem(stack, this.inventory.size(), this.slots.size(), true)) { + if (!this.insertItem(stack, this.inputInventory.size() + this.outputInventory.size(), this.slots.size(), true)) { return ItemStack.EMPTY; } } else { @@ -104,6 +110,6 @@ public class MagicalCraftingScreenHandler extends ScreenHandler { @Override public boolean canUse(PlayerEntity player) { - return inventory.canPlayerUse(player); + return inputInventory.canPlayerUse(player); } }