All players spawned at the exact same PlayerSpawn point and were on the
same collision layer, so two overlapping CharacterBody3Ds triggered a
physics depenetration shove that could launch one through the 1-unit-thick
floor.
- server.gd: golden-angle spawn spread -- each join lands ~1m+ from the
others, growing outward, never exactly repeating.
- server_player.tscn + player.tscn: players on collision layer 2, mask 1
(world only) -- players no longer collide with each other at all
(fine for a social game; also removes the shove).
- player_spawner.gd + player.gd: hard safety net -- if y < -10 (any
tunnel-through), snap back to the spawn instead of falling forever.
Verified: 3 server players spawn 1.15m+ apart, none fall, all rest on the
floor. 2p multiplayer + network_client smokes green.
Client-side -- rebuild the exe.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015jtxAiu5dAW4WxCNdGkrkJ
Made the multiplayer core actually work (it had never run a real sim):
- project.godot: physics pinned to 30 Hz -- the shared sim/snapshot/prediction rate.
- server.gd: loads the world's level (real ground + collision), spawns a
server Player per peer at the level's PlayerSpawn, broadcasts a full
snapshot every physics frame with each peer's state AND last_seq (the last
input seq applied for that peer). Accepts `--port/--world/--status-file`
args (status file = player count for a future idle reaper); defaults keep
`godot res://scenes/server.tscn` working standalone.
- player_spawner.gd (server Player): rewrote -- drains its whole input queue
each physics frame in seq order, integrates gravity/walk/jump with the
shared constants, move_and_slide(), tracks last_applied_seq. No more no-op
process_server_input().
- network_client.gd: rewrote reconciliation -- drops inputs the server has
acked (last_seq), re-simulates the still-in-flight ones from the
authoritative state, snaps only on real drift. Exposes get_remote_states()
(peer_id -> latest snapshot state) for the game to render other players.
predict_and_send() now also sends body yaw.
- proxies trimmed to the RPC set the rewrite actually uses.
- scripts/mp_test_client.gd + run_multiplayer_2p_test.sh: two real client
processes vs one real server -- client A sees client B and its own
movement round-trips. PASS.
- run_checkpoint_tests.py: also run the 2p shell test.
smoke_test_network_client 4/4, player 15/15, avatar_in_world 10/10, all green.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015jtxAiu5dAW4WxCNdGkrkJ