A from-scratch reimplementation of bits of Call of Duty (2003), patch 1.1, in Rust. A map viewer, a client that spectates and plays on real 1.1 servers, and a dedicated server a retail 1.1 client can join. It reads the game's own pk3s and speaks the original 1.1 wire protocol, which nobody ever wrote down, so I dug it out of the binaries.
vcod is a hobby project. I work on it while poking at a 2003 engine is fun, and I stop when it isn't.
- It is not a playable game. You can run around, shoot people and plant a bomb, and some evenings it almost feels like Call of Duty. It is still a research rig with a renderer bolted on. Expect missing pieces and the occasional soldier living inside a wall.
- Retail parity is not a goal. No roadmap, no "1.0". If it ever becomes fully playable, that's an accident and I'll take the credit anyway.
- It doesn't replace your copy of the game. It needs that copy to run.
- Don't host real players on it. No anti-cheat, an rcon that knows a couple of dozen commands, and strong opinions about tick order. Your regulars deserve a server that was tested on someone other than bots.
If you want to play Call of Duty, play Call of Duty. If you want to watch a 2003 game boot in a window someone rebuilt from the bytes up, stick around.
- Draws any stock or custom map with textures, lightmaps and props. Skies, water, fences, foliage and terrain blends run through the maps' own Q3-style shader scripts, sun disc and fog included.
- Culls with retail's cells, portals and occluders. F4 freezes or disables culling so you can fly out and see what the camera was drawing.
- Plays the map's ambient loop.
- Spawns a soldier on a spawn point with retail movement: gravity, crouch, prone, stepping, leaning, wall sliding.
- Seven weapons with their own viewmodels, animations, sounds and reserve ammo. Hitscan shots with per-surface impacts and tracers.
- Footsteps on retail's cadence per surface; the landing sound scales with fall speed.
- Opens on the stock main menu and server browser, drawn from the game's own
ui_mp/*.menufiles. The browser's three sources are retail's: Local (agetinfobroadcast to ports 28960-28963), Internet (whatcodmaster.activision.comknows) and Favorites (kept inservercache.datbesideCoDMP.exe, the file retail uses). It pings, filters, sorts by column, joins on double-click, and runs the stock password, server info, filter and favourite popups. Losing the server drops you back on the menu with the localized reason in the stock error popup. In a game, Esc opens the script menu and its Main Menu tab the stock in-game main menu (Back to Game, Disconnect). The stock Mods menu lists the mod directories besidemain/and switches to one, in a game too, and the menus load from the mod's ownui_mp/menus.txt. - The stock Options and Multiplayer Options screens: rebind keys, mouse
sensitivity and invert, player name, rate, master volume, video mode and
full screen. Choices land in the console's binds and cvars and persist in
vcod_mp.cfg; settings vcod has no use for are kept but do nothing. - Joins a 1.1 server (
--connector the browser): handshake, Huffman, netchan, delta snapshots, and pak downloads for whatever the server has and you don't, matched by checksum as retail does. Pure servers take its pak checksums. Modded servers work too: a server'sfs_gamelayers its mod directory overmain/and swaps in the mod's menus, itscl_allowDownloaddecides whether to download, and a mod's script menus andhud.menutext show up. - Answers the stock team and weapon menus as a keyboard list built from
their
.menufiles, or straight from--teamand--weapon. - Spectates. Every player is an assembled, animated soldier. Kill feed, chat, scoreboard, sounds, tracers, impacts and muzzle flashes come off the same events retail reads.
- Plays. Move, jump, crouch, prone, lean, fire, aim down the sight,
reload, melee, use, switch weapons, on retail's default binds. A usercmd
is built every 8 ms, like a 125 fps retail client, and they go out at
retail's
cl_maxpackets30 withcl_packetdup1 off the LAN. - Predicts your movement by replaying unacknowledged cmds through the same step the server runs. A correction eases out over 100 ms.
- Draws and stamps cmds on retail's client clock, which slews a millisecond
or two per snapshot instead of stepping on jitter;
cl_timeNudgeworks. - First-person weapon with your team's hands, sight zoom at the weapon's FOV, sniper scope overlay that follows the sway and the hit kick, and your own fire, reload and footstep sounds played off the prediction.
- Fire recoil off the weapon file's view and gun kick keys: the view kick rides your cmd angles, as retail's does, rolls the view with it, springs back to centre and clears on a respawn.
- Mounted MG42s with their fire anim and flash; on the gun the view rides
tag_playerand the crosshair turns into the gun's reticle. - The HUD the stock
hud.menulays out: crosshair that opens with spread, health, ammo, stance, compass with objectives and teammates, use hints, hit direction, chat and announcements. Also the gametype script's own HUD elements (S&D clock, bomb icons, progress bar) in retail's fonts, and head icons over players. A spectator following someone sees their HUD, weapon and scope. - Follows the server through a map change: loading screen, downloads, new map.
- Retail's drop-down console on
`. It takes the commands under Console; binds and archived cvars persist inmain/vcod_mp.cfg.
I have played it against my own server and the retail Linux 1.1d server, and spectated public servers with it. I haven't joined a public server as a player, and neither should you. Those people signed up for Call of Duty, not for my test suite.
- Answers server browsers, accepts retail 1.1 clients, sends the gamestate and delta snapshots.
- Runs Activision's own gametype and map scripts on
vcod-gsc, a VM for CoD's.gsclanguage that lives in this repo. Menus, spawns, scoring, rounds and limits all come from the stock scripts, not from Rust. All five stock gametypes are checked against retail:dm,tdmandsdend to end,reandbelthrough their key events (cod11-gametypes-re-bel.md). - Shared pmove with capsule players that block and push each other. Falls stun and hurt.
- Bullets trace the world, players and static props, through the stock damage callback with per-bone hit locations. Rifle rounds go through players, every round goes through glass. Melee works. Grenades are real missiles that bounce, rest and explode with retail's falloff, and a blast walks its victims in retail's area-tree order, so the guy in front still eats it for the guy behind.
- Corpses in the eight-slot body queue, dropped weapons, pickups by touch or use key, items that fly retail's arc and respawn on its timer.
- Search & Destroy end to end: plant, defuse, progress bar, compass objectives.
- Mounted MG42s: mount, aim inside the arc, fire, dismount.
- Map triggers, script movers that carry and shove players and items, and
linkToon script models, brush models, items, turrets and player tags. - Intermission,
map_restartandsv_mapRotationthe way retail runs them. - Spectator follow and the killcam, replayed from a ring of archived frames.
- rcon with retail's commands and replies, bans, the master heartbeat, zombie slots and retail-measured pings.
- Publishes retail's pak lists and checksums in the systeminfo, serves the
paks it lists to clients that lack them at retail's pace, and with
--set sv_pure=1checks each client's pak checksums the way a pure retail server does.sv_minPing/sv_maxPingrefuse off-LAN clients by challenge ping, and an off-LAN client's messages wait out its rate andsnaps. Big messages, the gamestate included, go one fragment per frame. --botsadds bots that join through the stock menus and roam a nav graph built from pmove runs, ladders and jumps. They play the objectives: S&D plants and defuses, Retrieval carries and escorts, Behind Enemy Lines hunts. With--bots-shootthey fight back, with a reaction delay, a turn cap and aim that settles on target. They are still bad at it. So am I.
The retail 1.1d Linux server is the oracle. When vcod and retail disagree, retail wins and the disagreement goes into a research doc.
- A headless probe client (
vcod --net-probe) joins a server and records what it sees, with a few dozen scripted modes: shoot someone, throw a grenade, plant the bomb, crawl prone up a hill, get stuck inside another player. - The retail recordings are committed as fixtures. A/B tests replay the same inputs on vcod's server and diff the result snapshot by snapshot.
- Small
.gscprobes run on retail and onvcod-gsc, and the suite compares their output.
This catches a lot. Anything about how it looks or sounds still needs a human squinting at a screen.
Retail does a lot more than this list. These are the gaps you'll hit first.
Front end. Main menu, browser and its popups, options, quit and error popups work. On the options screens only binds, sensitivity, invert mouse, name, rate, volume, video mode, full screen, brightness and the crosshair and HUD toggles take effect; texture, lighting, sound quality and language settings are stored but ignored. Show Compass and Team Overlay do nothing, as in retail 1.1. Start New Server and CD key print "not in vcod yet". The browser's game type filter, map preview and refresh date are missing.
Client
- Protocol 1 (patch 1.1) only. 1.5 and United Offensive servers won't talk to it.
- Prediction carries you on a moving brush model but not its rotation. Neither does retail's.
- A mod's UI DLL is ignored; only its menu files count (cod11-front-end.md sections 16-17). Switching mods in a game keeps the loaded map's geometry.
Server
- An entity linked to a player tag sits within a few units of retail's spot
and doesn't follow the body after
setPlayerAngles. A tag on anattached model can't take a link. - A brush model that turned and turned back keeps a sliver of yaw on retail and drifts its riders about 0.02 units a frame. vcod's comes back to zero. Yes, I'm calling that a bug in retail.
sv_puredefaults to 0 where retail's default is 1, and a client may download only the paks the server lists. Retail serves any file under its directories,..included, so I'm keeping that one on purpose.- rcon runs
map,devmap,map_restart,map_rotate,status,clientkick,kick,banUser,banClient,dumpuser,serverinfo,systeminfo,say,set,seta,cvarlist, cvar queries,heartbeat,killserverandquit. NotgameCompleteStatus,scriptUsageorstringUsage. Bans live in vcod (--ban-filekeeps them across runs) where retail hands them to Activision's authorize server. Like retail's, a ban refuses an address off the LAN and leaves the banned player connected until they leave on their own. Afterkillserverthe process sits there deaf, since there is no stdin console. - A heartbeat reaches the master, which probes back. I haven't seen vcod listed yet.
Rendering and sound
- Props are lit per vertex from the map's lights and its light-visibility grid at load, as retail does. Dynamic lights add on top with retail's falloff instead of competing for a prop's eight light slots. Players, other entity models and the viewmodel pick their eight lights from the grid every frame, fx lights among them (cod11-light-grid-and-leaf-lights.md).
- The frame holds retail's framebuffer bytes and the gamma ramp doubles it once at the end, as retail's hardware ramp did; windowed it draws as retail's windowed mode does, with no doubling and lightmaps shifted at load (cod11-gamma.md). Entity models draw only their first stage, lit, whatever their material says.
- Only the ocean's
deformVertexes wavemoves; the other forms parse and do nothing. NV/ATI hardware-path stages are dropped, as retail did on cards without them.$dlightand the ship's deckflag have no file behind them, so stand-ins draw (cod11-shader-scripts.md). - Visibility draws a bit more than retail on purpose. Retail assumes nobody looks over a cell's walls, and the mp_ship decks disagree.
- Audio follows the engine on paper (falloff, panning, voice pools, stealing, ducking) but hasn't been checked by ear against the real game. Wall occlusion is a vcod addition.
Things that look like gaps and aren't
- No mantling, no swimming. Retail 1.1 MP has neither (cod11-mantle.md).
- No grenade cooking. The fuse runs in full from the release, however long you clutched it.
- No doppler. The 1.1 engine never gives a sound a velocity.
- Quick chat (
vsay) does nothing on a stock install, same as retail: its.voicetables only ship in mods. - Asphalt footsteps are silent in retail because the engine asks for
asphaltand the sound table spells itasphault. vcod uses the table's spelling, so you can hear asphalt. Twenty-three years late, Infinity Ward, you're welcome.
- A purchased copy of Call of Duty (2003) with patch 1.1. This repo has no
game data. Patch 1.5 ships the same
pak1-4.pk3and a differentpak0.pk3; a 1.5 install works as the asset source, but itspak0is not on a pure 1.1 server's list. The netcode is 1.1 only. - Rust 1.90 or newer.
- For the client, a GPU with BC (DXT) texture compression. wgpu picks a
backend;
WGPU_BACKEND=vulkan|gl|dx12|metalnarrows it. The server needs no GPU. - Linux is the only platform I run. Windows and macOS builds exist and are untested, so godspeed.
- An audio device if you want sound. Without one the client warns and runs silent.
Prebuilt Linux amd64, Windows amd64 and macOS arm64 binaries are on the
nightly release,
rebuilt from master on every push that touches code. It's a rolling tag:
the assets are replaced in place and the previous build is gone.
cargo build --release
gives target/release/vcod and target/release/vcod-server. Both expect to
sit next to CoDMP.exe and read the paks from main/. Copy them there, set
COD_DIR=/path/to/CallOfDuty, or pass --game-dir. --game-dir beats
COD_DIR, which beats the executable's directory.
COD_DIR=/path/to/CallOfDuty cargo test
Without COD_DIR the tests that need game data return early and pass, so a
green run without the game proves nothing about the parsers. CI runs that
way, because it can't ship the game either.
vcod-server mp_carentan --bots 4 --bots-shoot
vcod --connect 127.0.0.1:28960
Pick a team, pick a weapon, go get shot by a bot.
vcod # main menu and server browser
vcod mp_pavlov # fly
vcod mp_pavlov --walk # walk, offline
vcod --list # every .bsp in the search path
vcod --connect <ip:port> --team axis --weapon kar98k_mp
- The map name is case-insensitive.
--team <allies|axis|autoassign|spectator>and--weapon <file>answer the menus without showing them, for--connectand every consoleconnect. If the menu refuses the weapon, you pick by hand.--mod-dir uoindexes United Offensive's pk3s instead ofmain/. One directory mounts at a time, so a UO map whose art lives inmain/shows missing textures. I've flown noville this way. Anything else in UO is untested.--debug-overlay(or F3) shows frame, draw, vis, net and audio counters.--no-audioruns silent;--volume <0..1>sets the master volume (mss_volume, default 0.8, also on the Sound screen).--net-probe <ip:port>is the headless probe client;vcod --helpdocuments its modes.
vcod-server mp_carentan --port 28960 --hostname "my server" --gametype tdm
- Binds
0.0.0.0and answersgetstatusfrom anyone, like retail. Keep it on a LAN or behind a firewall you control. --gametypepicks the script undermaps/mp/gametypes/(defaultdm);--max-clientssetssv_maxclients(default 8).--set NAME=VALUEis retail's+set, repeatable:--set scr_friendlyfire=1,--set sv_mapRotation="...".--set rconPassword=<pw>turns rcon on.--set g_password=<pw>makes the server private, checked as retail does at connect,map_restartand map change.--set sv_privateClients=N --set sv_privatePassword=<pw>reserves the first N slots for clients that send that password, as retail does.dedicateddefaults to 1, not retail's 2, so dev runs stay off the master list.--set dedicated=2heartbeatscodmaster.activision.comevery three minutes and sends a flatline on Ctrl-C orquit.--bots <n>addsnbots in real client slots. The nav graph builds on its own thread on the first tick (a second or two on big maps); bots wander until it's ready.--bots-shootlets them fight.--gametype-script <file>runs a gametype from disk instead of the paks.--test-entities <n>adds invisible entities that move on the wire, to exercise the entity encoding.--tracelogs every snapshot per client, and once a second the slowest tick and how far the loop fell behind its 20 Hz schedule.
Click to capture the mouse, Esc to release it.
| Input | Action |
|---|---|
| W / A / S / D | Move |
| Space / Ctrl | Up / down |
| Shift | Speed boost |
| Scroll | Fly speed |
The stock config_mp.cfg binds, with the sight on the right mouse button as
+speed. bind MOUSE2 "toggle cl_run" makes the sight a toggle.
| Input | Action |
|---|---|
| W / A / S / D | Move |
| Space | Stand up; jump when standing |
| C / Ctrl | Crouch / prone |
| Q / E | Lean (held) |
| LMB | Fire (semi-autos fire once per click) |
| RMB | Aim down the sight (held) |
| R | Reload |
| Shift | Melee |
| F | Use: pick up, mount an MG, plant, defuse, respawn |
| 1 / 2 / 3 / 4 | Primary, second primary, pistol, grenade |
| Scroll | Next / previous weapon |
| Tab | Scoreboard (held) |
| T / Y | Chat to everyone / your team |
| M | Script menu: team, or weapon once you have a team |
| Esc | Script menu, as retail; its Main Menu row opens the main menu (Back to Game, Disconnect, Quit) |
With a script menu open, digits pick a row, Up / Down and Enter navigate, and Esc closes it. The main menu takes the mouse; Esc or Back to Game returns to the game. As a spectator, Space rises and C sinks.
| Input | Action |
|---|---|
| W / A / S / D | Move |
| Space | Jump (no autohop) |
| Ctrl | Crouch (held) |
| Z | Toggle prone |
| Q / E | Lean |
| Shift | Slow walk |
| LMB / RMB | Fire / aim down sights |
| R | Reload |
| 1-7 | colt, thompson, mp40, mp44, enfield, kar98k, scoped kar98k |
` or ~ toggles it; while it's down the game gets no keys. Up / Down
walk history, Tab completes, Page Up / Page Down scroll. In game, a line
without a leading / or \ is chat, as in retail. ; separates commands.
| Command | Does |
|---|---|
connect <ip:port>, disconnect, reconnect, quit |
What they say |
say, say_team |
Chat |
cmd <text> |
Send a raw client command |
bind, unbind, unbindall, bindlist |
Binds |
set, seta, toggle, <cvar> [value] |
Cvars; seta also saves |
cvarlist, cmdlist, echo, clear |
The usual |
Anything else goes to the server while connected (callvote, kill,
follownext). Bindable commands and key names are retail's (+attack,
+speed, weaponslot pistol, MOUSE1, MWHEELUP). The client cvars are
name, cl_run, sensitivity, m_yaw, m_pitch, cg_fov
(cheat-protected, so 80 unless the server runs sv_cheats 1), rate
(25000; retail's first-run 5000 starves snapshots), snaps,
scr_conspeed, mss_volume, r_mode / r_fullscreen (applied at start
and by vid_restart), password and the browser's ui_netSource and
ui_browserShow*. exec <file> runs a config from the paks or main/,
and ui_load reloads the menus off the list ui_menuFiles names.
Binds only act while connected.
Mouse to pick, double-click or Enter to join, wheel or Page Up / Down to scroll, Esc to go back. Click Source to cycle Local, Internet and Favorites. On a bind, click or Enter, then press the new key (Esc cancels, Backspace clears); a third key replaces both of the old ones. Click a text field to type into it; Enter, Tab or Esc finishes.
| Input | Action |
|---|---|
` / ~ |
Console |
| F3 | Debug overlay |
| F4 | Culling: on, locked, off |
- docs/protocol-1.1.md: the 1.1 wire protocol, both directions, with every divergence from RTCW/Q3 at the end.
- docs/research/: about thirty documents on file formats, movement, combat, items, turrets, movers, HUD, sound, script semantics, the console, the front end, bots and more. Every claim names the module and address it rests on and says whether it was verified against a binary or capture, or inferred. I'd call this the most useful part of the repo.
- tools/re/: the scripts that pull tables out of the binaries, and the Linux server disassembly notes.
- AGENTS.md: the contributor guide. Layout, measuring against retail, and the traps I already paid for.
The code is four crates: vcod-common (formats, collision, movement,
protocol; no GPU or audio), vcod (client), vcod-server and vcod-gsc
(the script VM, which depends on nothing else here).
As of October 2026:
- about 173,000 lines of Rust,
- about 2,200 tests,
- about 270,000 words of protocol and research notes, longer than most novels and with a worse plot, except the twist where grenade cooking turned out not to exist,
- one asphalt footstep restored.
Mostly to see whether it could be done. CoD 1 descends from id Tech 3, and the Quake III and RTCW sources are public, but the 1.1 wire protocol was never documented. Every field width, enum order and place Infinity Ward wandered off from RTCW had to come out of the binaries and be confirmed against the retail server. Also, watching a 2003 game come back to life in a window you built yourself is a lot of fun.
An AI coding agent wrote most of the code and docs here, under my direction. I decide what to build, review what comes back, run it against the real game and the retail server, and do the pixel and by-ear checks the agent can't. The research docs label each fact verified or inferred so you can tell them apart. Treat the code as a working prototype, not a reference implementation.
- Quake III Arena and Return to Castle Wolfenstein by id Software, GPL. CoD1 descends from RTCW-MP, and its netcode, movement and animation code follow those sources closely. Where vcod ports a routine, the comment names the file it came from.
- ioquake3, for a cleaner reading of the same netcode.
- CoDExtended, a GPL server extension for CoD1 1.1 whose struct layouts were the starting point for the netfield tables.
- cod-asset-importer, a GPL Blender add-on. The xmodel triangle-strip decoder is ported from it.
- wgpu, winit and kira for graphics, windowing and audio.
vcod is not affiliated with or endorsed by Activision or Infinity Ward. Call of Duty is a trademark of Activision Publishing, Inc. This repository contains no game assets, game code or binaries; it reads the files of a copy you own. The reverse engineering was done to interoperate with the game's own files and servers. The research notes document file formats and the network protocol for that purpose: layouts and addresses recovered from the binaries, no copied code. The screenshots show art owned by Activision.
Quake III Arena and Return to Castle Wolfenstein are trademarks of id Software. Their GPL sources, and the other ported code, are credited in NOTICE.
GPL-3.0-or-later; see LICENSE. The network code was written with the RTCW (GPLv3) and Quake 3 (GPLv2-or-later) sources as reference, so the project is GPL to stay compatible with them.


