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

State flags on the living handles

host.living(entity) and host.mob(entity) make the handles of #7387's handle design, and the state flags sit where vanilla keeps them: the ones LivingEntity owns read and write through LivingRef, the ones Mob owns through MobRef. Reads take the shared host, writes the server one; a stale handle or a kind the game keeps no such state for reads None and writes false, as every handle does.

Through LivingRef: the glide (is_gliding, gliding_ticks, stop_gliding), the sleep of a server player or a villager (is_sleeping, sleeping_pos, start_sleeping, stop_sleeping, set_sleeping_pos, clear_sleeping_pos), the jump of the player in place (is_jumping, set_is_jumping), climbing (is_climbing, the same onClimbable movement uses, including the gliding exclusion for vines), the baby read (baby) and shield blocking (is_blocking: LivingEntity.isBlocking, an item with blocks_attacks in use past its block delay, for players and mobs). Server player sleep writes work for the integrated owner, LAN guests and dedicated-server guests in the level in place, both inside and outside their own turn; guests in another dimension and fake players refuse all four sleep writes without mutation. A suspended outer actor is unavailable during a nested turn. Glide counters and stops follow the same authoritative body across turns. These reads do not add client tracked-entity coverage.

start_sleeping requires a bed with an available sleep shape before changing any state (a villager also refuses an occupied bed). It moves the sleeper onto that shape and occupies the bed. stop_sleeping stands the sleeper up beside the bed with the normal wake animation, teleport and pose sync; the usual level pass frees a player's bed. set_sleeping_pos and clear_sleeping_pos only change a player's sleeping position/pose, without moving them, occupying a bed or playing a wake animation. The next entity sync publishes the changed pose and bed to clients. Villagers use start_sleeping/stop_sleeping, not the raw player position setters.

EntityRef::portal_cooldown takes &dyn Host and reads the remaining portal cooldown in ticks from the server's portal state: a player's own (the acting player inside its turn and other players outside it), and the level's for mobs, items, experience orbs, boats, minecarts, primed TNT, falling blocks, arrows and the thrown items and fireballs a chunk saves, fireworks and tridents; an ender pearl's comes from End gateways only. Zero means no cooldown; None means a kind with no portal cooldown (armor stands, wind charges, the separately modelled dragon and others), a client, or a stale, parked or otherwise unavailable handle. The read does not load levels.

MobRef::can_use_portal reads the existing server portal decision for a mob in the current level, including mounts and player riders. It takes &dyn Host: Some(false) means the mob is ineligible, while None means a client, stale or unavailable handle, parked mob, or the separately modelled dragon. It does not load a level.

EntityRef::leash_holder takes &dyn Host and reads the live lead of a mob or boat. LeashHolder::Unleashed is a definite absence; Entity holds an existing entity handle for a player, mob or boat, and Knot holds fence coordinates. None means an entity that takes no lead, that the entity or holder is unavailable, that the level is parked, or that a saved holder has not resolved; this read neither loads a level nor changes the lead.

EntityRef::leash_to and leash_to_knot take &mut dyn ServerHost and hand the lead to a player, mob, boat or fence as Leashable.setLeashedTo does: the entity lets go of any other holder and gets off its vehicle, and neither canBeLeashed nor the distance is asked, so a holder too far away snaps the lead on the next tick. detach_leash drops the lead as an item or removes it; a mob's removal fires entity_unleash.

Through MobRef: no_ai/set_no_ai (Mob.isNoAi and setNoAi: the mob stands, saved as NoAI) and set_baby (Mob.setBaby through the canonical species-specific setup). Ageable mobs start at their species' baby age (-48000 for a sniffer, otherwise -24000) or become adults at 0, including the age-boundary attribute, navigation and AI changes. Their age is saved as Age where the kind saves one. Non-ageable babies, such as zombies and piglins, keep their non-growing baby state instead. A species with no baby form refuses set_baby without changing anything. Neither set_baby nor set_no_ai changes PersistenceRequired.

The persistence pair persistence_required/set_persistence_required reads and sets Mob.isPersistenceRequired; vanilla gives that setter no way back. requires_custom_persistence is a separate despawn predicate (Mob.requiresCustomPersistence and its species overrides): passengers, leashes and species-specific reasons such as an enderman carrying a block. It is independent of PersistenceRequired and NoAI, not an alias for either flag. The separately modeled dragon has no such predicate and answers None to this query, even though its persistence flag can be read and set. A /summon and a spawn egg set their flags before the entity-join hook runs, so a mod's own write in that hook is what stays.

Riding topology and resident handles

EntityRef names only an entity resident in the current level. A rejected Mob passenger can remain a full object in a riding tree without becoming resident; its saved UUID does not make another object's handle refer to it.

is_passenger and is_vehicle describe the actual relationships, including retained off-level objects. Thus is_passenger can be Some(true) while vehicle is None, and is_vehicle can be Some(true) while passengers returns an empty list. Dying passengers keep their place until removed.

passengers returns only resident direct passengers in boarding order. indirect_passengers walks the full tree depth-first, through retained objects, and omits only unrepresentable handles: a resident grandchild below an off-level passenger still appears. root_vehicle also traverses retained intermediaries, but returns None if the actual root is off-level; it never returns a resident descendant in its place. is_passenger_of_same_vehicle compares the actual roots, so two resident entities can share an off-level root even though neither can obtain its handle. Cyclic relationships have no root and return None.

controlling_passenger chooses the actual first passenger before checking whether its handle can be returned. An off-level first passenger means None, not a later resident rider. All these reads refuse stale input handles; none loads a retained object into the level or exposes a handle for it.

During actual boat or cart destruction, Mob dismount notification still runs, but remaining edges are forcibly unlinked even when a listener cancels. This safe interim prevents references to dropped vehicles and preserves each Mob's resident or off-level classification and the frozen state of descendants. Retaining removed vehicles to preserve canceled relationships is deferred to #2446. Cancellation while the vehicle remains live, including underwater or activator ejection, still works.