tutorial:screenhandler
Differences
This shows you the differences between two versions of the page.
| Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
| tutorial:screenhandler [2020/08/16 19:51] – manymoney2 | tutorial:screenhandler [2024/08/27 04:49] (current) – some minor changes solidblock | ||
|---|---|---|---|
| Line 6: | Line 6: | ||
| **Screenhandler: | **Screenhandler: | ||
| A '' | A '' | ||
| + | |||
| Our subclass will have two constructors here: one will be used on the server side and will contain the real '' | Our subclass will have two constructors here: one will be used on the server side and will contain the real '' | ||
| Line 12: | Line 13: | ||
| ===== Block and BlockEntity classes ===== | ===== Block and BlockEntity classes ===== | ||
| - | |||
| First we need to create the '' | First we need to create the '' | ||
| Line 20: | Line 20: | ||
| protected BoxBlock(Settings settings) { | protected BoxBlock(Settings settings) { | ||
| super(settings); | super(settings); | ||
| + | } | ||
| + | |||
| + | // This method is required since 1.20.5. | ||
| + | @Override | ||
| + | protected MapCodec<? | ||
| + | return createCodec(BoxBlock:: | ||
| } | } | ||
| @Override | @Override | ||
| - | public BlockEntity createBlockEntity(BlockView world) { | + | public BlockEntity createBlockEntity(BlockPos pos, BlockState state) { |
| - | return new BoxBlockEntity(); | + | return new BoxBlockEntity(pos, state); |
| } | } | ||
| @Override | @Override | ||
| public BlockRenderType getRenderType(BlockState state) { | public BlockRenderType getRenderType(BlockState state) { | ||
| - | //With inheriting from BlockWithEntity this defaults to INVISIBLE, so we need to change that! | + | // With inheriting from BlockWithEntity this defaults to INVISIBLE, so we need to change that! |
| return BlockRenderType.MODEL; | return BlockRenderType.MODEL; | ||
| } | } | ||
| @Override | @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, 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 a namedScreenHandlerFactory | + | // a namedScreenHandlerFactory. If your block class does not extend BlockWithEntity, |
| NamedScreenHandlerFactory screenHandlerFactory = state.createScreenHandlerFactory(world, | NamedScreenHandlerFactory screenHandlerFactory = state.createScreenHandlerFactory(world, | ||
| if (screenHandlerFactory != null) { | if (screenHandlerFactory != null) { | ||
| - | //With this call the server will request the client to open the appropriate Screenhandler | + | // With this call the server will request the client to open the appropriate Screenhandler |
| player.openHandledScreen(screenHandlerFactory); | player.openHandledScreen(screenHandlerFactory); | ||
| } | } | ||
| Line 49: | Line 55: | ||
| | | ||
| | | ||
| - | //This method will drop all items onto the ground when the block is broken | + | // This method will drop all items onto the ground when the block is broken |
| @Override | @Override | ||
| public void onStateReplaced(BlockState state, World world, BlockPos pos, BlockState newState, boolean moved) { | public void onStateReplaced(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 BoxBlockEntity) { | + | if (blockEntity instanceof BoxBlockEntity |
| - | ItemScatterer.spawn(world, | + | ItemScatterer.spawn(world, |
| // update comparators | // update comparators | ||
| world.updateComparators(pos, | world.updateComparators(pos, | ||
| Line 70: | Line 76: | ||
| @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)); |
| } | } | ||
| } | } | ||
| Line 81: | Line 87: | ||
| private final DefaultedList< | private final DefaultedList< | ||
| - | public BoxBlockEntity() { | + | public BoxBlockEntity(BlockPos pos, BlockState state) { |
| - | super(ExampleMod.BOX_BLOCK_ENTITY); | + | super(ExampleMod.BOX_BLOCK_ENTITY, pos, state); |
| } | } | ||
| - | //From the ImplementedInventory Interface | + | // From the ImplementedInventory Interface |
| @Override | @Override | ||
| public DefaultedList< | public DefaultedList< | ||
| return inventory; | return inventory; | ||
| - | |||
| } | } | ||
| - | //These Methods are from the NamedScreenHandlerFactory Interface | + | // These Methods are from the NamedScreenHandlerFactory Interface |
| - | // | + | // createMenu creates the ScreenHandler itself |
| - | // | + | // `getDisplayName` will Provide its name which is normally shown at the top |
| @Override | @Override | ||
| public ScreenHandler createMenu(int syncId, PlayerInventory playerInventory, | public ScreenHandler createMenu(int syncId, PlayerInventory playerInventory, | ||
| - | //We provide *this* to the screenHandler as our class Implements Inventory | + | // We provide *this* to the screenHandler as our class Implements Inventory |
| - | //Only the Server has the Inventory at the start, this will be synced to the client in the ScreenHandler | + | // Only the Server has the Inventory at the start, this will be synced to the client in the ScreenHandler |
| return new BoxScreenHandler(syncId, | return new BoxScreenHandler(syncId, | ||
| } | } | ||
| Line 107: | Line 112: | ||
| @Override | @Override | ||
| public Text getDisplayName() { | public Text getDisplayName() { | ||
| - | return new TranslatableText(getCachedState().getBlock().getTranslationKey()); | + | |
| + | return Text.translatable(getCachedState().getBlock().getTranslationKey()); | ||
| + | // for earlier versions | ||
| + | // return new TranslatableText(getCachedState().getBlock().getTranslationKey()); | ||
| } | } | ||
| | | ||
| + | // For the following two methods, for earlier versions, remove the parameter `registryLookup`. | ||
| @Override | @Override | ||
| - | public void fromTag(BlockState state, CompoundTag tag) { | + | public void readNbt(NbtCompound nbt, RegistryWrapper.WrapperLookup registryLookup) { |
| - | super.fromTag(state, tag); | + | super.readNbt(nbt, registryLookup); |
| - | inventory = DefaultedList.ofSize(invsize, | + | Inventories.readNbt(nbt, this.inventory, registryLookup); |
| - | Inventories.fromTag(tag, this.inventory); | + | |
| } | } | ||
| @Override | @Override | ||
| - | public | + | public |
| - | super.toTag(tag); | + | super.writeNbt(nbt, registryLookup); |
| - | Inventories.toTag(tag, this.inventory); | + | Inventories.writeNbt(nbt, this.inventory, registryLookup); |
| - | return | + | return |
| } | } | ||
| } | } | ||
| Line 128: | Line 136: | ||
| ===== Registering Block, BlockItem and BlockEntity ===== | ===== Registering Block, BlockItem and BlockEntity ===== | ||
| - | + | In this example, the block, block item and block entity are directly registered in '' | |
| <code java [enable_line_numbers=" | <code java [enable_line_numbers=" | ||
| public class ExampleMod implements ModInitializer { | public class ExampleMod implements ModInitializer { | ||
| - | | + | // For versions before 1.21, replace `Identifier.of` with `new Identifier`. |
| - | public static final BlockItem BOX_BLOCK_ITEM; | + | |
| - | public static final BlockEntityType< | + | |
| - | + | | |
| - | public static final String MOD_ID = " | + | public static final BlockItem |
| - | | + | |
| - | public static final Identifier | + | |
| - | + | public static final BlockEntityType< | |
| - | static | + | BlockEntityType.Builder.create(BoxBlockEntity:: |
| - | | + | // In 1.17 use FabricBlockEntityTypeBuilder instead |
| - | BOX_BLOCK_ITEM = Registry.register(Registry.ITEM, | + | // public static final BlockEntityType< |
| - | + | // | |
| - | | + | |
| - | BOX_BLOCK_ENTITY = Registry.register(Registry.BLOCK_ENTITY_TYPE, | + | |
| - | } | + | |
| @Override | @Override | ||
| public void onInitialize() { | public void onInitialize() { | ||
| - | |||
| } | } | ||
| } | } | ||
| - | |||
| - | |||
| </ | </ | ||
| Line 167: | Line 168: | ||
| private final Inventory inventory; | private final Inventory inventory; | ||
| - | //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, | this(syncId, | ||
| } | } | ||
| - | //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, | public BoxScreenHandler(int syncId, PlayerInventory playerInventory, | ||
| super(ExampleMod.BOX_SCREEN_HANDLER, | super(ExampleMod.BOX_SCREEN_HANDLER, | ||
| checkSize(inventory, | checkSize(inventory, | ||
| this.inventory = inventory; | this.inventory = inventory; | ||
| - | //some inventories do custom logic when a player opens it. | + | // some inventories do custom logic when a player opens it. |
| inventory.onOpen(playerInventory.player); | inventory.onOpen(playerInventory.player); | ||
| - | //This will place the slot in the correct locations for a 3x3 Grid. The slots exist on both server and client! | + | // This will place the slot in the correct locations for a 3x3 Grid. The slots exist on both server and client! |
| - | //This will not render the background of the slots however, this is the Screens job | + | // This will not render the background of the slots however, this is the Screens job |
| int m; | int m; | ||
| int l; | int l; | ||
| - | //Our inventory | + | // Our inventory |
| for (m = 0; m < 3; ++m) { | for (m = 0; m < 3; ++m) { | ||
| for (l = 0; l < 3; ++l) { | for (l = 0; l < 3; ++l) { | ||
| Line 193: | Line 194: | ||
| } | } | ||
| } | } | ||
| - | //The player inventory | + | // The player inventory |
| for (m = 0; m < 3; ++m) { | for (m = 0; m < 3; ++m) { | ||
| for (l = 0; l < 9; ++l) { | for (l = 0; l < 9; ++l) { | ||
| Line 199: | Line 200: | ||
| } | } | ||
| } | } | ||
| - | //The player Hotbar | + | // The player Hotbar |
| for (m = 0; m < 9; ++m) { | for (m = 0; m < 9; ++m) { | ||
| this.addSlot(new Slot(playerInventory, | this.addSlot(new Slot(playerInventory, | ||
| Line 213: | Line 214: | ||
| // Shift + Player Inv Slot | // Shift + Player Inv Slot | ||
| @Override | @Override | ||
| - | public ItemStack | + | public ItemStack |
| ItemStack newStack = ItemStack.EMPTY; | ItemStack newStack = ItemStack.EMPTY; | ||
| Slot slot = this.slots.get(invSlot); | Slot slot = this.slots.get(invSlot); | ||
| Line 241: | Line 242: | ||
| <code java [enable_line_numbers=" | <code java [enable_line_numbers=" | ||
| - | public class BoxScreen extends HandledScreen< | + | public class BoxScreen extends HandledScreen< |
| - | //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(" | + | |
| + | private static final Identifier TEXTURE = Identifier.ofVanilla(" | ||
| + | // For versions before 1.21: | ||
| + | // private static final Identifier TEXTURE = new Identifier(" | ||
| - | public BoxScreen(ScreenHandler | + | public BoxScreen(BoxScreenHandler |
| super(handler, | super(handler, | ||
| } | } | ||
| @Override | @Override | ||
| - | protected void drawBackground(MatrixStack matrices, float delta, int mouseX, int mouseY) { | + | protected void drawBackground(DrawContext context, float delta, int mouseX, int mouseY) { |
| - | RenderSystem.color4f(1.0F, 1.0F, 1.0F, 1.0F); | + | RenderSystem.setShader(GameRenderer:: |
| - | | + | RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F); |
| + | | ||
| 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, | + | |
| } | } | ||
| @Override | @Override | ||
| - | public void render(MatrixStack matrices, int mouseX, int mouseY, float delta) { | + | public void render(DrawContext context, int mouseX, int mouseY, float delta) { |
| - | renderBackground(matrices); | + | renderBackground(context, mouseX, mouseY, delta); |
| - | super.render(matrices, mouseX, mouseY, delta); | + | super.render(context, mouseX, mouseY, delta); |
| - | drawMouseoverTooltip(matrices, mouseX, mouseY); | + | drawMouseoverTooltip(context, mouseX, mouseY); |
| } | } | ||
| Line 275: | Line 280: | ||
| ===== Registering our Screen and ScreenHandler ===== | ===== Registering our Screen and ScreenHandler ===== | ||
| - | |||
| As screens are a client-only concept, we can only register them on the client. | As screens are a client-only concept, we can only register them on the client. | ||
| <code java [enable_line_numbers=" | <code java [enable_line_numbers=" | ||
| - | |||
| @Environment(EnvType.CLIENT) | @Environment(EnvType.CLIENT) | ||
| public class ExampleClientMod implements ClientModInitializer { | public class ExampleClientMod implements ClientModInitializer { | ||
| @Override | @Override | ||
| public void onInitializeClient() { | public void onInitializeClient() { | ||
| - | | + | |
| } | } | ||
| } | } | ||
| + | </ | ||
| + | |||
| + | Don't forget to register this entrypoint in '' | ||
| + | <code javascript src/ | ||
| + | { | ||
| + | /* ... */ | ||
| + | " | ||
| + | /* ... */ | ||
| + | " | ||
| + | " | ||
| + | ] | ||
| + | }, | ||
| + | /* ... */ | ||
| + | } | ||
| </ | </ | ||
| Line 296: | Line 313: | ||
| public class ExampleMod implements ModInitializer { | public class ExampleMod implements ModInitializer { | ||
| [...] | [...] | ||
| - | public static final ScreenHandlerType< | + | public static final ScreenHandlerType< |
| - | [...] | + | |
| - | static { | + | |
| - | [...] | + | |
| - | //We use registerSimple here because our Entity is not an ExtendedScreenHandlerFactory | + | |
| - | //but a NamedScreenHandlerFactory. | + | |
| - | //In a later Tutorial you will see what ExtendedScreenHandlerFactory can do! | + | |
| - | BOX_SCREEN_HANDLER = ScreenHandlerRegistry.registerSimple(BOX, BoxScreenHandler:: | + | |
| - | | + | |
| @Override | @Override | ||
| public void onInitialize() { | public void onInitialize() { | ||
| + | [...] | ||
| } | } | ||
| } | } | ||
| Line 319: | Line 328: | ||
| ===== Further Reading ===== | ===== Further Reading ===== | ||
| - | - [[tutorial: | + | * [[tutorial: |
| - | + | | |
| - | - [[tutorial: | + | |
| - | - An example mod using the '' | + | An example mod using the '' |
tutorial/screenhandler.1597607503.txt.gz · Last modified: 2020/08/16 19:51 by manymoney2