Claim set · data/claims/doom-demo.yaml

Doom demos (id Software's linuxdoom-1.10 source)

Game: Doom.

In the Linux 1.10 source, a Doom demo starts with a 13-byte header: the version (110), skill, episode, map, the deathmatch, respawn, fast, and no-monsters options, the recording player's number, and one in-game flag for each of the 4 players. Then, for every tic and every player in the game, come 4 bytes: forward move, side move, the high byte of the turn, and the buttons. A byte of 0x80 ends it. A one-player demo takes 140 bytes a second at 35 tics a second (computed). doom-demo/lump-format

About: Doom Demo playback

Value
{
 "header": 13,
 "bytesPerPlayerTic": 4,
 "endMarker": 128,
 "version": 110
}
CodeDOOM: the Linux source release (id-Software/DOOM, linuxdoom-1.10) · linuxdoom-1.10/g_game.c#L1549-L1567demo_p = demobuffer; *demo_p++ = VERSION; *demo_p++ = gameskill; *demo_p++ = gameepisode; *demo_p++ = gamemap; *demo_p++ = deathmatch; *demo_p++ = respawnparm; *demo_p++ = fastparm; *demo_p++ = nomonsters; *demo_p++ = consoleplayer; for (i=0 ; i<MAXPLAYERS ; i++) *demo_p++ = playeringame[i];DOOM: the Linux source release (id-Software/DOOM, linuxdoom-1.10) · linuxdoom-1.10/g_game.c#L1488-L1513#define DEMOMARKER 0x80 … *demo_p++ = cmd->forwardmove; *demo_p++ = cmd->sidemove; *demo_p++ = (cmd->angleturn+128)>>8; *demo_p++ = cmd->buttons;DOOM: the Linux source release (id-Software/DOOM, linuxdoom-1.10) · linuxdoom-1.10/g_game.c#L660-L669if (playeringame[i]) … if (demoplayback) G_ReadDemoTiccmd (cmd); if (demorecording) G_WriteDemoTiccmd (cmd);DOOM: the Linux source release (id-Software/DOOM, linuxdoom-1.10) · linuxdoom-1.10/g_game.c#L1677-L1680if (demorecording) { *demo_p++ = DEMOMARKER; M_WriteFile (demoname, demobuffer, demo_p - demobuffer);DOOM: the Linux source release (id-Software/DOOM, linuxdoom-1.10) · linuxdoom-1.10/doomdef.h#L33enum { VERSION = 110 };DOOM: the Linux source release (id-Software/DOOM, linuxdoom-1.10) · linuxdoom-1.10/doomdef.h#L119-L122#define MAXPLAYERS 4 … #define TICRATE 35Checked by claude, 2026-10-22 — read G_BeginRecording, G_WriteDemoTiccmd, G_Ticker's per-player loop, and G_CheckDemoStatus's recording branch at the pinned commit; 9 + MAXPLAYERS = 13 header bytes; 4 × 35 = 140 bytes a second for one player; the DOS 1.9 executable's version byte isn't in this source

Cited by: Demo playback

Playback goes through the same path as play. Each tic, G_Ticker copies every player's command (ticcmd_t) from the network buffer, then, during a demo, overwrites its move, turn, and buttons from the demo; the player code then acts on that command as on a live one. doom-demo/ticcmd-path

About: Doom Demo playback

CodeDOOM: the Linux source release (id-Software/DOOM, linuxdoom-1.10) · linuxdoom-1.10/g_game.c#L654-L667// get commands, check consistancy, … cmd = &players[i].cmd; memcpy (cmd, &netcmds[i][buf], sizeof(ticcmd_t)); if (demoplayback) G_ReadDemoTiccmd (cmd);DOOM: the Linux source release (id-Software/DOOM, linuxdoom-1.10) · linuxdoom-1.10/g_game.c#L1491-L1503void G_ReadDemoTiccmd (ticcmd_t* cmd) … cmd->forwardmove = ((signed char)*demo_p++); cmd->sidemove = ((signed char)*demo_p++); cmd->angleturn = ((unsigned char)*demo_p++)<<8; cmd->buttons = (unsigned char)*demo_p++;Checked by claude, 2026-10-22 — read G_Ticker's command loop and G_ReadDemoTiccmd at the pinned commit; the consistancy and chatchar fields keep the network buffer's values

Cited by: Demo playback

A demo keeps only the turn's high byte, rounded, so a recorded turn moves in steps of 1/256 of a full circle, about 1.4 degrees (computed). While recording, the game writes the command and reads it straight back ("make SURE it is exactly the same"), so the recording player also plays with the rounded turn and the recording matches what the player saw. doom-demo/turn-rounding

About: Doom Demo playback

Value
{
 "turnStep": 0.00390625
}
CodeDOOM: the Linux source release (id-Software/DOOM, linuxdoom-1.10) · linuxdoom-1.10/g_game.c#L1506-L1522*demo_p++ = (cmd->angleturn+128)>>8; *demo_p++ = cmd->buttons; demo_p -= 4; … G_ReadDemoTiccmd (cmd); // make SURE it is exactly the sameDOOM: the Linux source release (id-Software/DOOM, linuxdoom-1.10) · linuxdoom-1.10/g_game.c#L1501cmd->angleturn = ((unsigned char)*demo_p++)<<8;DOOM: the Linux source release (id-Software/DOOM, linuxdoom-1.10) · linuxdoom-1.10/d_ticcmd.h#L40short angleturn; // <<16 for angle deltaDOOM: the Linux source release (id-Software/DOOM, linuxdoom-1.10) · linuxdoom-1.10/tables.h#L69#define ANG90 0x40000000Checked by claude, 2026-10-22 — the turn is shifted <<16 into a 32-bit angle where ANG90 is 2^30, so a full circle is 2^32; one step of the stored byte is 256 << 16 = 2^24, 1/256 of a circle (360/256 = 1.40625°); G_WriteDemoTiccmd steps demo_p back 4 bytes and re-reads them into the live command

Cited by: Demo playback

Playing a demo restores the recorded skill, episode, map, and options from its header and starts the level through G_InitNew, which resets both random indices to 0, as a recording started from the command line also does. A demo whose version byte isn't 110 is refused with "Demo is from a different game version!". doom-demo/determinism

About: Doom Demo playback

CodeDOOM: the Linux source release (id-Software/DOOM, linuxdoom-1.10) · linuxdoom-1.10/g_game.c#L1586-L1615demobuffer = demo_p = W_CacheLumpName (defdemoname, PU_STATIC); if ( *demo_p++ != VERSION) { fprintf( stderr, "Demo is from a different game version!\n"); … skill = *demo_p++; episode = *demo_p++; map = *demo_p++; deathmatch = *demo_p++; … G_InitNew (skill, episode, map);DOOM: the Linux source release (id-Software/DOOM, linuxdoom-1.10) · linuxdoom-1.10/g_game.c#L1414M_ClearRandom ();DOOM: the Linux source release (id-Software/DOOM, linuxdoom-1.10) · linuxdoom-1.10/m_random.c#L69-L72void M_ClearRandom (void) { rndindex = prndindex = 0; }DOOM: the Linux source release (id-Software/DOOM, linuxdoom-1.10) · linuxdoom-1.10/d_main.c#L1127-L1132p = M_CheckParm ("-record"); … G_RecordDemo (myargv[p+1]); autostart = true;DOOM: the Linux source release (id-Software/DOOM, linuxdoom-1.10) · linuxdoom-1.10/d_main.c#L1161-L1164if ( gameaction != ga_loadgame ) { if (autostart || netgame) G_InitNew (startskill, startepisode, startmap);Checked by claude, 2026-10-22 — read G_DoPlayDemo and G_InitNew's M_ClearRandom call at the pinned commit; -record sets autostart, which reaches G_InitNew at d_main.c L1164 unless -loadgame is given, and G_DoLoadGame calls G_InitNew too (doom/rng-reset, g_game.c L1227)

Cited by: Demo playback

Playback ends at the 0x80 byte. A demo given with -playdemo then quits the game, and one given with -timedemo reports the tics and the real time taken. Otherwise the attract loop moves on: the title picture, demo1, the credits, demo2, a third page, demo3, and, in the retail Ultimate Doom only, demo4. During an attract demo, a key press that the menu doesn't take first, or a mouse or joystick button, opens the menu, and the demo keeps running behind it. doom-demo/end-and-loop

About: Doom Demo playback

CodeDOOM: the Linux source release (id-Software/DOOM, linuxdoom-1.10) · linuxdoom-1.10/g_game.c#L1493-L1497if (*demo_p == DEMOMARKER) { // end of demo data stream G_CheckDemoStatus (); return;DOOM: the Linux source release (id-Software/DOOM, linuxdoom-1.10) · linuxdoom-1.10/g_game.c#L1651-L1674if (timingdemo) … I_Error ("timed %i gametics in %i realtics",gametic … if (demoplayback) { if (singledemo) I_Quit (); … D_AdvanceDemo ();DOOM: the Linux source release (id-Software/DOOM, linuxdoom-1.10) · linuxdoom-1.10/d_main.c#L1135-L1146p = M_CheckParm ("-playdemo"); … singledemo = true; // quit after one demo … p = M_CheckParm ("-timedemo"); … G_TimeDemo (myargv[p+1]);DOOM: the Linux source release (id-Software/DOOM, linuxdoom-1.10) · linuxdoom-1.10/d_main.c#L462-L518if ( gamemode == retail ) demosequence = (demosequence+1)%7; else demosequence = (demosequence+1)%6; … pagename = "TITLEPIC"; … G_DeferedPlayDemo ("demo1"); … pagename = "CREDIT"; … G_DeferedPlayDemo ("demo2"); …DOOM: the Linux source release (id-Software/DOOM, linuxdoom-1.10) · linuxdoom-1.10/g_game.c#L520-L530// any other key pops up menu if in demos if (gameaction == ga_nothing && !singledemo && (demoplayback || gamestate == GS_DEMOSCREEN) … M_StartControlPanel ();DOOM: the Linux source release (id-Software/DOOM, linuxdoom-1.10) · linuxdoom-1.10/d_main.c#L173-L175if (M_Responder (ev)) continue; // menu ate the event G_Responder (ev);DOOM: the Linux source release (id-Software/DOOM, linuxdoom-1.10) · linuxdoom-1.10/p_tick.c#L138-L144// pause if in menu and at least one tic has been run if ( !netgame && menuactive && !demoplaybackChecked by claude, 2026-10-22 — read G_ReadDemoTiccmd, G_CheckDemoStatus, the -playdemo and -timedemo branches, D_DoAdvanceDemo, G_Responder, and P_Ticker's menu pause at the pinned commit; the third page is HELP2 in shareware and registered Doom, CREDIT in retail, and TITLEPIC in commercial (Doom II), which is why the statement leaves it unnamed

Cited by: Demo playback

Recording (-record) ends when the player presses Q, when the buffer, 128 KiB unless -maxdemo sets another size in KiB, is within 16 bytes of full, when a player leaves a net game, or when the game stops on an error (I_Error); the game then writes the 0x80 byte and the file <name>.lmp, and exits with "Demo <name>.lmp recorded". doom-demo/recording-end

About: Doom Demo playback

Value
{
 "defaultBufferBytes": 131072
}
CodeDOOM: the Linux source release (id-Software/DOOM, linuxdoom-1.10) · linuxdoom-1.10/g_game.c#L1508-L1518if (gamekeydown['q']) // press q to end demo recording G_CheckDemoStatus (); … if (demo_p > demoend - 16) { // no more space G_CheckDemoStatus ();DOOM: the Linux source release (id-Software/DOOM, linuxdoom-1.10) · linuxdoom-1.10/g_game.c#L1536-L1543strcpy (demoname, name); strcat (demoname, ".lmp"); maxsize = 0x20000; i = M_CheckParm ("-maxdemo"); if (i && i<myargc-1) maxsize = atoi(myargv[i+1])*1024; … demoend = demobuffer + maxsize;DOOM: the Linux source release (id-Software/DOOM, linuxdoom-1.10) · linuxdoom-1.10/i_system.c#L162-L177void I_Error (char *error, ...) … if (demorecording) G_CheckDemoStatus();DOOM: the Linux source release (id-Software/DOOM, linuxdoom-1.10) · linuxdoom-1.10/d_net.c#L289-L293strcpy (exitmsg, "Player 1 left the game"); … if (demorecording) G_CheckDemoStatus ();DOOM: the Linux source release (id-Software/DOOM, linuxdoom-1.10) · linuxdoom-1.10/g_game.c#L1677-L1683if (demorecording) { *demo_p++ = DEMOMARKER; M_WriteFile (demoname, demobuffer, demo_p - demobuffer); Z_Free (demobuffer); demorecording = false; I_Error ("Demo %s recorded",demoname);Checked by claude, 2026-10-22 — read G_WriteDemoTiccmd, G_RecordDemo, I_Error's recording branch, the net-quit branch in d_net.c, and G_CheckDemoStatus at the pinned commit; I_Quit (i_system.c L116-L124) doesn't call G_CheckDemoStatus

Cited by: Demo playback