Skip to content

Suppress forwarding of redundant identical SetHealth packets to controlling clients#310

Open
iiroak wants to merge 1 commit into
rfresh2:1.21.4from
iiroak:fix/dedup-sethealth-forwarding
Open

Suppress forwarding of redundant identical SetHealth packets to controlling clients#310
iiroak wants to merge 1 commit into
rfresh2:1.21.4from
iiroak:fix/dedup-sethealth-forwarding

Conversation

@iiroak

@iiroak iiroak commented Jul 21, 2026

Copy link
Copy Markdown

Fixes #309.

Problem

Servers like 2b2t send ClientboundSetHealthPacket on (nearly) every tick even when health/food/saturation are unchanged. When a client is controlling the proxy on a GrimAC server, these are forwarded 1:1 to the controller. GrimAC emits a transaction (ClientboundPing) sandwich for every SetHealth, so this roughly doubles the transaction rate and delivers transactions in bursts, corrupting Grim's movement clock and producing constant Simulation/Timer setbacks.

Human players barely notice. Automated/pathfinding clients crawl at a fraction of normal speed or get stuck in a setback loop, since they can't absorb constant sub-block rewinds.

Change

SetHealthHandler still updates the cache and fires PlayerHealthChangedEvent for every packet (via applyAsync, unchanged), but now overrides apply to only forward a SetHealth to connected client(s) when health/food/saturation actually differ from the last one forwarded. Returning null drops the redundant packet from the forward path in ClientSession#callPacketReceived.

A freshly connected controller still receives its current health via the login cache sync (EntityPlayer#addPackets), so nothing observable changes client-side — only the redundant per-tick stream is dropped. The dedup state lives on the client session's netty thread (where apply runs), so no synchronization is needed.

Testing

Built as a shadowJar and run on two live instances against a 1.21.4 GrimAC backend, same account/route:

Metric Before After
SetHealth to client (idle) ~22/s 0/s
Grim Simulation setbacks constant 0
Grim Timer setbacks constant 0
Automated client throughput crawl / stuck normal walking speed

Note

I wasn't sure whether the 1:1 forwarding is intentional, so I kept the change minimal and conservative (drop only byte-for-byte-identical consecutive packets). If you'd prefer this gated behind a config flag, happy to adjust. See #309 for the full investigation.

…olling clients

Servers such as 2b2t send ClientboundSetHealthPacket every tick even when
health/food/saturation are unchanged. GrimAC emits a transaction sandwich for
every SetHealth it sends, so forwarding these 1:1 roughly doubles the
transaction rate and delivers transactions in bursts, corrupting Grim's
movement clock and producing constant Simulation/Timer setbacks. This is
mostly invisible to human players but makes automated/pathfinding clients
crawl or get stuck in a setback loop.

Still update the player cache and fire events for every packet, but only
forward a SetHealth to connected client(s) when health/food/saturation
actually change compared to the last one forwarded. Newly connected clients
receive their current health via the login cache sync (EntityPlayer#addPackets),
so nothing observable changes client-side.

Fixes rfresh2#309
@rfresh2

rfresh2 commented Jul 22, 2026

Copy link
Copy Markdown
Owner

i dont observe spammy sethealth packet behavior on 2b2t, nor a local server running open source grim

seems more like an issue for your "Automated/pathfinding clients" to solve

i feel like you may be confusing something. like for example the lower your ping the more frequently you are sent ping transactions. or that your health/food/saturation is indeed changing slightly every tick.

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.

Redundant per-tick SetHealth forwarding triggers GrimAC movement setbacks for controlling clients

2 participants