Skip to content

Implemented waterlogging#6840

Open
ipad54 wants to merge 27 commits intopmmp:minor-nextfrom
ipad54:waterlogging
Open

Implemented waterlogging#6840
ipad54 wants to merge 27 commits intopmmp:minor-nextfrom
ipad54:waterlogging

Conversation

@ipad54
Copy link
Member

@ipad54 ipad54 commented Oct 13, 2025

Introduction

This PR implements support for waterlogging. For now I've marked this PR as "incomplete" only because I added waterlogging support for basic blocks (stairs, slabs, doors, levers, fences) to make sure everything matches vanilla and to not add a lot of noise to the diff. When the API is fully discussed and we make sure everything works fine, I'll make other remaining blocks to be waterloggable.

Related issues & PRs

#3722

Changes

API changes

Added CoveredByWater interface, as well as CoveredByWaterTrait. Blocks which implement this interface are automatically considered waterloggable.

As Bedrock allows flowing water to waterlog some blocks too, I've decided to keep Water block inside of Waterloggable one, so the API is similar to the plant blocks in decorated pot.

To change block's waterlogging state there is a CoveredByWater->setWaterCover(?Water $waterCover) : self.
To get the water cover there is a CoveredByWater->getWaterCover() : ?Water.

Example usages:

  • $block->setWaterCover(VanillaBlocks::WATER());
  • $block->setWaterCover(VanillaBlocks::WATER()->setDecay(5));
  • $block->setWaterCover(null);

To check that the block is waterlogged you can do $block->getWaterCover() !== null.

Internal API changes

On the Internal level there is a new displaced block abbreviation, which refers to a block, placed on the second layer.

The following internal public methods have been added, which should NOT be used by plugins:

  • World->delayDisplacedBlockUpdate(Vector3 $pos, int $delay) : void Similar to scheduleDelayedBlockUpdate(), but for displaced blocks. Unfortunately, I had to add this to avoid collisions with blocks which use a regular schedule delayed updates apart from being waterloggable (e.x campfire, brewing stand for cooking). The name is probably quite odd, but I want to make sure no one accidentally uses this internal method instead of the regular one by IDE autocomplete.
  • Block->onDisplacedScheduledUpdate() : void Similar to onScheduledUpdate(), but called for displaced blocks.
  • Block->getDisplacedBlock() : Block Returns AIR by default, overriden by waterloggable blocks to return their water state. Called in the core to retrieve a displaced block to send an update block packet, write that block to the second layer, run a separate schedule delayed update, update its position.
  • Block->setDisplacedBlock(Block $block) : void called during getBlockAt() to retrieve a block from the second layer.

Backwards compatibility

Plugins which interact with the second layer via internal methods (like this one) will stop working because blocks, which are not expected to be in the second layer, will be overwritten by air.

Follow-up

Implement snowlogging, it can be done very quickly after these changes.

Tests

A small video can be found here: https://www.youtube.com/watch?v=_HQG7-bTJXg
From what can I see, it seems like it's fully matches vanilla behaviour, but I'm not 100% sure.

Note: in the end you might see that a lava can't collide with a non-waterlogged lever. I have fixed that bug after video recording.

@ipad54 ipad54 requested a review from a team as a code owner October 13, 2025 13:10
pmmp-admin-bot[bot]
pmmp-admin-bot bot previously approved these changes Oct 13, 2025
@ipad54 ipad54 added Category: Gameplay Related to Minecraft gameplay experience Status: Insufficiently Tested Status: Incomplete Work in progress Type: Enhancement Contributes features or other improvements to PocketMine-MP labels Oct 13, 2025
Copy link
Member

@dktapps dktapps left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was mostly discussed on Discord, but I'll leave some comments here too.

I haven't reviewed this fully yet so this is just a few nits.

$this->collisionBoxes = null;

