歷史記錄 · 2026-10-28¶
重大重構里程碑¶
2026-10-28 ─ 不可變形狀以登錄參照寫入快照、bounded-step 接受容差 0.2 mm(issue 001091、001074、001077)¶
八車 SavedState 最大 4.45 MB,超過 catch-up 單包上限 4 MiB,原生快照幾乎全是場地與零件的形狀內容
(trimesh 頂點、索引、BVH、擬法線)。同代 vendor repair(父代 wasm cf640ca2)把 parry witness 的
SharedShape serde 改為兩分支:未登錄的形狀維持上游原位元組;登錄的形狀寫 TypedShape 第 20 號 variant
(批次 id u64、序號 u32,16 B)。登錄表每個 wasm instance 一張,批次內容定址、插入後不可變;每次登錄有自己的
token,最後一筆釋放時移除整批。建構期場地一批、每台車一批(同款車成為別名),批次 id 取
coStaticShapeWireBytes 的 sha256 前 64 位元,SavedState envelope 新增 staticShapeBatches,載入前核對本機
登錄全等;外來快照的形狀驗證改核登錄鍵。未登錄時快照與父代逐位元相同(canonical golden revision-19 與公版
三場八車 120 幀的 state hash、原生快照與 SavedState bytes 全等),登錄後在 0.1 mm 容差下逐幀 state hash
與父代相同、完整內嵌快照等於父代原生快照。新 wasm 85547c0e。公版三場八車 960 幀 SavedState 最大由 2.89–4.45 MB 降為
0.74–1.17 MB(4 MiB 的 17.6–27.9%),觀戰 checkpoint 最大約 1.56 MB;save P50 由 3.9–5.0 降為 2.2–3.0 ms。
SYNC_SNAPSHOT_MAX_BYTES 退回 4 MiB,衍生配額同步回到 4 MiB 推導值。
bounded-step 接受容差由 0.1 mm 改為 0.2 mm,連同半幀起跑、深度 10、試算上限 63 改為 physics-pinned protocol
常數(BOUNDED_STEP_TOLERANCE_M 等);半幀起跑的理由是動態薄目標保證只在半幀上驗證。BOUNDED_STEP_STALL_FRAMES
維持 60,依據改為新容差下最長連續細分 6–9 幀。產品 T2 與研究 T2 的三場抽樣完賽 hash 鏈相同。每幀實付
P50/P95(父代 → 產品):snowy-mixed8 28.3/55.1 → 23.1/34.2 ms、urban-heavy8 59.4/126.6 → 28.2/52.8 ms、
jungle-heavy8 51.9/122.7 → 43.1/78.7 ms;每幀試算均 2.06–3.39 → 2.04–2.27 次。
canonical golden 不變(revision-19 80b1c05a…,兩車 120 幀沒有落在 0.1–0.2 mm 的被拒試算);conformance
track-entity-snapshot 重基線 snapshotSha256 為 82a7a6a0…(state hash 與 runtime hash 不變)。碰撞閘試算拒絕判準在 0.2 mm 下重量:倍數與基線 0 規則不變,騎縫例外上限
依重量由 8 收緊為 5 並守門量測容差;研究格 C 弧牆 heavy x=−0.8 拒絕列(基線 1–5、分段 0–10)轉紅,量級判準
試行在 CI 格 A 20° 新紅而未採用,另登 issue 001098 待裁決。
2026-10-28 ─ rollback checksum 只對已確定狀態計票(issue 001092)¶
舊版在 checksum 幀(每 120 幀)步進完立刻算 state hash 並廣播,此時遠端輸入可能還是預測;之後遲到輸入觸發 回滾重播,本地票卻不重算。按技能的 peer 與其他 peer 的票因此不同,形成假 mismatch strike,連續三輪就會誤判 persistent-desync 並驅逐。三人網格重現:按技能者在該幀被另兩方預測為空,按技能者自己反而成為少數方。
改為 checksum 幀只記暫定 hash,回滾重播經過時以重播後狀態覆寫;該幀「已確定」(受理窗
current − ROLLBACK_FRAME_BUFFER_DEFAULT 之外,或窗內每幀已收齊全部 roster 真值)後才轉為本地票並廣播。
回滾目標落在 StateBuffer 窗外時暫定 hash 作廢,catch-up 載入後清除舊時間線的暫定 hash。確定最晚在
f + R + 1,仍早於裁定時點 f + 30,常數不變。確定判定只依受理窗 R 與已收真值,不依賴固定延遲;
已核准待實作的輸入延遲有界回滾(D=6、R=2)落地時規則不需改,只是確定更早。
2026-10-28 ─ DataChannel 應用層分段與每 peer 有界重組(issue 001093)¶
Chromium DataChannel 單訊息上限 262,144 B,race/control/spectator 原本都單次 send,catch-up
sync-response 與觀戰 checkpoint(公版 1–4.5 MB)在真實 RTC 上送不出去,斷線重連與觀戰會失敗。
新增 network-sync/data-channel-fragment,三條通道共用:上限取 min(SCTP 協商值, 262,144),未知時
65,536;不超過上限的訊息原樣送出。分段框 0xff|messageId|index|count|totalLength|fragmentBytes
(21 bytes 標頭),每段(末段除外)至少 16 KiB;多段訊息送出前以 bufferedAmount 做位元組流量控制,
超量整筆不送。接收端每 peer 每通道一個重組器:分段框先過以 bytes 計的既有 ingress bucket,再驗標頭;
未完成重組合計受 *_REASSEMBLY_MAX_BYTES_PER_PEER(由既有 verified queue/ingress bucket 常數推導)
限制,5 秒逾時(=catch-up 請求逾時),亂序可收、相同重複段冪等、相異重複段與標頭不一致整筆放棄。
首段到達時預檢:race 只重組已請求的 sync-response,spectator 超過一般上限者須是 checkpoint 前綴且有
checkpoint token。重組完成的完整 wire 才進既有 admission、解碼與驗簽,偽造任一段只會讓驗簽失敗。
pre_launch 直接採用新 wire,不保留舊單包格式;pinning/signaling vendor 不受影響。
新常數 9 個登錄於 程式參數/network.md §8,不變式 5 條加入
check:invariants。
2026-10-28 ─ Rapier vendor 收斂為單一目錄並去除代號命名(issue 001091)¶
主 repo 的 vendor/ 原有兩份 Rapier 固定核心(現用與一份已無引用的前一代),現用目錄內另存 25 代父代
metadata(history/)與 71 份舊代驗證證據(verification/),目錄、binding crate、版本字串與 source-kit
路徑都帶修補代號。本次收斂為單一 vendor/rapier-deterministic/:刪除無引用的前一代與舊代證據;
REPAIR-OBLIGATIONS.json 的 inherited 鏈攤平為單一代,仍有效的義務逐條保留原文,來源改記機制名稱、ADR 或
issue,各代一次性驗證步驟不再帶入。binding crate 改名 open4wd_rapier_bindings、版本 0.20.0-open4wd,
rapier3d fork 版本 0.35.3-open4wd,source-kit 目錄、檔案、模組與測試名稱去除代號,套件版本 0.20.0-open4wd。
只改命名,求解、接觸、CCD 與快照程式不變。重建後新 wasm 3a2e20b5,runtime 版本
0.20.0-open4wd+open4wd.3a2e20b5…;與前一核心 85547c0e 相比,canonical golden revision-19
80b1c05a… 在 Node/Chromium 不變,公版三場八車 120 幀兩趟(含技能輸入)逐幀 state hash、原生快照、
完整內嵌快照與 SavedState bytes 在 Node/Chromium 全等,只有內嵌版本字串與 wasm digest 不同。
RAPIER_VERSION_CURRENT(程式參數/network.md)與
testing.md 的固定核心 digest 同步更新;conformance 向量不含 runtime 版本,不變。
pinning 的 RAPIER_VERSION_CURRENT 另行同步。
2026-10-28 ─ 輸入延遲有界回滾、模擬 Worker 與效能預算三層口徑(issue 001074、001090、001040、001094、001095、001101、001102、001106)¶
改版前 60 Hz 純 rollback、回滾窗 R=8、每幀先存快照,八車每幀實付 P50 47–92 ms,任何降本組合都做不到
「回滾 8 幀在一個 tick 內完成」。改為固定輸入延遲 D=6 幀(技能在 D 幀後生效,100 ms)加有界回滾
R=2(頓挫上限 3),延遲容忍維持單向 133 ms;local-test 同樣套 D。本機在 frame c 取樣的輸入以 c+D
廣播與簽章,frame 0 到 D−1 為隱含空輸入幀;ROLLBACK_FRAME_BUFFER_MIN 刪除,新增
INPUT_DELAY_FRAMES、SKILL_INPUT_DELAY_MAX_MS、NETWORK_ONE_WAY_LATENCY_TOLERANCE_MS 與五條不變式。
StateBuffer 改為前緣快照:只在對齊幀與含預測的幀存檔,回滾載入 ≤ 目標幀的最近快照、重播 ≤ R;
單向延遲 D+2 幀時實測每 240 幀約 255 次 save。引擎依幀序發出已確定步進串流,回合錨、觀戰 checkpoint
與回合終局只吃已確定記錄(預測中的衝線或時限不結束回合);catch-up 重置時補記載入幀的錨。
catch-up 的 sync-response 改帶快照加 provider 的簽章輸入日誌(每 peer F 前延續基準與 [F, front) 全部
真值、逐筆原簽章),恢復端重播並追到 front+RTT/2,之後輸入自 T+D 恢復;offer 預留最壞日誌 617,385 B,
候選為最多兩個已 final 對齊快照。八人 mesh(單向延遲 1–4 幀)對齊後 1800 幀 checksum 八票逐位元一致;
「[F, F+D) 一律空輸入」在 live 已超過 F+D+R 的情境出現反例,不採用。單向延遲 1–8 幀時 12 個 seed 有 7 個
在 catch-up 後分歧,與單一 provider 日誌無背書、最壞追趕卡頓分別登 issue 001104、001107、001108。
賽中持續落後改為自我驅逐 lag-evicted:嚴格過半且至少兩名遠端 lead > R 連續 300 幀,兩人場不觸發,
probe 期間延後到 probe 失敗才驅逐,不自報 DesyncEvent,結束畫面提示可觀戰。觀戰 replay 失配允許連續
重求 checkpoint 3 次,第 4 次降級。
比賽、local-test 與觀戰 replay 的模擬移入單一模擬 Worker(Rapier、rollback、固定 timestep 以
performance.now() 驅動);DataChannel、簽章驗簽、渲染與 GLB fetch 留主執行緒,GLB 以私有複本
Transferable 傳入,Worker 內 Draco 走生成的 ESM 包裝。Chromium 與 Firefox 的 Worker 內 WASM 編譯與
120 幀 hash 與主執行緒逐位元相同;Chromium 背景分頁不節流,Worker 不繼承 meta CSP。
效能預算改為三層口徑:L0 每幀實付 P95 ≤ T÷2、L1 P50 ≤ T÷2、L2 最深回滾事件 P95 ≤ 3T,常數放
network/perf;未達 L0 照常參賽,正式比賽中本機滾動 600 幀 P95 超過一個 tick 時顯示效能警告。
perf-baseline.json 改為 race-frame-cost-8players 與 rollback-worst-event(provisional,參考機重建前只警告)。