reasure Posted February 23, 2022 Posted February 23, 2022 (edited) I have created a fluid capability on the block entity. Now I want to display this liquid in the GUI. (Type and amount of liquid) But I have no idea how to synchronize this capability in container or screen. Edited February 28, 2022 by reasure solved! thanks for helping! Quote
Luis_ST Posted February 23, 2022 Posted February 23, 2022 (edited) there are two ways: you use a custom Network Packet and sync the data via a SimpleChannel (doc). whenever the data changes override BlockEntity#getUpdateTag and retrun a CompoundTag with the data to sync, override BlockEntity#handleUpdateTag to handle the data sync on client. whenever the data changes you need to call BlockEntity#setChanged choose the way you prefer Edited February 23, 2022 by Luis_ST Quote
reasure Posted February 24, 2022 Author Posted February 24, 2022 (edited) On 2/23/2022 at 4:47 PM, Luis_ST said: there are two ways: you use a custom Network Packet and sync the data via a SimpleChannel (doc). whenever the data changes override BlockEntity#getUpdateTag and retrun a CompoundTag with the data to sync, override BlockEntity#handleUpdateTag to handle the data sync on client. whenever the data changes you need to call BlockEntity#setChanged choose the way you prefer Expand I choose 2nd. And like this? MyBlockEntity private final FluidTank fluidTank = createFluidTank(); private final LazyOptional<IFluidHandler> fluidHandler = LazyOptional.of(() -> fluidTank); ... public void loadClient(CompoundTag tag) { if (tag.contains("Fluid")) { fluidTank.readFromNBT(tag.getCompound("Fluid")); } } public void saveClient(CompoundTag tag) { CompoundTag fluid = new CompoundTag(); fluidTank.writeToNBT(fluid); tag.put("Fluid", fluid); } @NotNull @Override public CompoundTag getUpdateTag() { CompoundTag tag = super.getUpdateTag(); saveClient(tag); return tag; } @Override public void handleUpdateTag(CompoundTag tag) { if (tag != null) { loadClient(tag); } } ... private FluidTank createFluidTank() { return new FluidTank(FLUID_CAPACITY, (fluid -> fluid.getFluid().is(FluidTags.LAVA))) { @Override protected void onContentsChanged() { setChanged(); } }; } @Override public <T> LazyOptional<T> getCapability(@NotNull Capability<T> cap, @Nullable Direction side) { if (cap == CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY) { return fluidHandler.cast(); } else { return super.getCapability(cap, side); } } And MyContainerMenu public FluidStack getFluid() { return blockEntity.getCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY).map(f -> f.getFluidInTank(0).copy()).orElse(null); } And In Screen: FluidStack fluid = menu.getFluid() The values are out of sync. Edited February 24, 2022 by reasure The values are out of sync. Quote
reasure Posted February 24, 2022 Author Posted February 24, 2022 In the mcjty tutorial I saw, inventory was synchronized using slots and energy using data slots. But what about fluid? Quote
reasure Posted February 25, 2022 Author Posted February 25, 2022 (edited) On 2/25/2022 at 9:57 AM, diesieben07 said: You probably need a custom packet if you want to synchronize it in the Menu. Expand Sorry to keep asking basic questions. More specifically, after creating a packet from the server to the client, should I send this packet in sendAllDataToRemoted() of the Menu class? Edited February 25, 2022 by reasure Quote
Luis_ST Posted February 25, 2022 Posted February 25, 2022 send the data whenever the data changed Quote
reasure Posted February 26, 2022 Author Posted February 26, 2022 (edited) Synchronization from server to client was successful. However, when I reconnected to the world, it was out of sync. (It says the fluid is empty.) What's wrong? (If the fluid changes again, it will resynchronize.) In MyBlockEntity.class @Override public void setChanged() { super.setChanged(); onFluidChanged(); } private void onFluidChanged() { if (level != null) { PacketHandler.INSTANCE.send(PacketDistributor.TRACKING_CHUNK.with(() -> level.getChunkAt(worldPosition)), new FluidUpdatePacket(worldPosition, fluidTank.getFluid())); } } @Override public void load(CompoundTag tag) { ... if (tag.contains("Fluid")) { fluidTank.readFromNBT(tag.getCompound("Fluid")); } super.load(tag); onFluidChanged(); } Edited February 26, 2022 by reasure Quote
reasure Posted February 27, 2022 Author Posted February 27, 2022 (edited) It seems to have been solved by rewriting the code from scratch. thank you! Is it correct to do this? If this method is correct, I will post the code for other people to refer to as well. (For other parts, refer to mcjty's block entity and container tutorial.) there's a bug again. Real-time update doesn't work. Should the block entity also send packets? (Container Class) public class LavaGeneratorMenu extends AbstractContainerMenu { private final BlockEntity blockEntity; private final ContainerLevelAccess access; private final IItemHandler playerInventory; private FluidStack fluid; private final Player player; public LavaGeneratorMenu(int windowId, Inventory playerInv, BlockPos pos) { super(ModMenuTypes.LAVA_GENERATOR_CONTAINER.get(), windowId); this.playerInventory = new InvWrapper(playerInv); this.access = ContainerLevelAccess.create(playerInv.player.level, pos); this.player = playerInv.player; this.fluid = FluidStack.EMPTY; ... } public FluidStack getFluid() { return blockEntity.getCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY).map(f -> f.getFluidInTank(0)).orElse(FluidStack.EMPTY); } @Override public boolean stillValid(@NotNull Player player) { return stillValid(access, player, ModBlocks.LAVA_GENERATOR.get()); } @Override public void broadcastChanges() { super.broadcastChanges(); if (player instanceof ServerPlayer sp) { FluidStack newFluid = getFluid(); if (fluid.getAmount() != newFluid.getAmount() || !fluid.isFluidEqual(newFluid)) { PacketHandler.INSTANCE.send(PacketDistributor.PLAYER.with(() -> sp), new FluidUpdatePacket(blockEntity.getBlockPos(), newFluid)); this.fluid = newFluid; } } } @Override public void sendAllDataToRemote() { super.sendAllDataToRemote(); if (player instanceof ServerPlayer sp) { PacketHandler.INSTANCE.send(PacketDistributor.PLAYER.with(() -> sp), new FluidUpdatePacket(blockEntity.getBlockPos(), getFluid())); } } } (FluidUpdatePacket) public class FluidUpdatePacket { public final BlockPos bePos; public final FluidStack fluid; public FluidUpdatePacket(BlockPos pos, FluidStack fluid) { this.bePos = pos; this.fluid = fluid; } public void encode(FriendlyByteBuf buffer) { buffer.writeBlockPos(bePos); buffer.writeFluidStack(fluid); } public static FluidUpdatePacket decode(FriendlyByteBuf buffer) { return new FluidUpdatePacket(buffer.readBlockPos(), buffer.readFluidStack()); } public static void handle(FluidUpdatePacket msg, Supplier<NetworkEvent.Context> ctx) { AtomicBoolean success = new AtomicBoolean(false); ctx.get().enqueueWork(() -> { DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> () -> success.set(ClientAccess.updateFluid(msg.bePos, msg.fluid))); }); ctx.get().setPacketHandled(success.get()); } } (ClientAccess (needed in Packet)) public class ClientAccess { public static boolean updateFluid(BlockPos pos, FluidStack fluid) { AtomicBoolean success = new AtomicBoolean(false); if (Minecraft.getInstance().level != null) { final BlockEntity blockEntity = Minecraft.getInstance().level.getBlockEntity(pos); if (blockEntity != null) { blockEntity.getCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY).ifPresent(h -> { if (h instanceof FluidTank tank) { tank.setFluid(fluid); success.set(true); } }); } } return success.get(); } } Edit) I removed the "if (fluid.getAmount() != newFluid.getAmount() || !fluid.isFluidEqual(newFluid)) {" which is part of the container class and synchronized it unconditionally, and it worked fine. Edited February 27, 2022 by reasure there's a bug again. Real-time update doesn't work. Quote
reasure Posted February 27, 2022 Author Posted February 27, 2022 In Container class, I change "this.fluid = newFluid;" -> "this.fluid = newFluid.copy();" And It works correctly. And I wonder if I should use fluid.copy() in Packet as well. Quote
Luis_ST Posted February 27, 2022 Posted February 27, 2022 On 2/27/2022 at 2:55 PM, reasure said: Should the block entity also send packets? Expand if you only change the Capability while the Menu is open, then no Quote
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.