User Tools

Site Tools


tutorial:screenhandler

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:screenhandler [2020/08/14 15:59] – [Creating a Container Block (DRAFT) (NEW)] manymoney2tutorial:screenhandler [2024/02/19 02:51] (current) – [ScreenHandler and Screen] transferSlot -> quickMove per yarn mappings netuserget
Line 1: Line 1:
-====== Creating a Container Block (DRAFT) (NEW) ====== +====== Creating a Container Block====== 
-In this tutorial we will create simple Storage Block similar to a dispenser. Explaining the ScreenHandler API from Fabric and Vanilla Minecraft along the way.+In this tutorial we will create simple storage block similar to a dispenser, explaining how to build a user interface with the ''ScreenHandler'' API from Fabric and Vanilla Minecraft along the way.
  
 Let us first explain some vocabulary: Let us first explain some vocabulary:
  
 **Screenhandler:** **Screenhandler:**
-A ScreenHandler is a class responsible for Synchronizing inventory contents between the Client and the Server. It can sync integer values like furnace progress aswell, which will be showed in the next Tutorial.+''ScreenHandler'' is a class responsible for synchronizing inventory contents between the client and the server. It can sync additional integer values like furnace progress as well, which will be showed in the next tutorial. 
 +Our subclass will have two constructors here: one will be used on the server side and will contain the real ''Inventory'' and another one will be used on the client side to hold the ''ItemStack''s and synchronize them.
  
 **Screen:** **Screen:**
-The Screen class only exists on the client and will render the background and other decorations for your ScreenHandler on the Screen+The ''Screen'' class only exists on the client and will render the background and other decorations for your ''ScreenHandler''.
  
