This is a very small walkthrough of some of Quake 1. Unfinished.
Files of interest:
view.chost.csys_win.ccl_main.csv_user.csv_phys.csv_move.ccl_input.cpr_cmds.c
Host_Init (host.c) is the actual entry point of Quake. This entry point is called from a main function located in one of the sys_*.c prefixed files. So I'm guessing you'd compile Quake with just one of these files that are targeting your platform (though in reality you'd just grab a modern quake engine like vkQuake). In any case, the main function calls, in order, these two functions:
Sys_Init() Host_Init(&parms);
Sys_Init does some platform system checks (OS version, memory etc).
Host_Init is where Quake actually starts initialization of the game/engine. This function seems to largely sets up commands (host.c):
Memory_Init (parms->membase, parms->memsize);
Cbuf_Init ();
Cmd_Init ();
V_Init ();
Chase_Init ();
Host_InitVCR (parms);
COM_Init (parms->basedir);
Host_InitLocal ();
W_LoadWadFile ("gfx.wad");
Key_Init ();
Con_Init ();
M_Init ();
PR_Init ();
Mod_Init ();
NET_Init ();
SV_Init ();
But once we've exited Host_Init, we enter the main loop of Quake where we update a delta time value and run game code (via Host_Frame):
oldtime = Sys_FloatTime () - 0.1;
while (1)
{
// find time spent rendering last frame
newtime = Sys_FloatTime ();
time = newtime - oldtime;
if (cls.state == ca_dedicated)
{ // play vcrfiles at max speed
if (time < sys_ticrate.value && (vcrFile == -1 || recording) )
{
usleep(1);
continue; // not time to run a server only tic yet
}
time = sys_ticrate.value;
}
if (time > sys_ticrate.value*2)
oldtime = newtime;
else
oldtime += time;
Host_Frame (time);
Note: cls is a struct defined in client.h as client_static_t that is extern'd as cls. And state is a field in this struct which is defined as an enum (cactive_t) that has three potential values: ca_dedicated, ca_disconnected, and ca_connected.
ca_dedicated: Running the game as an online server that quake players would literally connect to.
The other two values I'm not sure about. It might be the case that ca_disconnected is for quitting and final deallocation of the game, and that as you play the game normally the value is always ca_connected whether you're connected to a dedicated server or running a local one. From what I can tell, Quake employs a network architecture even as a singleplayer game.
In any case, what this main loop does every frame:
oldtime value
And finally, at the end of each iteration we call Host_Frame. This is where absolutely everything gets done, even though you can't tell at first glance.
For example, in its function body, it calls an "internal" or "shadow" function _Host_Frame and this function in turns has, among other things, these lines:
if (sv.active) Host_ServerFrame ();
Which updates all the physics. Eventually this calls Host_ServerFrame which calls the actual physics functions: SV_RunClients, SV_Physics etc.
Phew. And that's just the physics.
Alright, so we've figured out that the main loop of Quake is in a sys_*.c file, and that the bulk of behavior frame to frame is Host_Frame() and _Host_Frame(). Lets figure out where we get to the physics calculations. Well, eventually. A slight detour first.
Inside Host_Frame, ignoring some time precision stuff we enter _Host_Frame. Inside this function is:
if (sv.active)
Host_ServerFrame ();
And in this function, the third line is:
// run the world state pr_global_struct->frametime = host_frametime;
Where the hell does this pr_global_struct come from? A grep will show that its used all over the place. Often for storing some physics information but other things as well such as if our gamemode is a deathmatch or how many secrets we've found. It's obviously a struct but its seemingly not defined as one anywhere. Eventually we stumble upon pr_edict.c (awesome name) and in it:
globalvars_t *pr_global_struct;
And so another question: where and what is globalvars_t? Notice that every quake file includes quakedef.h. But even when we look in that file globalvars_t isn't present. But within this header file are more includes, and an important one: progs.h.
In progs.h we see a similar variable declaration as in pr_edict.c but as an extern:
extern globalvars_t *pr_global_struct;
So, we're on the right track. But again, where is this globalvars_t type/struct coming from? The file has two includes:
#include "pr_comp.h" // defs shared with qcc #include "progdefs.h" // generated by program cdefs
pr_comp.h doesn't show anything related, but it is useful to note that this file doesn't have any includes itself, so its self contained. That leaves progdefs.h and luckily for us, its a simple file. In its entirety:
#ifdef QUAKE2 #include "progdefs.q2" #else #include "progdefs.q1" #endif
We're interested in quake 1 so the .q1 file it is. And we finally see the actual struct that globalvars_t is:
typedef struct
{ int pad[28];
int self;
int other;
int world;
float time;
float frametime;
float force_retouch;
string_t mapname;
float deathmatch;
float coop;
float teamplay;
float serverflags;
float total_secrets;
float total_monsters;
float found_secrets;
float killed_monsters;
float parm1;
float parm2;
float parm3;
float parm4;
float parm5;
float parm6;
float parm7;
float parm8;
float parm9;
float parm10;
float parm11;
float parm12;
float parm13;
float parm14;
float parm15;
float parm16;
vec3_t v_forward;
vec3_t v_up;
vec3_t v_right;
float trace_allsolid;
float trace_startsolid;
float trace_fraction;
vec3_t trace_endpos;
vec3_t trace_plane_normal;
float trace_plane_dist;
int trace_ent;
float trace_inopen;
float trace_inwater;
int msg_entity;
func_t main;
func_t StartFrame;
func_t PlayerPreThink;
func_t PlayerPostThink;
func_t ClientKill;
func_t ClientConnect;
func_t PutClientInServer;
func_t ClientDisconnect;
func_t SetNewParms;
func_t SetChangeParms;
} globalvars_t;
There's also another struct called entvars_t in the file. So, this is a QuakeC'ism as the file indicates:
/* file generated by qcc, do not modify */
qcc = QuakeC Compiler
Looking at the struct I can't really see any coherence to this grouping of variables. It's quite random so I take it as these are variables used a lot and that needed to be stuffed somewhere.
And so, all our original line here is doing:
pr_global_struct->frametime = host_frametime;
Is updating the frametime of the local Quake server (as opposed to the client).