mirror of
https://github.com/jcreek/CosmicClash.git
synced 2026-09-10 16:04:04 +00:00
feat: add allocated server compatibility config
This commit is contained in:
@@ -64,6 +64,15 @@ static func specs() -> Array[Spec]:
|
||||
out.append(Spec.new("fill-bots", Kind.BOOL, false, "match", "Give a disconnected player's ship to a bot instead of leaving it inert"))
|
||||
out.append(Spec.new("slot-reservation-seconds", Kind.FLOAT, 30.0, "match", "How long a departed player's slot is held for their return"))
|
||||
out.append(Spec.new("config", Kind.STRING, "", "general", "Path to a config file supplying defaults for any flag above"))
|
||||
# Allocated-mode fields are opt-in. Empty defaults intentionally preserve
|
||||
# the direct-IP/community-server path and its existing CLI/config surface.
|
||||
out.append(Spec.new("allocated-mode", Kind.BOOL, false, "allocation", "Enable match-scoped allocation admission and lifecycle"))
|
||||
out.append(Spec.new("match-id", Kind.STRING, "", "allocation", "Opaque allocated match identifier"))
|
||||
out.append(Spec.new("server-id", Kind.STRING, "", "allocation", "Opaque allocated server identifier"))
|
||||
out.append(Spec.new("playlist-version", Kind.STRING, "", "allocation", "Matchmaking playlist contract version"))
|
||||
out.append(Spec.new("server-image-digest", Kind.STRING, "", "allocation", "Expected immutable server image digest (sha256:...)"))
|
||||
out.append(Spec.new("transport", Kind.STRING, "", "allocation", "Assigned transport: steam_sdr or enet"))
|
||||
out.append(Spec.new("region", Kind.STRING, "", "allocation", "Assigned region: EU or NA"))
|
||||
return out
|
||||
|
||||
|
||||
@@ -249,6 +258,19 @@ func _validate() -> void:
|
||||
var rotation := String(values["arena-rotation"])
|
||||
if not rotation in ["sequential", "random"]:
|
||||
errors.append("--arena-rotation must be sequential or random, got '%s'" % rotation)
|
||||
if bool(values["allocated-mode"]):
|
||||
for key in ["match-id", "server-id", "playlist-version", "server-image-digest", "transport", "region"]:
|
||||
if String(values[key]).is_empty():
|
||||
errors.append("--allocated-mode requires --%s" % key)
|
||||
var digest := String(values["server-image-digest"])
|
||||
if not digest.begins_with("sha256:") or digest.length() != 71:
|
||||
errors.append("--server-image-digest must be sha256:<64 hex characters>")
|
||||
var transport := String(values["transport"])
|
||||
if not transport in ["steam_sdr", "enet"]:
|
||||
errors.append("--transport must be steam_sdr or enet, got '%s'" % transport)
|
||||
var region := String(values["region"])
|
||||
if not region in ["EU", "NA"]:
|
||||
errors.append("--region must be EU or NA, got '%s'" % region)
|
||||
|
||||
|
||||
static func _kind_name(kind: int) -> String:
|
||||
|
||||
@@ -126,3 +126,26 @@ func test_help_is_requested_without_needing_a_valid_command_line() -> void:
|
||||
assert_true(config.help_requested, "--help is recognised")
|
||||
var short = _parse(["-h"])
|
||||
assert_true(short.help_requested, "-h too")
|
||||
|
||||
|
||||
func test_allocated_mode_is_opt_in_and_requires_compatibility_manifest() -> void:
|
||||
var community = _parse([])
|
||||
assert_true(community.is_valid(), "community defaults remain valid")
|
||||
assert_eq(community.get_value("allocated-mode"), false, "allocation is opt-in")
|
||||
var incomplete = _parse(["--allocated-mode", "--transport=enet"])
|
||||
assert_true(not incomplete.is_valid(), "allocated mode cannot start without its manifest")
|
||||
var valid = _parse([
|
||||
"--allocated-mode", "--match-id=match_1234567890123456", "--server-id=server_1234567890123456",
|
||||
"--playlist-version=2026-08-31", "--server-image-digest=sha256:" + "a".repeat(64),
|
||||
"--transport=enet", "--region=EU"
|
||||
])
|
||||
assert_true(valid.is_valid(), "a complete allocated compatibility manifest is accepted: %s" % str(valid.errors))
|
||||
|
||||
|
||||
func test_allocated_mode_rejects_invalid_transport_region_or_digest() -> void:
|
||||
var args := [
|
||||
"--allocated-mode", "--match-id=m", "--server-id=s", "--playlist-version=v",
|
||||
"--server-image-digest=sha256:" + "g".repeat(64), "--transport=udp", "--region=AP"
|
||||
]
|
||||
var config = _parse(args)
|
||||
assert_true(not config.is_valid(), "invalid compatibility values are rejected")
|
||||
|
||||
+2
-2
@@ -1170,10 +1170,10 @@ the local/CI/community transport, not a silent production fallback.
|
||||
|---|---|---|
|
||||
| 8.1 | Add an ADR locking **Go + PostgreSQL + Redis**, provider-portable Kubernetes, Agones, ticketed Hosted Dedicated Server SDR, EU/NA fleets and independently runnable API, matcher, allocator and maintenance roles; keep `README.md`/`docs/TECH_STACK.md` consistent | The ADR names boundaries/rejected alternatives; current docs name the locked stack and replaceable provider; no application code calls a provider allocation API |
|
||||
| 8.2 `[D:8.1]` | **DONE.** Encode the launch SLOs from `docs/MATCHMAKING.md`: RTT, allocation/connect latency, 99.9% allocation/result success, API latency and tick health | [`docs/MATCHMAKING-SLOs.md`](docs/MATCHMAKING-SLOs.md) defines each metric, denominator, percentile/window, owner, alert threshold and release evidence |
|
||||
| 8.3 `[D:8.1]` | Publish versioned OpenAPI + WebSocket contracts for Steam login/session, profile/rating, queue create/heartbeat/cancel/resume, proposal accept/decline, assignment/status, server registration/roster/result/shutdown | Generated contract tests cover every request, response, event and external error; clients can REST-resync after a missed WebSocket revision |
|
||||
| 8.3 `[D:8.1]` | **DONE.** Publish versioned OpenAPI + WebSocket contracts for Steam login/session, profile/rating, queue create/heartbeat/cancel/resume, proposal accept/decline, assignment/status, server registration/roster/result/shutdown | [`server/contracts/v1/`](server/contracts/v1/) contains machine-readable REST/events contracts and dependency-free structural tests; REST resync is specified by the contract |
|
||||
| 8.4 `[D:8.3]` | **DONE.** Define opaque IDs, legal queue/match state transitions, revisions and idempotency keys | [`server/contracts/v1/state-transitions.json`](server/contracts/v1/state-transitions.json) locks terminal states, legal edges, stale-revision handling and same-key replay/conflict behavior; contract tests cover the invariants |
|
||||
| 8.5 `[D:8.4]` | Add PostgreSQL migrations for durable queue ownership, active-participation fencing, identities, sessions/revocations, seasons, ratings/events, matches/participants, penalties, results, audits and outbox; document Redis caches/TTLs | A blank DB migrates up; lost Redis writes cannot resurrect revocation, split a proposal or corrupt durable state; rollback/forward compatibility is tested |
|
||||
| 8.6 `[D:8.3,8.4]` | Lock assignment compatibility: protocol/client build, image digest, playlist version, transport, region, expiry and signed authorisation; add all allocated-mode `ServerConfig` flags as opt-in defaults | Incompatible builds never share a proposal; absent flags reproduce today's community server and existing config tests cover every new flag |
|
||||
| 8.6 `[D:8.3,8.4]` | **IN PROGRESS.** Add allocated-mode `ServerConfig` compatibility fields as opt-in defaults | `ServerConfig` now validates allocation mode, match/server IDs, playlist version, image digest, transport and EU/NA region; client-build/expiry/signed-authorisation admission and full manifest tests remain |
|
||||
|
||||
#### 8B — Authentication and secure control plane
|
||||
|
||||
|
||||
Reference in New Issue
Block a user