Skip to content

Instantly share code, notes, and snippets.

@Beaness
Last active March 18, 2026 21:10
Show Gist options
  • Select an option

  • Save Beaness/e84ff2b77f02233be456d1968cfa5329 to your computer and use it in GitHub Desktop.

Select an option

Save Beaness/e84ff2b77f02233be456d1968cfa5329 to your computer and use it in GitHub Desktop.
Minecraft anticheat ping pong system

🏓 The Ping-Pong System

Minecraft uses TCP for connections. TCP ensures reliable, ordered delivery of data. This means if the server sends
A -> B -> C the client will receive it in that exact order.

🚨 The Problem

For anticheats to work properly, we need to know what "state" the client is in.
This is not as easy to detect because latency comes into play. For example, if we send Give Player Jump Boost to the player, it takes some time before the client actually receives it.

To track when state-changing packets are processed, we use ping-pong packets.

🙂 Ping-Pong (Simplified)

A ping-pong is simple: if the server sends a Ping, the client will respond with a Pong packet.

To detect state changes, we pair a ping with the state-changing packet. For example:

Ping -> Give Player Jump Boost 2

The client will eventually respond with a Pong.
Once we receive this, we can safely assume the Jump Boost 2 packet has been seen.
(Though in practice it’s a bit more complicated.)

➕ Ping-Pong (Extended)

Almost all anticheats follow the above logic with a few improvements. However, a problem arises:

TCP guarantees packet order but not that two Minecraft packets will be processed in the same tick.
We need to know which tick the client received them.

To solve this, we send two pings: one before and one after the state-changing packet.

Ping 1 -> Give Player Jump Boost -> Ping 2

It’s possible for the client to process these packets across different ticks or all in the same tick. Because of latency and lag, we can’t know exactly.

This leaves us with possible states, modeled like this:

State Outcome
No ping received yet We have no Jump Boost
Pong 1 received We either have Jump Boost or no Jump Boost
Pong 2 received We 100% have Jump Boost

When we simulate movement (on client tick packet) we can use this table to know which scenarios we have to simulate. For example, if we receive the following from the client: Pong 1 -> Client Tick, we know that we have 2 possible jump heights when we run the simulation on the client tick. But if we had received Pong 1 -> Pong 2 -> Client Tick, we only would have to simulate 1 scenario: the one with jump boost.

Note: Modern Minecraft solves this issue by introducing the bundle packet, which allows us to combine a ping with the state change. The bundle packet enforces all packets in it to be processed at once.

🥪 Performance Optimizations: one big sandwhich

To optimize the ping-pong system and not exaggerate the amount of pings, we send one ping at the start of a tick and one at the end of a tick to wrap/"sandwhich" all packets in between. For example if we send:

Give Player Jump Boost -> Give Player Speed

This can be put in the same ping "sandwhich"

Ping 1 -> Give Player Jump Boost -> Give Player Speed -> Ping 2

State Outcome
No ping received yet We have no Jump Boost
Pong 1 received Jump Boost or no Jump Boost + Speed or no Speed (4 scenarios!)
Pong 2 received We 100% have Jump Boost & Speed

⚠️ Limiting state to two possibilities at once

Its possible the same state property (like jump boost level) gets changed multiple times in the same ping sandwhich.

Give Player Jump Boost 1 -> Give Player Jump Boost 2

can be wrapped like

Ping 1 -> Give Player Jump Boost 1 -> Give Player Jump Boost 2 -> Ping 2

State Outcome
No ping received yet We have no Jump Boost
Pong 1 received Possible scenarios: no Jump Boost / Jump Boost 1 / Jump Boost 2
Pong 2 received We 100% have Jump Boost 2

However now we see that its possible the jump boost level can go up to 3 possible states (or even more if more jump boost packets were sent). To solve this we can insert extra pings to limit the amount of possible states

So instead we wrap our jump boosts like this:

Ping 1 -> Give Player Jump Boost 1 -> Ping 2 -> Give Player Jump Boost 2 -> Ping 3

State Outcome
No ping received yet We have no Jump Boost
Pong 1 received We either have Jump Boost 1 or no Jump Boost
Pong 2 received We either have Jump Boost 1 or Jump Boost 2
Pong 3 received We 100% have Jump Boost 2

As you can see we never reach more than 2 possibilities for jump boost, which will help limit the amount of scenarios to simulate in a movement simulation.

Basically we are ending our sandwhich by sending an extra ping in the middle to end our current sandwhich and we use that same ping to begin our next sandwhich.

❓ Questions

For questions you can join the Minecraft Anticheat Community or you can contact me on discord @1.7.10

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