feat(multiplayer): explain allocation lifecycle

This commit is contained in:
Josh Creek
2026-09-01 22:25:54 +01:00
parent 429fb87c08
commit 27017043f9
3 changed files with 32 additions and 1 deletions
+23 -1
View File
@@ -190,7 +190,9 @@ func _render(snapshot: Dictionary) -> void:
elif phase == MatchmakingState.PROPOSED:
detail_label.text = proposal_countdown_text(int(snapshot.get("expires_at_unix", 0)), int(Time.get_unix_time_from_system()))
elif phase == MatchmakingState.ACCEPTED:
detail_label.text = "All players accepted; preparing the match server"
detail_label.text = phase_detail_label(phase)
elif phase in [MatchmakingState.ALLOCATING, MatchmakingState.PROCESS_READY, MatchmakingState.ASSIGNMENT_READY, MatchmakingState.ASSIGNED, MatchmakingState.CONNECTING, MatchmakingState.LIVE]:
detail_label.text = phase_detail_label(phase)
elif phase == MatchmakingState.RESULT_PENDING:
detail_label.text = "The server is confirming the final result"
elif phase == MatchmakingState.COMPLETED:
@@ -210,6 +212,26 @@ static func _is_terminal(phase: String) -> bool:
return phase in [MatchmakingState.CANCELLED, MatchmakingState.EXPIRED, MatchmakingState.FAILED, MatchmakingState.LIVE, MatchmakingState.COMPLETED]
static func phase_detail_label(phase: String) -> String:
match phase:
MatchmakingState.ACCEPTED:
return "All players accepted; preparing the match server"
MatchmakingState.ALLOCATING:
return "Finding a dedicated match server"
MatchmakingState.PROCESS_READY:
return "Match server started; preparing player assignments"
MatchmakingState.ASSIGNMENT_READY:
return "Player assignments are ready"
MatchmakingState.ASSIGNED:
return "Your match server is ready"
MatchmakingState.CONNECTING:
return "Connecting to the match server"
MatchmakingState.LIVE:
return "Match in progress"
_:
return ""
static func proposal_countdown_text(expires_at_unix: int, now_unix: int) -> String:
if expires_at_unix <= 0:
return "Review the proposal before the countdown expires"
+7
View File
@@ -23,3 +23,10 @@ func test_proposal_countdown_uses_authoritative_expiry_and_clamps_after_expiry()
assert_eq(Matchmaking.proposal_countdown_text(1100, 1000), "Review proposal · 100s remaining", "proposal countdown uses server expiry")
assert_eq(Matchmaking.proposal_countdown_text(1000, 1001), "Review proposal · 0s remaining", "expired proposal countdown clamps to zero")
assert_eq(Matchmaking.proposal_countdown_text(0, 1000), "Review the proposal before the countdown expires", "missing expiry retains compatible copy")
func test_allocation_lifecycle_phases_have_specific_detail_copy() -> void:
for phase in [MatchmakingState.ACCEPTED, MatchmakingState.ALLOCATING, MatchmakingState.PROCESS_READY, MatchmakingState.ASSIGNMENT_READY, MatchmakingState.ASSIGNED, MatchmakingState.CONNECTING, MatchmakingState.LIVE]:
assert_true(not Matchmaking.phase_detail_label(phase).is_empty(), "phase %s has lifecycle detail copy" % phase)
assert_true(Matchmaking.phase_detail_label(MatchmakingState.ALLOCATING).contains("dedicated"), "allocation explains dedicated server provisioning")
assert_true(Matchmaking.phase_detail_label(MatchmakingState.CONNECTING).contains("Connecting"), "connecting explains the active transport step")
+2
View File
@@ -1568,3 +1568,5 @@ WebSocket event resource identifiers now enforce the contracts opaque 1612
The Go event hub now enforces the same resource-ID allowlist before publication, so malformed identifiers are rejected at the server boundary rather than only discarded by clients.
The matchmaking UI now displays the authoritative proposal countdown from the server expiry epoch, clamped at zero and retaining compatible copy when older responses omit expiry metadata. Adversarial countdown tests cover delayed and missing-expiry responses.
The UI now provides explicit detail copy for every non-terminal allocation and connection phase (`ACCEPTED` through `LIVE`), so server progress remains understandable throughout assignment and transport startup.