Skip to content

go-quake1/engine

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

304 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

go-quake1/engine

go-quake1 / engine

6-arch CI CGO Coverage License

Pure-Go Quake 1 (id Tech 1, 1996) engine for bare-metal TamaGo + UEFI, hand-ported from the tyrquake NetQuake (single-player) branch and wrapped in cloud-boot virtio adapters.

Sibling of go-doom/engine in the DOOM → Quake roadmap. Family siblings: go-quake2 (id Tech 2 — reserved) and go-quake3 (id Tech 3 — reserved).

Status

Phase Q-1a in flight. Hand-porting tyrquake-NQ to pure Go, one module at a time, with parity tests against the upstream C behaviour.

Component State
Porting conventions (CONVENTIONS.md) done
reference/ tyrquake source mirror (commit 6531579) done
common: mathlib, crc, cmd, cvar, zone, qstr, sizebuf, qpath, qparse, msg, qargs, pak, vfs, wad, keys, protocol, anorms done (17 packages · 100% cov)
models: bspfile, mdl, spr, model (magic-bytes dispatcher) done (4 packages · 100% cov)
bsptrace (Mod_HullPointContents + Mod_TraceHull) done (87.8% cov — backfill backlog)
progs (QuakeC VM — 65 opcodes + edicts + parser + 9 math builtins) done (100% cov)
server (host + sv_world + sv_main + sv_phys + sv_user) pending
client + soft renderer (r_*, d_*, cl_*) pending
sound (snd_dma + snd_mem + snd_mix) pending
64 remaining QuakeC builtins (need sv_world for traceline/findradius/...) pending
backend/tamago/ virtio adapters pending (mirrors godoom shape)
embedpak/ shareware loader pending
cmd/harvest-reference oracle pending
phaseQ1_oci_quake1_soft_boot.go (in cloud-boot/tamago-uefi) pending
provable-test gates A · B · C-1 · C-2 pending

Why hand-port (not ccgo, not ironwail-go)

Phase Q-1a's open question Q-1 was first answered "ccgo-transpile" — a mechanical C-to-Go translation via modernc.org/ccgo/v4 of a Linux-portable Quake source tree. We tried two upstreams in earnest and documented why both failed:

  • quakeforge uses C23 (nullptr, #embed, etc.); ccgo's parser predates C23. See internal/transpile/FINDING-C23.md.
  • tyrquake is clean C99 but ccgo's internal checker drops symbols on real game-code constructs (extern globals, function-pointer dispatch tables, packed structs). The link stage then reports "undefined: com_argc" even though com_argc IS defined in common.c. See internal/transpile/FINDING-CCGO-LIMITS.md.

ccgo is production-quality for SQLite-class C (it's literally how modernc.org/sqlite is built), but Quake's surface exposes the v4 checker's soft spots faster than ccgo's curated test corpus does.

The remaining options were:

Option Velocity Audit posture Picked
Fork darkliquid/ironwail-go fastest low (AI-assisted upstream, unreviewed) no
Hand-port (LLM-assisted, from tyrquake-NQ) medium high (every line under operator's git history) YES
Wait on ccgo upstream maturity slowest high deferred (track as a watch)

The hand-port also gives us natural alignment with TamaGo constraints (no malloc churn, no goroutines for the main loop, careful with go keyword pressure on the bare-metal scheduler) that no transpiled engine would ship with out of the box.

How a port lands

For each tyrquake C module:

  1. Mirror the C source verbatim into reference/ (no edits) so the diff against upstream stays inspectable.
  2. Port the module under <module>/<module>.go following the rules in CONVENTIONS.md. Hand-written or LLM- drafted then operator-reviewed; either way the final Go is operator-owned.
  3. Test parity against the C upstream: feed the same input through both and assert byte-equal output where the operation is deterministic (math, hash, parse), or bounded-tolerance where it is not (float trig).
  4. Coverage target = 100% of the new Go package (the project-wide convention inherited from go-virtio and go-deltasync).
  5. Commit with a port: <module> prefix. Each commit lands one buildable, tested module so bisecting the engine remains practical.

Data: shareware pak0.pak

pak0.pak for the shareware Episode 1 (Dimension of the Doomed) is freely redistributable per id Software's grant and lands in-tree under embedpak/. Operators can override at boot with their own pak0+pak1 via a probe env. CI gates always exercise the committed shareware pak so the reference oracle is reproducible.

Play in a browser (wasm)

The engine ships a GOOS=js GOARCH=wasm build alongside the bare-metal TamaGo target. The backend/wasm adapter wires backend.Backend onto Canvas2D (framebuffer), DOM events (input, with Pointer Lock for mouse-look), and WebAudio (sound). Two top-level tasks drive it:

task build-wasm   # compiles cmd/quake-wasm -> cmd/quake-wasm/web/quake.wasm
task serve-wasm   # binds localhost:8080 to cmd/quake-wasm/web/

then open http://localhost:8080/ in any modern browser. task wasm chains both. The single-step build is large (~180 MB; the Go runtime ships the full stdlib in wasm builds); first-load is a one-shot cache.

Streaming assets from an OCI registry

The default wasm build embeds embedpak/empty.pak + the music tracks, so the binary balloons to ~180 MB the moment a real shareware pak is dropped in. For browser distribution that's wasteful — every page load re-fetches the whole thing. Build with -tags no_embed_assets instead and the wasm payload stays ~11 MB; the pak + music tracks ship as individual layers in an OCI Distribution v2 registry (any registry that speaks the OCI spec: Distribution v2, ghcr.io, harbor, self-hosted, …) and the wasm fetches them on demand the first time a file is Open'd, with an IndexedDB write-through cache so reloads in the same browser skip the network round-trip.

# 1. Pack the local pak + music into an OCI image-layout dir.
go run ./cmd/oci-pack-quake --out _oci --ref quake-assets:latest

# 2. Push the layout to any registry. oras [1] is the easy path:
oras push localhost:5000/quake-assets:latest \
  --manifest-config /dev/null:application/vnd.go-quake1.config.v1+json \
  $(find _oci/blobs/sha256 -type f)

# 3. Build the slim wasm with the registry URL baked in. The
#    -X main.OCIReference linker flag is what tells the binary
#    which registry to hit at boot.
OCI_REFERENCE=http://localhost:5000/quake-assets:latest task build-wasm-oci

# 4. Serve the wasm + open in a browser.
task serve-wasm

The runtime falls back to the embedded pak (and from there to the synthetic-asset bootstrap) on any OCI failure, so an unreachable registry never aborts the boot — it just degrades to the local default. See engine/ociassets/ for the package + tests.

Provable test protocol (inherited)

Every Quake phase inherits the four-gate protocol shipped with DOOM (doom-provable-protocol.md):

  • GATE A — engine determinism, BYTE-EQUAL frames at checkpoint tics
  • GATE B — guest virtio-gpu fidelity, χ² ≤ tolerance
  • GATE C-1 — audio event stream, BYTE-EQUAL CacheSound/PlaySound log
  • GATE C-2 — guest WAV bounded tolerance (per-second RMS envelope)

License

  • Wrapper code (backend/, embedpak/, cmd/, internal/, .github/): BSD-3-Clause
  • Ported engine packages (anorms/, bspfile/, bsptrace/, cmd/, crc/, cvar/, keys/, mathlib/, mdl/, model/, msg/, pak/, progs/, protocol/, qargs/, qparse/, qpath/, qstr/, sizebuf/, spr/, vfs/, wad/, zone/): GPL-2.0-or-later (inherited from tyrquake / upstream id Quake source)
  • The reference/ mirror of tyrquake C: GPL-2.0-or-later (verbatim)

See LICENSE for the full split.

About

Pure-Go Quake 1 (id Tech 1, 1996) engine — hand-ported from tyrquake, CGO=0, bare-metal TamaGo target. 100% coverage discipline.

Topics

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages