User Tools

Site Tools


tutorial:containers

Differences

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

Link to this comparison view

Next revision
Previous revision
tutorial:containers [2020/02/27 13:31] – new tutorial mkpolitutorial:containers [2022/05/27 15:57] (current) solidblock
Line 3: Line 3:
  
 ==== Block and BlockItem ==== ==== Block and BlockItem ====
-First create and register our block and BlockItem+First we need to create the Block and register it as well as its BlockItem.
  
-<code java [enable_line_numbers="true"]>+<code java [enable_line_numbers="true"BiggerChestBlock.java>
 public class BiggerChestBlock extends BlockWithEntity { public class BiggerChestBlock extends BlockWithEntity {
     public BiggerChestBlock(Settings settings) {     public BiggerChestBlock(Settings settings) {
         super(settings);         super(settings);
     }     }
- +     
-    // We need to explicitly return a MODEL render type because BlockWithEntity has a side-effect of not showing the model. +    // side effect of extending BlockWithEntity is it changes the render type to INVISIBLE, so we have to revert this 
-    // You can also implement BlockEntityProvider Type to avoid this side-effect.+    @Override
     public BlockRenderType getRenderType(BlockState state) {     public BlockRenderType getRenderType(BlockState state) {
         return BlockRenderType.MODEL;         return BlockRenderType.MODEL;
Line 18: Line 18:
  
     // We will create the BlockEntity later.     // We will create the BlockEntity later.
 +    @Override
     public BlockEntity createBlockEntity(BlockView view) {     public BlockEntity createBlockEntity(BlockView view) {
         return new BiggerChestBlockEntity();         return new BiggerChestBlockEntity();
     }     }
  
 +    @Override
     public void onPlaced(World world, BlockPos pos, BlockState state, LivingEntity placer, ItemStack itemStack) {     public void onPlaced(World world, BlockPos pos, BlockState state, LivingEntity placer, ItemStack itemStack) {
         if (itemStack.hasCustomName()) {         if (itemStack.hasCustomName()) {
Line 31: Line 33:
     }     }
  
 +    @Override
     public ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) {     public ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) {
         if (!world.isClient) {         if (!world.isClient) {
Line 42: Line 45:
  
     // Scatter the items in the chest when it is removed.     // Scatter the items in the chest when it is removed.
 +    @Override
     public void onBlockRemoved(BlockState state, World world, BlockPos pos, BlockState newState, boolean moved) {     public void onBlockRemoved(BlockState state, World world, BlockPos pos, BlockState newState, boolean moved) {
         if (state.getBlock() != newState.getBlock()) {         if (state.getBlock() != newState.getBlock()) {
             BlockEntity blockEntity = world.getBlockEntity(pos);             BlockEntity blockEntity = world.getBlockEntity(pos);
             if (blockEntity instanceof BiggerChestBlockEntity) {             if (blockEntity instanceof BiggerChestBlockEntity) {
-                ItemScatterer.spawn(world, (BlockPos)pos, (Inventory)((BiggerChestBlockEntity)blockEntity));+                ItemScatterer.spawn(world, pos, (BiggerChestBlockEntity)blockEntity); 
 +                // update comparators
                 world.updateHorizontalAdjacent(pos, this);                 world.updateHorizontalAdjacent(pos, this);
             }             }
Line 53: Line 58:
     }     }
  
 +    @Override
     public boolean hasComparatorOutput(BlockState state) {     public boolean hasComparatorOutput(BlockState state) {
         return true;         return true;
     }     }
  
 +    @Override
     public int getComparatorOutput(BlockState state, World world, BlockPos pos) {     public int getComparatorOutput(BlockState state, World world, BlockPos pos) {
         return Container.calculateComparatorOutput(world.getBlockEntity(pos));         return Container.calculateComparatorOutput(world.getBlockEntity(pos));
Line 65: Line 72:
 Then we need to register our Block and BlockItem. Then we need to register our Block and BlockItem.
  
-<code java [enable_line_numbers="true"]>+<code java [enable_line_numbers="true"ExampleMod.java>
 public class ExampleMod implements ModInitializer public class ExampleMod implements ModInitializer
 { {
Line 74: Line 81:
     public static final Identifier BIGGER_CHEST = new Identifier(MOD_ID, "bigger_chest_block");     public static final Identifier BIGGER_CHEST = new Identifier(MOD_ID, "bigger_chest_block");
  
-    public static final Block BIGGER_CHEST_BLOCK = new BiggerChestBlock(FabricBlockSettings.of(Material.METAL).build());+    public static final Block BIGGER_CHEST_BLOCK = new BiggerChestBlock(FabricBlockSettings.of(Material.METAL));
        
     @Override     @Override
Line 80: Line 87:
     {     {
         Registry.register(Registry.BLOCK, BIGGER_CHEST, BIGGER_CHEST_BLOCK);         Registry.register(Registry.BLOCK, BIGGER_CHEST, BIGGER_CHEST_BLOCK);
-        Registry.register(Registry.BLOCK, BIGGER_CHEST, new BlockItem(BIGGER_CHEST_BLOCK, new Item.Settings().group(ItemGroup.REDSTONE)));+        Registry.register(Registry.ITEM, BIGGER_CHEST, new BlockItem(BIGGER_CHEST_BLOCK, new Item.Settings().group(ItemGroup.REDSTONE)));
     }     }
 } }
 </code> </code>
 +
 +You may refer to other tutorials to modify the appearance and other properties of the Block by adding models or adjusting rendering later.
  
 ==== BlockEntity ==== ==== BlockEntity ====
-We then need to add an ''onUse'' method in our Block class in order to open our chest.+BlockEntity is used for managing container inventories. Actually, it implements Inventory interface. It is required to save and load the inventory.
  
-<code java> +<code java [enable_line_numbers="true"] BiggerChestBlockEntity.java> 
-    +public class BiggerChestBlockEntity extends LootableContainerBlockEntity { 
 +    private DefaultedList<ItemStack> inventory; 
 +    private static final int INVENTORY_SIZE = 54; // 9 * 6 = 54 
 + 
 +    public BiggerChestBlockEntity() { 
 +        super(ExampleMod.BIGGER_CHEST_ENTITY_TYPE); 
 +        this.inventory = DefaultedList.ofSize(INVENTORY_SIZE, ItemStack.EMPTY); 
 +    } 
 + 
 +    @Override 
 +    protected Text getContainerName() { 
 +        // versions 1.18.2 and below 
 +        return new TranslatableText("container.chest"); 
 +        // versions since 1.19 
 +        return Text.translatable("container.chest"); 
 +    } 
 + 
 +    @Override 
 +    protected ScreenHandler createScreenHandler(int syncId, PlayerInventory playerInventory) { 
 +        return new BiggerChestScreenHandler(syncId, playerInventory, (Inventory) this); 
 +    } 
 + 
 +    @Override 
 +    protected DefaultedList<ItemStack> getInvStackList() { 
 +        return this.inventory; 
 +    } 
 + 
 +    @Override 
 +    protected void setInvStackList(DefaultedList<ItemStack> list) { 
 +        this.inventory = list; 
 +    } 
 + 
 +    @Override 
 +    public int size() { 
 +        return INVENTORY_SIZE; 
 +    } 
 + 
 +    @Override 
 +    public void fromTag(CompoundTag tag) { 
 +        super.fromTag(tag); 
 +        this.inventory = DefaultedList.ofSize(this.size(), ItemStack.EMPTY); 
 +        if (!this.deserializeLootTable(tag)) { 
 +            Inventories.fromTag(tag, this.inventory); 
 +        } 
 +    } 
 + 
 +    @Override 
 +    public CompoundTag toTag(CompoundTag tag) { 
 +        super.toTag(tag); 
 +        if (!this.serializeLootTable(tag)) { 
 +            Inventories.toTag(tag, this.inventory); 
 +        } 
 +        return tag; 
 +    } 
 +}
 </code> </code>
  
 ==== Container GUI and Screen ==== ==== Container GUI and Screen ====
-We need a Container Class and a ContainerScreen Class to display and sync the GUI. Container Classes are used to synchronize GUI state between the server and the client. ContainerScreen Classes are fully client-sided and are responsible for drawing GUI elements.+We need a ScreenHandler Class and a HandledScreen Class to display and sync the GUI. ScreenHandler classes are used to synchronize GUI state between the server and the client. HandledScreen classes are fully client-sided and are responsible for drawing GUI elements.
  
-<code java [enable_line_numbers="true"BiggerChestContainer.java> +<code java [enable_line_numbers="true"BiggerChestScreenHandler.java> 
-public class BiggerChestContainer extends Container {+public class BiggerChestScreenHandler extends ScreenHandler {
     private final Inventory inventory; // Chest inventory     private final Inventory inventory; // Chest inventory
     private static final int INVENTORY_SIZE = 54; // 6 rows * 9 cols     private static final int INVENTORY_SIZE = 54; // 6 rows * 9 cols
  
-    protected BiggerChestContainer(int syncId, PlayerInventory playerInventory, Inventory inventory) { +    protected BiggerChestScreenHandler(int syncId, PlayerInventory playerInventory, Inventory inventory) { 
-        super(null, syncId); // Since we didn't create a ContainerType, we will place null here.+        super(null, syncId); // Since we didn't create a ScreenHandlerType, we will place null here.
         this.inventory = inventory;         this.inventory = inventory;
-        checkContainerSize(inventory, INVENTORY_SIZE); +        checkSize(inventory, INVENTORY_SIZE); 
-        inventory.onInvOpen(playerInventory.player);+        inventory.onOpen(playerInventory.player);
  
-        // Creating Slots for GUI. A Slot is essentially a correspoding from inventory itemstacks to the GUI position.+        // Creating Slots for GUI. A Slot is essentially a corresponding from inventory ItemStacks to the GUI position.
         int i;         int i;
         int j;         int j;
Line 133: Line 196:
     @Override     @Override
     public boolean canUse(PlayerEntity player) {     public boolean canUse(PlayerEntity player) {
-        return this.inventory.canPlayerUseInv(player);+        return this.inventory.canPlayerUse(player);
     }     }
  
     // Shift + Player Inv Slot     // Shift + Player Inv Slot
 +    @Override
     public ItemStack transferSlot(PlayerEntity player, int invSlot) {     public ItemStack transferSlot(PlayerEntity player, int invSlot) {
         ItemStack newStack = ItemStack.EMPTY;         ItemStack newStack = ItemStack.EMPTY;
Line 164: Line 228:
  
 <code java [enable_line_numbers="true"] BiggerChestScreen.java> <code java [enable_line_numbers="true"] BiggerChestScreen.java>
-public class BiggerChestScreen extends ContainerScreen<BiggerChestContainer> {+public class BiggerChestScreen extends HandledScreen<BiggerChestScreenHandler> {
  
     // a path to gui texture, you may replace it with new Identifier(YourMod.MOD_ID, "textures/gui/container/your_container.png");     // a path to gui texture, you may replace it with new Identifier(YourMod.MOD_ID, "textures/gui/container/your_container.png");
     private static final Identifier TEXTURE = new Identifier("textures/gui/container/generic_54.png");     private static final Identifier TEXTURE = new Identifier("textures/gui/container/generic_54.png");
  
-    public BiggerChestScreen(BiggerChestContainer container, PlayerInventory playerInventory, Text title) { +    public BiggerChestScreen(BiggerChestScreenHandler handler, PlayerInventory playerInventory, Text title) { 
-        super(container, playerInventory, title); +        super(handler, playerInventory, title); 
-        this.containerHeight = 114 + 6 * 18;+        this.backgroundHeight = 114 + 6 * 18;
     }     }
  
     @Override     @Override
-    protected void drawForeground(int mouseX, int mouseY) { +    protected void drawForeground(MatrixStack matrices, int mouseX, int mouseY) { 
-        this.font.draw(this.title.asFormattedString(), 8.0F, 6.0F, 4210752); +        this.textRenderer.draw(matrices, this.title.asString(), 8.0F, 6.0F, 4210752); 
-        this.font.draw(this.playerInventory.getDisplayName().asFormattedString(), 8.0F, (float)(this.containerHeight - 96 + 2), 4210752);+        this.textRenderer.draw(matrices, this.playerInventory.getDisplayName().asString(), 8.0F, (float)(this.backgroundHeight - 96 + 2), 4210752);
     }     }
  
Line 183: Line 247:
     protected void drawBackground(float delta, int mouseX, int mouseY) {     protected void drawBackground(float delta, int mouseX, int mouseY) {
         RenderSystem.color4f(1.0F, 1.0F, 1.0F, 1.0F);         RenderSystem.color4f(1.0F, 1.0F, 1.0F, 1.0F);
-        this.minecraft.getTextureManager().bindTexture(TEXTURE); +        this.client.getTextureManager().bindTexture(TEXTURE); 
-        int i = (this.width - this.containerWidth) / 2; +        int i = (this.width - this.backgroundWidth) / 2; 
-        int j = (this.height - this.containerHeight) / 2; +        int j = (this.height - this.backgroundHeight) / 2; 
-        this.blit(i, j, 0, 0, this.containerWidth, 6 * 18 + 17); +        this.blit(i, j, 0, 0, this.backgroundWidth, 6 * 18 + 17); 
-        this.blit(i, j + 6 * 18 + 17, 0, 126, this.containerWidth, 96);+        this.blit(i, j + 6 * 18 + 17, 0, 126, this.backgroundWidth, 96);
     }     }
 } }
Line 198: Line 262:
     public static final String BIGGER_CHEST_TRANSLATION_KEY = Util.createTranslationKey("container", BIGGER_CHEST);     public static final String BIGGER_CHEST_TRANSLATION_KEY = Util.createTranslationKey("container", BIGGER_CHEST);
  
 +    @Override
     public void onInitialize() {     public void onInitialize() {
         [...]         [...]
         ContainerProviderRegistry.INSTANCE.registerFactory(BIGGER_CHEST, (syncId, identifier, player, buf) -> {         ContainerProviderRegistry.INSTANCE.registerFactory(BIGGER_CHEST, (syncId, identifier, player, buf) -> {
-            final BlockEntity blockEntity = player.world.getBlockEntity(buf.readBlockPos()); +            final World world = player.world
-            return((BiggerChestBlockEntityblockEntity).createContainer(syncId, player.inventory);+            final BlockPos pos = buf.readBlockPos(); 
 +            return world.getBlockState(pos).createContainerFactory(player.world, pos).createMenu(syncId, player.inventory, player);
         });         });
     }     }
Line 208: Line 274:
  
 <code java [enable_line_numbers="true"]> <code java [enable_line_numbers="true"]>
 +    @Override
     public void onInitializeClient() {     public void onInitializeClient() {
         [...]         [...]
-        ScreenProviderRegistry.INSTANCE.<BiggerChestContainer>registerFactory(ExampleMod.BIGGER_CHEST, (container) -> new BiggerChestScreen(container, MinecraftClient.getInstance().player.inventory, new TranslatableText(ExampleMod.BIGGER_CHEST_TRANSLATION_KEY)));+        ScreenProviderRegistry.INSTANCE.<BiggerChestContainer>registerFactory(ExampleMod.BIGGER_CHEST, (container) -> new BiggerChestScreen(container, MinecraftClient.getInstance().player.inventory, Text.translatable(ExampleMod.BIGGER_CHEST_TRANSLATION_KEY)));
     }     }
 </code> </code>
  
-==== Orgnizing ==== +==== Organizing ==== 
-After following  +After all the steps, you should have your ExampleMod Class and ExampleClientMod Class as such:
- +
-You should have your ExampleMod Class and ExampleClientMod Class as such:+
  
 <code java [enable_line_numbers="true"] ExampleMod.java> <code java [enable_line_numbers="true"] ExampleMod.java>
Line 243: Line 308:
     @Override     @Override
     public void onInitializeClient() {     public void onInitializeClient() {
-        ScreenProviderRegistry.INSTANCE.<BiggerChestContainer>registerFactory(ExampleMod.BIGGER_CHEST, (container) -> new BiggerChestScreen(container, MinecraftClient.getInstance().player.inventory, new TranslatableText(ExampleMod.BIGGER_CHEST_TRANSLATION_KEY)));+        ScreenProviderRegistry.INSTANCE.<BiggerChestContainer>registerFactory(ExampleMod.BIGGER_CHEST, (container) -> new BiggerChestScreen(container, MinecraftClient.getInstance().player.inventory, Text.translatable(ExampleMod.BIGGER_CHEST_TRANSLATION_KEY)));
     }     }
 </code> </code>
 +
 +{{tutorial:bigger_chest.png}}
  
 It's over and enjoy your custom container! It's over and enjoy your custom container!
tutorial/containers.1582810298.txt.gz · Last modified: 2020/02/27 13:31 by mkpoli