User Tools

Site Tools


zh_cn:tutorial:propertydelegates

使用 PropertyDelegates 同步整形数据

PropertyDelegate:PropertyDelegate 是一种包含多个可读写的整型值的容器。

在这章教程中,我们将会同步客户端和服务器之间的整型值,比如原版熔炉熔炼进度。

要理解这篇教程,你需要先阅读 ScreenHandler 教程,其中提到的方法代码本页不再提及。

为了减小复杂性,这章教程不再使用 ExtendedScreenHandler。

BlockEntity

由于 Block 类完全不需要更改,所以我们把它放在这里。

我们的 BlockEntity 现在实现了 Tickable,这将提供 tick() 方法,每刻都会调用该方法,我们用这个方法来增加想要同步的整数值。

BoxBlockEnity.java
  1. public class BoxBlockEntity extends BlockEntity implements NamedScreenHandlerFactory, ImplementedInventory, Tickable {
  2. private final DefaultedList<ItemStack> inventory = DefaultedList.ofSize(9, ItemStack.EMPTY);
  3. // 这是我们希望同步的整型,每刻会增加 1。
  4. private int syncedInt;
  5.  
  6. // PropertyDelegate 是一个接口,我们将在这里使用内联实现。
  7. // 它通常可以包含多个整型作为索引标志的数据,但是在本例中只有一个整型
  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. // 这里应该返回你的 delegate 中整型的数量,在本例中只有一个
  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. // 来自 ImplementedInventory
  32.  
  33. @Override
  34. public DefaultedList<ItemStack> getItems() {
  35. return inventory;
  36.  
  37. }
  38.  
  39. //这些方法来自 NamedScreenHandlerFactory 接口
  40.  
  41. @Override
  42. public @Nullable ScreenHandler createMenu(int syncId, PlayerInventory playerInventory, PlayerEntity player) {
  43. // 当我们的类执行完物品栏时,我们将此提供给 screenHandler
  44. // 只有服务器在开始时拥有物品栏,这将会在 ScreenHandler 中同步到客户端
  45.  
  46. // 类似于物品栏:服务器获得 PropertyDelegate 并将其直接给 screen handler 的服务器实例
  47. return new BoxScreenHandler(syncId, playerInventory, this,propertyDelegate);
  48. }
  49.  
  50. @Override
  51. public Text getDisplayName() {
  52. // 对于 1.18.2 或更低版本,请使用 return new TranslatableText(getCachedState().getBlock().getTranslationKey());
  53. return Text.translatable(getCachedState().getBlock().getTranslationKey());
  54. }
  55.  
  56. // 每刻增加一个同步的整型,我们仅在服务器上这么做,从而用于演示。
  57. @Override
  58. public void tick() {
  59. if(!world.isClient)
  60. syncedInt++;
  61. }
  62. }

我们的新的 ScreenHandler

BoxScreenHandler.java
  1. public class BoxScreenHandler extends ScreenHandler {
  2. private final Inventory inventory;
  3. PropertyDelegate propertyDelegate;
  4.  
  5. // 当服务器想要打开 screenHandler 时,客户端就会调用这个构造函数
  6. // 客户端将调用父级构造函数,其中物品栏是空的。screenHandler 将自动将这个空的物品栏同步到服务器上
  7.  
  8. // 类似于物品栏,客户端将分配一个空的 propertyDelegate 并且它将自动与服务器同步
  9.  
  10. public BoxScreenHandler(int syncId, PlayerInventory playerInventory) {
  11. this(syncId, playerInventory, new SimpleInventory(9),new ArrayPropertyDelegate(1));
  12. }
  13.  
  14. // 该构造函数从服务器上的 BlockEntity 调用,服务器知道容器(Container)中的物品栏,因此可以直接将其作为参数提供。这个物品栏以及 propertyDelegate 将被同步到客户端
  15. public BoxScreenHandler(int syncId, PlayerInventory playerInventory, Inventory inventory, PropertyDelegate propertyDelegate) {
  16. super(Test.BOX_SCREEN_HANDLER, syncId);
  17. checkSize(inventory, 9);
  18. this.inventory = inventory;
  19. this.propertyDelegate = propertyDelegate;
  20. // 有些物品栏会在玩家打开时作自定义逻辑操作(Custom Logic)
  21. inventory.onOpen(playerInventory.player);
  22.  
  23. // 我们需要告诉 screenHandler 关于 propertyDelegate 里的数据,否则它不会同步这里面的数据
  24. this.addProperties(propertyDelegate);
  25.  
  26. // 这将会把物品槽(Slot)放在 3x3 网格中正确的位置,物品槽(Slot)在服务端和客户端都存在!
  27. [...]
  28.  
  29. }
  30.  
  31. // 我们为已同步的整型提供了 getter, 所以 Screen 可以访问它并且在屏幕上显示它。
  32. public int getSyncedNumber(){
  33. return propertyDelegate.get(0);
  34. }
  35.  
  36. @Override
  37. public boolean canUse(PlayerEntity player) {
  38. return this.inventory.canPlayerUse(player);
  39. }
  40.  
  41. @Override
  42. public ItemStack transferSlot(PlayerEntity player, int invSlot) {[...]}
  43. }

使用 Screen 显示信息

当屏幕在其构造函数中获得 ScreenHandler 时,我们可以从上面访问 property delegates,并可以在屏幕上渲染这个整数。

  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. // 我们保存了对 screenhandler 的引用,这样我们就可以在屏幕上呈现 propertyDelegate 中的数字
  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. // 我们只是在容器的某个地方渲染我们同步的数字,这毕竟是一个演示
  18. // 最后一个参数是一个颜色代码,使字体亮绿色
  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. // 居中标题
  29. titleX = (backgroundWidth - textRenderer.getWidth(title)) / 2;
  30. }
  31. }

最后

由于 ScreenHandler 的注册与第一个教程相同,我们已经可以看到结果了!放置这个 BlockEntity 时,它将每游戏刻增加一个 syncedInt;当我们查看容器内部时,这个整数将自动同步到客户端并呈现在左上角。

示例视频

如果您想要一个更现实的例子,您可以去看看 Minecraft 代码中的 AbstractFurnaceEntity 和 AbstractFurnaceScreenHandler。

zh_cn/tutorial/propertydelegates.txt · Last modified: 2023/02/07 09:21 by solidblock