Jump to content

Data synchronization issue between client and server


Huntwer

Recommended Posts

My goal is to create a block. Right-clicking on the block opens a GUI, a simple window that contains an EditBox and a button. When I press the button, the value of the EditBox should be saved persistently, so that in every gaming session, it is retrieved and automatically inserted back into the window when I right-click again.

The GUI is implemented with the following code:

public class PadGui extends Screen {
...
    public PadGui(String title, BlockState padblockState, String stage, BlockEntity blockEntity) {
        super(Component.literal(title));
        this.title = title;
        this.padBlockState = padblockState;
        this.stage = stage;
        this.blockEntity = (PadBlockEntity) blockEntity;
    }

  	@Override
    protected void init() {
      super.init();
      ...
      LabelWidget passwordLabel = new LabelWidget(this.width / 2 - widthRef / 2, heightRef - 10, "Password", true);
      this.inputPassword = new EditBox(this.font, this.width / 2 - widthRef / 2, heightRef, widthRef, 20, Component.literal("Password"));
      Button.Builder confirmInsertBtnBuilder = new Button.Builder(Component.literal("Save Password"), button -> this.onSavePassword(inputPassword.getValue()));
      confirmInsertBtnBuilder.width(widthRef);
      confirmInsertBtnBuilder.pos(this.width / 2 - widthRef / 2, heightRef + 65);
      confirmInsertButton = confirmInsertBtnBuilder.build();
      confirmInsertButton.active = false;

      addRenderableWidget(passwordLabel);
      addRenderableWidget(inputPassword);
      addRenderableWidget(confirmInsertButton);
	}

    private void onSavePassword(String password) {
      this.blockEntity.setPassword(password);
      onClose();
    }

    @Override
      public void onClose() {
        PadBlockPacket padBlockPacket = new PadBlockPacket(this.blockEntity.getPos(), this.blockEntity.getSupportBlockPos(), this.blockEntity.getPassword());
        PadLockDoorMod.INSTANCE.sendToServer(padBlockPacket);
        Minecraft.getInstance().setScreen(null);
    }
}

This is code for BlockEntity:
 

public class PadBlockEntity extends BlockEntity {
    private BlockPos pos;
    private BlockPos supportBlockPos;
    private String password;
  
    public PadBlockEntity(BlockPos pos, BlockState state) {
        super(PadLockDoorMod.PAD_BLOCK_ENTITY.get(), pos, state);
        this.pos = pos;
    }

    public BlockPos getPos() {
        return pos;
    }

    public void setPos(BlockPos pos) {
        this.pos = pos;
    }

    public BlockPos getSupportBlockPos() {
        return supportBlockPos;
    }

    public void setSupportBlockPos(BlockPos supportBlockpos) {
        this.supportBlockPos = supportBlockpos;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    @Override
    protected void saveAdditional(CompoundTag nbt) {
        super.saveAdditional(nbt);
        nbt.putString("password", this.getPassword());
        nbt.putLong("pos", this.getPos().asLong());
        nbt.putLong("supportBlockPos", this.getSupportBlockPos().asLong());
    }

    @Override
    public void load(CompoundTag nbt) {
        super.load(nbt);
        this.password = nbt.getString("password");
        this.pos = BlockPos.of(nbt.getLong("pos"));
        this.supportBlockPos = BlockPos.of(nbt.getLong("supportBlockPos"));
    }

    @Override
    public CompoundTag getUpdateTag() {
        super.getUpdateTag();
        return saveWithoutMetadata();
    }

    @Override
    public void handleUpdateTag(CompoundTag nbt) {
        super.handleUpdateTag(nbt);
        this.load(nbt);
    }
}

From this code, my doubts begin. If I understand correctly, getUpdateTag is called by the server, and handleUpdateTag is called by the client to synchronize data from the server to the client, right?

Then I read from here https://forge.gemwire.uk/wiki/SimpleChannel that to synchronize data from the client to the server, I need to send a data packet to the server using SimpleChannel. So, I created a class representing the message, a class representing the packet, and then registered the SimpleChannel with the message in the mod's main class.

This is the class representing the packet. It contains the encode and decode methods for the packet data:
 

public class PadBlockPacket {
    private BlockPos pos;
    private BlockPos supportBlockPos;
    private final String password;

