Fix: rejoining stacked players on top of each other (one fell through)
checkpoint / checkpoint (push) Successful in 1m40s

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
This commit is contained in:
2026-09-06 00:00:27 +00:00
parent 80f24bfd01
commit da4e1420ee
5 changed files with 39 additions and 1 deletions
+17 -1
View File
@@ -30,6 +30,9 @@ var status_file_path: String = ""
var players: Dictionary = {}
var tick: int = 0
var is_running: bool = false
## Monotonic count of joins ever -- drives spawn spread so two players never
## land on the same spot (which used to shove one through the floor).
var _join_count: int = 0
var _player_scene: PackedScene = preload("res://scenes/server_player.tscn")
var _level_scene: PackedScene = preload("res://scenes/worlds/test_level.tscn")
@@ -65,6 +68,16 @@ func _ready() -> void:
print("[Server] world=%s listening on ENet %d (%d Hz)" % [world_id, port, PHYSICS_HZ])
## A spawn point offset from the level's marker so joins don't stack. Golden-
## angle spiral: evenly spread, never exactly repeats, grows slowly outward.
func _spawn_for(index: int) -> Vector3:
if index == 0:
return _spawn_point
var angle := index * 2.399963 # golden angle (radians)
var radius := 0.9 + 0.25 * float(index)
return _spawn_point + Vector3(cos(angle) * radius, 0.0, sin(angle) * radius)
func _parse_args() -> void:
var args := OS.get_cmdline_user_args()
var i := 0
@@ -127,7 +140,10 @@ func _on_peer_connected(peer_id: int) -> void:
player.name = "Player_%d" % peer_id
player.peer_id = peer_id
add_child(player)
player.global_position = _spawn_point
var spawn := _spawn_for(_join_count)
_join_count += 1
player.global_position = spawn
player.spawn_position = spawn
players[peer_id] = player
print("[Server] peer %d joined (%d players)" % [peer_id, players.size()])
_write_status()