mirror of
https://github.com/jcreek/CosmicClash.git
synced 2026-09-10 16:04:04 +00:00
docs(multiplayer): split spec into MULTIPLAYER_SPEC.md, trim task doc to outstanding work
multiplayer-next.md was a 1662-line mix of standing architecture spec and task-completion tracking, most of which was dense per-task DONE evidence for finished Phases 0-6. Split it: - MULTIPLAYER_SPEC.md (new): the locked architecture decisions, wire format, server-side input handling, prediction/reconciliation, latency/frame-rate budget, and match lifecycle state machine - standing design reference, not task-tracked. - multiplayer-next.md (trimmed 1662 -> ~370 lines): only outstanding work remains - §0 status, §7 Phase 7/8 task tables condensed to "what's left" per task, §8-11 reference material (refactoring notes, gotchas, testing, flagged items). Phases 0-6 collapsed to a pointer at git history instead of ~500 lines of DONE evidence. Also: - Repointed every `multiplayer-next.md §N` code comment (N 1-6) across Game/scripts, Game/tools and Game/tests to MULTIPLAYER_SPEC.md, since those sections moved. Task-number references (`task N.N`, §7-11) correctly still point at multiplayer-next.md. - Updated CLAUDE.md's doc index and docs/TECH_STACK.md's spec-section citations to match. - TODO.md: added a "what's left to actually finish multiplayer (human-actionable)" checklist pulled from multiplayer-next.md §0 and docs/MATCHMAKING.md - things that need a person (hardware, a design decision, a Steam App ID, hands on a controller), not more agent code.
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
class_name InputJitterBuffer
|
||||
extends RefCounted
|
||||
|
||||
# Per-player server-side input state (multiplayer-next.md §3, task 3.2).
|
||||
# Per-player server-side input state (MULTIPLAYER_SPEC.md §3; multiplayer-next.md task 3.2).
|
||||
# Deliberately a standalone RefCounted with no scene/RPC dependency — same
|
||||
# reason net_codec.gd and net_interpolator.gd are pure classes — so task
|
||||
# 3.5's unit tests can drive it with scripted arrival traces with no live
|
||||
@@ -18,7 +18,7 @@ extends RefCounted
|
||||
# class's, since only the caller knows the current server tick.
|
||||
|
||||
const RING_SIZE := 32
|
||||
# 500ms at 60Hz (multiplayer-next.md §3.2's own numbers) — a duration, not a
|
||||
# 500ms at 60Hz (MULTIPLAYER_SPEC.md §3.2's own numbers) — a duration, not a
|
||||
# tick-rate-derived constant, so left as a literal rather than pulling in
|
||||
# SimConstants for one number.
|
||||
const STARVE_ZERO_TICKS := 30
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
class_name InputLeadController
|
||||
extends RefCounted
|
||||
|
||||
# Client-owned input_lead control loop (multiplayer-next.md §3.3, task 3.3).
|
||||
# Client-owned input_lead control loop (MULTIPLAYER_SPEC.md §3.3; multiplayer-next.md task 3.3).
|
||||
# Standalone RefCounted, same reason as input_jitter_buffer.gd — scene-free
|
||||
# so it's directly unit-testable against scripted depth traces.
|
||||
#
|
||||
|
||||
@@ -3,7 +3,7 @@ extends RefCounted
|
||||
|
||||
const NetBodyState = preload("res://scripts/net_body_state.gd")
|
||||
|
||||
# Client-owned local-ship prediction history (multiplayer-next.md §4.3).
|
||||
# Client-owned local-ship prediction history (MULTIPLAYER_SPEC.md §4.3).
|
||||
# This is deliberately independent of NetworkedMatch and the scene tree so
|
||||
# sequence/ring behaviour can be tested from scripted traces. Each entry is
|
||||
# tagged with its full sequence number: an old value in a wrapped slot is
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
extends Node
|
||||
|
||||
# Autoload (project.godot [autoload] MatchNet). Handshake + roster layer on
|
||||
# top of NetworkManager's raw transport (§2.5, §1.3 of multiplayer-next.md).
|
||||
# top of NetworkManager's raw transport (§2.5, §1.3 of MULTIPLAYER_SPEC.md).
|
||||
# hello/welcome, strict protocol_version and physics_ticks_per_second
|
||||
# gating, player_joined/player_left, and — since lobby.tscn (task 1.5) needs
|
||||
# somewhere durable to keep it across the lobby→match scene transition —
|
||||
|
||||
@@ -41,7 +41,7 @@ signal match_bootstrap_received(state: int, at_tick: int, score: Dictionary, end
|
||||
# §6.3 task 5.8: a spectator has been given a vacated slot at a kickoff.
|
||||
signal slot_assigned_received(peer_id: int, slot_index: int)
|
||||
|
||||
# Input validation (multiplayer-next.md §3.1 steps 2-3, task 3.4). Deliberately
|
||||
# Input validation (MULTIPLAYER_SPEC.md §3.1 steps 2-3; multiplayer-next.md task 3.4). Deliberately
|
||||
# lives here rather than in NetworkedMatch: framing/rate abuse is a protocol-
|
||||
# level concern independent of any particular match's roster/slot state, and
|
||||
# this autoload already owns the RPC that receives the raw bytes.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
class_name MatchState
|
||||
|
||||
# Match lifecycle states (multiplayer-next.md §6.1, task 5.1).
|
||||
# Match lifecycle states (MULTIPLAYER_SPEC.md §6.1; multiplayer-next.md task 5.1).
|
||||
#
|
||||
# Pure data + a transition table, deliberately with no scene, RPC or
|
||||
# NetworkedMatch dependency — same reason net_codec.gd and
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
extends RefCounted
|
||||
|
||||
# Plain data holder for one body's snapshot state (§2.4 of multiplayer-next.md).
|
||||
# Plain data holder for one body's snapshot state (§2.4 of MULTIPLAYER_SPEC.md).
|
||||
# Deliberately not Ship/Ball themselves, and deliberately not a scene-tree
|
||||
# node — NetCodec's pack/unpack must stay callable from pure-function tests
|
||||
# with no live scene. Phase 2's snapshot writer fills one of these per body
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
class_name NetCodec
|
||||
|
||||
# Wire-format constants, quantisers, and pack/unpack for the two hot-path
|
||||
# packets (§2 of multiplayer-next.md). Pure functions only — no networking,
|
||||
# packets (§2 of MULTIPLAYER_SPEC.md). Pure functions only — no networking,
|
||||
# no autoload state — so they're testable head-on by tests/test_runner.tscn
|
||||
# without a live connection.
|
||||
#
|
||||
@@ -48,7 +48,7 @@ const BODY_FLAG_STALLED := 1 << 5
|
||||
const BODY_FLAG_QUAT_W_SIGN := 1 << 6
|
||||
|
||||
# --- Quantisation ranges (§2.4 — derived from arena/gameplay constants, not
|
||||
# restated prose; see multiplayer-next.md for the ArenaBoundary/Ship/Ball
|
||||
# restated prose; see MULTIPLAYER_SPEC.md for the ArenaBoundary/Ship/Ball
|
||||
# constants these are sized against) ---
|
||||
const POS_RANGE := 64.0 # metres, ±
|
||||
const VEL_RANGE := 64.0 # m/s, ±
|
||||
|
||||
@@ -3,7 +3,7 @@ extends RefCounted
|
||||
|
||||
# Buffers recent snapshot samples for ONE remote body and produces
|
||||
# interpolated states at any requested (possibly fractional) server tick —
|
||||
# used twice per body (multiplayer-next.md §4.1/§4.6, "dual-time remote
|
||||
# used twice per body (MULTIPLAYER_SPEC.md §4.1/§4.6, "dual-time remote
|
||||
# entities"): once at the present-time estimate for the collider, once
|
||||
# further back at present-minus-INTERP_DELAY for $Visual.
|
||||
#
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
extends RefCounted
|
||||
|
||||
# Local-ship reconciliation policy (multiplayer-next.md §4.4). Kept out of
|
||||
# Local-ship reconciliation policy (MULTIPLAYER_SPEC.md §4.4). Kept out of
|
||||
# NetworkedMatch so the decision table is pure-testable; the imperative half
|
||||
# only writes Ship's existing Jolt-safe queued correction hooks.
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ extends Node
|
||||
# Autoload (project.godot [autoload] NetworkManager). Owns transport-neutral
|
||||
# hosting, joining, shutdown, and connection-state signals. Lives
|
||||
# at a fixed autoload path so RPC NodePaths never depend on which scene is
|
||||
# loaded (§1.3 of multiplayer-next.md's derived decisions).
|
||||
# loaded (§1.3 of MULTIPLAYER_SPEC.md's derived decisions).
|
||||
#
|
||||
# server_relay = false is set the moment a peer exists: the default `true`
|
||||
# lets any client rpc() any other client *through the server*, which this
|
||||
|
||||
@@ -145,7 +145,7 @@ func queue_teleport_with_velocity(to: Transform3D, new_linear_velocity: Vector3,
|
||||
_has_pending_teleport = true
|
||||
|
||||
|
||||
# --- Netcode correction hooks (Phase 4; see multiplayer-next.md §4.4) ---
|
||||
# --- Netcode correction hooks (Phase 4; see MULTIPLAYER_SPEC.md §4.4) ---
|
||||
# Both stay zero until Phase 4 wires a reconciliation pass in, so the guarded
|
||||
# hook in _integrate_forces below is a no-op today.
|
||||
# Velocity delta from a soft correction, consumed once then cleared —
|
||||
|
||||
@@ -4,7 +4,7 @@ class_name SimConstants
|
||||
# constant derived from "60 Hz" (Ship._tick_scaled's decay reference,
|
||||
# reaction_ticks' export range, TrainingMode.TICKS_PER_SIM_SECOND) reads this
|
||||
# instead of restating the literal, so changing it changes every derived
|
||||
# constant coherently — see multiplayer-next.md §5.6 on why a future 120 Hz
|
||||
# constant coherently — see MULTIPLAYER_SPEC.md §5.6 on why a future 120 Hz
|
||||
# simulation needs to be a config change plus a retrain, not a protocol
|
||||
# rewrite hunting down bare 60s.
|
||||
#
|
||||
|
||||
@@ -24,7 +24,7 @@ extends Node
|
||||
# independently of stretch mode, since it scales the 3D viewport's own internal
|
||||
# resolution before this blit rather than the window itself. Task 0.15b also
|
||||
# found an unexplained ~6% non-uniform width scaling on this project's one
|
||||
# tested (Mac/Retina) machine — see multiplayer-next.md §5.5.1 — which needs
|
||||
# tested (Mac/Retina) machine — see MULTIPLAYER_SPEC.md §5.5.1 — which needs
|
||||
# understanding before stretch mode is touched, not blindly carrying into a
|
||||
# resolution-dependent change.
|
||||
#
|
||||
@@ -49,7 +49,7 @@ const SETTINGS_PATH := "user://settings.cfg"
|
||||
# preset -> bundle applied to the individual fields below. CUSTOM has no
|
||||
# bundle: selecting it just stops future preset changes from overwriting
|
||||
# whatever the individual fields currently hold. Task 0.15b's measured
|
||||
# per-effect costs (multiplayer-next.md §5.5.1) were too noisy to rank these
|
||||
# per-effect costs (MULTIPLAYER_SPEC.md §5.5.1) were too noisy to rank these
|
||||
# against each other, so each rung is "meaningfully fewer full-screen passes
|
||||
# than the one above it" rather than a precisely tuned ladder.
|
||||
const PRESET_BUNDLES := {
|
||||
@@ -252,7 +252,7 @@ func apply_fps_cap() -> void:
|
||||
# Called once by each arena's _ready() (and again on settings_changed, so an
|
||||
# already-loaded arena updates live) to fold the user's glow/brightness
|
||||
# preference into that arena's own baked Environment tuning, and to gate the
|
||||
# preset-controlled full-screen passes (§5.5 of multiplayer-next.md).
|
||||
# preset-controlled full-screen passes (§5.5 of MULTIPLAYER_SPEC.md).
|
||||
func apply_to_environment(env: Environment) -> void:
|
||||
if env == null:
|
||||
return
|
||||
|
||||
Reference in New Issue
Block a user