Jump to content

Blocking the flow of fluids once and for all. Is it even possible?


Recommended Posts

Posted (edited)

EDIT: Not Possible(At least in 1.12.2), consider this closed

 

Here is what I need: Chunk claiming system, blocking all block updates between claimed and unclaimed chunks so to speak. Specifically here, fluid flow over some chunk borders. Everything is server-side.

 

What I tried:

 - BlockEvent.FluidPlaceBlockEvent, canceling it even completely does nothing in this case, it blocks cobble and obsidian creation, so still useful.

 - BlockEvent.NeighborNotifyEvent, canceling it even completely again, does nothing for this. It does prevent fluids from clearing up after the source is removed... But not the other way around.

 - FluidEvent. *, seems to be for containers, some kind of a fluid API? Non the less not applicable to this.

 

I tried this in SpongeForge and got somewhere but, in short,  some bizarre and buggy behavior made more problems than it solved...

**So my question is:** Is this even possible in Forge? I saw that you can manipulate source block merging and block creation from a liquid, this leads me to believe that forge can do this.

 

Version: 1.12.2 recommended

 

ps. it would be amazing if @diesieben07 could roast my ass, this is my first post. And reading his(I presume) sarcasm filled icy cold buckets of replies to dumb questions made my day so many times :D

Edited by SkenonS
Posted

Doesn't even need to be "will be", if there is a way for me to know which block "placed" which, like that event for cobble and obsidian but for liquid blocks, I can just immediately return it to air.  

I already did that by some method.

 

Only then I would need to block further block updates from originating from that replacement. I have not managed to do that, not even close, any ideas?

Posted

You can't. The best I ever managed was a block that, when placed, stored a record of what it could see around it. Then, every tick, it reverted any changes (ignoring certain things, like doors opening, redstone, etc). 

 

Water flow was a notorious problem. 

Apparently I'm a complete and utter jerk and come to this forum just like to make fun of people, be confrontational, and make your personal life miserable.  If you think this is the case, JUST REPORT ME.  Otherwise you're just going to get reported when you reply to my posts and point it out, because odds are, I was trying to be nice.

 

Exception: If you do not understand Java, I WILL NOT HELP YOU and your thread will get locked.

 

DO NOT PM ME WITH PROBLEMS. No help will be given.

Posted (edited)

You might want to look at WorldEventListeners, they aren’t cancelable, and don’t have write access to a world, but you can probably do an instanceof WorldServer and cast. You need to be efficient if you use this method, as it is called every BlockState change.

You can see an example in WorldRenderer/RenderGlobal and at https://github.com/Cadiboo/NoCubes/blob/396d3e731da4c3f7f6b8dd3107877ea442f09fe0/src/main/java/io/github/cadiboo/nocubes/world/ModWorldEventListener.java#L26. My code increases the chunk update distance when a block is placed from 1 block to 2 blocks to remove seams that form in my world caused by outdated meshes. 

Edited by Cadiboo

About Me

Spoiler

My Discord - Cadiboo#8887

My WebsiteCadiboo.github.io

My ModsCadiboo.github.io/projects

My TutorialsCadiboo.github.io/tutorials

Versions below 1.14.4 are no longer supported on this forum. Use the latest version to receive support.

When asking support remember to include all relevant log files (logs are found in .minecraft/logs/), code if applicable and screenshots if possible.

Only download mods from trusted sites like CurseForge (minecraft.curseforge.com). A list of bad sites can be found here, with more information available at stopmodreposts.org

Edit your own signature at www.minecraftforge.net/forum/settings/signature/ (Make sure to check its compatibility with the Dark Theme)

Posted
2 minutes ago, Cadiboo said:

You might want to look at WorldEventListeners, they aren’t cancelable, and don’t have write access to a world, but you can probably do an instanceof WorldServer and cast. You need to be efficient if you use this method, as it is called every BlockState change.

I'm obsessed with efficiency :) so that's fine, but still, that only gives me knowledge of when the block was placed, it would be up to me to return that block to air, and without a way to block further updates that would start the process all over again :/, with many of these blocks in the world constantly trying to "break through" that would destroy my tick time. Did I understand that correctly?

Posted (edited)

The event fires every time a block is placed. I believe you could check if that block is liquid (cheap), if it is also a source (cheap) and is a flowing block (cheap) set it to stationary water. If the block isn’t a source set it to air (cheap) and optionally find the source and set it to air (potentially expensive).