    public PadBlockPacket(BlockPos pos, BlockPos supportBlockPos, String password) {
        this.pos = pos;
        this.supportBlockPos = supportBlockPos;
        this.password = password;
    }
    public void encode(FriendlyByteBuf friendlyByteBuf) {
        friendlyByteBuf.writeBlockPos(this.supportBlockPos);
        friendlyByteBuf.writeBlockPos(this.pos);
        friendlyByteBuf.writeUtf(this.password);
    }

    public static PadBlockPacket decode(FriendlyByteBuf buffer) {
        BlockPos supportBlockPos = buffer.readBlockPos();
        BlockPos pos = buffer.readBlockPos();
        String password = buffer.readUtf(32767);
        return new PadBlockPacket(pos, supportBlockPos, password);
    }

    public BlockPos getPos() {
        return pos;
    }

    public BlockPos getSupportBlockPos() {
        return supportBlockPos;
    }

    public String getPassword() {
        return password;
    }
}

This is the packet handler:
 

public class PadLockPacketServerHandler {
    public static void handle(PadBlockPacket padBlockPacket, Supplier<NetworkEvent.Context> ctx) {
        ctx.get().enqueueWork(() -> {
            ServerPlayer player = ctx.get().getSender();
            Level level = player.level;
            BlockPos pos = padBlockPacket.getPos();
            BlockPos supportBlockPos = padBlockPacket.getSupportBlockPos();
            String password = padBlockPacket.getPassword();
            PadBlockEntity blockEntity = (PadBlockEntity) level.getBlockEntity(pos);
            blockEntity.setPos(pos);
            blockEntity.setSupportBlockPos(supportBlockPos);
            blockEntity.setPassword(password);
        });
        ctx.get().setPacketHandled(true);
    }
}

I assume that the encode method is called by the client, and the decode and handle methods are called by the server when it receives data from the packet through SimpleChannel, right?

Finally, this is how I register the SimpleChannel and the message in the main class of the mod:
 

public static final SimpleChannel INSTANCE = NetworkRegistry.newSimpleChannel(
  new ResourceLocation(MODID, "main"),
  () -> PROTOCOL_VERSION,
  PROTOCOL_VERSION::equals,
  PROTOCOL_VERSION::equals
);

public PadLockDoorMod() {
    IEventBus modEventBus = FMLJavaModLoadingContext.get().getModEventBus();

    ...
    INSTANCE.registerMessage(0, PadBlockPacket.class, PadBlockPacket::encode, PadBlockPacket::decode, PadLockPacketServerHandler::handle);
}


However, this doesn't work; currently, the client and server data aren't synchronized. It seems that when clicking the button in the GUI, the BlockEntity isn't updated on the server thread. So, I have two questions:

  1. Is the approach for persisting block information and the GUI correct?
  2. In the tutorial at the link above, it's also mentioned to use a packet handler for client-server data, showing this code:
    // In Packet class
    public static void handle(MyClientMessage msg, Supplier<NetworkEvent.Context> ctx) {
        ctx.get().enqueueWork(() ->
            // Make sure it's only executed on the physical client
            DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> () -> ClientPacketHandlerClass.handlePacket(msg, ctx))
        );
        ctx.get().setPacketHandled(true);
    }
    
    // In ClientPacketHandlerClass
    public static void handlePacket(MyClientMessage msg, Supplier<NetworkEvent.Context> ctx) {
        // Do stuff
    }

    This confuses me because I had previously understood that to synchronize data from the server to the client, it was sufficient to implement the getUpdateTag and handleUpdateTag methods on the BlockEntity and not use data packets in this case. In that case, how should the handlePacket method be implemented in my situation?

 

