User Tools

Site Tools


tutorial:inventory

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
tutorial:inventory [2021/04/19 02:19] – onUse has a return type of ActionResult platymemotutorial:inventory [2025/03/30 22:06] (current) – [Storing items in a block as an Inventory] technici4n
Line 2: Line 2:
 Make sure you've [[tutorial:blockentity|made a block entity]] before reading this tutorial.   Make sure you've [[tutorial:blockentity|made a block entity]] before reading this tutorial.  
  
-The standard way to store items in a BlockEntity is to make it an ''Inventory'' +The simplest way to store items in a BlockEntity is to make it an ''Inventory''. This allows hoppers (or other mods) to insert and extract items from your BlockEntity without any extra work. For more flexible and complex ways to store items, refer to the [[tutorial:transfer-api_item_storage|Item transfer with Storage<ItemVariant>]] tutorial. 
-This allows hoppers (or other mods) to insert and extract items from your BlockEntity without any extra work. + 
 +This tutorial is written for 1.21. For older versions, some methods may change. 
 ===== Implementing Inventory ===== ===== Implementing Inventory =====
-''Inventory'' is just an interface, which means the actual ''ItemStack'' state will need to be stored on your ''BlockEntity''. + 
-A ''DefaultedList<ItemStack>'' can be used as an easy way to store  these ''ItemStacks'',  +''Inventory'' is just an interface, which means the actual ''ItemStack'' state will need to be stored on your ''BlockEntity''. A ''DefaultedList<ItemStack>'' can be used as an easy way to store  these ''ItemStacks'', as it can be set to default to ''ItemStack.Empty'', which is the proper way of saying that there is no item in a slot. Implementing ''Inventory'' is fairly simple, but is tedious and prone to error, so we'll use a default implementation of it which only requires giving it a ''DefaultList<ItemStack>'' (copy this as a new file): 
-as it can be set to default to ''ItemStack.Empty'', which is the proper way of saying that there is no item in a slot. +
-Implementing ''Inventory'' is fairly simple, but is tedious and prone to error,  +
-so we'll use a default implementation of it which only requires giving it a ''DefaultList<ItemStack>'' (copy this as a new file):+
 <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.
  *  *
