mirror of
https://github.com/jcreek/CosmicClash.git
synced 2026-09-10 16:04:04 +00:00
fix(multiplayer): enforce drain at admission
This commit is contained in:
@@ -169,9 +169,10 @@ static func reservation_identity_matches(slot_identity: String, incoming_identit
|
||||
func _on_peer_disconnected(peer_id: int) -> void:
|
||||
if not multiplayer.is_server():
|
||||
return
|
||||
if not admissions_open:
|
||||
await _reject(multiplayer.get_remote_sender_id(), "server is draining")
|
||||
return
|
||||
_cleanup_disconnected_peer(peer_id)
|
||||
|
||||
|
||||
func _cleanup_disconnected_peer(peer_id: int) -> void:
|
||||
NetworkManager.invalidate_peer(peer_id)
|
||||
_remove_player(peer_id)
|
||||
|
||||
@@ -210,7 +211,8 @@ func _remove_player(peer_id: int) -> void:
|
||||
# from inside signal-handling: by then poll() has fully returned, every
|
||||
# disconnect event in this batch has been dispatched, and get_peers()
|
||||
# reflects the settled, genuinely-still-connected set.
|
||||
call_deferred("_broadcast_player_left", peer_id)
|
||||
if is_inside_tree():
|
||||
call_deferred("_broadcast_player_left", peer_id)
|
||||
|
||||
|
||||
func _broadcast_player_left(peer_id: int) -> void:
|
||||
@@ -239,6 +241,10 @@ static func _sanitize_shutdown_reason(raw: String) -> String:
|
||||
return clean if not clean.is_empty() else "server_shutdown"
|
||||
|
||||
|
||||
static func admission_rejection(is_open: bool) -> String:
|
||||
return "" if is_open else "server is draining"
|
||||
|
||||
|
||||
# Balances a new joiner onto whichever team currently has fewer players
|
||||
# (ties go to team 0). Server only.
|
||||
func _pick_balanced_team() -> int:
|
||||
@@ -261,6 +267,10 @@ func _hello(protocol_version: int, tick_hz: int, player_name: String, supplied_j
|
||||
var peer_id := multiplayer.get_remote_sender_id()
|
||||
if roster.has(peer_id):
|
||||
return # duplicate hello from an already-accepted peer; ignore
|
||||
var admission_error := admission_rejection(admissions_open)
|
||||
if not admission_error.is_empty():
|
||||
await _reject(peer_id, admission_error)
|
||||
return
|
||||
|
||||
if protocol_version != NetCodec.PROTOCOL_VERSION:
|
||||
await _reject(peer_id, "protocol version mismatch: server=%d client=%d" % [NetCodec.PROTOCOL_VERSION, protocol_version])
|
||||
|
||||
@@ -52,6 +52,24 @@ func test_server_shutdown_message_is_bounded_and_emitted() -> void:
|
||||
assert_eq(instance.last_server_shutdown_reason.length(), 96, "bounded shutdown reason is retained for UI")
|
||||
|
||||
|
||||
func test_drain_fences_new_hello_admissions() -> void:
|
||||
assert_eq(MatchNet.admission_rejection(true), "", "an active server accepts new hello requests")
|
||||
assert_eq(MatchNet.admission_rejection(false), "server is draining", "a draining server rejects new hello requests")
|
||||
|
||||
|
||||
func test_draining_disconnect_still_releases_roster_and_join_token() -> void:
|
||||
var match_net := MatchNet.new()
|
||||
var token := "opaque-join-token"
|
||||
match_net.admissions_open = false
|
||||
match_net.roster[42] = MatchNet.PlayerInfo.new(42, "Alice", 0, false, "player-1")
|
||||
match_net._active_join_peers[token] = 42
|
||||
match_net._join_history[token] = {"generation": 1}
|
||||
match_net._cleanup_disconnected_peer(42)
|
||||
assert_true(not match_net.roster.has(42), "drain does not retain a disconnected roster entry")
|
||||
assert_true(not match_net._active_join_peers.has(token), "drain releases the disconnected peer's join token")
|
||||
assert_true(float(match_net._join_history[token].get("lost_at", 0.0)) > 0.0, "disconnect records the reclaim boundary during drain")
|
||||
|
||||
|
||||
func test_reservation_reclaim_requires_stable_identity() -> void:
|
||||
assert_true(MatchNet.reservation_identity_matches("player-a", "player-a", "Alice", "Impostor"), "the verified identity can reclaim despite a changed display name")
|
||||
assert_true(not MatchNet.reservation_identity_matches("player-a", "player-b", "Alice", "Alice"), "a same-name peer cannot reclaim another identity's slot")
|
||||
|
||||
Reference in New Issue
Block a user