Setting a BlockState will likely call your listener again, so be careful.

Edited by Cadiboo

About Me

Spoiler

My Discord - Cadiboo#8887

My WebsiteCadiboo.github.io

My ModsCadiboo.github.io/projects

My TutorialsCadiboo.github.io/tutorials

Versions below 1.14.4 are no longer supported on this forum. Use the latest version to receive support.

When asking support remember to include all relevant log files (logs are found in .minecraft/logs/), code if applicable and screenshots if possible.

Only download mods from trusted sites like CurseForge (minecraft.curseforge.com). A list of bad sites can be found here, with more information available at stopmodreposts.org

Edit your own signature at www.minecraftforge.net/forum/settings/signature/ (Make sure to check its compatibility with the Dark Theme)

Posted
4 minutes ago, Cadiboo said:

The event fires every time a block is placed. I believe you could check if that block is liquid (cheap), if it is also a source (cheap) and is a flowing block (cheap) set it to stationary water. If the block isn’t a source set it to air.

That would remove all flowing water everywhere?

I just need to block it on some chunk borders, if it's flowing between/ inside chunks of the same territory I want it to behave naturally.

Posted (edited)
2 hours ago, SkenonS said:

That would remove all flowing water everywhere?

I just need to block it on some chunk borders, if it's flowing between/ inside chunks of the same territory I want it to behave naturally.

Depends on how you program it.  If you filter using the block position correctly, you can have it apply only to blocks on borders.  I'll probably be playing with this idea before too long since I'm rolling my own protection for something as well (and have everything working except fluids and falling blocks iirc).

 

I haven't gotten around to playing with it yet, but you might be able to do something like this:
1. Find the block being placed
2. Find the block which is placing it
3. Check if their positions lie in 2 different chunks, and if so, take whatever actions you plan to take to prevent it
 

My boundaries don't lie on chunk boundaries, so I can't use this method of comparison, but I'll be trying something along the same lines when I get to it.

Also, there is a whole series of fluid-related events that may be useful.  I haven't gone through them yet, so I can't say for sure, but for now, here's a list:
~SNIP~ Just saw you already tried these events, so nvm about the events.
 

If you experiment with it, maybe you can post your findings...so I can use them for reference when I get around to this again XD

Edited by Laike_Endaril
Posted
3 minutes ago, Laike_Endaril said:

I haven't gotten around to playing with it yet, but you might be able to do something like this:
1. Find the block being placed
2. Find the block which is placing it
3. Check if their positions lie in 2 different chunks, and if so, take whatever actions you plan to take to prevent it

 

That part is easy, I did that, the problem lies in those "whatever actions", because you can't prevent that block from trying to flow again in a few ticks. And if I need to constantly delete fluid blocks that go over the line, well, then that's a very vulnerable system, isn't it? Anybody with a hill and a bucket can overwhelm my block updates for all eternity.

 

3 hours ago, SkenonS said:

- FluidEvent. *, seems to be for containers, some kind of a fluid API? Non the less not applicable to this.

Draining, filling, motion and spilled are all subevents of this.

 

As @diesieben07 and @Draco18s said, forge can't do this, at least not on larger scales. And as it turns out, SpongeForge might not either.

Posted

Good point.

The only approaches I can think which would bypass that issue are...
1. Preventing the flow using actual blockstate properties, but I haven't delved into that concept yet and it's probably chock full of caveats.
2. Preventing the flow by altering the behavior of fluids directly.  Getting this to work right with all modded fluids would probably be a nightmare unless you find a neat trick.

Posted (edited)

Exactly!

1. Forge doesn't have that capability, at least not yet :/

2. Very possible, but as far as I know that constitutes as a requirement for a core mod, and I am not very willing to do that :D

 

I'm experimenting with SpongeForge right now, there is hope. I'll report back, maybe you could port your mod to Sponge, It's almost a must-have for Forge servers, so optimized :D

Edited by SkenonS
Posted
47 minutes ago, SkenonS said:

you can't prevent that block from trying to flow again in a few ticks

Yes you can. Fluids have a non-flowing Block that doesn’t update by default. 

 

27 minutes ago, SkenonS said:

core mod

Not necessary

About Me

Spoiler

