Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Authority: who is allowed to simulate

Verified against Minecraft 26.3 · Part VI · A zombie, a player and a boat each take one step, on the server and on the client, and only one side of each pair decides where it ends up.

A zombie walks towards you across a field. Both programs have a Zombie object; both tick it every twentieth of a second; both call the same Entity.tick. Only one of them works out where it goes. Standing next to the zombie is another player, and their copy is the other way up — and the boat you are steering is a third case again, authoritative on your machine and nowhere else. Five predicates on Entity — two of them final — decide all of it, and they invert the naive picture in both directions: the client runs no physics at all for the mob chasing you, while the server runs your player’s physics every tick and then overwrites the answer with a number your client sent.

This is the single most error-prone idea in the entity part, and this part and three later ones rest on it — movement and collision here, every page in Part VIII about a player, what the client is told in Part IX, and in Part X both the client level, for what the client is allowed to simulate, and prediction and acknowledgement, for what it is allowed to guess while it waits to be told. Nineteen pages in the book’s thirteen parts link back to this one. It is stated in full once, here.

Three cases, read on both sides

The whole answer fits in one table, and the rest of the page is that table explained. The columns are three shapes an entity can have; the rows are what each predicate answers and what follows from it.

a tracked moba playera boat you are steering
Entity.isClientAuthoritativefalsetrue, unconditionallytrue, inherited from you
Entity.isLocalInstanceAuthoritative, servertruefalsefalse
Entity.isLocalInstanceAuthoritative, clientfalsetrue only on your own playertrue, on your machine only
Entity.canSimulateMovement, servertruetrue — its move-simulation typefalse
LivingEntity.travel runs on the serveryesyes, and the result is overwrittenn/a
LivingEntity.travel runs on the clientneveronly for your own playern/a — AbstractBoat.floatBoat and AbstractBoat.controlBoat do it instead
Entity.checkFallDamage inside Entity.moveserver onlyyour own client only — the server reaches it by the packet pathclient only
what the other side does insteadis interpolated, and stands still when the interpolation runs outapplies your movement packetapplies its own copy’s packet

A tracked mob is one the server has told your client about at all, which is every mob you can see. A fourth shape is worth naming here so that it does not look like an omission later: an entity nobody is riding and nothing is steering — an empty boat, a minecart — reaches the base implementations, which answer false to both roots, and is simulated by the server alone. A dropped item, any projectile in flight, an experience orb, a falling block and lit TNT (and a dying ender dragon) answer false to both roots too, but each declares MoveSimulationType.SERVER_AND_CLIENT, so Entity.canSimulateMovement is true for it on both sides.

The row that surprises people is Entity.checkFallDamage. Entity.move gates it on Entity.isLocalInstanceAuthoritative, which for a player is true on your own client and false on the server — so the copy that runs it every tick is the one that cannot hurt you. Three names sit close together here: Entity.checkFallDamage is the gate inside Entity.move; LivingEntity.checkFallDamage overrides it and adds the landing particles only on a ServerLevel, so your client’s copy accumulates the fall distance and calls Block.fallOn, whose damage reaches Entity.hurt, which acts only on a ServerLevel; and Entity.doCheckFallDamage is the entrance the server uses instead, driven by the movement packet, which calls the same method and is Part VIII’s subject.

The cast

classwhat it decidesthread
Entitythe five predicates, and every gate inside Entity.move that reads themthe Server thread and the Render thread
Playeroverrides three of the five and answers its own move-simulation type, and is the reason the picture invertsboth
Mobnarrows Entity.isEffectiveAi with Mob.isNoAiboth; the AI that acts on it runs on the server
LivingEntityLivingEntity.aiStep, which either simulates or coastsboth
ClientPacketListenerwhether an inbound position packet moves the entity or only updates a codecthe Render thread
ServerGamePacketListenerImplre-runs the client’s numbers for the entities the client ownsthe Server thread

Five predicates, and the final one the other four hang off

Entity.isLocalInstanceAuthoritative is the root and it is final — no class overrides it. It asks a different question on each side: on the client, am I locally client-authoritative?; on the server, am I not client-authoritative? Everything else hangs off those two, bar the answers a type or Player gives first: on the server a player asks neither question, and the entities above simulate on both sides whatever the root says.

flowchart TD
    SIM["Entity.canSimulateMovement — final, it reads the type"]
    AI["Entity.isEffectiveAi"]
    SIMT["the type: a player's is not a client or the local player, and an item's, a projectile's, TNT's and a few more are both sides"]
    Q["Entity.isLocalInstanceAuthoritative — final"]
    AIP["Player: not a client, or I am the local player"]
    AIM["Mob: and not Mob.isNoAi"]
    LCA["Entity.isLocalClientAuthoritative"]
    NCA["Entity.isClientAuthoritative, negated"]
    LCAD["base: the controlling passenger, or false"]
    LCAP["Player: am I the local player?"]
    CAD["base: the controlling passenger, or false"]
    CAP["Player: true, so the root is false"]
    SIM -- "or" --> SIMT
    SIM -- "by default" --> Q
    AI -- "by default" --> Q
    AI -- "or" --> AIP
    AI -- "or" --> AIM
    Q -- "on the client" --> LCA
    Q -- "on the server" --> NCA
    LCA --> LCAD
    LCA --> LCAP
    NCA --> CAD
    NCA --> CAP

