mirror of
https://github.com/jcreek/CosmicClash.git
synced 2026-09-14 21:32:05 +00:00
fix(multiplayer): align websocket assignment ids
This commit is contained in:
@@ -1591,6 +1591,8 @@ Ranked profile projection now enforces the OpenAPI tier enum, rejecting unknown
|
|||||||
|
|
||||||
Assignment projections and assignment-changed events now enforce the published opaque-ID shape for match, server, and player identifiers; short or unsafe IDs fail closed.
|
Assignment projections and assignment-changed events now enforce the published opaque-ID shape for match, server, and player identifiers; short or unsafe IDs fail closed.
|
||||||
|
|
||||||
|
The WebSocket contract and Go event hub now enforce opaque match and server IDs on assignment notifications, keeping server publication aligned with the Godot client validator.
|
||||||
|
|
||||||
Signed MatchNet claims now also require exact JSON string/integer types for every identity, protocol, expiry, slot, team, and generation field; string-number coercion is rejected before canonical signature verification.
|
Signed MatchNet claims now also require exact JSON string/integer types for every identity, protocol, expiry, slot, team, and generation field; string-number coercion is rejected before canonical signature verification.
|
||||||
|
|
||||||
Presentation progress: a shared `Game/themes/cosmic_clash_theme.tres` now gives the menu, lobby, matchmaking, and settings surfaces consistent button, input, option, and label styling. The custom-font portion of `TODO.md` remains open until a distributable font asset is selected.
|
Presentation progress: a shared `Game/themes/cosmic_clash_theme.tres` now gives the menu, lobby, matchmaking, and settings surfaces consistent button, input, option, and label styling. The custom-font portion of `TODO.md` remains open until a distributable font asset is selected.
|
||||||
|
|||||||
@@ -131,7 +131,7 @@ func validateControlPlaneEvent(event ControlPlaneEvent) error {
|
|||||||
return errors.New("invalid proposal-changed event")
|
return errors.New("invalid proposal-changed event")
|
||||||
}
|
}
|
||||||
case "assignment_changed":
|
case "assignment_changed":
|
||||||
if event.MatchID == "" || event.ServerID == "" {
|
if !controlPlaneResourceIDRE.MatchString(event.MatchID) || !controlPlaneResourceIDRE.MatchString(event.ServerID) {
|
||||||
return errors.New("invalid assignment-changed event")
|
return errors.New("invalid assignment-changed event")
|
||||||
}
|
}
|
||||||
case "error":
|
case "error":
|
||||||
|
|||||||
@@ -437,6 +437,8 @@ func TestEventHubRejectsEventsOutsideTheV1Vocabulary(t *testing.T) {
|
|||||||
{Event: "state_changed", State: "NOT_A_STATE", Revision: base.Revision, ResourceID: base.ResourceID, OccurredAt: base.OccurredAt, PlayerID: base.PlayerID},
|
{Event: "state_changed", State: "NOT_A_STATE", Revision: base.Revision, ResourceID: base.ResourceID, OccurredAt: base.OccurredAt, PlayerID: base.PlayerID},
|
||||||
{Event: "proposal_changed", State: "LIVE", Revision: base.Revision, ResourceID: base.ResourceID, OccurredAt: base.OccurredAt, PlayerID: base.PlayerID},
|
{Event: "proposal_changed", State: "LIVE", Revision: base.Revision, ResourceID: base.ResourceID, OccurredAt: base.OccurredAt, PlayerID: base.PlayerID},
|
||||||
{Event: "assignment_changed", Revision: base.Revision, ResourceID: base.ResourceID, OccurredAt: base.OccurredAt, PlayerID: base.PlayerID},
|
{Event: "assignment_changed", Revision: base.Revision, ResourceID: base.ResourceID, OccurredAt: base.OccurredAt, PlayerID: base.PlayerID},
|
||||||
|
{Event: "assignment_changed", MatchID: "match-1", ServerID: "server-1", Revision: base.Revision, ResourceID: base.ResourceID, OccurredAt: base.OccurredAt, PlayerID: base.PlayerID},
|
||||||
|
{Event: "assignment_changed", MatchID: "match_1234567890", ServerID: "server/unsafe", Revision: base.Revision, ResourceID: base.ResourceID, OccurredAt: base.OccurredAt, PlayerID: base.PlayerID},
|
||||||
{Event: "error", Code: "SECRET_LEAK", Revision: base.Revision, ResourceID: base.ResourceID, OccurredAt: base.OccurredAt, PlayerID: base.PlayerID},
|
{Event: "error", Code: "SECRET_LEAK", Revision: base.Revision, ResourceID: base.ResourceID, OccurredAt: base.OccurredAt, PlayerID: base.PlayerID},
|
||||||
}
|
}
|
||||||
for _, event := range invalid {
|
for _, event := range invalid {
|
||||||
|
|||||||
@@ -9,10 +9,11 @@
|
|||||||
{"$ref": "#/$defs/error"}
|
{"$ref": "#/$defs/error"}
|
||||||
],
|
],
|
||||||
"$defs": {
|
"$defs": {
|
||||||
"envelope": {"type": "object", "required": ["event", "revision", "resource_id", "occurred_at"], "properties": {"revision": {"type": "integer", "minimum": 0}, "resource_id": {"type": "string", "pattern": "^[A-Za-z0-9_-]{16,128}$"}, "occurred_at": {"type": "string", "format": "date-time"}}},
|
"opaqueId": {"type": "string", "pattern": "^[A-Za-z0-9_-]{16,128}$"},
|
||||||
|
"envelope": {"type": "object", "required": ["event", "revision", "resource_id", "occurred_at"], "properties": {"revision": {"type": "integer", "minimum": 0}, "resource_id": {"$ref": "#/$defs/opaqueId"}, "occurred_at": {"type": "string", "format": "date-time"}}},
|
||||||
"stateChanged": {"allOf": [{"$ref": "#/$defs/envelope"}, {"type": "object", "properties": {"event": {"const": "state_changed"}, "state": {"type": "string", "enum": ["QUEUED", "PROPOSED", "ACCEPTED", "ALLOCATING", "PROCESS_READY", "ASSIGNMENT_READY", "ASSIGNED", "CONNECTING", "LIVE", "RESULT_PENDING", "COMPLETED", "CANCELLED", "EXPIRED", "FAILED"]}}, "required": ["event", "state"]}]},
|
"stateChanged": {"allOf": [{"$ref": "#/$defs/envelope"}, {"type": "object", "properties": {"event": {"const": "state_changed"}, "state": {"type": "string", "enum": ["QUEUED", "PROPOSED", "ACCEPTED", "ALLOCATING", "PROCESS_READY", "ASSIGNMENT_READY", "ASSIGNED", "CONNECTING", "LIVE", "RESULT_PENDING", "COMPLETED", "CANCELLED", "EXPIRED", "FAILED"]}}, "required": ["event", "state"]}]},
|
||||||
"proposalChanged": {"allOf": [{"$ref": "#/$defs/envelope"}, {"type": "object", "properties": {"event": {"const": "proposal_changed"}, "state": {"type": "string", "enum": ["OPEN", "ACCEPTED", "DECLINED", "EXPIRED", "CANCELLED"]}}, "required": ["event", "state"]}]},
|
"proposalChanged": {"allOf": [{"$ref": "#/$defs/envelope"}, {"type": "object", "properties": {"event": {"const": "proposal_changed"}, "state": {"type": "string", "enum": ["OPEN", "ACCEPTED", "DECLINED", "EXPIRED", "CANCELLED"]}}, "required": ["event", "state"]}]},
|
||||||
"assignmentChanged": {"allOf": [{"$ref": "#/$defs/envelope"}, {"type": "object", "properties": {"event": {"const": "assignment_changed"}, "match_id": {"type": "string"}, "server_id": {"type": "string"}}, "required": ["event", "match_id", "server_id"]}]},
|
"assignmentChanged": {"allOf": [{"$ref": "#/$defs/envelope"}, {"type": "object", "properties": {"event": {"const": "assignment_changed"}, "match_id": {"$ref": "#/$defs/opaqueId"}, "server_id": {"$ref": "#/$defs/opaqueId"}}, "required": ["event", "match_id", "server_id"]}]},
|
||||||
"error": {"allOf": [{"$ref": "#/$defs/envelope"}, {"type": "object", "properties": {"event": {"const": "error"}, "code": {"type": "string", "enum": ["REVISION_GAP", "NOT_AUTHORISED", "INVALID_STATE", "RATE_LIMITED"]}}, "required": ["event", "code"]}]}
|
"error": {"allOf": [{"$ref": "#/$defs/envelope"}, {"type": "object", "properties": {"event": {"const": "error"}, "code": {"type": "string", "enum": ["REVISION_GAP", "NOT_AUTHORISED", "INVALID_STATE", "RATE_LIMITED"]}}, "required": ["event", "code"]}]}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user