My Discord - Cadiboo#8887

My WebsiteCadiboo.github.io

My ModsCadiboo.github.io/projects

My TutorialsCadiboo.github.io/tutorials

Versions below 1.14.4 are no longer supported on this forum. Use the latest version to receive support.

When asking support remember to include all relevant log files (logs are found in .minecraft/logs/), code if applicable and screenshots if possible.

Only download mods from trusted sites like CurseForge (minecraft.curseforge.com). A list of bad sites can be found here, with more information available at stopmodreposts.org

Edit your own signature at www.minecraftforge.net/forum/settings/signature/ (Make sure to check its compatibility with the Dark Theme)

Posted
10 minutes ago, Cadiboo said:

Yes you can. Fluids have a non-flowing Block that doesn’t update by default. 

Isn't that non-flowing variant only a "full" block of liquid? What I mean if the lowest level of let's say water tries to flow over, wouldn't this change it to a full block of water?

 

Also when that water tries to drain, would that stationary block prevent that, be "undrainable"?

Posted

You could wrap fluids in the registry and override their method that makes them flow into an area

About Me

Spoiler

My Discord - Cadiboo#8887

My WebsiteCadiboo.github.io

My ModsCadiboo.github.io/projects

My TutorialsCadiboo.github.io/tutorials

Versions below 1.14.4 are no longer supported on this forum. Use the latest version to receive support.

When asking support remember to include all relevant log files (logs are found in .minecraft/logs/), code if applicable and screenshots if possible.

Only download mods from trusted sites like CurseForge (minecraft.curseforge.com). A list of bad sites can be found here, with more information available at stopmodreposts.org

Edit your own signature at www.minecraftforge.net/forum/settings/signature/ (Make sure to check its compatibility with the Dark Theme)

Posted

Yes, I was hoping for something like that, can you please tell me does that also require a client-side mod? I was under the impression that any tampering with the registry requires that?

Posted

Adding stuff to the registry requires it be on both sides. If your only replacing server-side logic, then it’s not required on the sever side

About Me

Spoiler

My Discord - Cadiboo#8887

My WebsiteCadiboo.github.io

My ModsCadiboo.github.io/projects

My TutorialsCadiboo.github.io/tutorials

Versions below 1.14.4 are no longer supported on this forum. Use the latest version to receive support.

When asking support remember to include all relevant log files (logs are found in .minecraft/logs/), code if applicable and screenshots if possible.

Only download mods from trusted sites like CurseForge (minecraft.curseforge.com). A list of bad sites can be found here, with more information available at stopmodreposts.org

Edit your own signature at www.minecraftforge.net/forum/settings/signature/ (Make sure to check its compatibility with the Dark Theme)

Posted

