tutorial:propertydelegates

Syncing Integers with PropertyDelegates

PropertyDelegate: A PropertyDelegate is a kind of Container which contains a specific amounts of integer values which can be read or changed.

In this Tutorial we will sync Integer values between the client and the server, an example for this in Vanilla would be the smelting progress of a furnace.

To understand this tutorial you need to read the first ScreenHandler tutorial. Methods which have no code here were already shown in that tutorial.

We will not use the ExtendedScreenHandler in this tutorial any more to save us some complexity.

BlockEntity

As the Block class does not need to be changed at all we leave it out here.

Our BlockEntity now implements Tickable, this will provide the tick() method which gets called every tick. We use it to increase our Integer we want to sync.

BoxBlockEntity.java
  1. public class BoxBlockEntity extends BlockEntity implements NamedScreenHandlerFactory, ImplementedInventory, Tickable {
  2. private final DefaultedList<ItemStack> inventory = DefaultedList.ofSize(9, ItemStack.EMPTY);
  3. //this is the int we want to sync, it gets increased by one each tick
  4. private int syncedInt;
  5.  
  6. //PropertyDelegate is an interface which we will implement inline here.
  7. //It can normally contain multiple integers as data identified by the index, but in this example we only have one.
  8. private final PropertyDelegate propertyDelegate = new PropertyDelegate() {
  9. @Override
  10. public int get(int index) {
  11. return syncedInt;
  12. }
  13.  
  14. @Override
  15. public void set(int index, int value) {
  16. syncedInt = value;
  17. }
  18.  
  19. //this is supposed to return the amount of integers you have in your delegate, in our example only one
  20. @Override
  21. public int size() {
  22. return 1;
  23. }
  24. };
  25.  
  26. public BoxBlockEntity() {
  27. super(Test.BOX_BLOCK_ENTITY);
  28. }
  29.  
  30.  
  31. //From the ImplementedInventory Interface
  32.  
  33. @Override
  34. public DefaultedList<ItemStack> getItems() {
  35. return inventory;
  36.  
  37. }
  38.  
  39. //These Methods are from the NamedScreenHandlerFactory Interface
  40.  
  41. @Override
  42. public @Nullable ScreenHandler createMenu(int syncId, PlayerInventory playerInventory, PlayerEntity player) {
  43. //We provide this to the screenHandler as our class Implements Inventory
  44. //Only the Server has the Inventory at the start, this will be synced to the client in the ScreenHandler
  45.  
  46. //Similar to the inventory: The server has the PropertyDelegate and gives it to the server instance of the screen handler directly
  47. return new BoxScreenHandler(syncId, playerInventory, this,propertyDelegate);
  48. }
  49.  
  50. @Override
  51. public Text getDisplayName() {
  52. // For versions 1.18.2 and below, please use return new TranslatableText(getCachedState().getBlock().getTranslationKey());
  53. return Text.translatable(getCachedState().getBlock().getTranslationKey());
  54. }
  55.  
  56. //increase the synced Integer by one each tick, we only do this on the server for demonstration purposes.
  57. @Override
  58. public void tick() {
  59. if(!world.isClient)
  60. syncedInt++;
  61. }
  62. }

Our new ScreenHandler

BoxScreenHandler.java
  1. public class BoxScreenHandler extends ScreenHandler {
  2. private final Inventory inventory;
  3. PropertyDelegate propertyDelegate;
  4.  
  5. //This constructor gets called on the client when the server wants it to open the screenHandler,
  6. //The client will call the super constructor with an empty Inventory and the screenHandler will automatically
  7. //sync this empty inventory with the inventory on the server
  8.  
  9. //Similar to the inventory, the client will allocate an empty propertyDelegate which will be synced with the Server automatically
  10.  
  11. public BoxScreenHandler(int syncId, PlayerInventory playerInventory) {
  12. this(syncId, playerInventory, new SimpleInventory(9),new ArrayPropertyDelegate(1));
  13. }
  14.  
  15. //This constructor gets called from the BlockEntity on the server, the server knows the inventory of the container
  16. //and can therefore directly provide it as an argument. This inventory aswell as the propertyDelegate will then be synced to the Client
  17. public BoxScreenHandler(int syncId, PlayerInventory playerInventory, Inventory inventory, PropertyDelegate propertyDelegate) {
  18. super(Test.BOX_SCREEN_HANDLER, syncId);
  19. checkSize(inventory, 9);
  20. this.inventory = inventory;
  21. this.propertyDelegate = propertyDelegate;
  22. //some inventories do custom logic when a player opens it.
  23. inventory.onOpen(playerInventory.player);
  24.  
  25. //we need to tell the screenhandler about our propertyDelegate, otherwise it will not sync the data inside.
  26. this.addProperties(propertyDelegate);
  27.  
  28. //This will place the slot in the correct locations for a 3x3 Grid. The slots exist on both server and client!
  29. [...]
  30.  
  31. }
  32.  
  33. //we provide this getter for the synced integer so the Screen can access this to show it on screen
  34. public int getSyncedNumber(){
  35. return propertyDelegate.get(0);
  36. }
  37.  
  38. @Override
  39. public boolean canUse(PlayerEntity player) {
  40. return this.inventory.canPlayerUse(player);
  41. }
  42.  
  43. @Override
  44. public ItemStack transferSlot(PlayerEntity player, int invSlot) {[...]}
  45. }

Showing the Information with the Screen

As the screen gets the ScreenHandler in its constructor, we have access to the property delegate from above and can render the integer on screen.

BoxScreen.java
  1. public class BoxScreen extends HandledScreen<ScreenHandler> {
  2. private static final Identifier TEXTURE = new Identifier("minecraft", "textures/gui/container/dispenser.png");
  3. BoxScreenHandler screenHandler;
  4.  
  5. public BoxScreen(ScreenHandler handler, PlayerInventory inventory, Text title) {
  6. super(handler, inventory, title);
  7. //we save a reference to the screenhandler so we can render the number from our propertyDelegate on screen
  8. screenHandler = (BoxScreenHandler) handler;
  9.  
  10. }
  11.  
  12. @Override
  13. protected void drawBackground(MatrixStack matrices, float delta, int mouseX, int mouseY) {[...]}
  14.  
  15. @Override
  16. public void render(MatrixStack matrices, int mouseX, int mouseY, float delta) {
  17. //We just render our synced number somewhere in the container, this is a demonstration after all
  18. //the last argument is a color code, making the font bright green
  19. textRenderer.draw(matrices, Integer.toString(screenHandler.getSyncedNumber()), 0, 0, 65280);
  20. renderBackground(matrices);
  21. super.render(matrices, mouseX, mouseY, delta);
  22. drawMouseoverTooltip(matrices, mouseX, mouseY);
  23. }
  24.  
  25. @Override
  26. protected void init() {
  27. super.init();
  28. // Center the title
  29. titleX = (backgroundWidth - textRenderer.getWidth(title)) / 2;
  30. }
  31. }

Result

As the registration of the ScreenHandler is identical to that of the first tutorial we can see the result already! When the BlockEntity is placed it will increase the syncedInt by one each tick; when we look inside the container, the integer will automatically be synced to the client and rendered in the top left corner.

Example Video

If you want a more realistic example, you might want to have a look at AbstractFurnaceEntity and AbstractFurnaceScreenHandler in the Minecraft code.

tutorial/propertydelegates.txt · Last modified: 2022/05/27 16:00 by solidblock