Skip to content

Inventory, menus, and crafting

Inventory is split into storage and interaction. Containers own stacks; slots adapt container indices to menu rules; menus coordinate cursor actions and route them to slots. This lets a chest, furnace, workbench, and player inventory share click behavior while keeping their layouts and constraints separate.

Type Responsibility
IItemContainer Read, write, and remove stacks by slot index.
SimpleContainer Fixed-size in-memory stack storage with change notifications.
Slot Connect a container index to menu position and allow place/take policy overrides.
AbstractContainerMenu Shared cursor, click, split, merge, swap, quick-move, and outside-drop behavior.
Concrete menus Define slot order, positions, and transfer ranges for one screen or workstation.
CraftingGrid and IRecipe Hold a recipe input matrix and match/assemble recipe results.

A UI click should enter through the active menu, not mutate a container from presentation code. The menu interprets the slot index and button, then uses Slot.MayPlace, MayPickup, and stack operations to apply the action. Shift-click delegates to the concrete menu’s quick-move ranges. Outside-click sends dropped stacks to the supplied world callback. A menu broadcasts changes after the operation so the view can refresh.

The container remains the source of truth for its slots. When implementing a new screen, define the container and slot policy first, then slot order and screen positions, then quick-transfer direction and result-slot behavior. Test both directions of each transfer path because slot ranges are easy to reverse.

CraftingGrid supports arbitrary width and height, including the player’s 2×2 and the workbench’s 3×3 matrices. ShapedRecipe scans valid offsets and mirrored patterns. ShapelessRecipe matches each occupied input against a remaining ingredient list, so duplicate ingredients are accounted for individually. RecipeRegistry.FindMatchingRecipe returns the first registered match; Assemble returns a fresh copy of the result.

The result slot is special: taking a result must consume the inputs according to its recipe/workstation rules, not behave like an ordinary storage slot. Follow ResultSlot and the concrete menu when changing consumption, container remainders, or shift-crafting.

RecipeLoader reads JSON definitions from assets/data/minecraft/recipe/. The data covers shaped and shapeless crafting, cooking, stonecutting, and smithing transforms. Ingredients can name an item, refer to a tag with #, or provide a list of alternatives. For example:

{
"type": "minecraft:crafting_shaped",
"key": { "#": "minecraft:stick", "X": "#minecraft:stone_tool_materials" },
"pattern": ["XXX", " # ", " # "],
"result": { "id": "minecraft:stone_pickaxe" }
}

The loader feeds the existing recipe registries; matching and result consumption remain runtime responsibilities. Unsupported or unresolved definitions are reported and skipped, and hand-registered recipes still provide coverage for special cases. When adding a recipe, confirm its type is supported, ensure referenced tags are loaded, and inspect the target workstation’s result-slot behavior.

  • Shared clicks: src/Inventory/AbstractContainerMenu.cs
  • Slot constraints and placement: src/Inventory/Slot.cs and concrete slot subclasses
  • Workbench layout: src/Inventory/CraftingMenu.cs
  • Recipe matching and registration: src/Crafting/IRecipe.cs, ShapedRecipe.cs, ShapelessRecipe.cs, RecipeRegistry.cs
  • Storage widgets: src/Inventory/*Menu.cs and the owning block entity/container

A UI can present menu state, but it should not become the rule owner for recipe matching, stack compatibility, or transfer policy.

Recipe definitions are JSON-backed for the supported recipe types, with hand-registered fallbacks for special cases. This is not a general-purpose data-pack reload system. Menu implementations exist for many workstation types, but their feature completeness varies. Inspect each concrete menu before assuming every interaction is implemented.