mirror of
https://github.com/jcreek/CosmicClash.git
synced 2026-09-15 00:32:03 +00:00
feat: consume assignment matchmaking events
This commit is contained in:
@@ -30,6 +30,7 @@ var _websocket: WebSocketPeer
|
||||
var _websocket_status := "DISCONNECTED"
|
||||
var _websocket_retry_seconds := 0.0
|
||||
var _websocket_backoff := 1.0
|
||||
var _pending_assignment_match_id := ""
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
@@ -67,6 +68,10 @@ func _process(_delta: float) -> void:
|
||||
_websocket_retry_seconds = _websocket_backoff
|
||||
_websocket_backoff = minf(_websocket_backoff * 2.0, 30.0)
|
||||
connect_event_stream()
|
||||
if not _pending_assignment_match_id.is_empty() and _operation.is_empty() and not player_id.is_empty():
|
||||
var match_id := _pending_assignment_match_id
|
||||
_pending_assignment_match_id = ""
|
||||
fetch_assignment(match_id)
|
||||
|
||||
|
||||
func configure(url: String, token: String) -> bool:
|
||||
@@ -324,6 +329,12 @@ func _handle_websocket_packet(packet: PackedByteArray) -> void:
|
||||
var proposal_update := event.duplicate(true)
|
||||
proposal_update["proposal_id"] = String(event["resource_id"])
|
||||
state.apply_proposal_update(proposal_update)
|
||||
elif event_name == "assignment_changed":
|
||||
state.mark_assignment_ready()
|
||||
_pending_assignment_match_id = String(event["match_id"])
|
||||
elif event_name == "error":
|
||||
state.set_notice("Control-plane error: %s" % String(event["code"]))
|
||||
_on_resync_required(String(event["resource_id"]))
|
||||
|
||||
|
||||
func _valid_websocket_event(event: Dictionary) -> bool:
|
||||
@@ -336,7 +347,15 @@ func _valid_websocket_event(event: Dictionary) -> bool:
|
||||
if not event.has("occurred_at") or not event["occurred_at"] is String or String(event["occurred_at"]).is_empty():
|
||||
return false
|
||||
var event_name := String(event["event"])
|
||||
return event_name in ["state_changed", "proposal_changed", "assignment_changed", "error"]
|
||||
if event_name == "assignment_changed":
|
||||
return event.has("match_id") and event["match_id"] is String and not String(event["match_id"]).is_empty() and event.has("server_id") and event["server_id"] is String and not String(event["server_id"]).is_empty()
|
||||
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 event_name == "proposal_changed":
|
||||
return event.has("state") and String(event["state"]) in ["OPEN", "ACCEPTED", "DECLINED", "EXPIRED", "CANCELLED"]
|
||||
return false
|
||||
|
||||
|
||||
func _on_resync_required(resource_id: String) -> void:
|
||||
|
||||
@@ -38,6 +38,18 @@ func test_ticket_normalization_preserves_payload_and_derives_expiry() -> void:
|
||||
assert_true(not payload.has("expires_at_unix"), "normalization does not mutate the HTTP payload")
|
||||
|
||||
|
||||
func test_websocket_event_validation_requires_contract_specific_fields() -> void:
|
||||
var envelope := {"event": "state_changed", "revision": 1, "resource_id": "ticket-1", "occurred_at": "2026-08-31T12:00:00Z", "state": "QUEUED"}
|
||||
assert_true(ControlPlaneClient._valid_websocket_event(envelope), "valid state event is accepted")
|
||||
var bad_state := envelope.duplicate()
|
||||
bad_state["state"] = "SECRET"
|
||||
assert_true(not ControlPlaneClient._valid_websocket_event(bad_state), "unknown state event is rejected")
|
||||
var assignment := {"event": "assignment_changed", "revision": 0, "resource_id": "match-1", "occurred_at": "2026-08-31T12:00:00Z", "match_id": "match-1", "server_id": "server-1"}
|
||||
assert_true(ControlPlaneClient._valid_websocket_event(assignment), "complete assignment event is accepted")
|
||||
assignment.erase("server_id")
|
||||
assert_true(not ControlPlaneClient._valid_websocket_event(assignment), "incomplete assignment event is rejected")
|
||||
|
||||
|
||||
func test_ranked_profile_is_backend_display_data_and_rejects_unsafe_values() -> void:
|
||||
var profile := RankedProfileState.new()
|
||||
assert_true(profile.apply({"rating": 1500.0, "rd": 200.0, "volatility": 0.06, "ranked_games": 3, "tier": "GOLD", "provisional": true, "season_id": "s1"}), "valid profile applies")
|
||||
|
||||
Reference in New Issue
Block a user