-===== Further Reading ===== 
-This and the following Tutorials are heavily inspired by the ScreenHandlerAPI ExampleMod. 
-It can be found here: [[https://github.com/FabricMC/fabric/tree/1.16/fabric-screen-handler-api-v1/src/testmod|ExampleMod on Github]] 
-The documentation for the Fabric ScreenHandlerAPI can be found here: ##javadoc link here## 
 ===== Block and BlockEntity classes ===== ===== Block and BlockEntity classes =====
  
  
-First we need to create the Block and its BlockEntity+First we need to create the ''Block'' and its ''BlockEntity''.
  
-<code java [enable_line_numbers="true"BiggerChestBlock.java>+<code java [enable_line_numbers="true"BoxBlock.java>
 public class BoxBlock extends BlockWithEntity { public class BoxBlock extends BlockWithEntity {
     protected BoxBlock(Settings settings) {     protected BoxBlock(Settings settings) {
Line 26: Line 23:
  
     @Override     @Override
-    public BlockEntity createBlockEntity(BlockView world) { +    public BlockEntity createBlockEntity(BlockPos pos, BlockState state) { 
-        return new BoxBlockEntity();+        return new BoxBlockEntity(pos, state);
     }     }
  
Line 39: Line 36:
     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) {
-            //This will call the createScreenHandlerFactory method from blockWithEntity, which will return our blockEntity casted +            //This will call the createScreenHandlerFactory method from BlockWithEntity, which will return our blockEntity casted to 
-            //to a namedScreenHandlerFactory+            //a namedScreenHandlerFactory. If your block class does not extend BlockWithEntity, it needs to implement createScreenHandlerFactory.
             NamedScreenHandlerFactory screenHandlerFactory = state.createScreenHandlerFactory(world, pos);             NamedScreenHandlerFactory screenHandlerFactory = state.createScreenHandlerFactory(world, pos);
  
Line 73: Line 70:
     @Override     @Override
     public int getComparatorOutput(BlockState state, World world, BlockPos pos) {     public int getComparatorOutput(BlockState state, World world, BlockPos pos) {
-        return ScreenHandler.calculateComparatorOutput(world.getBlockEntity(pos))+        return ScreenHandler.calculateComparatorOutput(world.getBlockEntity(pos));
     }     }
 } }
 </code> </code>
  
-Now we will create our BlockEntity, it will use the ImplementedInventory Interface from the [[tutorial:inventory|Inventory Tutorial]]+Now we will create our ''BlockEntity'', it will use the ''ImplementedInventory'' interface from the [[tutorial:inventory|Inventory Tutorial]].
  
 <code java [enable_line_numbers="true"] BoxBlockEntity.java> <code java [enable_line_numbers="true"] BoxBlockEntity.java>
Line 84: Line 81:
     private final DefaultedList<ItemStack> inventory = DefaultedList.ofSize(9, ItemStack.EMPTY);     private final DefaultedList<ItemStack> inventory = DefaultedList.ofSize(9, ItemStack.EMPTY);
  
-    public BoxBlockEntity() { +    public BoxBlockEntity(BlockPos pos, BlockState state) { 
-        super(ExampleMod.BOX_BLOCK_ENTITY);+        super(ExampleMod.BOX_BLOCK_ENTITY, pos, state);
     }     }
  
Line 94: Line 91:
     public DefaultedList<ItemStack> getItems() {     public DefaultedList<ItemStack> getItems() {
         return inventory;         return inventory;
- 
     }     }
  
Line 110: Line 106:
     @Override     @Override
     public Text getDisplayName() {     public Text getDisplayName() {
-        return new TranslatableText(getCachedState().getBlock().getTranslationKey());+        // for 1.19+ 
 +        return Text.translatable(getCachedState().getBlock().getTranslationKey()); 
 +        // for earlier versions 
 +        // return new TranslatableText(getCachedState().getBlock().getTranslationKey());
     }     }
          
     @Override     @Override
-    public void fromTag(BlockState state, CompoundTag tag) { +    public void readNbt(NbtCompound nbt) { 
-        super.fromTag(state, tag); +        super.readNbt(nbt); 
-        inventory = DefaultedList.ofSize(invsize, ItemStack.EMPTY); +        Inventories.readNbt(nbt, this.inventory);
-        Inventories.fromTag(tag, this.inventory);+
     }     }
  
     @Override     @Override
-    public CompoundTag toTag(CompoundTag tag) { +    public NbtCompound writeNbt(NbtCompound nbt) { 
-        super.toTag(tag); +        super.writeNbt(nbt); 
-        Inventories.toTag(tag, this.inventory); +        Inventories.writeNbt(nbt, this.inventory); 
-        return tag;+        return nbt;
     }     }
 } }
  
 </code> </code>
 +
 +===== Registering Block, BlockItem and BlockEntity =====
 +
 +
  
 <code java [enable_line_numbers="true"] ExampleMod.java> <code java [enable_line_numbers="true"] ExampleMod.java>
Line 136: Line 138:
     public static final BlockItem BOX_BLOCK_ITEM;     public static final BlockItem BOX_BLOCK_ITEM;
     public static final BlockEntityType<BoxBlockEntity> BOX_BLOCK_ENTITY;     public static final BlockEntityType<BoxBlockEntity> BOX_BLOCK_ENTITY;
-    public static final ScreenHandlerType<BoxScreenHandler> BOX_SCREEN_HANDLER; 
  
     public static final String MOD_ID = "testmod";     public static final String MOD_ID = "testmod";
     // a public identifier for multiple parts of our bigger chest     // a public identifier for multiple parts of our bigger chest
-    public static final Identifier BOX = new Identifier(MOD_ID, "bigger_chest_block");+    public static final Identifier BOX = new Identifier(MOD_ID, "box_block");
  
     static {     static {
-        BOX_BLOCK = Registry.register(Registry.BLOCK, BOX, new BoxBlock(FabricBlockSettings.copyOf(Blocks.CHEST))); +        BOX_BLOCK = Registry.register(Registries.BLOCK, BOX, new BoxBlock(FabricBlockSettings.copyOf(Blocks.CHEST))); 
-        BOX_BLOCK_ITEM = Registry.register(Registry.ITEM, BOX, new BlockItem(BOX_BLOCK, new Item.Settings().group(ItemGroup.MISC)));+        BOX_BLOCK_ITEM = Registry.register(Registries.ITEM, BOX, new BlockItem(BOX_BLOCK, new Item.Settings()));
  
         //The parameter of build at the very end is always null, do not worry about it         //The parameter of build at the very end is always null, do not worry about it
 +        // pre-1.17
         BOX_BLOCK_ENTITY = Registry.register(Registry.BLOCK_ENTITY_TYPE, BOX, BlockEntityType.Builder.create(BoxBlockEntity::new, BOX_BLOCK).build(null));         BOX_BLOCK_ENTITY = Registry.register(Registry.BLOCK_ENTITY_TYPE, BOX, BlockEntityType.Builder.create(BoxBlockEntity::new, BOX_BLOCK).build(null));
- +        // In 1.17 use FabricBlockEntityTypeBuilder instead of BlockEntityType.Builder 
-        BOX_SCREEN_HANDLER ScreenHandlerRegistry.registerSimple(BOX, BoxScreenHandler::new);+        BOX_BLOCK_ENTITY Registry.register(Registry.BLOCK_ENTITY_TYPE, BOX, FabricBlockEntityTypeBuilder.create(BoxBlockEntity::new, BOX_BLOCK).build(null));
     }     }
  
Line 161: Line 163:
 </code> </code>
  
-==== Container GUI and Screen ==== +===== ScreenHandler and Screen ====
-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.+ 
 +As explained earlier, we need both ''ScreenHandler'' and a ''HandledScreen'' 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"] BoxScreenHandler.java> <code java [enable_line_numbers="true"] BoxScreenHandler.java>
Line 170: Line 173:
     //This constructor gets called on the client when the server wants it to open the screenHandler,     //This constructor gets called on the client when the server wants it to open the screenHandler,
     //The client will call the other constructor with an empty Inventory and the screenHandler will automatically     //The client will call the other constructor with an empty Inventory and the screenHandler will automatically
-    //sync this empty inventory with the inventory on the server+    //sync this empty inventory with the inventory on the server.
     public BoxScreenHandler(int syncId, PlayerInventory playerInventory) {     public BoxScreenHandler(int syncId, PlayerInventory playerInventory) {
         this(syncId, playerInventory, new SimpleInventory(9));         this(syncId, playerInventory, new SimpleInventory(9));
Line 176: Line 179:
  
     //This constructor gets called from the BlockEntity on the server without calling the other constructor first, the server knows the inventory of the container     //This constructor gets called from the BlockEntity on the server without calling the other constructor first, the server knows the inventory of the container
-    //and can therefore directly provide it as an argument. This inventory will then be synced to the Client+    //and can therefore directly provide it as an argument. This inventory will then be synced to the client.
     public BoxScreenHandler(int syncId, PlayerInventory playerInventory, Inventory inventory) {     public BoxScreenHandler(int syncId, PlayerInventory playerInventory, Inventory inventory) {
         super(ExampleMod.BOX_SCREEN_HANDLER, syncId);         super(ExampleMod.BOX_SCREEN_HANDLER, syncId);
Line 214: Line 217:
     // Shift + Player Inv Slot     // Shift + Player Inv Slot
     @Override     @Override
-    public ItemStack transferSlot(PlayerEntity player, int invSlot) {+    public ItemStack quickMove(PlayerEntity player, int invSlot) {
         ItemStack newStack = ItemStack.EMPTY;         ItemStack newStack = ItemStack.EMPTY;
         Slot slot = this.slots.get(invSlot);         Slot slot = this.slots.get(invSlot);
Line 241: Line 244:
 </code> </code>
  
-<code java [enable_line_numbers="true"BiggerChestBlock.java> +<code java [enable_line_numbers="true"BoxScreen.java> 
-public class BoxScreen extends HandledScreen<ScreenHandler> {+public class BoxScreen extends HandledScreen<BoxScreenHandler> {
     //A path to the gui texture. In this example we use the texture from the dispenser     //A path to the gui texture. In this example we use the texture from the dispenser
     private static final Identifier TEXTURE = new Identifier("minecraft", "textures/gui/container/dispenser.png");     private static final Identifier TEXTURE = new Identifier("minecraft", "textures/gui/container/dispenser.png");
  
-    public BoxScreen(ScreenHandler handler, PlayerInventory inventory, Text title) {+    public BoxScreen(BoxScreenHandler handler, PlayerInventory inventory, Text title) {
         super(handler, inventory, title);         super(handler, inventory, title);
     }     }
Line 252: Line 255:
     @Override     @Override
     protected void drawBackground(MatrixStack matrices, float delta, int mouseX, int mouseY) {     protected void drawBackground(MatrixStack matrices, float delta, int mouseX, int mouseY) {
-        RenderSystem.color4f(1.0F, 1.0F, 1.0F, 1.0F); +        RenderSystem.setShader(GameRenderer::getPositionTexProgram); 
-        client.getTextureManager().bindTexture(TEXTURE);+        RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F); 
 +        RenderSystem.setShaderTexture(0, TEXTURE);
         int x = (width - backgroundWidth) / 2;         int x = (width - backgroundWidth) / 2;
         int y = (height - backgroundHeight) / 2;         int y = (height - backgroundHeight) / 2;
         drawTexture(matrices, x, y, 0, 0, backgroundWidth, backgroundHeight);         drawTexture(matrices, x, y, 0, 0, backgroundWidth, backgroundHeight);
 +        //in 1.20 or above,this method is in DrawContext class.
     }     }
  
Line 275: Line 280:
 </code> </code>
  
-<code java [enable_line_numbers="true"BiggerChestBlock.java>+===== Registering our Screen and ScreenHandler ===== 
 + 
 + 
 +As screens are a client-only concept, we can only register them on the client. 
 + 
 +<code java [enable_line_numbers="true"ExampleModClient.java>
  
 @Environment(EnvType.CLIENT) @Environment(EnvType.CLIENT)
Line 281: Line 291:
     @Override     @Override
     public void onInitializeClient() {     public void onInitializeClient() {
-        ScreenRegistry.register(ExampleMod.BOX_SCREEN_HANDLER, BoxScreen::new);+        HandledScreens.register(ExampleMod.BOX_SCREEN_HANDLER, BoxScreen::new);
     }     }
 } }
  
 </code> </code>
 +
 +Don't forget to register this entrypoint in ''fabric.mod.json'' if you haven't done it yet:
 +<code json>
 +/* ... */
 +  "entrypoints": {
 +    /* ... */
 +    "client": [
 +      "tutorial.path.to.ExampleModClient"
 +    ]
 +  },
 +</code>
 +
 +''ScreenHandler''s exist both on the client and on the server and therefore have to be registered on both.
  
 <code java [enable_line_numbers="true"] ExampleMod.java> <code java [enable_line_numbers="true"] ExampleMod.java>
Line 306: Line 329:
 } }
 </code> </code>
 +
 +===== Result =====
 +You have now created your own container Block, you could easily change it to contain a smaller or bigger Inventory. Maybe even apply a texture :-P
 +
 +{{:tutorial:bildschirmfoto_vom_2020-08-14_18-32-07.png?nolink&400|}}
 +
 +===== Further Reading =====
 +- [[tutorial:extendedscreenhandler|Syncing Custom Data with Extended ScreenHandlers when screen is opened]]
 +
 +- [[tutorial:propertydelegates|Syncing Integers continuously with PropertyDelegates]]
 +
 +- An example mod using the ''ScreenHandler'' API: [[https://github.com/FabricMC/fabric/tree/1.16/fabric-screen-handler-api-v1/src/testmod|ExampleMod on Github]].
 +
tutorial/screenhandler.1597420764.txt.gz · Last modified: 2020/08/14 15:59 by manymoney2