From d1aba46e48638c8375a2107c8227e5fdcfee1337 Mon Sep 17 00:00:00 2001 From: BitForge <150640405+CompileRider@users.noreply.github.com> Date: Sun, 2 Aug 2026 23:23:53 -0400 Subject: [PATCH] fix(entity): reset fall distance on Spectator/Creative gamemode switch (#2382) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(entity): reset fall distance on Spectator/Creative gamemode switch Prevents fall damage when a player switches to Spectator mid-fall and then back to Survival before landing. This is a pragmatic workaround — vanilla achieves this through multiple independent defense layers (noPhysics, per-tick reset, on_ground force, mayfly check) rather than an explicit reset. The TODOs in the code track the remaining gaps for a full vanilla-faithful implementation. Fixes #2372 * docs(entity): document Creative fall-distance over-forgiveness edge case Add TODO clarifying that once vanilla fall-damage layers 1-4 are implemented, the reset should be restricted to Spectator only. In vanilla a non-flying Creative player keeps fallDistance across a Survival switch (guarded by abilities.mayfly in causeFallDamage); resetting here over-forgives that edge case. Co-Authored-By: Claude Sonnet 4.6 * docs: condense fall damage comment to reference vanilla methods Reduced comment from 23 lines to 6 by: - Removing detailed explanations of vanilla defense layers - Directly referencing Player.aiStep() and Player.causeFallDamage() - Keeping only actionable TODOs - Switching to /* */ style --------- Co-authored-by: Claude Sonnet 4.6 --- pumpkin/src/entity/player.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/pumpkin/src/entity/player.rs b/pumpkin/src/entity/player.rs index 2fa376d72..c75e2a83f 100644 --- a/pumpkin/src/entity/player.rs +++ b/pumpkin/src/entity/player.rs @@ -2961,6 +2961,17 @@ impl Player { } } + /* Vanilla doesn't reset fallDistance in setGameMode(), instead relies on + * Player.aiStep() resetting when abilities.flying=true and + * Player.causeFallDamage() returning false when abilities.mayfly=true. + * TODO: Reset fall_distance each tick when abilities.flying=true (mirrors Player.aiStep()) + * TODO: Add abilities.allow_flying check in LivingEntity::handle_fall_damage() (mirrors Player.causeFallDamage()) + * TODO: Once implemented, restrict this reset to Spectator only. + */ + if matches!(gamemode, GameMode::Creative | GameMode::Spectator) { + self.living_entity.fall_distance.store(0.0); + } + if gamemode != GameMode::Spectator && self.camera_target_id.load().is_some() { self.camera_target_id.store(None); self.client.send_packet_now(&CSetCamera::new(