From ce1bd9052f78dd69b9548e3c62cab3131233642c Mon Sep 17 00:00:00 2001 From: Lucas Vas Date: Fri, 5 Sep 2025 07:31:39 -0400 Subject: [PATCH 1/8] feat: build crafting type and block entity, pc change --- .../screen/MagicalCraftingScreen.java | 2 +- .../uncreativebunch/magicmod/MagicMod.java | 4 + .../block/crafting/MagicalCrafterBlock.java | 41 ++++- .../entity/MagicalCrafterBlockEntity.java | 21 +++ .../block/entity/ModBlockEntityTypes.java | 22 +++ .../datagen/StickTagRecipeProvider.java | 10 ++ .../magicmod/recipe/MagicShapedInput.java | 23 +++ .../magicmod/recipe/MagicShapedRecipe.java | 158 ++++++++++++++++++ .../magicmod/recipe/ModRecipeSerializers.java | 16 ++ .../magicmod/recipe/ModRecipeTypes.java | 16 ++ .../magicmod/util/ImplementedInventory.java | 77 +++++++++ 11 files changed, 384 insertions(+), 6 deletions(-) create mode 100644 src/main/java/com/uncreativebunch/magicmod/block/entity/MagicalCrafterBlockEntity.java create mode 100644 src/main/java/com/uncreativebunch/magicmod/block/entity/ModBlockEntityTypes.java create mode 100644 src/main/java/com/uncreativebunch/magicmod/recipe/MagicShapedInput.java create mode 100644 src/main/java/com/uncreativebunch/magicmod/recipe/MagicShapedRecipe.java create mode 100644 src/main/java/com/uncreativebunch/magicmod/recipe/ModRecipeSerializers.java create mode 100644 src/main/java/com/uncreativebunch/magicmod/recipe/ModRecipeTypes.java create mode 100644 src/main/java/com/uncreativebunch/magicmod/util/ImplementedInventory.java diff --git a/src/client/java/com/uncreativebunch/magicmod/screen/MagicalCraftingScreen.java b/src/client/java/com/uncreativebunch/magicmod/screen/MagicalCraftingScreen.java index 1cfed57..80791e3 100644 --- a/src/client/java/com/uncreativebunch/magicmod/screen/MagicalCraftingScreen.java +++ b/src/client/java/com/uncreativebunch/magicmod/screen/MagicalCraftingScreen.java @@ -8,7 +8,7 @@ import net.minecraft.text.Text; import net.minecraft.util.Identifier; public class MagicalCraftingScreen extends HandledScreen { - private static final Identifier TEXTURE = Identifier.of("textures/gui/container/anvil.png"); + private static final Identifier TEXTURE = Identifier.of("textures/gui/container/dispenser.png"); public MagicalCraftingScreen(MagicalCraftingScreenHandler handler, PlayerInventory inventory, Text title) { super(handler, inventory, title); diff --git a/src/main/java/com/uncreativebunch/magicmod/MagicMod.java b/src/main/java/com/uncreativebunch/magicmod/MagicMod.java index 787bf10..c2c1efb 100644 --- a/src/main/java/com/uncreativebunch/magicmod/MagicMod.java +++ b/src/main/java/com/uncreativebunch/magicmod/MagicMod.java @@ -1,8 +1,10 @@ package com.uncreativebunch.magicmod; 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.ModRecipeTypes; import com.uncreativebunch.magicmod.screen.ModScreenHandlers; import net.fabricmc.api.ModInitializer; @@ -31,6 +33,8 @@ public class MagicMod implements ModInitializer { ModItems.init(); ModItemGroups.init(); ModScreenHandlers.init(); + ModRecipeTypes.init(); + ModBlockEntityTypes.init(); LOGGER.info("Finished Magic Mod initialization!"); } } \ No newline at end of file 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 af5da16..a36744a 100644 --- a/src/main/java/com/uncreativebunch/magicmod/block/crafting/MagicalCrafterBlock.java +++ b/src/main/java/com/uncreativebunch/magicmod/block/crafting/MagicalCrafterBlock.java @@ -1,10 +1,12 @@ package com.uncreativebunch.magicmod.block.crafting; +import com.mojang.serialization.MapCodec; import com.uncreativebunch.magicmod.MagicMod; +import com.uncreativebunch.magicmod.block.entity.MagicalCrafterBlockEntity; import com.uncreativebunch.magicmod.screen.MagicalCraftingScreenHandler; -import net.minecraft.block.AbstractBlock; -import net.minecraft.block.BlockState; -import net.minecraft.block.CraftingTableBlock; +import net.minecraft.block.*; +import net.minecraft.block.entity.BlockEntity; +import net.minecraft.block.enums.SlabType; import net.minecraft.entity.EntityType; import net.minecraft.entity.LightningEntity; import net.minecraft.entity.player.PlayerEntity; @@ -12,19 +14,34 @@ import net.minecraft.screen.NamedScreenHandlerFactory; import net.minecraft.screen.ScreenHandlerContext; import net.minecraft.screen.SimpleNamedScreenHandlerFactory; import net.minecraft.stat.Stats; +import net.minecraft.state.StateManager; import net.minecraft.text.Text; import net.minecraft.util.ActionResult; import net.minecraft.util.hit.BlockHitResult; import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; -public class MagicalCrafterBlock extends CraftingTableBlock { +public class MagicalCrafterBlock extends BlockWithEntity { + + private static final MapCodec CODEC = null; /** * Creates a new MagicalCrafterBlock. * @param settings The settings of the block. */ - public MagicalCrafterBlock(AbstractBlock.Settings settings) { super(settings); } + public MagicalCrafterBlock(AbstractBlock.Settings settings) { + super(settings); + this.setDefaultState( + this.stateManager.getDefaultState() + .with(SlabBlock.TYPE, SlabType.BOTTOM) + .with(SlabBlock.WATERLOGGED, false) + ); + } + + @Override + protected MapCodec getCodec() { + return CODEC; + } /** * Occurs whenever this block is used. @@ -71,4 +88,18 @@ public class MagicalCrafterBlock extends CraftingTableBlock { Text.translatable("container.magical_crafting_table") ); } + + @Override + public BlockEntity createBlockEntity(BlockPos pos, BlockState state) { + return new MagicalCrafterBlockEntity(pos, state); + } + + /** + * Adds properties to the block state. This crafting table is a slab, so it has a type and waterlogged property. + * @param builder The {@link StateManager.Builder} to add properties to. + */ + @Override + protected void appendProperties(StateManager.Builder builder) { + builder.add(SlabBlock.TYPE, SlabBlock.WATERLOGGED); + } } diff --git a/src/main/java/com/uncreativebunch/magicmod/block/entity/MagicalCrafterBlockEntity.java b/src/main/java/com/uncreativebunch/magicmod/block/entity/MagicalCrafterBlockEntity.java new file mode 100644 index 0000000..2c2c75d --- /dev/null +++ b/src/main/java/com/uncreativebunch/magicmod/block/entity/MagicalCrafterBlockEntity.java @@ -0,0 +1,21 @@ +package com.uncreativebunch.magicmod.block.entity; + +import com.uncreativebunch.magicmod.util.ImplementedInventory; +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.util.math.BlockPos; + +public class MagicalCrafterBlockEntity extends BlockEntity implements ImplementedInventory { + protected DefaultedList items = DefaultedList.ofSize(3, ItemStack.EMPTY); + + @Override + public DefaultedList getItems() { + return items; + } + + public MagicalCrafterBlockEntity(BlockPos pos, BlockState state) { + super(ModBlockEntityTypes.MAGICAL_CRAFTING_TABLE, pos, state); + } +} diff --git a/src/main/java/com/uncreativebunch/magicmod/block/entity/ModBlockEntityTypes.java b/src/main/java/com/uncreativebunch/magicmod/block/entity/ModBlockEntityTypes.java new file mode 100644 index 0000000..7cd0e70 --- /dev/null +++ b/src/main/java/com/uncreativebunch/magicmod/block/entity/ModBlockEntityTypes.java @@ -0,0 +1,22 @@ +package com.uncreativebunch.magicmod.block.entity; + +import com.uncreativebunch.magicmod.block.ModBlocks; +import net.fabricmc.fabric.api.object.builder.v1.block.entity.FabricBlockEntityTypeBuilder; +import net.minecraft.block.entity.BlockEntity; +import net.minecraft.block.entity.BlockEntityType; +import net.minecraft.registry.Registries; +import net.minecraft.registry.Registry; + +public class ModBlockEntityTypes { + + public static final BlockEntityType MAGICAL_CRAFTING_TABLE = register( + "magical_crafting_table", + FabricBlockEntityTypeBuilder.create(MagicalCrafterBlockEntity::new, ModBlocks.MAGICAL_CRAFTING_TABLE).build() + ); + + public static BlockEntityType register(String id, BlockEntityType type) { + return Registry.register(Registries.BLOCK_ENTITY_TYPE, id, type); + } + + public static void init() {} +} diff --git a/src/main/java/com/uncreativebunch/magicmod/datagen/StickTagRecipeProvider.java b/src/main/java/com/uncreativebunch/magicmod/datagen/StickTagRecipeProvider.java index 7c3da6d..7689ba8 100644 --- a/src/main/java/com/uncreativebunch/magicmod/datagen/StickTagRecipeProvider.java +++ b/src/main/java/com/uncreativebunch/magicmod/datagen/StickTagRecipeProvider.java @@ -21,6 +21,16 @@ public class StickTagRecipeProvider extends FabricRecipeProvider { 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") + // ); } }; } diff --git a/src/main/java/com/uncreativebunch/magicmod/recipe/MagicShapedInput.java b/src/main/java/com/uncreativebunch/magicmod/recipe/MagicShapedInput.java new file mode 100644 index 0000000..0ec7ada --- /dev/null +++ b/src/main/java/com/uncreativebunch/magicmod/recipe/MagicShapedInput.java @@ -0,0 +1,23 @@ +package com.uncreativebunch.magicmod.recipe; + +import net.minecraft.item.ItemStack; +import net.minecraft.recipe.input.RecipeInput; + +import java.util.List; + +public record MagicShapedInput(List stacks) implements RecipeInput { + @Override + public ItemStack getStackInSlot(int slot) { + return stacks.get(slot); + } + + @Override + public int size() { + return stacks.size(); + } + + @Override + public boolean isEmpty() { + return RecipeInput.super.isEmpty(); + } +} diff --git a/src/main/java/com/uncreativebunch/magicmod/recipe/MagicShapedRecipe.java b/src/main/java/com/uncreativebunch/magicmod/recipe/MagicShapedRecipe.java new file mode 100644 index 0000000..f829c3f --- /dev/null +++ b/src/main/java/com/uncreativebunch/magicmod/recipe/MagicShapedRecipe.java @@ -0,0 +1,158 @@ +package com.uncreativebunch.magicmod.recipe; + +import com.mojang.serialization.Codec; +import com.mojang.serialization.MapCodec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +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.RegistryWrapper; +import net.minecraft.util.Identifier; +import net.minecraft.world.World; + +import java.util.ArrayList; +import java.util.List; + +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 boolean fits(int width, int height) { + return true; + } + + @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)); + } + + @Override + public ItemStack craft(MagicShapedInput input, RegistryWrapper.WrapperLookup registries) { + return this.output.copy(); + } + + @Override + public boolean isIgnoredInRecipeBook() { + return Recipe.super.isIgnoredInRecipeBook(); + } + + @Override + public boolean showNotification() { + return Recipe.super.showNotification(); + } + + @Override + public String getGroup() { + return Recipe.super.getGroup(); + } + + @Override + public RecipeSerializer> getSerializer() { + return null; + } + + // 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(); + } + + @Override + public RecipeBookCategory getRecipeBookCategory() { + return null; + } + + // 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) + ); + + 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 + ); + + /** + * Gets the codec for this serializer. This determines how the recipe is parsed from JSON. + * @return The codec for this serializer. + */ + @Override + public MapCodec codec() { + return CODEC; + } + + /** + * Gets the packet codec for this serializer. This determines how the recipe is parsed from packets. + * @return The packet codec for this serializer. + */ + @Override + public PacketCodec packetCodec() { + return PACKET_CODEC; + } + } +} diff --git a/src/main/java/com/uncreativebunch/magicmod/recipe/ModRecipeSerializers.java b/src/main/java/com/uncreativebunch/magicmod/recipe/ModRecipeSerializers.java new file mode 100644 index 0000000..564ff2f --- /dev/null +++ b/src/main/java/com/uncreativebunch/magicmod/recipe/ModRecipeSerializers.java @@ -0,0 +1,16 @@ +package com.uncreativebunch.magicmod.recipe; + +import net.minecraft.recipe.Recipe; +import net.minecraft.recipe.RecipeSerializer; +import net.minecraft.registry.Registries; +import net.minecraft.registry.Registry; + +public class ModRecipeSerializers { + public static RecipeSerializer MAGIC_SHAPED = new MagicShapedRecipe.Serializer(); + + public static > RecipeSerializer register(String id, RecipeSerializer type) { + return Registry.register(Registries.RECIPE_SERIALIZER, 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 new file mode 100644 index 0000000..15fc56e --- /dev/null +++ b/src/main/java/com/uncreativebunch/magicmod/recipe/ModRecipeTypes.java @@ -0,0 +1,16 @@ +package com.uncreativebunch.magicmod.recipe; + +import net.minecraft.recipe.Recipe; +import net.minecraft.recipe.RecipeType; +import net.minecraft.registry.Registries; +import net.minecraft.registry.Registry; + +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); + } + + public static void init() {} +} diff --git a/src/main/java/com/uncreativebunch/magicmod/util/ImplementedInventory.java b/src/main/java/com/uncreativebunch/magicmod/util/ImplementedInventory.java new file mode 100644 index 0000000..b594e5a --- /dev/null +++ b/src/main/java/com/uncreativebunch/magicmod/util/ImplementedInventory.java @@ -0,0 +1,77 @@ +package com.uncreativebunch.magicmod.util; + +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.inventory.Inventories; +import net.minecraft.inventory.Inventory; +import net.minecraft.item.ItemStack; +import net.minecraft.util.collection.DefaultedList; + +public interface ImplementedInventory extends Inventory { + DefaultedList getItems(); + + static ImplementedInventory of(DefaultedList items) { + return () -> items; + } + + static ImplementedInventory ofSize(int size) { + return of(DefaultedList.ofSize(size, ItemStack.EMPTY)); + } + + @Override + default int size() { + return getItems().size(); + } + + @Override + default boolean isEmpty() { + for (int i = 0; i < size(); i++) { + ItemStack stack = getStack(i); + if (!stack.isEmpty()) { + return false; + } + } + return true; + } + + @Override + default ItemStack getStack(int slot) { + return getItems().get(slot); + } + + @Override + default ItemStack removeStack(int slot, int count) { + ItemStack result = Inventories.splitStack(getItems(), slot, count); + if (!result.isEmpty()) { + markDirty(); + } + return result; + } + + @Override + default ItemStack removeStack(int slot) { + return Inventories.removeStack(getItems(), slot); + } + + @Override + default void setStack(int slot, ItemStack stack) { + getItems().set(slot, stack); + if (stack.getCount() > stack.getMaxCount()) { + stack.setCount(stack.getMaxCount()); + } + } + + @Override + default void clear() { + getItems().clear(); + } + + @Override + default void markDirty() { + // Doesn't need to be overridden in this case. + } + + @Override + default boolean canPlayerUse(PlayerEntity player) { + return true; + } +} -- 2.54.0 From c92563a6725e3af453eef780160f9a33b94fc0e9 Mon Sep 17 00:00:00 2001 From: Lucas Vas Date: Sat, 6 Sep 2025 12:48:30 -0400 Subject: [PATCH 2/8] build: remapped to Minecraft version 1.21.8 No breaking changes, minor version update. --- gradle.properties | 9 +++++---- gradlew | 0 2 files changed, 5 insertions(+), 4 deletions(-) mode change 100644 => 100755 gradlew diff --git a/gradle.properties b/gradle.properties index a631032..9469bc5 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,9 +4,9 @@ org.gradle.parallel=true # Fabric Properties # check these on https://fabricmc.net/develop -minecraft_version=1.21.5 -yarn_mappings=1.21.5+build.1 -loader_version=0.16.12 +minecraft_version=1.21.8 +yarn_mappings=1.21.8+build.1 +loader_version=0.17.2 loom_version=1.11-SNAPSHOT # Mod Properties @@ -15,4 +15,5 @@ maven_group=com.uncreativebunch.magicmod archives_base_name=magic_mod # Dependencies -fabric_version=0.128.2+1.21.5 \ No newline at end of file +# Fabric API +fabric_version=0.133.0+1.21.8 \ No newline at end of file diff --git a/gradlew b/gradlew old mode 100644 new mode 100755 -- 2.54.0 From ad9c46c0b473e1eae917c69fb820ec750cabebb6 Mon Sep 17 00:00:00 2001 From: Lucas Vas Date: Sat, 6 Sep 2025 13:25:46 -0400 Subject: [PATCH 3/8] build: revert to Minecraft version 1.21.5 due to undocumented breaking changes --- gradle.properties | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/gradle.properties b/gradle.properties index 9469bc5..19a3caf 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,8 +4,8 @@ org.gradle.parallel=true # Fabric Properties # check these on https://fabricmc.net/develop -minecraft_version=1.21.8 -yarn_mappings=1.21.8+build.1 +minecraft_version=1.21.5 +yarn_mappings=1.21.5+build.1 loader_version=0.17.2 loom_version=1.11-SNAPSHOT @@ -16,4 +16,4 @@ archives_base_name=magic_mod # Dependencies # Fabric API -fabric_version=0.133.0+1.21.8 \ No newline at end of file +fabric_version=0.128.2+1.21.5 \ No newline at end of file -- 2.54.0 From 632772b666bc2e734941d15206074298820295db Mon Sep 17 00:00:00 2001 From: Lucas Vas Date: Mon, 8 Sep 2025 23:38:11 -0400 Subject: [PATCH 4/8] 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); } } -- 2.54.0 From a558428b58a72d1ca566d3270beeec9bfe4f23dd Mon Sep 17 00:00:00 2001 From: Lucas Vas Date: Tue, 9 Sep 2025 17:44:19 -0400 Subject: [PATCH 5/8] chore: remove player electrocution on block usage --- .../block/crafting/MagicalCrafterBlock.java | 16 ---------------- 1 file changed, 16 deletions(-) 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 36616df..fde8adb 100644 --- a/src/main/java/com/uncreativebunch/magicmod/block/crafting/MagicalCrafterBlock.java +++ b/src/main/java/com/uncreativebunch/magicmod/block/crafting/MagicalCrafterBlock.java @@ -65,13 +65,6 @@ public class MagicalCrafterBlock extends BlockWithEntity { if (!world.isClient) { MagicMod.LOGGER.info("Block was used at location {}", pos.toShortString()); - MagicMod.LOGGER.info("Electrocuting player..."); - BlockPos playerPos = player.getBlockPos(); - LightningEntity lightningEntity = new LightningEntity(EntityType.LIGHTNING_BOLT, world); - lightningEntity.setPosition(playerPos.toCenterPos()); - world.spawnEntity(lightningEntity); - MagicMod.LOGGER.info("Player electrocuted."); - NamedScreenHandlerFactory screenFactory = state.createScreenHandlerFactory(world, pos); if (screenFactory != null) { player.openHandledScreen(screenFactory); @@ -84,15 +77,6 @@ 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 BlockEntity createBlockEntity(BlockPos pos, BlockState state) { return new MagicalCrafterBlockEntity(pos, state); -- 2.54.0 From 5331aa8f972e541302b6f1d722f409bfd63cfaea Mon Sep 17 00:00:00 2001 From: Lucas Vas Date: Tue, 9 Sep 2025 17:44:37 -0400 Subject: [PATCH 6/8] chore: remove unused method --- .../magicmod/recipe/MagicShapedRecipe.java | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/src/main/java/com/uncreativebunch/magicmod/recipe/MagicShapedRecipe.java b/src/main/java/com/uncreativebunch/magicmod/recipe/MagicShapedRecipe.java index eb2d3c4..b86dd79 100644 --- a/src/main/java/com/uncreativebunch/magicmod/recipe/MagicShapedRecipe.java +++ b/src/main/java/com/uncreativebunch/magicmod/recipe/MagicShapedRecipe.java @@ -21,11 +21,6 @@ import java.util.Optional; public record MagicShapedRecipe(Ingredient coreIngredient, Ingredient materialIngredient, Ingredient gripIngredient, ItemStack output, Identifier id) implements Recipe { - - public boolean fits(int width, int height) { - return true; - } - @Override public boolean matches(MagicShapedInput input, World world) { if (input.stacks().size() < 2) return false; @@ -87,8 +82,7 @@ public record MagicShapedRecipe(Ingredient coreIngredient, Ingredient materialIn // Type definition public static class Type implements RecipeType { - private Type() { - } + private Type() { } public static final Type INSTANCE = new Type(); public static final String ID = "magic_crafting"; @@ -129,12 +123,8 @@ public record MagicShapedRecipe(Ingredient coreIngredient, Ingredient materialIn return CODEC; } - /** - * Gets the packet codec for this serializer. This determines how the recipe is parsed from packets. - * - * @return The packet codec for this serializer. - */ @Override + @Deprecated public PacketCodec packetCodec() { return PACKET_CODEC; } -- 2.54.0 From b888aa5a7852b65a8baeecec37deb83bf0a9cf85 Mon Sep 17 00:00:00 2001 From: Lucas Vas Date: Tue, 9 Sep 2025 17:47:35 -0400 Subject: [PATCH 7/8] chore: remove unused imports --- .../magicmod/block/crafting/MagicalCrafterBlock.java | 7 ------- 1 file changed, 7 deletions(-) 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 fde8adb..2a26c7b 100644 --- a/src/main/java/com/uncreativebunch/magicmod/block/crafting/MagicalCrafterBlock.java +++ b/src/main/java/com/uncreativebunch/magicmod/block/crafting/MagicalCrafterBlock.java @@ -2,24 +2,17 @@ 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; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.screen.NamedScreenHandlerFactory; -import net.minecraft.screen.ScreenHandlerContext; -import net.minecraft.screen.SimpleNamedScreenHandlerFactory; import net.minecraft.stat.Stats; import net.minecraft.state.StateManager; -import net.minecraft.text.Text; import net.minecraft.util.ActionResult; import net.minecraft.util.hit.BlockHitResult; import net.minecraft.util.math.BlockPos; -- 2.54.0 From 430c59aed4c4b8a181425192b96812b4f7a9932a Mon Sep 17 00:00:00 2001 From: Lucas Vas Date: Mon, 15 Sep 2025 21:54:45 -0400 Subject: [PATCH 8/8] feat: implement very simple crafting output slot Note that this is likely full of bugs that we'll find later on, but this should be good enough to merge for now as a simple example. --- .../entity/MagicalCrafterBlockEntity.java | 8 ++-- .../magicmod/inventory/InputInventory.java | 24 +++++++++++ .../magicmod/screen/MagicResultSlot.java | 42 +++++++++++++++++++ .../screen/MagicalCraftingScreenHandler.java | 16 +++---- 4 files changed, 78 insertions(+), 12 deletions(-) create mode 100644 src/main/java/com/uncreativebunch/magicmod/inventory/InputInventory.java create mode 100644 src/main/java/com/uncreativebunch/magicmod/screen/MagicResultSlot.java 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 257bc4b..a01f6aa 100644 --- a/src/main/java/com/uncreativebunch/magicmod/block/entity/MagicalCrafterBlockEntity.java +++ b/src/main/java/com/uncreativebunch/magicmod/block/entity/MagicalCrafterBlockEntity.java @@ -1,5 +1,6 @@ package com.uncreativebunch.magicmod.block.entity; +import com.uncreativebunch.magicmod.inventory.InputInventory; import com.uncreativebunch.magicmod.inventory.OutputInventory; import com.uncreativebunch.magicmod.recipe.MagicShapedInput; import com.uncreativebunch.magicmod.recipe.ModRecipeTypes; @@ -9,7 +10,6 @@ import net.minecraft.block.BlockState; import net.minecraft.block.entity.BlockEntity; 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; @@ -27,7 +27,7 @@ public class MagicalCrafterBlockEntity extends BlockEntity implements NamedScree /** * The input inventory for crafting ingredients. It goes in the order core, material, and grip. */ - private final SimpleInventory inputInventory = new SimpleInventory(3); + private final InputInventory inputInventory = new InputInventory(3); /** * The output inventory for crafting ingredients. */ @@ -40,9 +40,9 @@ public class MagicalCrafterBlockEntity extends BlockEntity implements NamedScree /** * Gets the input inventory for this block entity. - * @return A {@link SimpleInventory} that represents the input inventory. + * @return A {@link InputInventory} that represents the input inventory. */ - public SimpleInventory getInputInventory() { return inputInventory; } + public InputInventory getInputInventory() { return inputInventory; } /** * Gets the output inventory for this block entity. diff --git a/src/main/java/com/uncreativebunch/magicmod/inventory/InputInventory.java b/src/main/java/com/uncreativebunch/magicmod/inventory/InputInventory.java new file mode 100644 index 0000000..cc00bea --- /dev/null +++ b/src/main/java/com/uncreativebunch/magicmod/inventory/InputInventory.java @@ -0,0 +1,24 @@ +package com.uncreativebunch.magicmod.inventory; + +import net.minecraft.inventory.SimpleInventory; +import net.minecraft.item.ItemStack; + +public class InputInventory extends SimpleInventory { + public InputInventory(int size) { + super(size); + } + + @Override + public boolean canInsert(ItemStack stack) { + return true; + } + + /** + * Executes basic crafting logic and decrements all input stacks. + */ + public void onCrafted() { + for (var stack : this) { + stack.decrement(1); + } + } +} diff --git a/src/main/java/com/uncreativebunch/magicmod/screen/MagicResultSlot.java b/src/main/java/com/uncreativebunch/magicmod/screen/MagicResultSlot.java new file mode 100644 index 0000000..ca3ba49 --- /dev/null +++ b/src/main/java/com/uncreativebunch/magicmod/screen/MagicResultSlot.java @@ -0,0 +1,42 @@ +package com.uncreativebunch.magicmod.screen; + +import com.uncreativebunch.magicmod.inventory.InputInventory; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.inventory.Inventory; +import net.minecraft.item.ItemStack; +import net.minecraft.screen.slot.Slot; + +public class MagicResultSlot extends Slot { + /** + * The crafting input inventory. There is not a current need for custom logic, but this may come in the future. + */ + protected InputInventory inputInventory; + + public MagicResultSlot(InputInventory inputInventory, Inventory outputInventory, int index, int x, int y) { + super(outputInventory, index, x, y); + this.inputInventory = inputInventory; + } + + /** + * Runs when the player takes the item out of the slot. + * This slot will run the crafting logic of the {@link InputInventory}. + * @param player The player that took the item. + * @param stack The item that was taken. + */ + @Override + public void onTakeItem(PlayerEntity player, ItemStack stack) { + this.inputInventory.onCrafted(); + + super.onTakeItem(player, stack); + } + + /** + * Returns false, as the result slot does not allow items to be inserted. + * @param stack The item to insert. + * @return False. + */ + @Override + public boolean canInsert(ItemStack stack) { + return false; + } +} diff --git a/src/main/java/com/uncreativebunch/magicmod/screen/MagicalCraftingScreenHandler.java b/src/main/java/com/uncreativebunch/magicmod/screen/MagicalCraftingScreenHandler.java index a2e2805..e2ad680 100644 --- a/src/main/java/com/uncreativebunch/magicmod/screen/MagicalCraftingScreenHandler.java +++ b/src/main/java/com/uncreativebunch/magicmod/screen/MagicalCraftingScreenHandler.java @@ -1,18 +1,16 @@ package com.uncreativebunch.magicmod.screen; +import com.uncreativebunch.magicmod.inventory.InputInventory; 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; import net.minecraft.screen.slot.Slot; public class MagicalCraftingScreenHandler extends ScreenHandler { - private final SimpleInventory inputInventory; + private final InputInventory inputInventory; private final OutputInventory outputInventory; /** @@ -21,7 +19,7 @@ public class MagicalCraftingScreenHandler extends ScreenHandler { * @param playerInventory The player's inventory. */ public MagicalCraftingScreenHandler(int syncId, PlayerInventory playerInventory) { - this(syncId, playerInventory, new SimpleInventory(3), new OutputInventory(1)); + this(syncId, playerInventory, new InputInventory(3), new OutputInventory(1)); } /** @@ -29,9 +27,10 @@ public class MagicalCraftingScreenHandler extends ScreenHandler { * It handles creating the inventory slots and syncs all information from the server to the client. * @param syncId The sync ID that's sent from the server. * @param playerInventory The player's inventory. - * @param screenHandlerContext The context of the screen handler. Not sure if this is needed anymore. + * @param inputInventory The crafting inventory. + * @param outputInventory The output inventory. */ - public MagicalCraftingScreenHandler(int syncId, PlayerInventory playerInventory, SimpleInventory inputInventory, + public MagicalCraftingScreenHandler(int syncId, PlayerInventory playerInventory, InputInventory inputInventory, OutputInventory outputInventory) { super(ModScreenHandlers.MAGICAL_CRAFTING_SCREEN_HANDLER, syncId); this.inputInventory = inputInventory; @@ -43,6 +42,7 @@ public class MagicalCraftingScreenHandler extends ScreenHandler { // Ensure that the inventory's custom logic runs (if any) inputInventory.onOpen(playerInventory.player); + outputInventory.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. @@ -55,7 +55,7 @@ public class MagicalCraftingScreenHandler extends ScreenHandler { } // TODO I don't actually know where the output slot should be located. Get correct coords. - this.addSlot(new Slot(outputInventory, 0, 80, 35)); + this.addSlot(new MagicResultSlot(inputInventory, outputInventory, 0, 80, 35)); // Add all player inventory slots. Start with the main inventory, then the hotbar. for (m = 0; m < 3; m++) { -- 2.54.0