mirror of
https://github.com/jcreek/CosmicClash.git
synced 2026-09-15 06:32:05 +00:00
fix(multiplayer): lock signed team assignments
This commit is contained in:
@@ -492,17 +492,29 @@ func _set_team(team: int) -> void:
|
||||
if not multiplayer.is_server():
|
||||
return
|
||||
var peer_id := multiplayer.get_remote_sender_id()
|
||||
if not roster.has(peer_id) or team < 0 or team >= TEAM_COUNT:
|
||||
if not _apply_team_change(peer_id, team):
|
||||
return
|
||||
var info: PlayerInfo = roster[peer_id]
|
||||
if info.team == team:
|
||||
return
|
||||
info.team = team
|
||||
info.ready = false # switching teams un-readies — the roster you were ready against just changed
|
||||
player_state_changed.emit(peer_id, info.team, info.ready)
|
||||
_state_changed.rpc(peer_id, info.team, info.ready)
|
||||
|
||||
|
||||
func _apply_team_change(peer_id: int, team: int) -> bool:
|
||||
# In allocated matches team and global slot are signed together. Changing
|
||||
# only team would produce a roster that disagrees with the assignment and
|
||||
# leave spawn_index anchored to the old team.
|
||||
if require_join_authorisation:
|
||||
return false
|
||||
if not roster.has(peer_id) or team < 0 or team >= TEAM_COUNT:
|
||||
return false
|
||||
var info: PlayerInfo = roster[peer_id]
|
||||
if info.team == team:
|
||||
return false
|
||||
info.team = team
|
||||
info.ready = false # switching teams un-readies — the roster you were ready against just changed
|
||||
return true
|
||||
|
||||
|
||||
@rpc("any_peer", "call_remote", "reliable")
|
||||
func _set_ready(ready: bool) -> void:
|
||||
if not multiplayer.is_server():
|
||||
|
||||
@@ -70,6 +70,23 @@ func test_draining_disconnect_still_releases_roster_and_join_token() -> void:
|
||||
assert_true(float(match_net._join_history[token].get("lost_at", 0.0)) > 0.0, "disconnect records the reclaim boundary during drain")
|
||||
|
||||
|
||||
func test_signed_assignment_locks_team_and_spawn_slot_together() -> void:
|
||||
var match_net := MatchNet.new()
|
||||
var info := MatchNet.PlayerInfo.new(42, "Alice", 0, true, "player-1")
|
||||
info.spawn_index = 2
|
||||
match_net.roster[42] = info
|
||||
match_net.require_join_authorisation = true
|
||||
assert_true(not match_net._apply_team_change(42, 1), "allocated clients cannot override their signed team")
|
||||
assert_eq(info.team, 0, "signed team is unchanged")
|
||||
assert_eq(info.spawn_index, 2, "signed spawn index remains paired with its team")
|
||||
assert_true(info.ready, "rejected mutation does not alter readiness")
|
||||
|
||||
match_net.require_join_authorisation = false
|
||||
assert_true(match_net._apply_team_change(42, 1), "direct lobbies retain team switching")
|
||||
assert_eq(info.team, 1, "direct team switch applies")
|
||||
assert_true(not info.ready, "direct team switch still clears readiness")
|
||||
|
||||
|
||||
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