feat(multiplayer): show authoritative proposal countdown

This commit is contained in:
Josh Creek
2026-09-01 22:23:33 +01:00
parent 17e6a9bc20
commit fd1a4d9577
3 changed files with 15 additions and 1 deletions
+7 -1
View File
@@ -188,7 +188,7 @@ func _render(snapshot: Dictionary) -> void:
waited = float(ControlPlaneClient.state.waited_seconds(int(Time.get_unix_time_from_system())))
detail_label.text = "Waiting %.0fs · revision %d" % [waited, int(snapshot.get("revision", 0))]
elif phase == MatchmakingState.PROPOSED:
detail_label.text = "Review the proposal before the countdown expires"
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"
elif phase == MatchmakingState.RESULT_PENDING:
@@ -210,5 +210,11 @@ static func _is_terminal(phase: String) -> bool:
return phase in [MatchmakingState.CANCELLED, MatchmakingState.EXPIRED, MatchmakingState.FAILED, MatchmakingState.LIVE, MatchmakingState.COMPLETED]
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"
return "Review proposal · %ds remaining" % maxi(0, expires_at_unix - now_unix)
static func _can_start_new_search(phase: String) -> bool:
return phase == MatchmakingState.IDLE or phase in [MatchmakingState.CANCELLED, MatchmakingState.EXPIRED, MatchmakingState.FAILED, MatchmakingState.COMPLETED]
+6
View File
@@ -17,3 +17,9 @@ func test_terminal_state_policy_does_not_leave_cancel_or_proposal_actions_enable
assert_true(Matchmaking._can_start_new_search(MatchmakingState.FAILED), "failed search can be retried")
assert_true(not Matchmaking._can_start_new_search(MatchmakingState.LIVE), "live match cannot start a second search")
assert_true(Matchmaking._can_start_new_search(MatchmakingState.COMPLETED), "completed match can start a new search")
func test_proposal_countdown_uses_authoritative_expiry_and_clamps_after_expiry() -> void:
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")