if(($block = $this->getDisplacedBlock())->getTypeId() !== BlockTypeIds::AIR){
$block->position($world, $x, $y, $z);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems a bit sketch. In the default case, this will allocate a new air block, position it, and then immediately throw it away. That doesn't seem good for performance.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should probably be fine now, as getDisplacedBlock() now returns null in most cases.

pmmp-admin-bot[bot]
pmmp-admin-bot bot previously approved these changes Oct 14, 2025
@dktapps dktapps self-assigned this Oct 14, 2025
pmmp-admin-bot[bot]
pmmp-admin-bot bot previously approved these changes Oct 14, 2025
pmmp-admin-bot[bot]
pmmp-admin-bot bot previously approved these changes Oct 15, 2025
pmmp-admin-bot[bot]
pmmp-admin-bot bot previously approved these changes Oct 15, 2025
@kostamax27
Copy link
Contributor

vanilla:

vanilla.mp4

this rp:

pr.mp4

@dktapps
Copy link
Member

dktapps commented Oct 27, 2025

Once again Bedrock makes no sense...

$targetBlock = $coveredBlock;
$resultBlock = $this->liquid instanceof Water ?
(clone $targetBlock)->setWaterCover((clone $this->liquid)->setStill(false)) :
clone $this->liquid;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't this going to cause partial-loggable blocks to get replaced by lava? This doesn't feel right, if we have any partial-loggable blocks that have canBeReplaced() = false in the future, this is going to cause unexpected behaviour

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

e.g. if a plugin made signs partial-loggable, they'd get replaced here by lava

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've changed the logic, but it's still remains incorrect. Vanilla for some reason allows some non-source waterloggable blocks to be replaced by lava here. Yeah, Bedrock again makes no sense, and I don't know if we want to entirely fix it (we would probably have to add something like Block->canBeReplacedBy(Block $block) : bool).


if($block instanceof CoveredByWater && $displacedBlock !== Block::EMPTY_STATE_ID){
$displacedBlock = $this->blockStateRegistry->fromStateId($displacedBlock);
$block->setWaterCover($displacedBlock instanceof Water ? $displacedBlock : null);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm. Maybe we need some more general way to deal with this, will need to think about this more

pmmp-admin-bot[bot]
pmmp-admin-bot bot previously approved these changes Oct 31, 2025
@ipad54
Copy link
Member Author

ipad54 commented Oct 31, 2025

vanilla:

vanilla.mp4
this rp:

pr.mp4

Fixed, thank you!

pmmp-admin-bot[bot]
pmmp-admin-bot bot previously approved these changes Oct 31, 2025
UpdateBlockPacket::FLAG_NETWORK,
UpdateBlockPacket::DATA_LAYER_NORMAL
);
if($fullBlock->getTypeId() === BlockTypeIds::AIR || $fullBlock instanceof Waterloggable){
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this checking for air?

Copy link
Member Author

@ipad54 ipad54 Nov 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is needed to sync the second layer if a waterlogged block was destroyed by plugins, explosions, etc.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, I feel like this isn't reliable. The block could've been directly replaced by something else

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can’t come up with any other reliable ways to save the bandwidth, as we always need to keep the client in sync with the current blocks anyway.

pmmp-admin-bot[bot]
pmmp-admin-bot bot previously approved these changes Nov 8, 2025
@kostamax27
Copy link
Contributor

  1. you forgot Trapdoor
  2. shift + right-click with Water Bucket doesn't work

@ipad54
Copy link
Member Author

ipad54 commented Jan 23, 2026

  1. you forgot Trapdoor
  2. shift + right-click with Water Bucket doesn't work

I just haven’t made trapdoor to be waterloggable yet. I added waterlogging functionality just to few basic blocks to not add a bunch of noise to the diff and make reviewing waterlogging logic easier, this is why it’s marked as incomplete. When the final API is fully discussed, I’ll make all the remaining blocks waterloggable.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Category: Gameplay Related to Minecraft gameplay experience Status: Incomplete Work in progress Status: Insufficiently Tested Type: Enhancement Contributes features or other improvements to PocketMine-MP

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants