fix(multiplayer): validate persisted matchmaking snapshots

This commit is contained in:
Josh Creek
2026-09-01 22:34:59 +01:00
parent 3985b74bcf
commit a10c6d47f9
3 changed files with 30 additions and 2 deletions
+25 -2
View File
@@ -197,6 +197,25 @@ func restore_snapshot(saved: Dictionary) -> bool:
_reset()
if saved.is_empty():
return true
if saved.has("phase") and not saved["phase"] is String:
return false
if saved.has("ticket_id") and not saved["ticket_id"] is String:
return false
if saved.has("playlist") and not saved["playlist"] is String:
return false
if saved.has("proposal_id") and not saved["proposal_id"] is String:
return false
if saved.has("proposal_state") and not saved["proposal_state"] is String:
return false
if saved.has("message") and not saved["message"] is String:
return false
if saved.has("revision") and not _valid_revision(saved["revision"]):
return false
for epoch_key in ["enqueued_at_unix", "expires_at_unix"]:
if saved.has(epoch_key) and not _valid_epoch(saved[epoch_key]):
return false
if saved.has("proposal_revision") and not _valid_revision(saved["proposal_revision"]):
return false
var saved_phase := String(saved.get("phase", IDLE))
var saved_ticket_id := String(saved.get("ticket_id", ""))
if saved_ticket_id.is_empty() or not _is_ticket_state(saved_phase):
@@ -204,15 +223,19 @@ func restore_snapshot(saved: Dictionary) -> bool:
var saved_playlist := String(saved.get("playlist", ""))
if saved_playlist != "casual" and saved_playlist != "ranked":
return false
var saved_proposal_id := String(saved.get("proposal_id", ""))
var saved_proposal_state := String(saved.get("proposal_state", ""))
if saved_proposal_state not in ["", "OPEN", "ACCEPTED", "DECLINED", "EXPIRED", "CANCELLED"] or (not saved_proposal_state.is_empty() and saved_proposal_id.is_empty()):
return false
ticket_id = saved_ticket_id
playlist = saved_playlist
phase = saved_phase
revision = maxi(0, int(saved.get("revision", 0)))
enqueued_at_unix = maxi(0, int(saved.get("enqueued_at_unix", 0)))
expires_at_unix = maxi(0, int(saved.get("expires_at_unix", 0)))
proposal_id = String(saved.get("proposal_id", ""))
proposal_id = saved_proposal_id
proposal_revision = maxi(0, int(saved.get("proposal_revision", 0)))
proposal_state = String(saved.get("proposal_state", ""))
proposal_state = saved_proposal_state
message = "Recovering authoritative matchmaking state"
needs_resync = phase != CANCELLED and phase != EXPIRED and phase != FAILED and phase != COMPLETED
_emit_changed()