Jump to content

[1.18.1] Syncing Fluid Capability in BlockEntity [Solved]


reasure

Recommended Posts

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!
Link to comment
Share on other sites

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
Link to comment
Share on other sites

14 hours ago, 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

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.
Link to comment
Share on other sites

2 hours ago, diesieben07 said:

You probably need a custom packet if you want to synchronize it in the Menu.

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
Link to comment
Share on other sites

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
Link to comment
Share on other sites

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.
Link to comment
Share on other sites

  • 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.



  • Recently Browsing

    • No registered users viewing this page.
  • Posts

    • The mobs are moving normally for a second, then freeze, then go back to normal, and this repeats. I describe this as mob lag, bc they stutter as if they were lagging. I don't have the best wifi, but that shouldn't matter since its 10mbps which is enough for a server. I have 8 gigs of ram for it, no optimizations have worked, if there is anything more you may need to help lmk, ill describe. The modlist is pretty hefty: VS_ClockworkSurvivalSupportV1.4 aether-1.20.1-1.2.0-neoforge.jar alexsdelight-1.5.jar alexsmobs-1.22.7.jar appleskin-forge-mc1.20.1-2.5.1.jar backpacked-forge-1.20.1-2.2.5.jar balm-forge-1.20.1-7.2.1.jar bettervillage-forge-1.20.1-3.2.0.jar BiomesOPlenty-1.20.1-18.0.0.598.jar botarium-forge-1.20.1-2.3.2.jar CGM-Unofficial-1.4.11+Forge+1.20.1.jar citadel-2.5.2-1.20.1.jar clockwork-1.20.1-0.1.2-forge-05f5d5fbbb.jar configured-forge-1.20.1-2.2.2.jar connectivity-1.20.1-4.9.jar create-1.20.1-0.5.1.f.jar create-new-age-forge-1.20.1-1.1.2.jar createarmoryv0.6.1n.jar createbigcannons-forge-1.20.1-0.5.3.b.jar createchunkloading-1.6.0-forge.jar createdeco-2.0.1-1.20.1-forge.jar createdieselgenerators-1.20.1-1.2h.jar createendertransmission-2.0.7-1.20.1.jar create_misc_and_things_ 1.20.1_4.0A.jar cristellib-1.1.5-forge.jar cupboard-1.20.1-2.3.jar curios-forge-5.7.0+1.20.1.jar destroy-1.20.1-0.8.jar Dungeon Crawl-1.20.1-2.3.14.jar FallingTree-1.20.1-4.3.4.jar FarmersDelight-1.20.1-1.2.4.jar framework-forge-1.20.1-0.6.26.jar geckolib-forge-1.20.1-4.4.2.jar InsaneLib-1.12.1-mc1.20.1.jar Jade-1.20.1-forge-11.8.0.jar jei-1.20.1-forge-15.3.0.4.jar journeymap-1.20.1-5.9.18-forge.jar JustEnoughResources-1.20.1-1.4.0.247.jar kotlinforforge-4.10.0-all.jar l2library-2.4.23.jar libraryferret-forge-1.20.1-4.0.0.jar macawsbridgesbop-1.20-1.1.jar mcw-bridges-2.1.0-mc1.20.4forge.jar moonlight-1.20-2.9.17-forge.jar morevillagers-forge-1.20.1-5.0.0.jar Mo_ZombiesWave-1.20.1-0.1.0-forge.jar NaturesCompass-1.20.1-1.11.2-forge.jar Placebo-1.20.1-8.6.1.jar refinedstorage-1.12.4.jar soundphysics-forge-1.20.1-1.1.2.jar spark-1.10.53-forge.jar stealing_villagers-forge-1.20.1-1.0.0.jar s_a_b-1.3.0_1.20.1.jar takesapillage-1.0.3-1.20.1.jar TerraBlender-forge-1.20.1-3.0.1.4.jar tombstone-1.20.1-8.6.2.jar Towns-and-Towers-1.12-Fabric+Forge.jar twilightforest-1.20.1-4.3.2145-universal.jar valkyrienskies-120-2.1.3-beta.1.jar veinmining-forge-1.3.0+1.20.1.jar waystones-forge-1.20-14.1.3.jar YungsApi-1.20-Forge-4.0.3.jar YungsBetterDesertTemples-1.20-Forge-3.0.3.jar YungsBetterDungeons-1.20-Forge-4.0.3.jar YungsBetterEndIsland-1.20-Forge-2.0.5.jar YungsBetterJungleTemples-1.20-Forge-2.0.4.jar YungsBetterMineshafts-1.20-Forge-4.0.4.jar YungsBetterNetherFortresses-1.20-Forge-2.0.5.jar YungsBetterOceanMonuments-1.20-Forge-3.0.4.jar YungsBetterStrongholds-1.20-Forge-4.0.3.jar YungsBetterWitchHuts-1.20-Forge-3.0.3.jar YungsBridges-1.20-Forge-4.0.3.jar zaynens_leather_from_rotten_flesh_mod_1.20.1.jar
    • Thank you so much! Unfortunately before reading this I reinstalled my java installer. Now when I try to open the forge installer I receive an error message stating 'Unable to install Java' even though I have definitely downloaded and installed the latest version. Any idea what this could be or how I could fix it?  
    • IT WORKED! THANK YOU!
    • sorry for the stuborn ness ss   test code logs and planks return nothing on minable but i can use the other tags  name minecraft:oak_log BlockTags.LOGS name minecraft:oak_planks BlockTags.PLANKS   it kinda works but its not like like perfect             
    • I'm making a custom modpack for 1.20.1 and everything else seems to work except this Passive skilltree mod. The world loads fine but when I press the keybind to open the mod's GUI, the world immediately saves and crashes with this report: https://pastebin.com/svEPsRty Any help or insight towards the right direction is greatly appreciated!
  • Topics

×
×
  • Create New...

Important Information

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