Skip to content

feat(simulation): add hardened liquid movement - #2

Merged
HashimTheArab merged 7 commits into
mainfrom
liquid-simulation
Jul 20, 2026
Merged

feat(simulation): add hardened liquid movement#2
HashimTheArab merged 7 commits into
mainfrom
liquid-simulation

Conversation

@HashimTheArab

@HashimTheArab HashimTheArab commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

What changed

  • Ports the movement-relevant water and lava simulation from implement liquid movement simulation oomph#145, pinned to source head 0bcbb8be25593f836a66ee6a4e302d4fb81fd2bb.
  • Adds liquid-layer and Depth Strider provider contracts, swimming state and hitbox behavior, flow, drag, gravity, exit probing, dolphin boost, and liquid/gliding interactions.
  • Adds bounded server-observed water grace so client-controlled swimming flags cannot disable gravity or shrink the hitbox indefinitely in air.
  • Adds explicit liquid-provider wiring and an opt-in fail-closed requirement for authoritative callers.
  • Documents intentional bedsim adaptations and the optional upstream-compatible input clamping mode.

Why

Cinnabar and other bedsim consumers need a reusable, deterministic liquid oracle instead of maintaining client-specific liquid physics in each caller. The security hardening deliberately narrows the upstream behavior where an untrusted swimming flag could otherwise create indefinite air hovering.

Validation

  • go test ./... -count=3 — 145 tests pass.
  • go vet ./... — clean.
  • git diff --check — clean.
  • Two independent Claude Opus 4.8/xhigh review cycles inspected the full main..0202be6 range; the final verdict was APPROVE with no Critical or Important findings.

Remaining risk

This is structurally aligned to the pinned Oomph PR, but bedsim uses float64 while Oomph uses float32, so it is not bit-identical. Native/consumer integration evidence remains separate from this library PR.

Summary by CodeRabbit

  • New Features

    • Added underwater/lava movement with swimming, liquid flow, Depth Strider scaling, and dolphin boost tracking.
    • Added second-layer liquid support for waterlogged blocks, plus liquid-layer detection utilities and optional enforcement.
    • Expanded simulator configuration with RequireLiquidLayer, SwimWaterGraceTicks, and UpstreamImpulseClamping, including a dedicated liquids provider.
  • Bug Fixes

    • Hardened spoofed swimming/hovering prevention with deterministic water-travel grace, and improved reliability across teleports, unloaded chunks, and surface transitions.
  • Documentation

    • Updated README with liquid-layer setup, grace semantics, and behavior divergences.
  • Tests

    • Added extensive liquid/swimming hardening and regression scenarios.

NopeNotDark and others added 5 commits July 19, 2026 19:17
Audits the liquid-simulation branch against oomph-ac/oomph PR #145 at its
head commit 0bcbb8be25593f836a66ee6a4e302d4fb81fd2bb and closes the gaps
found, then covers the whole feature with tests.

Implementation fixes:

- Add the swimming hitbox. While Swimming, BoundingBox and ClientBoundingBox
  collapse the height to the entity width, matching the client's swim pose.
  This was missing entirely and affects collisions, liquid detection and the
  liquid exit probe, not just liquid travel.
- Restore edge-triggered jumping. state.Jumping was widened to
  StartJumping || Jumping, which re-armed a ground jump on every tick the
  jump key was held. Upstream is StartJumping only; the held-key and
  automatic ascent cases are already carried by EffectiveJumping. This was a
  regression in non-liquid movement.
- Add DolphinBoostTicks and count it down in tickState, resetting
  SwimSpeedMultiplier to its default on expiry, matching upstream. Callers
  previously had to manage boost expiry themselves.
- Only clear Gliding and GlideBoostTicks when actually gliding, so a pending
  glide boost survives water contact as it does upstream.
- Revert the sneak impulse clamp to its v0.1.3 form. The WantDown/WantDownSlow
  exception had no basis in the source and changed non-liquid movement.
- Route liquid flow's collision lookups through a nil-safe helper so an absent
  world reads as empty space rather than panicking.
- Fix the climb debug label to report effectiveJumping, matching the branch
  condition.

Documents in liquid.go the deliberate divergences from the source: float64
arithmetic, LiquidType matching, the LiquidProvider layer-1 adaptation,
zero-means-default speed fields, nil effects tolerance, and why the impulse
clamps are retained.

Adds liquid_test.go: 107 tests covering water and lava detection across both
layers, the swim hitbox, effective jump/ascend/descend inputs, acceleration
and drag, gravity, levitation, depth strider, dolphin boost, flow including
falling liquid and solid faces, collision and exit probing, gliding, ladder
climbing, reliability, determinism, and the provider fallback paths.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Addresses an independent review of the liquid port. The physics remain a 1:1
port of oomph-ac/oomph PR #145; these changes cover the gap between a
proxy-embedded simulator that can trust its anticheat and a standalone
authoritative library that cannot.

Critical: bound water travel on server evidence.

Upstream gates water travel on `touchingWater || Swimming`, and sizes the
hitbox off the same flag. Since Swimming comes straight from the client, a
latched flag granted indefinite zero-gravity hovering in open air with no
correction raised, and shrank the server-side hitbox from 1.8 to 0.6 so the
player fit through gaps a standing player cannot. Verified against the previous
commit: 60 ticks of spoofed swimming in a dry world ended with velocity 0 and
zero altitude lost.