- Originally by Juuz+ @author Juuz
  */  */
 public interface ImplementedInventory extends Inventory { public interface ImplementedInventory extends Inventory {
Line 103: Line 103:
     default void setStack(int slot, ItemStack stack) {     default void setStack(int slot, ItemStack stack) {
         getItems().set(slot, stack);         getItems().set(slot, stack);
-        if (stack.getCount() > getMaxCountPerStack()) { +        if (stack.getCount() > stack.getMaxCount()) { 
-            stack.setCount(getMaxCountPerStack());+            stack.setCount(stack.getMaxCount());
         }         }
     }     }
Line 136: Line 136:
 </code> </code>
  
-Now in your ''BlockEntity'' Implement ''ImplementedInventory'',  +Now in your ''BlockEntity'', implement ''ImplementedInventory'', and provide it with an instance of ''DefaultedList<ItemStack> items'' that stores the items. For this example we'll store a maximum of 2 items in the inventory: 
-and provide it with an instance of ''DefaultedList<ItemStack> items'' that stores the items. +<code java DemoBlockEntity.java>
-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<ItemStack> items = DefaultedList.ofSize(2, ItemStack.EMPTY);     private final DefaultedList<ItemStack> items = DefaultedList.ofSize(2, ItemStack.EMPTY);
Line 152: Line 150:
  
 </code> </code>
-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. ''Inventories'' has helper methods that make this very easy: 
-''Inventories'' has helper methods that makes this very easy: +<code java DemoBlockEntity.java>
-<code java>+
 public class DemoBlockEntity extends BlockEntity implements ImplementedInventory { public class DemoBlockEntity extends BlockEntity implements ImplementedInventory {
     [...]     [...]
     @Override     @Override
-    public void fromTag(BlockState stateCompoundTag tag) { +    public void readNbt(NbtCompound nbtRegistryWrapper.WrapperLookup registryLookup) { 
-        super.fromTag(statetag); +        super.readNbt(nbtregistryLookup); 
-        Inventories.fromTag(tag,items);+        Inventories.readNbt(nbt, items, registryLookup);
     }     }
  
     @Override     @Override
-    public CompoundTag toTag(CompoundTag tag) { +    public NbtCompound writeNbt(NbtCompound nbt, RegistryWrapper.WrapperLookup registryLookup) { 
-        Inventories.toTag(tag,items); +        Inventories.writeNbt(nbt, items, registryLookup); 
-        return super.toTag(tag);+        return super.writeNbt(nbt, registryLookup);
     }     }
 } }
 </code> </code>
-===== Extracting and inserting from your inventory (or any inventory) ===== + 
-In our block class, we'll override the `onUsebehavior to insert and extract items from our inventory.  +===== Extracting and inserting from your inventory ===== 
-Note that this can be done to any ''Inventory'' instance, not just our own (so you could do the same thing to a chest block, for example). + 
-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 ''onUse'' behavior to insert and extract items from our inventory. Note that this can be done to any ''Inventory'' instance, not just our own (so you could do the same thing to a chest block, for example). 
-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 ''copy()'' when inserting the ''ItemStack'' into the inventory so it doesn't get destroyed alongside the player's ''ItemStack''. Note that we call ''copy()'' when inserting the ''ItemStack'' into the inventory so it doesn't get destroyed alongside the player's ''ItemStack''.
-<code java> +<code java DemoBlock.java> 
-public class ExampleBlock extends Block implements BlockEntityProvider {+public class DemoBlock extends BlockWithEntity {
     [...]     [...]
     @Override     @Override
-    public ActionResult onUse(BlockState blockState, World world, BlockPos blockPos, PlayerEntity player, Hand hand, BlockHitResult blockHitResult) {+    protected ItemActionResult onUseWithItem(ItemStack stack, BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) {
         if (world.isClient) return ActionResult.SUCCESS;         if (world.isClient) return ActionResult.SUCCESS;
-        Inventory blockEntity = (Inventory) world.getBlockEntity(blockPos); +         
 +        if (!(world.getBlockEntity(posinstanceof DemoBlockEntity blockEntity)) { 
 +            return ItemActionResult.PASS_TO_DEFAULT_BLOCK_INTERACTION
 +        }
  
         if (!player.getStackInHand(hand).isEmpty()) {         if (!player.getStackInHand(hand).isEmpty()) {
Line 197: Line 197:
                 player.getStackInHand(hand).setCount(0);                 player.getStackInHand(hand).setCount(0);
             } else {             } else {
-                // If the inventory is full we'll print it's contents +                // If the inventory is full we'll notify the player 
-                System.out.println("The first slot holds " +                player.sendMessage(Text.literal("The inventory is full! The first slot holds ") 
-                        blockEntity.getStack(0) " and the second slot holds " blockEntity.getStack(1));+                    .append(blockEntity.getStack(0).getName()) 
 +                    .append(" and the second slot holds "
 +                    .append(blockEntity.getStack(1).getName()));
             }             }
         }          } 
-        return ActionResult.SUCCESS;+        return ItemActionResult.SUCCESS;
     }     }
 } }
Line 208: 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> +<code java DemoBlock.java> 
-public class ExampleBlock extends Block implements BlockEntityProvider {+public class DemoBlock extends BlockWithEntity {
     [...]     [...]
     @Override     @Override
-    public ActionResult onUse(BlockState blockState, World world, BlockPos blockPos, PlayerEntity player, Hand hand, BlockHitResult blockHitResult) {+    protected ItemActionResult onUseWithItem(ItemStack stack, BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) {
         ...         ...
         if (!player.getStackInHand(hand).isEmpty()) {         if (!player.getStackInHand(hand).isEmpty()) {
Line 221: Line 223:
             // If the player is not holding anything we'll get give him the items in the block entity one by one             // If the player is not holding anything we'll get give him the items in the block entity one by one
  
-             // 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.getStack(1).isEmpty()) {             if (!blockEntity.getStack(1).isEmpty()) {
                 // Give the player the stack in the inventory                 // Give the player the stack in the inventory
-                player.inventory.offerOrDrop(world, blockEntity.getStack(1));+                player.getInventory().offerOrDrop(blockEntity.getStack(1));
                 // Remove the stack from the inventory                 // Remove the stack from the inventory
                 blockEntity.removeStack(1);                 blockEntity.removeStack(1);
             } else if (!blockEntity.getStack(0).isEmpty()) {             } else if (!blockEntity.getStack(0).isEmpty()) {
-                player.inventory.offerOrDrop(world, blockEntity.getStack(0));+                player.getInventory().offerOrDrop(blockEntity.getStack(0));
                 blockEntity.removeStack(0);                 blockEntity.removeStack(0);
 +            } else {
 +                return ItemActionResult.PASS_TO_DEFAULT_BLOCK_INTERACTION;
             }             }
         }         }
                  
-        return ActionResult.SUCCESS;+        return ItemActionResult.SUCCESS;
     }     }
 } }
Line 239: 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 ''SidedInventory''. If say you wanted to make it so you cannot insert from the upper side of the block, you would do this: 
-you need to implement ''SidedInventory'' + 
-If say you wanted to make it so you cannot insert from the upper side of the block, you would do this: +<code java DemoBlockEntity.java>
-<code java>+
 public class DemoBlockEntity extends BlockEntity implements ImplementedInventory, SidedInventory { public class DemoBlockEntity extends BlockEntity implements ImplementedInventory, SidedInventory {
     [...]     [...]
     @Override     @Override
-    public int[] getInvAvailableSlots(Direction var1) {+    public int[] getInvAvailableSlots(Direction side) {
         // Just return an array of all slots         // Just return an array of all slots
-        int[] result = new int[getItems().size()]; +        return IntStream.of(getItems().size()).toArray();
-        for (int i = 0; i < result.length; i+++
-            result[i] = i; +
-        } +
- +
-        return result;+
     }     }
  
Line 266: Line 264:
     }     }
 } }
- 
 </code> </code>
  
tutorial/inventory.1618798778.txt.gz · Last modified: 2021/04/19 02:19 by platymemo