tutorial:inventory
Differences
This shows you the differences between two versions of the page.
Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
tutorial:inventory [2019/08/06 18:29] – fudge | tutorial:inventory [2025/03/30 22:06] (current) – [Storing items in a block as an Inventory] technici4n | ||
---|---|---|---|
Line 1: | Line 1: | ||
====== Storing items in a block as an Inventory ====== | ====== Storing items in a block as an Inventory ====== | ||
- | The standard | + | Make sure you've [[tutorial: |
- | This allows hoppers (or other mods) to insert and extract items from your BlockEntity without any extra work. | + | |
+ | The simplest | ||
+ | |||
+ | This tutorial is written for 1.21. For older versions, some methods may change. | ||
===== Implementing Inventory ===== | ===== Implementing Inventory ===== | ||
- | '' | + | |
- | A '' | + | '' |
- | as it can be set to default to '' | + | |
- | Implementing '' | + | |
- | so we'll use a default implementation of it which only requires giving it a '' | + | |
<code java ImplementedInventory.java> | <code java ImplementedInventory.java> | ||
/** | /** | ||
* A simple {@code Inventory} implementation with only default methods + an item list getter. | * A simple {@code Inventory} implementation with only default methods + an item list getter. | ||
* | * | ||
- | | + | |
*/ | */ | ||
public interface ImplementedInventory extends Inventory { | public interface ImplementedInventory extends Inventory { | ||
+ | |||
/** | /** | ||
- | | + | |
* Must return the same instance every time it's called. | * Must return the same instance every time it's called. | ||
*/ | */ | ||
DefaultedList< | DefaultedList< | ||
- | | + | |
/** | /** | ||
* Creates an inventory from the item list. | * Creates an inventory from the item list. | ||
Line 27: | Line 30: | ||
return () -> items; | return () -> items; | ||
} | } | ||
+ | | ||
/** | /** | ||
- | * Creates a new inventory with the size. | + | * Creates a new inventory with the specified |
*/ | */ | ||
static ImplementedInventory ofSize(int size) { | static ImplementedInventory ofSize(int size) { | ||
return of(DefaultedList.ofSize(size, | return of(DefaultedList.ofSize(size, | ||
} | } | ||
- | | + | |
/** | /** | ||
* Returns the inventory size. | * Returns the inventory size. | ||
*/ | */ | ||
@Override | @Override | ||
- | default int getInvSize() { | + | default int size() { |
return getItems().size(); | return getItems().size(); | ||
} | } | ||
+ | | ||
/** | /** | ||
- | * @return true if this inventory has only empty stacks, false otherwise | + | * Checks if the inventory is empty. |
+ | * @return true if this inventory has only empty stacks, false otherwise. | ||
*/ | */ | ||
@Override | @Override | ||
- | default boolean | + | default boolean |
- | for (int i = 0; i < getInvSize(); i++) { | + | for (int i = 0; i < size(); i++) { |
- | ItemStack stack = getInvStack(i); | + | ItemStack stack = getStack(i); |
if (!stack.isEmpty()) { | if (!stack.isEmpty()) { | ||
return false; | return false; | ||
Line 54: | Line 60: | ||
return true; | return true; | ||
} | } | ||
+ | | ||
/** | /** | ||
- | | + | |
*/ | */ | ||
@Override | @Override | ||
- | default ItemStack | + | default ItemStack |
return getItems().get(slot); | return getItems().get(slot); | ||
} | } | ||
+ | | ||
/** | /** | ||
- | | + | |
- | | + | |
- | * takes all items in that slot. | + | * @param count How many items to remove. |
+ | | ||
*/ | */ | ||
@Override | @Override | ||
- | default ItemStack | + | default ItemStack |
ItemStack result = Inventories.splitStack(getItems(), | ItemStack result = Inventories.splitStack(getItems(), | ||
if (!result.isEmpty()) { | if (!result.isEmpty()) { | ||
Line 74: | Line 83: | ||
return result; | return result; | ||
} | } | ||
+ | | ||
/** | /** | ||
- | * Removes | + | * Removes |
+ | | ||
*/ | */ | ||
@Override | @Override | ||
- | default ItemStack | + | default ItemStack |
return Inventories.removeStack(getItems(), | return Inventories.removeStack(getItems(), | ||
} | } | ||
+ | | ||
/** | /** | ||
- | * Replaces the current stack in the {@code | + | * Replaces the current stack in an inventory |
- | | + | |
- | * it gets resized to this inventory' | + | * @param stack The replacing itemstack. |
+ | | ||
+ | | ||
*/ | */ | ||
@Override | @Override | ||
- | default void setInvStack(int slot, ItemStack stack) { | + | default void setStack(int slot, ItemStack stack) { |
getItems().set(slot, | getItems().set(slot, | ||
- | if (stack.getCount() > getInvMaxStackAmount()) { | + | if (stack.getCount() > stack.getMaxCount()) { |
- | stack.setCount(getInvMaxStackAmount()); | + | stack.setCount(stack.getMaxCount()); |
} | } | ||
} | } | ||
+ | | ||
/** | /** | ||
- | * Clears | + | * Clears the inventory. |
*/ | */ | ||
@Override | @Override | ||
Line 100: | Line 115: | ||
getItems().clear(); | getItems().clear(); | ||
} | } | ||
+ | | ||
+ | /** | ||
+ | * Marks the state as dirty. | ||
+ | * Must be called after changes in the inventory, so that the game can properly save | ||
+ | * the inventory contents and notify neighboring blocks of inventory changes. | ||
+ | | ||
@Override | @Override | ||
default void markDirty() { | default void markDirty() { | ||
// Override if you want behavior. | // Override if you want behavior. | ||
} | } | ||
+ | | ||
+ | /** | ||
+ | * @return true if the player can use the inventory, false otherwise. | ||
+ | | ||
@Override | @Override | ||
- | default boolean | + | default boolean |
return true; | return true; | ||
} | } | ||
Line 111: | Line 136: | ||
</ | </ | ||
- | Now in your '' | + | Now in your '' |
- | and provide it with an instance of '' | + | < |
- | For this example we'll store a maximum of 2 items in the inventory: | + | |
- | <code java> | + | |
public class DemoBlockEntity extends BlockEntity implements ImplementedInventory { | public class DemoBlockEntity extends BlockEntity implements ImplementedInventory { | ||
private final DefaultedList< | private final DefaultedList< | ||
Line 122: | Line 145: | ||
return items; | return items; | ||
} | } | ||
- | ... | + | |
} | } | ||
</ | </ | ||
- | We're also gonna need to save the inventories to tag and load it from there. | + | We're also gonna need to save the inventories to NBT and load it from there. '' |
- | '' | + | < |
- | <code java> | + | |
public class DemoBlockEntity extends BlockEntity implements ImplementedInventory { | public class DemoBlockEntity extends BlockEntity implements ImplementedInventory { | ||
- | ... | + | |
@Override | @Override | ||
- | public void fromTag(CompoundTag tag) { | + | public void readNbt(NbtCompound nbt, RegistryWrapper.WrapperLookup registryLookup) { |
- | super.fromTag(tag); | + | super.readNbt(nbt, registryLookup); |
- | Inventories.fromTag(tag,items); | + | Inventories.readNbt(nbt, items, registryLookup); |
} | } | ||
@Override | @Override | ||
- | public | + | public |
- | Inventories.toTag(tag,items); | + | Inventories.writeNbt(nbt, items, registryLookup); |
- | return super.toTag(tag); | + | return super.writeNbt(nbt, registryLookup); |
} | } | ||
} | } | ||
</ | </ | ||
- | ===== Extracting and inserting from your inventory | + | |
- | In our block class, we'll override the `activate` | + | ===== Extracting and inserting from your inventory ===== |
- | Note that this can be done to any '' | + | |
- | First we'll handle inserting into the inventory. The player will insert the item he is holding if he is holding one. | + | In our block class, we'll override the '' |
- | It'll go into the first slot if it is empty, or to the second slot if the first one is empty, | + | |
- | or if the second is empty too we'll print some information about the inventory. | + | First we'll handle inserting into the inventory. The player will insert the item he is holding if he is holding one. It'll go into the first slot if it is empty, or to the second slot if the first one is empty, or if the second is empty too we'll print some information about the inventory. |
Note that we call '' | Note that we call '' | ||
- | <code java> | + | < |
- | public class ExampleBlock | + | public class DemoBlock |
- | ... | + | |
@Override | @Override | ||
- | | + | |
- | if (world.isClient) return | + | if (world.isClient) return |
- | | + | |
+ | if (!(world.getBlockEntity(pos) instanceof DemoBlockEntity blockEntity)) { | ||
+ | return ItemActionResult.PASS_TO_DEFAULT_BLOCK_INTERACTION; | ||
+ | } | ||
if (!player.getStackInHand(hand).isEmpty()) { | if (!player.getStackInHand(hand).isEmpty()) { | ||
// Check what is the first open slot and put an item from the player' | // Check what is the first open slot and put an item from the player' | ||
- | if (blockEntity.getInvStack(0).isEmpty()) { | + | if (blockEntity.getStack(0).isEmpty()) { |
// Put the stack the player is holding into the inventory | // Put the stack the player is holding into the inventory | ||
- | blockEntity.setInvStack(0, player.getStackInHand(hand).copy()); | + | blockEntity.setStack(0, player.getStackInHand(hand).copy()); |
// Remove the stack from the player' | // Remove the stack from the player' | ||
player.getStackInHand(hand).setCount(0); | player.getStackInHand(hand).setCount(0); | ||
- | } else if (blockEntity.getInvStack(1).isEmpty()) { | + | } else if (blockEntity.getStack(1).isEmpty()) { |
- | blockEntity.setInvStack(1, player.getStackInHand(hand).copy()); | + | blockEntity.setStack(1, player.getStackInHand(hand).copy()); |
player.getStackInHand(hand).setCount(0); | player.getStackInHand(hand).setCount(0); | ||
} else { | } else { | ||
- | // If the inventory is full we' | + | // If the inventory is full we' |
- | | + | |
- | | + | |
+ | .append(" and the second slot holds ") | ||
+ | .append(blockEntity.getStack(1).getName())); | ||
} | } | ||
} | } | ||
- | return | + | return |
} | } | ||
} | } | ||
Line 183: | Line 210: | ||
- | We'll have the opposite behavior when the player is not holding an item. We'll take the item from the second slot, and then the first one of the second is empty. | + | We'll have the opposite behavior when the player is not holding an item. We'll take the item from the second slot, and then the first one if the second is empty. |
- | If the first is empty as well we won't do anything. | + | If the first is empty we pass it to default behavior. |
- | <code java> | + | < |
- | public class ExampleBlock | + | public class DemoBlock |
- | ... | + | |
@Override | @Override | ||
- | | + | |
... | ... | ||
if (!player.getStackInHand(hand).isEmpty()) { | if (!player.getStackInHand(hand).isEmpty()) { | ||
Line 197: | Line 224: | ||
// Find the first slot that has an item and give it to the player | // Find the first slot that has an item and give it to the player | ||
- | if (!blockEntity.getInvStack(1).isEmpty()) { | + | if (!blockEntity.getStack(1).isEmpty()) { |
// Give the player the stack in the inventory | // Give the player the stack in the inventory | ||
- | player.giveItemStack(blockEntity.getInvStack(1)); | + | player.getInventory().offerOrDrop(blockEntity.getStack(1)); |
// Remove the stack from the inventory | // Remove the stack from the inventory | ||
- | blockEntity.removeInvStack(1); | + | blockEntity.removeStack(1); |
- | } else if (!blockEntity.getInvStack(0).isEmpty()) { | + | } else if (!blockEntity.getStack(0).isEmpty()) { |
- | player.giveItemStack(blockEntity.getInvStack(0)); | + | player.getInventory().offerOrDrop(blockEntity.getStack(0)); |
- | blockEntity.removeInvStack(0); | + | blockEntity.removeStack(0); |
+ | } else { | ||
+ | return ItemActionResult.PASS_TO_DEFAULT_BLOCK_INTERACTION; | ||
} | } | ||
} | } | ||
| | ||
- | return | + | return |
} | } | ||
} | } | ||
Line 214: | Line 243: | ||
===== Implementing SidedInventory ===== | ===== Implementing SidedInventory ===== | ||
- | If you want to have different logic based on what side things (hopper or other mods) interact with your block | + | If you want to have different logic based on what side things (hopper or other mods) interact with your block you need to implement '' |
- | you need to implement '' | + | |
- | If say you wanted to make it so you cannot insert from the upper side of the block, you would do this: | + | < |
- | <code java> | + | |
public class DemoBlockEntity extends BlockEntity implements ImplementedInventory, | public class DemoBlockEntity extends BlockEntity implements ImplementedInventory, | ||
- | ... | + | |
@Override | @Override | ||
- | public int[] getInvAvailableSlots(Direction | + | public int[] getInvAvailableSlots(Direction |
// Just return an array of all slots | // Just return an array of all slots | ||
- | | + | |
- | for (int i = 0; i < result.length; i++) { | + | |
- | result[i] = i; | + | |
- | } | + | |
- | + | ||
- | return result; | + | |
} | } | ||
@Override | @Override | ||
- | public boolean | + | public boolean |
return direction != Direction.UP; | return direction != Direction.UP; | ||
} | } | ||
@Override | @Override | ||
- | public boolean | + | public boolean |
return true; | return true; | ||
} | } | ||
} | } | ||
- | |||
</ | </ | ||
tutorial/inventory.1565116191.txt.gz · Last modified: 2019/08/06 18:29 by fudge