Jump to content

[1.18.1] Syncing Fluid Capability in BlockEntity [Solved]


Recommended Posts

Posted (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 by reasure
solved! thanks for helping!
Posted (edited)

there are two ways:

  1. you use a custom Network Packet and sync the data via a SimpleChannel (doc). whenever the data changes
  2. 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 by Luis_ST
Posted (edited)
  On 2/23/2022 at 4:47 PM, Luis_ST said:

there are two ways:

  1. you use a custom Network Packet and sync the data via a SimpleChannel (doc). whenever the data changes
  2. 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 by reasure
The values ​​are out of sync.
Posted (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 by reasure
Posted (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();
    }

lavagen.png

Edited by reasure
Posted (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 by reasure
there's a bug again. Real-time update doesn't work.
Posted

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.

  • reasure changed the title to [1.18.1] Syncing Fluid Capability in BlockEntity [Solved]

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.

Guest
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Announcements



  • Recently Browsing

    • No registered users viewing this page.
  • Posts

    • As a stay-at-home dad and a farmer in Kentucky, my primary focus has always been on nurturing my family and managing our land. When Bitcoin began to gain traction, I saw it as a potential opportunity to secure our financial future. With my wife encouragement, I decided to invest $10,000 from our savings into cryptocurrency. Over time, my investment flourished beyond my wildest dreams, eventually soaring to an astonishing $200,000. Just as I began to feel a sense of financial security, disaster struck. In a shocking act of betrayal fueled by jealousy, my wife hacked into my email and gained access to my Bitcoin wallet. The devastation I felt was overwhelming years of careful planning and smart investing were wiped out in an instant. I was left feeling helpless and uncertain, grappling with the fear that I might never recover what I had lost. In the midst of this turmoil, a close friend reached out to me and mentioned RAPID DIGITAL RECOVERY. Desperate for a solution but still holding onto a glimmer of hope, I contacted them, praying for a lifeline. Their team responded promptly, providing both reassurance and a clear plan of action. With remarkable expertise, they tracked down the digital footprints left by the theft and successfully recovered a substantial portion of my stolen funds. But their assistance didn’t end there. Understanding the vulnerability I felt after such a betrayal, they also offered invaluable advice on how to strengthen my digital security to prevent future breaches. Their support was not just technical; it was a source of empowerment that helped me regain my confidence and sense of control over my financial situation. While the betrayal was a painful lesson, it ultimately taught me resilience and the importance of safeguarding my assets. Thanks to RAPID DIGITAL RECOVERY, I didn’t just reclaim my lost Bitcoin; I also regained my peace of mind. This experience has transformed my perspective on both trust and security in the digital age. I now approach investments with a renewed sense of caution and awareness, ensuring that I protect not only my financial future but also my emotional well-being. The journey has been challenging, but it has also been a testament to my strength and determination to rise above adversity as I continue to balance my roles as a dad and a farmer.
    • Same issue without Oculus Flywheel Compat?
    • When i start my game with a shader active the game crashes with this Error: java.lang.NoSuchFieldError: TESSELATION_SHADERS Minecraft java 1.20.1 in forge 47.4.0 crash report in pastebin: https://pastebin.com/6xiwHqZW thanks in advance
    • If you are using AMD/ATI, update your drivers - get the drivers from their website - do not update via system
    • Not sure why this is happening, but I would love some help. The reason I restarted the server was because I was getting an error while trying to join regarding this "Internal Exception: io.netty.handler.codec.DecoderException: io.netty.handler.codec.EncoderException: java.io.IOException: Root tag must be a named compound tag" Im using ServerMiner if thats any help
  • Topics

×
×
  • Create New...

Important Information

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