Builds the browser game around the existing Rust runtime: a window into the universe, not a second simulation. Pure std, no external crates. New crates: - protocol: versioned, hashable client/server messages + hand-rolled JSON value and total parser (malformed packet -> Err, never panic). - game_runtime: authoritative match state. Resolves turns through the INDEPENDENT interpreter (runtime_under_test::native_resolve), not the reference engine; filters visibility/knowledge; records and regenerates replays. A match is a pure function of (seed, roster, ordered inputs). - web_assets/web_client: embedded browser client (arena, rune editor, knowledge panels, replay viewer) + static HTTP delivery. - server: std::net HTTP + WebSocket (hand-rolled SHA-1/base64/RFC-6455 framing), turn timer, disconnect handling, panic-proof dispatch, poison-tolerant lock. - web_tests: dependency-free WebSocket test client + Phase H gates. Trust hardening per review: - game_runtime no longer delegates to reference_runtime::execute; it runs the independent interpreter that the runtime-equivalence gate proves correct. - Protocol/socket/replay/visibility/resilience gates are merge-blocking (added to the merge_group-required job in merge-gates.yml): 1k matches/0 drift, 10k fuzz/0 panics, 100 headless socket E2E, 0 hidden-state leaks. - Rendered-browser E2E is marked EXTERNAL-BLOCKED: Playwright runs advisory-only (continue-on-error, artifacts) until CI infrastructure with a browser exists; it is treated as unsatisfied, not green. The headless 100-match gate is labeled protocol-level coverage, not rendered-browser coverage. - README documents the hand-rolled crypto/parser audit risk explicitly. Fixes an integer-overflow panic in observed-volatility inference (i64 sum / abs near i64::MIN) that could poison the server mutex. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
83 lines
2.8 KiB
HTML
83 lines
2.8 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="utf-8" />
|
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
<title>Magicka VM — playable window</title>
|
|
<link rel="stylesheet" href="/style.css" />
|
|
</head>
|
|
<body>
|
|
<header>
|
|
<h1>Magicka VM</h1>
|
|
<div id="conn" class="badge off">disconnected</div>
|
|
<div id="timer" class="timer">turn —</div>
|
|
</header>
|
|
|
|
<main>
|
|
<section id="arena-panel" class="panel">
|
|
<h2>Arena</h2>
|
|
<div id="arena" class="arena" aria-label="arena grid"></div>
|
|
<div class="actions">
|
|
<div class="dpad">
|
|
<button data-move="0,-1">↑</button>
|
|
<div class="dpad-row">
|
|
<button data-move="-1,0">←</button>
|
|
<button data-move="0,1">↓</button>
|
|
<button data-move="1,0">→</button>
|
|
</div>
|
|
</div>
|
|
<div class="action-buttons">
|
|
<button id="btn-cast">Cast</button>
|
|
<button id="btn-attack">Attack</button>
|
|
<button id="btn-inspect">Inspect</button>
|
|
<button id="btn-wait">Wait</button>
|
|
</div>
|
|
</div>
|
|
<p class="hint">Select a target entity (click it), then Attack/Inspect. Cast uses your current program.</p>
|
|
</section>
|
|
|
|
<section id="editor-panel" class="panel">
|
|
<h2>Rune editor</h2>
|
|
<div id="library" class="library"></div>
|
|
<div id="tokens" class="tokens"></div>
|
|
<div class="editor-controls">
|
|
<select id="op-select"></select>
|
|
<label>a<input id="tok-a" type="number" value="0" min="0" max="255" /></label>
|
|
<label>b<input id="tok-b" type="number" value="0" min="0" max="255" /></label>
|
|
<label>c<input id="tok-c" type="number" value="0" min="0" max="255" /></label>
|
|
<label>imm<input id="tok-imm" type="number" value="0" /></label>
|
|
<button id="btn-add">Add rune</button>
|
|
<button id="btn-clear">Clear</button>
|
|
<button id="btn-save">Save program</button>
|
|
</div>
|
|
<div id="diagnostics" class="diagnostics">
|
|
<h3>Observed diagnostics</h3>
|
|
<div id="diag-body">cast or save a program to preview observed diagnostics</div>
|
|
</div>
|
|
</section>
|
|
|
|
<section id="domains-panel" class="panel">
|
|
<h2>Domains (observed)</h2>
|
|
<div id="domains" class="domains"></div>
|
|
<p id="redactions" class="redactions"></p>
|
|
<h3>Inferred</h3>
|
|
<ul id="inferred"></ul>
|
|
</section>
|
|
|
|
<section id="log-panel" class="panel">
|
|
<h2>Turn log</h2>
|
|
<ul id="log" class="log"></ul>
|
|
<h3>Replay</h3>
|
|
<div class="replay-controls">
|
|
<button id="btn-replay">Request replay</button>
|
|
<button id="btn-replay-step">Step ▶</button>
|
|
<span id="replay-status">no replay loaded</span>
|
|
</div>
|
|
<div id="hashes" class="hashes"></div>
|
|
</section>
|
|
</main>
|
|
|
|
<script src="/app.js"></script>
|
|
</body>
|
|
</html>
|