Well, for timing, if the registry event is used, setting priority to lowest should *usually* catch all fluids...but if some other mod adds a fluid using lowest priority and comes after your mod alphabetically then that could be an issue.  Even with post init and lowest priority, I wouldn't be surprised if some things ended up getting added after it (depending on the name), as some mods add fluids dynamically based on ores loaded.  I think tinkers construct might do this, but I'm not sure (also not sure what timing it's using if so).

 

The ForgeRegistries#register() method has a callback that might be useful for catching things as they are registered, but I haven't dug deep enough to see what it's actually being used for, so I don't know if trying to hook into that will screw something up or not.  Maybe there's a better way?  I haven't tried intercepting registry entries as they're added before.

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

    • It is 1.12.2 - I have no idea if there is a 1.12 pack
    • Okay, but does the modpack works with 1.12 or just with 1.12.2, because I need the Forge client specifically for Minecraft 1.12, not 1.12.2
    • Version 1.19 - Forge 41.0.63 I want to create a wolf entity that I can ride, so far it seems to be working, but the problem is that when I get on the wolf, I can’t control it. I then discovered that the issue is that the server doesn’t detect that I’m riding the wolf, so I’m struggling with synchronization. However, it seems to not be working properly. As I understand it, the server receives the packet but doesn’t register it correctly. I’m a bit new to Java, and I’ll try to provide all the relevant code and prints *The comments and prints are translated by chatgpt since they were originally in Spanish* Thank you very much in advance No player is mounted, or the passenger is not a player. No player is mounted, or the passenger is not a player. No player is mounted, or the passenger is not a player. No player is mounted, or the passenger is not a player. No player is mounted, or the passenger is not a player. MountableWolfEntity package com.vals.valscraft.entity; import com.vals.valscraft.network.MountSyncPacket; import com.vals.valscraft.network.NetworkHandler; import net.minecraft.client.Minecraft; import net.minecraft.network.syncher.EntityDataAccessor; import net.minecraft.network.syncher.EntityDataSerializers; import net.minecraft.network.syncher.SynchedEntityData; import net.minecraft.server.MinecraftServer; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.Mob; import net.minecraft.world.entity.ai.attributes.AttributeSupplier; import net.minecraft.world.entity.ai.attributes.Attributes; import net.minecraft.world.entity.animal.Wolf; import net.minecraft.world.entity.player.Player; import net.minecraft.world.entity.Entity; import net.minecraft.world.InteractionHand; import net.minecraft.world.InteractionResult; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; import net.minecraft.world.level.Level; import net.minecraft.world.phys.Vec3; import net.minecraftforge.event.TickEvent; import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.network.PacketDistributor; public class MountableWolfEntity extends Wolf { private boolean hasSaddle; private static final EntityDataAccessor<Byte> DATA_ID_FLAGS = SynchedEntityData.defineId(MountableWolfEntity.class, EntityDataSerializers.BYTE); public MountableWolfEntity(EntityType<? extends Wolf> type, Level level) { super(type, level); this.hasSaddle = false; } @Override protected void defineSynchedData() { super.defineSynchedData(); this.entityData.define(DATA_ID_FLAGS, (byte)0); } public static AttributeSupplier.Builder createAttributes() { return Wolf.createAttributes() .add(Attributes.MAX_HEALTH, 20.0) .add(Attributes.MOVEMENT_SPEED, 0.3); } @Override public InteractionResult mobInteract(Player player, InteractionHand hand) { ItemStack itemstack = player.getItemInHand(hand); if (itemstack.getItem() == Items.SADDLE && !this.hasSaddle()) { if (!player.isCreative()) { itemstack.shrink(1); } this.setSaddle(true); return InteractionResult.SUCCESS; } else if (!level.isClientSide && this.hasSaddle()) { player.startRiding(this); MountSyncPacket packet = new MountSyncPacket(true); // 'true' means the player is mounted NetworkHandler.CHANNEL.sendToServer(packet); // Ensure the server handles the packet return InteractionResult.SUCCESS; } return InteractionResult.PASS; } @Override public void travel(Vec3 travelVector) { if (this.isVehicle() && this.getControllingPassenger() instanceof Player) { System.out.println("The wolf has a passenger."); System.out.println("The passenger is a player."); Player player = (Player) this.getControllingPassenger(); // Ensure the player is the controller this.setYRot(player.getYRot()); this.yRotO = this.getYRot(); this.setXRot(player.getXRot() * 0.5F); this.setRot(this.getYRot(), this.getXRot()); this.yBodyRot = this.getYRot(); this.yHeadRot = this.yBodyRot; float forward = player.zza; float strafe = player.xxa; if (forward <= 0.0F) { forward *= 0.25F; } this.flyingSpeed = this.getSpeed() * 0.1F; this.setSpeed((float) this.getAttributeValue(Attributes.MOVEMENT_SPEED) * 1.5F); this.setDeltaMovement(new Vec3(strafe, travelVector.y, forward).scale(this.getSpeed())); this.calculateEntityAnimation(this, false); } else { // The wolf does not have a passenger or the passenger is not a player System.out.println("No player is mounted, or the passenger is not a player."); super.travel(travelVector); } } public boolean hasSaddle() { return this.hasSaddle; } public void setSaddle(boolean hasSaddle) { this.hasSaddle = hasSaddle; } @Override protected void dropEquipment() { super.dropEquipment(); if (this.hasSaddle()) { this.spawnAtLocation(Items.SADDLE); this.setSaddle(false); } } @SubscribeEvent public static void onServerTick(TickEvent.ServerTickEvent event) { if (event.phase == TickEvent.Phase.START) { MinecraftServer server = net.minecraftforge.server.ServerLifecycleHooks.getCurrentServer(); if (server != null) { for (ServerPlayer player : server.getPlayerList().getPlayers()) { if (player.isPassenger() && player.getVehicle() instanceof MountableWolfEntity) { MountableWolfEntity wolf = (MountableWolfEntity) player.getVehicle(); System.out.println("Tick: " + player.getName().getString() + " is correctly mounted on " + wolf); } } } } } private boolean lastMountedState = false; @Override public void tick() { super.tick(); if (!this.level.isClientSide) { // Only on the server boolean isMounted = this.isVehicle() && this.getControllingPassenger() instanceof Player; // Only print if the state changed if (isMounted != lastMountedState) { if (isMounted) { Player player = (Player) this.getControllingPassenger(); // Verify the passenger is a player System.out.println("Server: Player " + player.getName().getString() + " is now mounted."); } else { System.out.println("Server: The wolf no longer has a passenger."); } lastMountedState = isMounted; } } } @Override public void addPassenger(Entity passenger) { super.addPassenger(passenger); if (passenger instanceof Player) { Player player = (Player) passenger; if (!this.level.isClientSide && player instanceof ServerPlayer) { // Send the packet to the server to indicate the player is mounted NetworkHandler.CHANNEL.send(PacketDistributor.PLAYER.with(() -> (ServerPlayer) player), new MountSyncPacket(true)); } } } @Override public void removePassenger(Entity passenger) { super.removePassenger(passenger); if (passenger instanceof Player) { Player player = (Player) passenger; if (!this.level.isClientSide && player instanceof ServerPlayer) { // Send the packet to the server to indicate the player is no longer mounted NetworkHandler.CHANNEL.send(PacketDistributor.PLAYER.with(() -> (ServerPlayer) player), new MountSyncPacket(false)); } } } @Override public boolean isControlledByLocalInstance() { Entity entity = this.getControllingPassenger(); return entity instanceof Player; } @Override public void positionRider(Entity passenger) { if (this.hasPassenger(passenger)) { double xOffset = Math.cos(Math.toRadians(this.getYRot() + 90)) * 0.4; double zOffset = Math.sin(Math.toRadians(this.getYRot() + 90)) * 0.4; passenger.setPos(this.getX() + xOffset, this.getY() + this.getPassengersRidingOffset() + passenger.getMyRidingOffset(), this.getZ() + zOffset); } } } MountSyncPacket package com.vals.valscraft.network; import com.vals.valscraft.entity.MountableWolfEntity; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.player.Player; import net.minecraftforge.network.NetworkEvent; import java.util.function.Supplier; public class MountSyncPacket { private final boolean isMounted; public MountSyncPacket(boolean isMounted) { this.isMounted = isMounted; } public void encode(FriendlyByteBuf buffer) { buffer.writeBoolean(isMounted); } public static MountSyncPacket decode(FriendlyByteBuf buffer) { return new MountSyncPacket(buffer.readBoolean()); } public void handle(NetworkEvent.Context context) { context.enqueueWork(() -> { ServerPlayer player = context.getSender(); // Get the player from the context if (player != null) { // Verifies if the player has dismounted if (!isMounted) { Entity vehicle = player.getVehicle(); if (vehicle instanceof MountableWolfEntity wolf) { // Logic to remove the player as a passenger wolf.removePassenger(player); System.out.println("Server: Player " + player.getName().getString() + " is no longer mounted."); } } } }); context.setPacketHandled(true); // Marks the packet as handled } } networkHandler package com.vals.valscraft.network; import com.vals.valscraft.valscraft; import net.minecraft.resources.ResourceLocation; import net.minecraftforge.network.NetworkRegistry; import net.minecraftforge.network.simple.SimpleChannel; import net.minecraftforge.network.NetworkEvent; import java.util.function.Supplier; public class NetworkHandler { private static final String PROTOCOL_VERSION = "1"; public static final SimpleChannel CHANNEL = NetworkRegistry.newSimpleChannel( new ResourceLocation(valscraft.MODID, "main"), () -> PROTOCOL_VERSION, PROTOCOL_VERSION::equals, PROTOCOL_VERSION::equals ); public static void init() { int packetId = 0; // Register the mount synchronization packet CHANNEL.registerMessage( packetId++, MountSyncPacket.class, MountSyncPacket::encode, MountSyncPacket::decode, (msg, context) -> msg.handle(context.get()) // Get the context with context.get() ); } }  
    • Do you use features of inventory profiles next (ipnext) or is there a change without it?
  • Topics

×
×
  • Create New...

Important Information

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