From 7ccdd3063a75752e0f46cc888c435b4fc4dfec0a Mon Sep 17 00:00:00 2001 From: daddy Date: Sat, 5 Sep 2026 22:58:55 +0000 Subject: [PATCH] Fix: players fall through the floor in multiplayer scenes/server_player.tscn was a bare CharacterBody3D with NO CollisionShape3D, so on the server move_and_slide() never hit the ground and is_on_floor() was always false -> the server player fell forever, and the client (which snaps to the server's authoritative position past a drift threshold) fell with it. Give the server player the same CapsuleShape3D (r=0.35, h=1.6) as the client. The 2-player test now also asserts the server keeps the player on the ground (on_floor / min_y), which is how this slipped through -- the tests only checked "position changes" and "sees the other player". Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_015jtxAiu5dAW4WxCNdGkrkJ --- scenes/server_player.tscn | 9 ++++++++- scripts/mp_test_client.gd | 12 +++++++++--- scripts/run_multiplayer_2p_test.sh | 4 +++- 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/scenes/server_player.tscn b/scenes/server_player.tscn index 4f77f6d..cbd6858 100644 --- a/scenes/server_player.tscn +++ b/scenes/server_player.tscn @@ -1,6 +1,13 @@ -[gd_scene load_steps=2 format=3] +[gd_scene load_steps=3 format=3] [ext_resource type="Script" path="res://scripts/player_spawner.gd" id="1"] +[sub_resource type="CapsuleShape3D" id="CapsuleShape_server_player"] +radius = 0.35 +height = 1.6 + [node name="Player" type="CharacterBody3D"] script = ExtResource("1") + +[node name="CollisionShape3D" type="CollisionShape3D" parent="."] +shape = SubResource("CapsuleShape_server_player") diff --git a/scripts/mp_test_client.gd b/scripts/mp_test_client.gd index fb69de5..488127d 100644 --- a/scripts/mp_test_client.gd +++ b/scripts/mp_test_client.gd @@ -45,14 +45,20 @@ func _run() -> void: quit(0) return - # role a: move forward, watch for a remote and our own displacement. + # role a: move forward, watch for a remote and our own displacement, and + # confirm the server keeps us on the ground (not falling through it). var start: Vector3 = net.position var max_remotes := 0 + var min_server_y := 1000.0 for i in 90: net.predict_and_send(1.0 / 30.0, 0.0, -1.0, false, 0.0) await process_frame max_remotes = maxi(max_remotes, net.get_remote_states().size()) + var st: Dictionary = net.get_own_server_state() + if st.has("position"): + min_server_y = minf(min_server_y, st["position"].y) var moved: bool = net.position.distance_to(start) > 1.0 - print("RESULT a remotes=%d moved=%s" % [max_remotes, str(moved)]) + var on_floor: bool = min_server_y > -2.0 + print("RESULT a remotes=%d moved=%s on_floor=%s (min_y=%.2f)" % [max_remotes, str(moved), str(on_floor), min_server_y]) net.disconnect_from_server() - quit(0 if (moved and max_remotes >= 1) else 1) + quit(0 if (moved and max_remotes >= 1 and on_floor) else 1) diff --git a/scripts/run_multiplayer_2p_test.sh b/scripts/run_multiplayer_2p_test.sh index 51647c7..40b7740 100755 --- a/scripts/run_multiplayer_2p_test.sh +++ b/scripts/run_multiplayer_2p_test.sh @@ -37,7 +37,9 @@ echo "server: $(grep -c 'joined' /tmp/mp2p_server.log) joins seen" echo "A: ${A_LINE:-}" echo "B: ${B_LINE:-}" -if echo "$A_LINE" | grep -q 'moved=true' && echo "$A_LINE" | grep -qE 'remotes=[1-9]'; then +if echo "$A_LINE" | grep -q 'moved=true' \ + && echo "$A_LINE" | grep -qE 'remotes=[1-9]' \ + && echo "$A_LINE" | grep -q 'on_floor=true'; then echo "=== SMOKE RESULT: PASS ===" exit 0 fi