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.
This commit is contained in:
alphafetus committed 2025-09-08 23:38:11 -04:00
1 parent ad9c46c0b4
commit 632772b666
14 files changed
+298 -148

No files matched your search

@@ -4,6 +4,7 @@ import com.uncreativebunch.magicmod.block.ModBlocks;
import com.uncreativebunch.magicmod.block.entity.ModBlockEntityTypes; import com.uncreativebunch.magicmod.block.entity.ModBlockEntityTypes;
import com.uncreativebunch.magicmod.item.ModItemGroups; import com.uncreativebunch.magicmod.item.ModItemGroups;
import com.uncreativebunch.magicmod.item.ModItems; import com.uncreativebunch.magicmod.item.ModItems;
import com.uncreativebunch.magicmod.recipe.ModRecipeSerializers;
import com.uncreativebunch.magicmod.recipe.ModRecipeTypes; import com.uncreativebunch.magicmod.recipe.ModRecipeTypes;
import com.uncreativebunch.magicmod.screen.ModScreenHandlers; import com.uncreativebunch.magicmod.screen.ModScreenHandlers;
import net.fabricmc.api.ModInitializer; import net.fabricmc.api.ModInitializer;
@@ -33,6 +34,7 @@ public class MagicMod implements ModInitializer {
ModItems.init(); ModItems.init();
ModItemGroups.init(); ModItemGroups.init();
ModScreenHandlers.init(); ModScreenHandlers.init();
ModRecipeSerializers.init();
ModRecipeTypes.init(); ModRecipeTypes.init();
ModBlockEntityTypes.init(); ModBlockEntityTypes.init();
LOGGER.info("Finished Magic Mod initialization!"); LOGGER.info("Finished Magic Mod initialization!");
@@ -2,10 +2,14 @@ package com.uncreativebunch.magicmod.block.crafting;
import com.mojang.serialization.MapCodec; import com.mojang.serialization.MapCodec;
import com.uncreativebunch.magicmod.MagicMod; import com.uncreativebunch.magicmod.MagicMod;
import com.uncreativebunch.magicmod.block.ModBlocks;
import com.uncreativebunch.magicmod.block.entity.MagicalCrafterBlockEntity; import com.uncreativebunch.magicmod.block.entity.MagicalCrafterBlockEntity;
import com.uncreativebunch.magicmod.block.entity.ModBlockEntityTypes;
import com.uncreativebunch.magicmod.screen.MagicalCraftingScreenHandler; import com.uncreativebunch.magicmod.screen.MagicalCraftingScreenHandler;
import net.minecraft.block.*; import net.minecraft.block.*;
import net.minecraft.block.entity.BlockEntity; 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.block.enums.SlabType;
import net.minecraft.entity.EntityType; import net.minecraft.entity.EntityType;
import net.minecraft.entity.LightningEntity; import net.minecraft.entity.LightningEntity;
@@ -80,14 +84,14 @@ public class MagicalCrafterBlock extends BlockWithEntity {
return ActionResult.SUCCESS; return ActionResult.SUCCESS;
} }
@Override // @Override
public NamedScreenHandlerFactory createScreenHandlerFactory(BlockState state, World world, BlockPos pos) { // public NamedScreenHandlerFactory createScreenHandlerFactory(BlockState state, World world, BlockPos pos) {
return new SimpleNamedScreenHandlerFactory( // return new SimpleNamedScreenHandlerFactory(
((syncId, playerInventory, player) -> // ((syncId, playerInventory, player) ->
new MagicalCraftingScreenHandler(syncId, playerInventory, ScreenHandlerContext.create(world, pos))), // new MagicalCraftingScreenHandler(syncId, playerInventory, ScreenHandlerContext.create(world, pos))),
Text.translatable("container.magical_crafting_table") // Text.translatable("container.magical_crafting_table")
); // );
} // }
@Override @Override
public BlockEntity createBlockEntity(BlockPos pos, BlockState state) { public BlockEntity createBlockEntity(BlockPos pos, BlockState state) {
@@ -102,4 +106,18 @@ public class MagicalCrafterBlock extends BlockWithEntity {
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) { protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
builder.add(SlabBlock.TYPE, SlabBlock.WATERLOGGED); 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 <T> The {@link BlockEntity} to use.
*/
@Override
public <T extends BlockEntity> BlockEntityTicker<T> getTicker(World world, BlockState state, BlockEntityType<T> type) {
return validateTicker(type, ModBlockEntityTypes.MAGICAL_CRAFTING_TABLE, MagicalCrafterBlockEntity::tick);
}
} }
@@ -1,21 +1,87 @@
package com.uncreativebunch.magicmod.block.entity; 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.BlockState;
import net.minecraft.block.entity.BlockEntity; import net.minecraft.block.entity.BlockEntity;
import net.minecraft.item.ItemStack; import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.util.collection.DefaultedList; 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.util.math.BlockPos;
import net.minecraft.world.World;
public class MagicalCrafterBlockEntity extends BlockEntity implements ImplementedInventory { import java.util.Optional;
protected DefaultedList<ItemStack> items = DefaultedList.ofSize(3, ItemStack.EMPTY);
@Override public class MagicalCrafterBlockEntity extends BlockEntity implements NamedScreenHandlerFactory {
public DefaultedList<ItemStack> getItems() { private final ServerRecipeManager.MatchGetter<MagicShapedInput, ? extends MagicShapedRecipe> recipeGetter;
return items;
} /**
* 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) { public MagicalCrafterBlockEntity(BlockPos pos, BlockState state) {
super(ModBlockEntityTypes.MAGICAL_CRAFTING_TABLE, pos, 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<? extends RecipeEntry<? extends MagicShapedRecipe>> 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());
} }
} }
@@ -13,6 +13,6 @@ public class DataGenerator implements DataGeneratorEntrypoint {
MagicMod.LOGGER.info("Hello from the data generator!"); MagicMod.LOGGER.info("Hello from the data generator!");
// Add providers to the pack here. // Add providers to the pack here.
pack.addProvider(StickTagRecipeProvider::new); pack.addProvider(MagicRecipeProvider::new);
} }
} }
@@ -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<RegistryWrapper.WrapperLookup> registriesFuture) {
super(output, registriesFuture);
}
@Override
protected RecipeGenerator getRecipeGenerator(RegistryWrapper.WrapperLookup registryLookup, RecipeExporter exporter) {
return new RecipeGenerator(registryLookup, exporter) {
@Override
public void generate() {
RegistryWrapper.Impl<Item> 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";
}
}
@@ -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<RegistryWrapper.WrapperLookup> 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";
}
}
@@ -1,4 +1,4 @@
package com.uncreativebunch.magicmod.util; package com.uncreativebunch.magicmod.inventory;
import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.inventory.Inventories; import net.minecraft.inventory.Inventories;
@@ -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);
}
}
@@ -93,7 +93,7 @@ public class ModItems {
Item::new, Item::new,
new Item.Settings() new Item.Settings()
); );
public static RegistryKey<Item> keyOf(String id) { public static RegistryKey<Item> keyOf(String id) {
return RegistryKey.of(RegistryKeys.ITEM, Identifier.of(MagicMod.MOD_ID, id)); return RegistryKey.of(RegistryKeys.ITEM, Identifier.of(MagicMod.MOD_ID, id));
} }
@@ -7,8 +7,8 @@ import java.util.List;
public record MagicShapedInput(List<ItemStack> stacks) implements RecipeInput { public record MagicShapedInput(List<ItemStack> stacks) implements RecipeInput {
@Override @Override
public ItemStack getStackInSlot(int slot) { public ItemStack getStackInSlot(int index) {
return stacks.get(slot); return stacks.get(index);
} }
@Override @Override
@@ -1,50 +1,26 @@
package com.uncreativebunch.magicmod.recipe; package com.uncreativebunch.magicmod.recipe;
import com.mojang.serialization.Codec;
import com.mojang.serialization.MapCodec; import com.mojang.serialization.MapCodec;
import com.mojang.serialization.codecs.RecordCodecBuilder; 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.item.ItemStack;
import net.minecraft.network.RegistryByteBuf; import net.minecraft.network.RegistryByteBuf;
import net.minecraft.network.codec.PacketCodec; import net.minecraft.network.codec.PacketCodec;
import net.minecraft.network.codec.PacketCodecs;
import net.minecraft.recipe.*; import net.minecraft.recipe.*;
import net.minecraft.recipe.book.RecipeBookCategory; import net.minecraft.recipe.book.RecipeBookCategory;
import net.minecraft.recipe.display.RecipeDisplay; import net.minecraft.recipe.display.RecipeDisplay;
import net.minecraft.registry.RegistryKey;
import net.minecraft.registry.RegistryKeys;
import net.minecraft.registry.RegistryWrapper; import net.minecraft.registry.RegistryWrapper;
import net.minecraft.util.Identifier; import net.minecraft.util.Identifier;
import net.minecraft.world.World; import net.minecraft.world.World;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Optional;
public class MagicShapedRecipe implements Recipe<MagicShapedInput> { public record MagicShapedRecipe(Ingredient coreIngredient, Ingredient materialIngredient, Ingredient gripIngredient,
private final Ingredient inputA; ItemStack output, Identifier id) implements Recipe<MagicShapedInput> {
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) { public boolean fits(int width, int height) {
return true; return true;
@@ -53,7 +29,9 @@ public class MagicShapedRecipe implements Recipe<MagicShapedInput> {
@Override @Override
public boolean matches(MagicShapedInput input, World world) { public boolean matches(MagicShapedInput input, World world) {
if (input.stacks().size() < 2) return false; 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 @Override
@@ -78,32 +56,14 @@ public class MagicShapedRecipe implements Recipe<MagicShapedInput> {
@Override @Override
public RecipeSerializer<? extends Recipe<MagicShapedInput>> getSerializer() { public RecipeSerializer<? extends Recipe<MagicShapedInput>> getSerializer() {
return null; return ModRecipeSerializers.MAGIC_SHAPED;
} }
// Implement the recipe's type.
public static class Type implements RecipeType<MagicShapedRecipe> {
private Type() { }
public static final Type INSTANCE = new Type();
public static final String ID = "magic_crafting";
}
/**
* {@return the type of this recipe}
*
* <p>The {@code type} in the recipe JSON format is the {@linkplain
* #getSerializer() serializer} instead.
*/
@Override @Override
public RecipeType<? extends Recipe<MagicShapedInput>> getType() { public RecipeType<? extends Recipe<MagicShapedInput>> getType() {
return ModRecipeTypes.MAGIC_SHAPED; return ModRecipeTypes.MAGIC_SHAPED;
} }
@Override
public IngredientPlacement getIngredientPlacement() {
return null;
}
@Override @Override
public List<RecipeDisplay> getDisplays() { public List<RecipeDisplay> getDisplays() {
return Recipe.super.getDisplays(); return Recipe.super.getDisplays();
@@ -114,31 +74,54 @@ public class MagicShapedRecipe implements Recipe<MagicShapedInput> {
return null; return null;
} }
@Override
public IngredientPlacement getIngredientPlacement() {
List<Optional<Ingredient>> ingredients = List.of(
Optional.of(coreIngredient),
Optional.of(materialIngredient),
Optional.of(gripIngredient)
);
return IngredientPlacement.forMultipleSlots(ingredients);
}
// Type definition
public static class Type implements RecipeType<MagicShapedRecipe> {
private Type() {
}
public static final Type INSTANCE = new Type();
public static final String ID = "magic_crafting";
}
// Serializer definition // Serializer definition
public static class Serializer implements RecipeSerializer<MagicShapedRecipe> { public static class Serializer implements RecipeSerializer<MagicShapedRecipe> {
public static final Serializer INSTANCE = new Serializer(); public static final Serializer INSTANCE = new Serializer();
private static final MapCodec<MagicShapedRecipe> CODEC = RecordCodecBuilder.mapCodec( private static final MapCodec<MagicShapedRecipe> CODEC = RecordCodecBuilder.mapCodec(
// This function essentially creates a way to deserialize from a JSON using the constructor's // This function essentially creates a way to deserialize from a JSON using the constructor's
// parameters, as defined above. // parameters, as defined above.
instance -> instance.group( instance -> instance.group(
Ingredient.CODEC.fieldOf("inputA").forGetter(MagicShapedRecipe::getInputA), Ingredient.CODEC.fieldOf("coreIngredient").forGetter(MagicShapedRecipe::coreIngredient),
Ingredient.CODEC.fieldOf("inputB").forGetter(MagicShapedRecipe::getInputB), Ingredient.CODEC.fieldOf("materialIngredient").forGetter(MagicShapedRecipe::materialIngredient),
ItemStack.CODEC.fieldOf("output").forGetter(MagicShapedRecipe::getOutput), Ingredient.CODEC.fieldOf("gripIngredient").forGetter(MagicShapedRecipe::gripIngredient),
Identifier.CODEC.fieldOf("id").forGetter(MagicShapedRecipe::getId) ItemStack.CODEC.fieldOf("output").forGetter(MagicShapedRecipe::output),
).apply(instance, MagicShapedRecipe::new) Identifier.CODEC.fieldOf("id").forGetter(MagicShapedRecipe::id)
).apply(instance, MagicShapedRecipe::new)
); );
private static final PacketCodec<RegistryByteBuf, MagicShapedRecipe> PACKET_CODEC = PacketCodec.tuple( private static final PacketCodec<RegistryByteBuf, MagicShapedRecipe> PACKET_CODEC = PacketCodec.tuple(
Ingredient.PACKET_CODEC, MagicShapedRecipe::getInputA, Ingredient.PACKET_CODEC, MagicShapedRecipe::coreIngredient,
Ingredient.PACKET_CODEC, MagicShapedRecipe::getInputB, Ingredient.PACKET_CODEC, MagicShapedRecipe::materialIngredient,
ItemStack.PACKET_CODEC, MagicShapedRecipe::getOutput, Ingredient.PACKET_CODEC, MagicShapedRecipe::gripIngredient,
Identifier.PACKET_CODEC, MagicShapedRecipe::getId, ItemStack.PACKET_CODEC, MagicShapedRecipe::output,
MagicShapedRecipe::new Identifier.PACKET_CODEC, MagicShapedRecipe::id,
MagicShapedRecipe::new
); );
/** /**
* Gets the codec for this serializer. This determines how the recipe is parsed from JSON. * Gets the codec for this serializer. This determines how the recipe is parsed from JSON.
*
* @return The codec for this serializer. * @return The codec for this serializer.
*/ */
@Override @Override
@@ -148,6 +131,7 @@ public class MagicShapedRecipe implements Recipe<MagicShapedInput> {
/** /**
* Gets the packet codec for this serializer. This determines how the recipe is parsed from packets. * Gets the packet codec for this serializer. This determines how the recipe is parsed from packets.
*
* @return The packet codec for this serializer. * @return The packet codec for this serializer.
*/ */
@Override @Override
@@ -155,4 +139,36 @@ public class MagicShapedRecipe implements Recipe<MagicShapedInput> {
return PACKET_CODEC; 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<Recipe<?>> 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());
}
}
} }
@@ -1,15 +1,27 @@
package com.uncreativebunch.magicmod.recipe; package com.uncreativebunch.magicmod.recipe;
import com.uncreativebunch.magicmod.MagicMod;
import net.minecraft.recipe.Recipe; import net.minecraft.recipe.Recipe;
import net.minecraft.recipe.RecipeSerializer; import net.minecraft.recipe.RecipeSerializer;
import net.minecraft.registry.Registries; import net.minecraft.registry.Registries;
import net.minecraft.registry.Registry; import net.minecraft.registry.Registry;
import net.minecraft.util.Identifier;
public class ModRecipeSerializers { public class ModRecipeSerializers {
public static RecipeSerializer<MagicShapedRecipe> MAGIC_SHAPED = new MagicShapedRecipe.Serializer(); public static RecipeSerializer<MagicShapedRecipe> 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 <T> The type of the {@link Recipe} that is serialized.
*/
public static <T extends Recipe<?>> RecipeSerializer<T> register(String id, RecipeSerializer<T> type) { public static <T extends Recipe<?>> RecipeSerializer<T> register(String id, RecipeSerializer<T> 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() {} public static void init() {}
@@ -1,15 +1,18 @@
package com.uncreativebunch.magicmod.recipe; package com.uncreativebunch.magicmod.recipe;
import com.uncreativebunch.magicmod.MagicMod;
import net.minecraft.recipe.Recipe; import net.minecraft.recipe.Recipe;
import net.minecraft.recipe.RecipeType; import net.minecraft.recipe.RecipeType;
import net.minecraft.recipe.input.RecipeInput;
import net.minecraft.registry.Registries; import net.minecraft.registry.Registries;
import net.minecraft.registry.Registry; import net.minecraft.registry.Registry;
import net.minecraft.util.Identifier;
public class ModRecipeTypes { public class ModRecipeTypes {
public static final RecipeType<MagicShapedRecipe> MAGIC_SHAPED = register("magic_shaped", MagicShapedRecipe.Type.INSTANCE); public static final RecipeType<MagicShapedRecipe> MAGIC_SHAPED = register("magic_shaped", MagicShapedRecipe.Type.INSTANCE);
public static <T extends Recipe<?>> RecipeType<T> register(String id, RecipeType<T> type) { public static <T extends Recipe<?>> RecipeType<T> register(String id, RecipeType<T> 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() {} public static void init() {}
@@ -1,8 +1,10 @@
package com.uncreativebunch.magicmod.screen; package com.uncreativebunch.magicmod.screen;
import com.uncreativebunch.magicmod.inventory.OutputInventory;
import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.player.PlayerInventory; import net.minecraft.entity.player.PlayerInventory;
import net.minecraft.inventory.CraftingInventory; import net.minecraft.inventory.CraftingInventory;
import net.minecraft.inventory.SimpleInventory;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import net.minecraft.screen.ScreenHandler; import net.minecraft.screen.ScreenHandler;
import net.minecraft.screen.ScreenHandlerContext; import net.minecraft.screen.ScreenHandlerContext;
@@ -10,8 +12,8 @@ import net.minecraft.screen.slot.Slot;
public class MagicalCraftingScreenHandler extends ScreenHandler { public class MagicalCraftingScreenHandler extends ScreenHandler {
private final ScreenHandlerContext context; private final SimpleInventory inputInventory;
private final CraftingInventory inventory; private final OutputInventory outputInventory;
/** /**
* Client-side constructor. * Client-side constructor.
@@ -19,7 +21,7 @@ public class MagicalCraftingScreenHandler extends ScreenHandler {
* @param playerInventory The player's inventory. * @param playerInventory The player's inventory.
*/ */
public MagicalCraftingScreenHandler(int syncId, PlayerInventory playerInventory) { 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 playerInventory The player's inventory.
* @param screenHandlerContext The context of the screen handler. Not sure if this is needed anymore. * @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); super(ModScreenHandlers.MAGICAL_CRAFTING_SCREEN_HANDLER, syncId);
this.context = screenHandlerContext; this.inputInventory = inputInventory;
this.inventory = new CraftingInventory(this, 3, 3); this.outputInventory = outputInventory;
// Ensure that the crafting inventory is the correct size. // 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) // 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, // 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. // 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; int m, l;
for (m = 0; m < 3; m++) { for (l = 0; l < 3; l++) {
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
// 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.
// 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));
this.addSlot(new Slot(inventory, l + m * 3, 62 + l * 18, 17 + m * 18));
}
} }
// 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. // Add all player inventory slots. Start with the main inventory, then the hotbar.
for (m = 0; m < 3; m++) { for (m = 0; m < 3; m++) {
for (l = 0; l < 9; l++) { 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. * @return True if the slot index is within the crafting inventory, false otherwise.
*/ */
protected boolean isCraftingInventorySlot(int slotIndex) { protected boolean isCraftingInventorySlot(int slotIndex) {
return slotIndex < this.inventory.size(); return slotIndex < this.inputInventory.size();
} }
@Override @Override
@@ -83,7 +89,7 @@ public class MagicalCraftingScreenHandler extends ScreenHandler {
newStack = stack.copy(); newStack = stack.copy();
if (isCraftingInventorySlot(slotIndex)) { if (isCraftingInventorySlot(slotIndex)) {
// Attempt to move the stack into the player's inventory. // 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; return ItemStack.EMPTY;
} }
} else { } else {
@@ -104,6 +110,6 @@ public class MagicalCraftingScreenHandler extends ScreenHandler {
@Override @Override
public boolean canUse(PlayerEntity player) { public boolean canUse(PlayerEntity player) {
return inventory.canPlayerUse(player); return inputInventory.canPlayerUse(player);
} }
} }