mirror of
https://github.com/jcreek/CosmicClash.git
synced 2026-09-13 22:12:03 +00:00
fix(multiplayer): recover assignment handoff
This commit is contained in:
@@ -493,7 +493,8 @@ func _on_request_completed(result: HTTPRequest.Result, response_code: int, _head
|
||||
request_failed.emit(operation, response_code, "invalid proposal identifier")
|
||||
return
|
||||
if operation.begins_with("queue_"):
|
||||
state.apply_ticket_update(normalize_ticket(payload))
|
||||
if state.apply_ticket_update(normalize_ticket(payload), operation == "queue_recover"):
|
||||
_queue_assignment_if_ready(payload)
|
||||
elif operation.begins_with("proposal_"):
|
||||
state.apply_proposal_update(normalize_proposal(payload))
|
||||
elif operation == "ranked_profile":
|
||||
@@ -518,6 +519,18 @@ func _handle_websocket_packet(packet: PackedByteArray) -> void:
|
||||
websocket_event.emit(event)
|
||||
var event_name := String(event["event"])
|
||||
if event_name == "state_changed":
|
||||
# Allocation and match lifecycle rows are keyed by match ID, not ticket
|
||||
# ID. Recover the owner-scoped ticket projection instead of feeding the
|
||||
# match revision/resource into the ticket reducer. ASSIGNMENT_READY also
|
||||
# carries the durable lookup key, so the assignment fetch can follow the
|
||||
# recovery request without depending on a circular assignment_changed
|
||||
# notification from the assignment GET itself.
|
||||
if event.has("match_id"):
|
||||
var match_id := String(event["match_id"])
|
||||
_on_resync_required(state.ticket_id)
|
||||
if String(event["state"]) == "ASSIGNMENT_READY":
|
||||
_pending_assignment_match_id = match_id
|
||||
return
|
||||
var update := event.duplicate(true)
|
||||
update["ticket_id"] = String(event["resource_id"])
|
||||
if not state.apply_ticket_update(update):
|
||||
@@ -549,7 +562,11 @@ static func _valid_websocket_event(event: Dictionary) -> bool:
|
||||
if event_name == "error":
|
||||
return event.has("code") and String(event["code"]) in ["REVISION_GAP", "NOT_AUTHORISED", "INVALID_STATE", "RATE_LIMITED"]
|
||||
if event_name == "state_changed":
|
||||
return event.has("state") and String(event["state"]) in ["QUEUED", "PROPOSED", "ACCEPTED", "ALLOCATING", "PROCESS_READY", "ASSIGNMENT_READY", "ASSIGNED", "CONNECTING", "LIVE", "RESULT_PENDING", "COMPLETED", "CANCELLED", "EXPIRED", "FAILED"]
|
||||
if not event.has("state") or String(event["state"]) not in ["QUEUED", "PROPOSED", "ACCEPTED", "ALLOCATING", "PROCESS_READY", "ASSIGNMENT_READY", "ASSIGNED", "CONNECTING", "LIVE", "RESULT_PENDING", "COMPLETED", "CANCELLED", "EXPIRED", "FAILED"]:
|
||||
return false
|
||||
if event.has("match_id"):
|
||||
return event["match_id"] is String and is_valid_resource_id(String(event["match_id"])) and String(event["match_id"]) == String(event["resource_id"])
|
||||
return true
|
||||
if event_name == "proposal_changed":
|
||||
return event.has("state") and String(event["state"]) in ["OPEN", "ACCEPTED", "DECLINED", "EXPIRED", "CANCELLED"]
|
||||
return false
|
||||
@@ -577,11 +594,24 @@ static func _valid_queue_response(payload: Dictionary) -> bool:
|
||||
return false
|
||||
if not payload["state"] is String or not String(payload["state"]) in ["QUEUED", "PROPOSED", "ACCEPTED", "ALLOCATING", "PROCESS_READY", "ASSIGNMENT_READY", "ASSIGNED", "CONNECTING", "LIVE", "RESULT_PENDING", "COMPLETED", "CANCELLED", "EXPIRED", "FAILED"]:
|
||||
return false
|
||||
if payload.has("match_id"):
|
||||
if not payload["match_id"] is String or not is_valid_resource_id(String(payload["match_id"])):
|
||||
return false
|
||||
if String(payload["state"]) not in ["ALLOCATING", "PROCESS_READY", "ASSIGNMENT_READY", "ASSIGNED", "CONNECTING", "LIVE", "RESULT_PENDING", "COMPLETED", "FAILED", "CANCELLED"]:
|
||||
return false
|
||||
if not _valid_revision(payload["revision"]):
|
||||
return false
|
||||
return payload["enqueued_at"] is String and is_valid_rfc3339_timestamp(String(payload["enqueued_at"])) and payload["expires_at"] is String and is_valid_rfc3339_timestamp(String(payload["expires_at"]))
|
||||
|
||||
|
||||
func _queue_assignment_if_ready(payload: Dictionary) -> void:
|
||||
if String(payload.get("state", "")) != "ASSIGNMENT_READY":
|
||||
return
|
||||
var match_id := String(payload.get("match_id", ""))
|
||||
if is_valid_resource_id(match_id):
|
||||
_pending_assignment_match_id = match_id
|
||||
|
||||
|
||||
static func _valid_proposal_response(payload: Dictionary) -> bool:
|
||||
if not _valid_response_opaque_id(payload, "proposal_id") or not payload.has("expires_at") or not payload["expires_at"] is String or not is_valid_rfc3339_timestamp(String(payload["expires_at"])) or not payload.has("participants") or not payload["participants"] is Array:
|
||||
return false
|
||||
|
||||
Reference in New Issue
Block a user