Both the water-travel branch and the swim pose now require recent
server-observed water contact, bounded by SwimWaterGraceTicks (default 10).
The budget refills only on real contact, is clamped to its bound before
anything reads it, and is cleared on every frame that was not simulated:
unreliable, unloaded chunk, immobile, and teleport. Lava the player is actually
standing in takes priority over a retained water grace. The budget is constant
across a tick, so collision, liquid detection and exit probing always agree on
one hitbox; the cost is that entering water adopts the swim pose one tick later
than upstream, erring toward the larger box.

Important: make liquid-layer support explicit and checkable.

Layer-1 liquids were discovered only by type-asserting World, so a missing or
mistyped implementation silently mis-simulated waterlogged blocks as dry. Adds
an explicit Simulator.Liquids field, HasLiquidLayer, and an opt-in
RequireLiquidLayer that fails closed with SimulationOutcomeUnreliable. The
World type assertion still works as a fallback for existing integrations.

Important: replace tests that did not distinguish behavior.

- TestFlyingSkipsLiquidPhysics was vacuous, since simulationIsReliable exits
  before physics. Split into a test for that contract and one exercising the
  liquid gate directly.
- TestLiquidSimulationIsDeterministic compared the implementation to itself.
  Replaced by a pinned golden, plus a repeat-run check for nondeterminism.
- TestLiquidExitProbeBlockedByWall had liquid in the probe box, so it could not
  attribute the result. Rebuilt to isolate the collision term, verifying the
  probe box is liquid-free.

Adds coverage for stairs solid-face flow blocking, the nil-world guard, the +8
drop weight and -6 falling magnitude, swim-hitbox collision interaction, and
the DefaultSwimWaterGraceTicks literal. Each new security branch was confirmed
by mutation to fail a test when removed.

Also: surfaces the sneak/consuming impulse-clamp divergence in the README and
adds SimulationOptions.UpstreamImpulseClamping to opt into upstream's behavior
without a breaking change; captures EffectiveJumping before updateSwimTravel to
match upstream ordering; replaces the stringly liquid type with an internal
liquidKind; documents the defensive depth-strider and nil-world adaptations;
deprecates the unused MaxNormalizedImpulse; and drops a nil check on a type
assertion that could never fire.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 20, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 7b452d1f-5be8-44bb-99de-4c0d9b331126

📥 Commits

Reviewing files that changed from the base of the PR and between 36c1b92 and 98e44e4.

📒 Files selected for processing (1)
  • liquid_test.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • liquid_test.go

📝 Walkthrough

Walkthrough

The simulator now supports water and lava movement physics, layered liquid providers, swimming state and pose handling, Depth Strider and dolphin effects, water-contact grace, liquid flow, and configurable reliability and impulse-clamping behavior. Tests cover physics, transitions, hardening cases, determinism, and golden outcomes.

Changes

Liquid movement simulation

Layer / File(s) Summary
Liquid movement contracts and state
README.md, constants.go, input.go, interfaces.go, movement.go, simulator.go
Adds liquid providers, movement-state fields, simulation options, speed constants, swimming inputs, and documentation for layered liquids and liquid movement.
Liquid detection and travel physics
liquid.go, liquid_test.go
Implements water and lava travel, liquid detection, layered block lookup, swimming steering, Depth Strider effects, flow vectors, exit probing, and collision handling with comprehensive physics tests.
Tick integration and movement gates
bbox.go, simulation.go, liquid_test.go
Integrates swimming and jumping inputs, water-contact grace, boost expiration, pose-dependent hitboxes, reliability resets, flying gates, impulse clamping, and gliding interactions.
Hardening and deterministic validation
liquid_hardening_test.go
Adds coverage for grace boundaries, liquid-layer requirements, impulse-clamping modes, flying behavior, flow weighting, nil-world safety, collision changes, repeatability, and a golden scenario.

Estimated code review effort: 5 (Critical) | ~120 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant Simulator
  participant LiquidProvider
  participant MovementState
  Client->>Simulator: submit movement and swimming inputs
  Simulator->>LiquidProvider: query liquid contacts and second-layer liquids
  LiquidProvider-->>Simulator: return water or lava data
  Simulator->>MovementState: update grace, velocity, flow, and pose state
  Simulator-->>Client: return simulated movement outcome
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is concise and accurately summarizes the main change: hardened liquid movement simulation.
Docstring Coverage ✅ Passed Docstring coverage is 88.24% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch liquid-simulation

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@liquid_test.go`:
- Around line 106-112: Update layeredLiquidWorld.Liquid to call the promoted
Block method directly through w instead of explicitly selecting the embedded
liquidWorld field, preserving the existing type assertion and return behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: bddbe93b-1468-4637-83b0-0b0a5af3bb4b

📥 Commits

Reviewing files that changed from the base of the PR and between 5be9149 and 0202be6.

📒 Files selected for processing (11)
  • README.md
  • bbox.go
  • constants.go
  • input.go
  • interfaces.go
  • liquid.go
  • liquid_hardening_test.go
  • liquid_test.go
  • movement.go
  • simulation.go
  • simulator.go

Comment thread liquid_test.go
@HashimTheArab
HashimTheArab merged commit 3a517d7 into main Jul 20, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants