A port of the original 1996 Quake engine to Luau on Roblox.
Run.one.mp4
Live game with librequake assets
- Both original engines. NetQuake (campaign/coop) and QuakeWorld (competitive) are ported to Luau and boot from the same codebase, selected per place. Both are verified function-by-function against the original C sources (see Fidelity below).
- Assets from Quake shareware, full Quake, or LibreQuake. Publishing to Roblox is limited to LibreQuake assets.
- Full mod support. Unmodified third-party QuakeC mods run as gamedirs
stacked over the base game, exactly like
-gamein DOS Quake. Working configurations ship for Rocket Arena, Threewave CTF, and Team Fortress (each needs its original mod files), plus an in-house instagib overlay. See docs/MODS.md.
default.project.json Rojo map for the code (daily sync)
assets.project.json Rojo map for all asset bundles (occasional sync)
assets-current.project.json generated per-mode asset map (gitignored)
src/shared/engine/ The engine (platform-independent Luau)
common/ buffers, math, tokenizer, net messages, cvars
bsp/ models/ gfx/ BSP29, MDL/SPR, WAD2 + palette loaders
progs/ progs.dat loader + QuakeC VM + ABI defs
server/ world collision, physics, monster move, host loop
client/ cl_parse/cl_main port (state machine, lerp)
src/server/ Roblox server bootstrap (remotes, heartbeat)
src/client/ Roblox client (renderer, input, view, HUD)
docs/coverage/ function-by-function coverage manifests
external_assets/ game + mod data (gitignored)
assets/ built base64 chunks (gitignored build artifact)
reference/ WinQuake + QuakeC sources (gitignored, GPL)
tools/build_assets.py splits pak files into base64 chunks under
assets/; at runtime the server holds them in
ServerStorage.QuakeAssets and streams a per-map bundle to clients. The
repo ships the tools, not the data.
You need Rokit, Python 3, and Roblox Studio.
1. Put game files in place (everything under external_assets/ is
gitignored):
| Content | Where it goes |
|---|---|
| Quake shareware / full | external_assets/quake106/extracted/id1/pak0.pak (+ pak1.pak from the full game) |
| LibreQuake | external_assets/librequake/full/id1/ |
| Threewave CTF | external_assets/threewave/ |
| Rocket Arena | external_assets/rocketarena/ |
| Team Fortress 2.9 | external_assets/fortress/ |
Mod dirs hold the files exactly as the mod was distributed (paks and/or
loose qwprogs.dat, maps/, sound/, ...). Before adding a new mod,
read docs/MODS.md — step one is recording its license in
docs/mods-licenses.md.
2. Build and import the assets:
rokit install # toolchain
tools/sync_assets.sh id1 # or: tools/sync_assets.sh lq1
python3 tools/build_soundbank.py --game id1
# mods: python3 tools/build_assets.py --game threewave --source external_assets/threewave
lune run tools/mode assets ctf # asset import project for a preset
rojo serve assets-current.project.json # connect in Studio, accept, disconnectIn Studio, enable Allow Mesh & Image APIs (Game Settings → Security; the place must be published once). Without it you get untextured wedge-part rendering instead of textured EditableMesh geometry.
3. Run it:
rojo serve # day-to-day: code onlythen hit Play in Studio.
4. Switch configurations. A configuration ("mode") is just a set of
attributes on ServerStorage.QuakeAssets, read at boot: engine, base
game, gamedir, rules, map rotation. Named presets set all of them at
once. In development, switch from the terminal:
lune run tools/mode # show active preset + all presets
lune run tools/mode off # stop overriding; place attributes rule
lune run tools/mode campaign # id1 singleplayer (needs quake106 assets)
lune run tools/mode coop # id1 campaign, coop rules
lune run tools/mode qw-dm # QuakeWorld deathmatch on dm1-dm6
lune run tools/mode ctf # Threewave CTF (needs threewave assets)
lune run tools/mode arena # Rocket Arena (needs rocketarena assets)
lune run tools/mode tf # Team Fortress (needs fortress assets; dev only)
lune run tools/mode lq-dm # LibreQuake deathmatch (needs librequake assets)Restart Play after switching. The full preset list is campaign, coop,
nq-dm, qw-dm, ctf, arena, tf, instagib, lq-dm, and lq-ctf;
modifiers compose as preset+modifier, e.g. qw-dm+original for the
untouched 1996 presentation.
Two things to know:
- A preset only boots if the place's
QuakeAssetsfolder actually holds the game folders it needs (a mismatch is a boot error naming both sides).lune run tools/mode assets <preset>builds the import project with exactly those folders — that's the step 2 import above. - While a dev preset is active it overrides the place's attributes.
For a shipped place, run
lune run tools/mode offand set the attributes permanently instead:lune run tools/mode stamp <preset>prints a snippet to paste into the Studio command bar, then save the place. Never hand-type attributes — a missingfraglimit/timelimitsilently boots a server with no round-end limits.
Details in docs/MODES.md; the full publish flow (build
lq1, strip id1, stamp, publish) is
docs/PUBLISHING.md.
Every subsystem has offline tests (Lune)
that run against the real game data. One script runs luau-lsp analyze
strict-mode checking plus the full sweep:
tools/check.sh # analyze + all tests
tools/check.sh --analyze # static analysis onlyAnalysis is baselined against tools/analyze_baseline.txt so pre-existing
debt doesn't re-flag; tools/check.sh --update-baseline grandfathers new
debt in. To run tests by hand: for f in tests/test_*.luau; do lune run "$f"; done.
The heavyweights: tests/test_trace.luau compares hull traces against
ground truth from compiling the verbatim WinQuake collision code,
tests/test_server.luau boots e1m1 headless and plays it with a fake
client, tests/test_loopback.luau runs the real client state machine
against the real server in one process, and the test_scenario_*.luau
files play full CTF / Rocket Arena / Team Fortress rounds against each
mod's shipped progs.
The port is accounted for function by function against the original C. docs/coverage/ holds four manifests — NQ server, NQ client, QW server, QW client — where every C function is VERIFIED (a test or compiled-C ground-truth harness proves the behavior, evidence cited per row), SUBSTITUTED (replaced by a platform mechanism, justification stated), or N/A (dead code, DOS-era machinery, or flows Roblox owns). 825 functions verified, zero rows open.
Behavioral ground truth comes from compiling the actual WinQuake code:
hull collision is bit-exact against tools/trace_truth.c, the player
movement chain is within 0.0002 units over 300 scripted ticks, and QW
view/weapon feel is frame-traced against verbatim view.c. Known
divergences live in FIDELITY.md — every one is verified,
fixed, open, or a justified substitution.
The big deliberate departures from stock Quake, all product-layer — engine files aren't involved, and each is removable:
- Roblox avatars (
roblox_avatars, default on): players render as their own Roblox avatar rig instead of player.mdl, and the HUD status bar face becomes your avatar head (tinted and pained like the bitmaps). Set the attributefalsefor classic models. - Roblox-native menus: the original DrawPic menu is replaced with a touch-friendly pause menu, settings, and an about screen. Mobile gets on-screen controls.
- Match director: optional map rotation with intermission voting
(
maplistattribute enables it; absent = authentic exit chain), mid-round callvotes, and a host admin menu for live rule changes. - Stats and leaderboards: per-player kill/death/win tracking in DataStores with global, weekly, and friends leaderboards.
- Mild mode (
mildmode, default on): render-only content softening — white gibs/blood, corpses fade out. The sim, wire protocol, and QC are untouched;+originalrestores the 1996 presentation. - Platform substitutions: the software rasterizer is replaced by EditableMesh world geometry with lightmap vertex lighting, and UDP/WinSock networking by RemoteEvents carrying the original protocol.
The engine port is a derivative work of id Software's Quake sources, released under GPL-2.0 — so this repository's code is GPL-2.0 (see LICENSE). Game content is separate: published places use LibreQuake assets (separately licensed, build-time dependency, not part of this repo). Original id Software assets are proprietary and never ship; per-mod provenance and ship gates are in docs/mods-licenses.md.