Every arrow means one thing, is answered by: two of the predicates are answered by the root unless a type or Player’s override answers instead, and the root is answered by the other two, one on each machine. Entity.canSimulateMovement is final like the root, so what a player or an item changes there is its move-simulation type, not the method.

Two things in that picture are easy to miss. The base implementations of both Entity.isLocalClientAuthoritative and Entity.isClientAuthoritative delegate to the controlling passenger — an entity with nobody steering it answers false to both, and an entity with a controlling passenger inherits that passenger’s answer. That single line is the whole vehicle model. And Player answers Entity.getMoveSimulationType with MoveSimulationType.AUTHORITATIVE_SIDE_AND_SERVER, which the final Entity.canSimulateMovement reads, and overrides Entity.isEffectiveAi, both to something different from the root — not a client, or I am the local player, the second half being Player.isLocalPlayer — which is what lets the server simulate a player it is not authoritative for. The member that inverts the picture is Player.isClientAuthoritative, an unconditional true.

Three classes narrow the AI predicate further and are worth naming because they are the exceptions people trip over: Mob.isEffectiveAi adds Mob.isNoAi, which is where the NoAI tag bites (what the guard around it does and does not wrap is goals and brains), and both ArmorStand.isEffectiveAi and Mannequin.isEffectiveAi add a physics or immovability flag of their own.

The mob: the client is not correcting, it is replaying

A client-side zombie fails Entity.isLocalInstanceAuthoritative because nothing is riding it, so Entity.canSimulateMovement is false and LivingEntity.aiStep never reaches LivingEntity.travel. What it does instead is two things: Entity.commonTick steps its InterpolationHandler just before the zombie’s own tick, and LivingEntity.aiStep opens with the other half: when nothing is interpolating, scale the stored delta by 0.98. Nothing then applies that delta, because the only thing that would is Entity.move, which nothing in the mob’s own tick reaches on this side, and this is precisely the seam where the client’s other answer begins: what it may guess rather than simulate belongs to prediction and acknowledgement. (A piston or a shulker box can still drive a client-side mob into Entity.move, with a vector of their own rather than its stored delta — movement and collision has the five MoverType constants that say who is pushing.) Inside the mob’s own tick, though, there is no collision, no gravity, no friction and no attempt at prediction. It is not simulating and being corrected — it is replaying what ClientboundMoveEntityPacket and ClientboundEntityPositionSyncPacket tell it, and standing perfectly still when the interpolation runs out.

The player: simulated twice, believed once

A ServerPlayer passes Entity.canSimulateMovement and Entity.isEffectiveAi — both true on the server, by Player’s move-simulation type and its override — so the server’s copy runs the whole of LivingEntity.travel. It also fails Entity.isLocalInstanceAuthoritative, so the one gate inside Entity.move that reads it alone, the fall damage, does not fire. The predicates are why a copy that is not authoritative simulates at all; what the server then does with the answer is the two-phase tick — the physics run in the connection phase, after every level has ticked, inside a bracket that puts the player straight back where it found them, and the authoritative position moves in the movement packet instead. The server’s own simulation is a sanity check whose position nobody uses.

Authority is also about which of two numbers is real

A player’s position on the server moved by the packet path rather than by the server’s own arithmetic, so how far did this entity move this tick has two possible answers for a player, and for anything a player is steering, and one for everything else — and a block has to know which it is being given. SweetBerryBushBlock.entityInside asks Entity.isClientAuthoritative to decide how to find out: Entity.getKnownMovement, the distance the client reported, for a player or anything a player steers, whose movement the server did not compute, and old-position-minus-current for everything else. The predicate is not only a switch for physics.

The boat: authoritative on exactly one machine

Sit in a boat’s first seat and the base delegation makes it yours. On your client Entity.isLocalClientAuthoritative walks to the controlling passenger, finds you, and returns true, so your machine simulates the boat for real. On the server the same delegation makes Entity.isClientAuthoritative true, so the server’s copy is not authoritative and does not simulate — it zeroes its own delta outright. Every other client’s copy does the same, and is moved by its LinearInterpolationHandler.

sequenceDiagram
    box transparent your machine
    participant LP as LocalPlayer
    participant AB as AbstractBoat
    participant CPL as ClientPacketListener
    end
    participant Wire as the network
    box transparent the server
    participant SGPL as ServerGamePacket<br/>ListenerImpl
    participant SAB as AbstractBoat
    end

    AB->>AB: floatBoat, then controlBoat, then move for real
    LP->>Wire: ServerboundMoveVehiclePacket.fromEntity, once per client tick
    Wire->>SGPL: handleMoveVehicle
    SGPL->>SAB: move, MoverType.PLAYER over the reported distance
    alt the move is accepted
    SGPL->>SAB: absSnapTo the reported position
    SGPL->>SAB: setOnGroundWithMovement, then doCheckFallDamage
    SGPL-->>Wire: nothing
    else it is rejected
    SGPL->>SAB: absSnapTo the old position
    SGPL->>Wire: ClientboundMoveVehiclePacket
    Wire->>CPL: handleMoveVehicle
    CPL->>AB: absSnapTo the server's position
    CPL->>Wire: ServerboundMoveVehiclePacket, echoed back to confirm
    end

Two copies of one boat, one lane each: yours is simulated, and the server’s is moved over the distance yours reports and then kept or snapped back (a report too far off is refused before it is moved at all). The alt is one moment with two outcomes, and only the accepted one lands the fall damage on the server’s copy.

The inbound half of that is the sharpest demonstration of what the predicate is for. ClientboundMoveVehiclePacket is not a routine update — the server sends it only when it has rejected your movement, and ClientPacketListener.handleMoveVehicle applies it only for a vehicle the client is authoritative for, then immediately echoes a ServerboundMoveVehiclePacket back to confirm. Meanwhile the ordinary per-entity position packets take the opposite branch: ClientPacketListener.handleEntityPositionSync and ClientPacketListener.handleMoveEntity both check Entity.isLocalInstanceAuthoritative and, when it holds, do not move the entity — ClientPacketListener.handleMoveEntity decodes the delta into the entity’s position codec and stops there, and ClientPacketListener.handleEntityPositionSync records the absolute position in the codec on either branch. The server’s opinion about where your boat is gets recorded and ignored.

Where the gates actually sit

Authority is not one flag consulted once. Entity.move and LivingEntity.aiStep read a predicate at eight places between them, and one of the eight reads two, so there are nine readings in all:

wherewhich memberreads
the vertical collision flags, and Entity.setOnGroundWithMovementEntity.movelocally authoritative or moved vertically — the horizontal flags are always updated
fall damageEntity.checkFallDamagelocally authoritative
the bounceEntity.restituteMovementAfterCollisionsEntity.canSimulateMovement
the step sound and GameEvent.STEPEntity.applyMovementEmissionAndPlaySoundnot a client or locally authoritative
the 0.98 decay of the stored deltaLivingEntity.aiStepEntity.canSimulateMovement is false
the AI stepMob.serverAiStepEntity.isEffectiveAi and not client-side
the travel forkLivingEntity.travelEntity.canSimulateMovement and Entity.isEffectiveAi — the one place that reads a pair
the block effects that follow travelEntity.applyEffectsFromBlocksthe same not-a-client-or-authoritative test as the step sound

The row worth slowing down for is the fifth. The 0.98 decay is what runs because Entity.canSimulateMovement is false — the not-authoritative branch, not a fallback inside the authoritative one — which is the whole of what a client-side mob does with its stored delta.

The travel fork has a fork of its own in front of it, the ridden branch, which movement and collision walks. What belongs here is that LivingEntity.travelRidden is a ninth site: an Entity.canSimulateMovement test that zeroes the delta outright when it fails.

Questions players ask

Why does a mob rubber-band and my own player does not? On an ordinary tick neither one is being corrected, and for opposite reasons. Your client does simulate your own player, and the boat or horse you are steering — but it never simulates a tracked mob, so there is nothing about the mob for the server to disagree with. What you see on a mob is the gap between position packets, walked by InterpolationHandler.

Why does a boat feel responsive and a horse feel heavy? You steer both, and both are authoritative on your machine — but a horse is a LivingEntity going through LivingEntity.travelRidden, which asks the mob for its own speed and drag, while a boat runs its own physics directly. The authority answer is the same; the layer above it is not.

Does NoAI stop a mob moving? On its own, completely — a NoAI mob does not even fall. Mob.isEffectiveAi is the gate on Mob.serverAiStep, which is the obvious half, and it is also the second half of the gate on LivingEntity.travel, so nothing in the mob’s own tick reaches Entity.move. What still moves it comes from outside: a piston, a shulker or a shulker box, a vehicle carrying it, and a player steering it, whose branch asks only Entity.canSimulateMovement.

Where to look

Read the five predicates first, from the root out: Entity.isLocalInstanceAuthoritative, then the two it asks — Entity.isLocalClientAuthoritative and Entity.isClientAuthoritative — then Entity.canSimulateMovement and Entity.isEffectiveAi, which default to it. Player.isClientAuthoritative and Mob.isEffectiveAi, with its Mob.isNoAi, are the two overrides that matter. Then read the two methods that spend them, Entity.move and LivingEntity.aiStep, with the table above open. For the packet half, ServerGamePacketListenerImpl.handleMoveVehicle on the server and ClientPacketListener.handleEntityPositionSync on the client are the two branches the boat’s section turns on, and InterpolationHandler is what moves a mob when nothing simulates it. SweetBerryBushBlock.entityInside is a door worth opening for one line: it is the only place in the game that reads a predicate to choose which measurement of a movement to believe.


Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes tools/verify_names.py.