I would like to know if my current understanding of how data synchronization between the client and server works is correct or incorrect and incomplete. Thank you for your help.

Link to comment
Share on other sites

I managed to solve it.

I'm not sure why exactly, but in my class that extends EntityBlock, if I implement the newBlockEntity method like this:

@Nullable
@Override
public BlockEntity newBlockEntity(BlockPos pos, BlockState state) {
  return new PadBlockEntity(pos, state);
}

it doesn't work. But it does work if I do it this way:

@Nullable
@Override
public BlockEntity newBlockEntity(BlockPos pos, BlockState state) {
	return PadLockDoorMod.PAD_BLOCK_ENTITY.get().create(pos, state);
}

I don't know why. Maybe you could help me with this, please.

Link to comment
Share on other sites

Please sign in to comment

You will be able to leave a comment after signing in



Sign In Now

Announcements



  • Recently Browsing

    • No registered users viewing this page.
  • Posts

    • https://pastebin.com/FaQ43RnJ  Este es un error que me lanza al momento de querer iniciar forge Intente de todo pero no sirve nada. This is an error that I get when I want to start forge. I tried everything but nothing works.
    • It was a stupid Eclipse problem ,the debugger was set to a lower version of java.  In what world would i want my development version different from the debugger version.   ty for looking
    • Caused by: java.lang.NullPointerException: Cannot read field "level" because "this.minecraft" is null This is my first big error when coding. I looked it up, but there are a bunch of references to Dawncraft and not enough to figure out what was going wrong. I found this, but it involves a command and my GUI only opens when you shift + right-click while holding an item. They fixed it by not calling it with a command, but I am not using a command. Here's the render of my menu, which is the only part that changed between it working and not:   @Override public void render(@Nonnull PoseStack mstack, int mouseX, int mouseY, float partialTicks) { int midx = this.width / 2; int midy = this.height / 2; this.renderBackground(mstack); drawCenteredString(mstack, font, I18n.get("config.shape_tool"), midx, 4, 0xC3D1D8); Utils.renderImage(midx - 12, midy, 20, 20, "textures/gui/plus.png", mstack); addRenderableWidget(new Button(midx - 12, midy, 10, 10, Component.translatable(""), this::increaseax)); Utils.renderImage(midx + 12, midy, 20, 20, "textures/gui/minus.png", mstack); addRenderableWidget(new Button(midx + 12, midy, 10, 10, Component.translatable(""), this::decreaseax)); drawCenteredString(mstack, font, String.valueOf(shapetoolax), midx, midy + 10, 0xC3D1D8); super.render(mstack, mouseX, mouseY, partialTicks); } Utils.renderImage is this:   public static void renderImage(int x, int y, int width, int height, String img, PoseStack mstack) { RenderSystem.setShaderColor(1, 1, 1, 1); RenderSystem.enableBlend(); RenderSystem.defaultBlendFunc(); RenderSystem.setShaderTexture(0, new ResourceLocation(PNegative.MODID, img)); blit(mstack, x, y, 0, 0, width, height, width, height); RenderSystem.disableBlend(); } I have used it before and it has worked, so it can't be a problem. 
    • I found a topic  with the very simmilar problems. I run modpack Direwolf20 1.19.2 version from FTB with dedicated server with my friends and i am the only who have this type of crashes. It has been through login process and after 5-6 crashes i can connect. And randombly it could crash me in game but mostly when i am tp'ing using Nether portals etc. I already setted up all privileges for javaw in firewall, flushed dns, reset network settings and etc. Changed java version to from FAQ too. Here's some logs from client side https://gnomebot.dev/paste/1158502890168664194 https://gnomebot.dev/paste/1158187264657063946    
    • Don't know why game keeps crashing after a few seconds or walking towards something, tried removing larger mods and someaddon performance mods but it's not working. Minecraft launchers telling me "Exit Code 1"
  • Topics

×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.