/* Copyright (C) 1996-1997 Id Software, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // cl_main.c -- client main loop #include "quakedef.h" // we need to declare some mouse variables here, because the menu system // references them even when on a unix system. // these two are not intended to be set directly cvar_t *cl_name; cvar_t *cl_color; cvar_t *cl_shownet; // can be 0, 1, or 2 cvar_t *cl_nolerp; cvar_t *lookspring; cvar_t *lookstrafe; cvar_t *sensitivity; cvar_t *m_pitch; cvar_t *m_yaw; cvar_t *m_forward; cvar_t *m_side; cvar_t *m_look; // 2001-12-16 M_LOOK cvar by Heffo/Maddes cvar_t *cl_showfps; // 2001-11-31 FPS display by QuakeForge/Muff cvar_t *cl_compatibility; // 2001-12-24 Keeping full backwards compatibility by Maddes // 2001-09-20 Configurable entity limits by Maddes start cvar_t *cl_entities_min; cvar_t *cl_entities_min_static; cvar_t *cl_entities_min_temp; // 2001-09-20 Configurable entity limits by Maddes end client_static_t cls; client_state_t cl; // FIXME: put these on hunk? efrag_t cl_efrags[MAX_EFRAGS]; // 2001-09-20 Configurable entity limits by Maddes start /* entity_t cl_entities[MAX_EDICTS]; entity_t cl_static_entities[MAX_STATIC_ENTITIES]; */ entity_t *cl_entities; entity_t *cl_static_entities; // 2001-09-20 Configurable entity limits by Maddes end lightstyle_t cl_lightstyle[MAX_LIGHTSTYLES]; dlight_t cl_dlights[MAX_DLIGHTS]; int cl_numvisedicts; entity_t *cl_visedicts[MAX_VISEDICTS]; /* ===================== CL_ClearState ===================== */ void CL_ClearState (void) { int i; if (!sv.active) Host_ClearMemory (); // wipe the entire cl structure memset (&cl, 0, sizeof(cl)); SZ_Clear (&cls.message); // clear other arrays memset (cl_efrags, 0, sizeof(cl_efrags)); // memset (cl_entities, 0, sizeof(cl_entities)); // 2001-09-20 Configurable entity limits by Maddes memset (cl_dlights, 0, sizeof(cl_dlights)); memset (cl_lightstyle, 0, sizeof(cl_lightstyle)); // memset (cl_temp_entities, 0, sizeof(cl_temp_entities)); // 2001-09-20 Configurable entity limits by Maddes memset (cl_beams, 0, sizeof(cl_beams)); // // allocate the efrags and chain together into a free list // cl.free_efrags = cl_efrags; for (i=0 ; iCL by Maddes start if (!sv.active) { Cvar_Set(nvs_current_ssvc, "0"); } Cvar_Set(nvs_current_csvc, "0"); Cvar_Set(nvs_current_cclc, "0"); // 2001-12-24 Keeping full backwards compatibility by Maddes start if (!(cl_compatibility->value)) // request, unlike the original Quake executable { // 2001-12-24 Keeping full backwards compatibility by Maddes end MSG_WriteByte (&cls.message, clc_stringcmd); MSG_WriteString (&cls.message, va("nvs_request %1.2f\n", nvs_current_cclc->maxvalue)); } // 2001-12-24 Keeping full backwards compatibility by Maddes // 2000-04-30 NVS HANDSHAKE SRV<->CL by Maddes end // 2001-09-20 Configurable limits by Maddes start // 2001-09-20 Configurable entity limits by Maddes start cl.max_edicts = 0; cl.max_static_edicts = 0; cl.max_temp_edicts = 0; cl_entities = NULL; cl_static_entities = NULL; cl_temp_entities = NULL; // 2001-09-20 Configurable entity limits by Maddes end // 2001-12-24 Keeping full backwards compatibility by Maddes start if (!(cl_compatibility->value)) // request, unlike the original Quake executable { // 2001-12-24 Keeping full backwards compatibility by Maddes end MSG_WriteByte (&cls.message, clc_stringcmd); MSG_WriteString (&cls.message, "limit_request\n"); } // 2001-12-24 Keeping full backwards compatibility by Maddes // 2001-09-20 Configurable limits by Maddes end MSG_WriteByte (&cls.message, clc_stringcmd); MSG_WriteString (&cls.message, "prespawn\n"); break; case 2: MSG_WriteByte (&cls.message, clc_stringcmd); MSG_WriteString (&cls.message, va("name \"%s\"\n", cl_name->string)); MSG_WriteByte (&cls.message, clc_stringcmd); MSG_WriteString (&cls.message, va("color %i %i\n", ((int)cl_color->value)>>4, ((int)cl_color->value)&15)); MSG_WriteByte (&cls.message, clc_stringcmd); sprintf (str, "spawn %s\n", cls.spawnparms); MSG_WriteString (&cls.message, str); break; case 3: MSG_WriteByte (&cls.message, clc_stringcmd); MSG_WriteString (&cls.message, "begin\n"); Cache_Report (); // print remaining memory break; case 4: SCR_EndLoadingPlaque (); // allow normal screen updates break; } } /* ===================== CL_NextDemo Called to play the next demo in the demo loop ===================== */ void CL_NextDemo (void) { char str[1024]; if (cls.demonum == -1) return; // don't play demos SCR_BeginLoadingPlaque (); if (!cls.demos[cls.demonum][0] || cls.demonum == MAX_DEMOS) { cls.demonum = 0; if (!cls.demos[cls.demonum][0]) { Con_Printf ("No demos listed with startdemos\n"); cls.demonum = -1; return; } } sprintf (str,"playdemo %s\n", cls.demos[cls.demonum]); Cbuf_InsertText (str); cls.demonum++; } /* ============== CL_PrintEntities_f ============== */ void CL_PrintEntities_f (void) { entity_t *ent; int i; for (i=0,ent=cl_entities ; imodel) { Con_Printf ("EMPTY\n"); continue; } Con_Printf ("%s:%2i (%5.1f,%5.1f,%5.1f) [%5.1f %5.1f %5.1f]\n" ,ent->model->name,ent->frame, ent->origin[0], ent->origin[1], ent->origin[2], ent->angles[0], ent->angles[1], ent->angles[2]); } } /* =============== SetPal Debugging tool, just flashes the screen =============== */ void SetPal (int i) { #if 0 static int old; byte pal[768]; int c; if (i == old) return; old = i; if (i==0) VID_SetPalette (host_basepal); else if (i==1) { for (c=0 ; c<768 ; c+=3) { pal[c] = 0; pal[c+1] = 255; pal[c+2] = 0; } VID_SetPalette (pal); } else { for (c=0 ; c<768 ; c+=3) { pal[c] = 0; pal[c+1] = 0; pal[c+2] = 255; } VID_SetPalette (pal); } #endif } /* =============== CL_AllocDlight =============== */ dlight_t *CL_AllocDlight (int key) { int i; dlight_t *dl; // first look for an exact key match if (key) { dl = cl_dlights; for (i=0 ; ikey == key) { memset (dl, 0, sizeof(*dl)); dl->key = key; // 2001-09-11 Colored lightning by LordHavoc/Sarcazm/Maddes start dl->color[0] = dl->color[1] = dl->color[2] = 1.0f; dl->flashcolor[0] = 1.0f; dl->flashcolor[1] = 0.5f; dl->flashcolor[2] = 0.0f; dl->flashcolor[3] = 0.2f; // 2001-09-11 Colored lightning by LordHavoc/Sarcazm/Maddes end return dl; } } } // then look for anything else dl = cl_dlights; for (i=0 ; idie < cl.time) { memset (dl, 0, sizeof(*dl)); dl->key = key; // 2001-09-11 Colored lightning by LordHavoc/Sarcazm/Maddes start dl->color[0] = dl->color[1] = dl->color[2] = 1.0f; dl->flashcolor[0] = 1.0f; dl->flashcolor[1] = 0.5f; dl->flashcolor[2] = 0.0f; dl->flashcolor[3] = 0.2f; // 2001-09-11 Colored lightning by LordHavoc/Sarcazm/Maddes end return dl; } } dl = &cl_dlights[0]; memset (dl, 0, sizeof(*dl)); dl->key = key; // 2001-09-11 Colored lightning by LordHavoc/Sarcazm/Maddes start dl->color[0] = dl->color[1] = dl->color[2] = 1.0f; dl->flashcolor[0] = 1.0f; dl->flashcolor[1] = 0.5f; dl->flashcolor[2] = 0.0f; dl->flashcolor[3] = 0.2f; // 2001-09-11 Colored lightning by LordHavoc/Sarcazm/Maddes end return dl; } /* =============== CL_DecayLights =============== */ void CL_DecayLights (void) { int i; dlight_t *dl; float time; time = cl.time - cl.oldtime; dl = cl_dlights; for (i=0 ; idie < cl.time || !dl->radius) continue; dl->radius -= time*dl->decay; if (dl->radius < 0) dl->radius = 0; } } /* =============== CL_LerpPoint Determines the fraction between the last two messages that the objects should be put at. =============== */ float CL_LerpPoint (void) { float f, frac; f = cl.mtime[0] - cl.mtime[1]; if (!f || cl_nolerp->value || cls.timedemo || sv.active) { cl.time = cl.mtime[0]; return 1; } if (f > 0.1) { // dropped packet, or start of demo cl.mtime[1] = cl.mtime[0] - 0.1; f = 0.1; } frac = (cl.time - cl.mtime[1]) / f; //Con_Printf ("frac: %f\n",frac); if (frac < 0) { if (frac < -0.01) { SetPal(1); cl.time = cl.mtime[1]; // Con_Printf ("low frac\n"); } frac = 0; } else if (frac > 1) { if (frac > 1.01) { SetPal(2); cl.time = cl.mtime[0]; // Con_Printf ("high frac\n"); } frac = 1; } else SetPal(0); return frac; } /* =============== CL_RelinkEntities =============== */ void CL_RelinkEntities (void) { entity_t *ent; int i, j; float frac, f, d; vec3_t delta; float bobjrotate; vec3_t oldorg; dlight_t *dl; // determine partial update time frac = CL_LerpPoint (); cl_numvisedicts = 0; // // interpolate player info // for (i=0 ; i<3 ; i++) cl.velocity[i] = cl.mvelocity[1][i] + frac * (cl.mvelocity[0][i] - cl.mvelocity[1][i]); if (cls.demoplayback) { // interpolate the angles for (j=0 ; j<3 ; j++) { d = cl.mviewangles[0][j] - cl.mviewangles[1][j]; if (d > 180) d -= 360; else if (d < -180) d += 360; cl.viewangles[j] = cl.mviewangles[1][j] + frac*d; } } bobjrotate = anglemod(100*cl.time); // start on the entity after the world for (i=1,ent=cl_entities+1 ; imodel) { // empty slot if (ent->forcelink) R_RemoveEfrags (ent); // just became empty continue; } // if the object wasn't included in the last packet, remove it if (ent->msgtime != cl.mtime[0]) { ent->model = NULL; continue; } VectorCopy (ent->origin, oldorg); if (ent->forcelink) { // the entity was not updated in the last message // so move to the final spot VectorCopy (ent->msg_origins[0], ent->origin); VectorCopy (ent->msg_angles[0], ent->angles); } else { // if the delta is large, assume a teleport and don't lerp f = frac; for (j=0 ; j<3 ; j++) { delta[j] = ent->msg_origins[0][j] - ent->msg_origins[1][j]; if (delta[j] > 100 || delta[j] < -100) f = 1; // assume a teleportation, not a motion } // interpolate the origin and angles for (j=0 ; j<3 ; j++) { ent->origin[j] = ent->msg_origins[1][j] + f*delta[j]; d = ent->msg_angles[0][j] - ent->msg_angles[1][j]; if (d > 180) d -= 360; else if (d < -180) d += 360; ent->angles[j] = ent->msg_angles[1][j] + f*d; } } // rotate binary objects locally if (ent->model->flags & EF_ROTATE) ent->angles[1] = bobjrotate; if (ent->effects & EF_BRIGHTFIELD) R_EntityParticles (ent); #ifdef QUAKE2 if (ent->effects & EF_DARKFIELD) R_DarkFieldParticles (ent); #endif if (ent->effects & EF_MUZZLEFLASH) { vec3_t fv, rv, uv; dl = CL_AllocDlight (i); VectorCopy (ent->origin, dl->origin); dl->origin[2] += 16; AngleVectors (ent->angles, fv, rv, uv); VectorMA (dl->origin, 18, fv, dl->origin); dl->radius = 200 + (rand()&31); dl->minlight = 32; dl->die = cl.time + 0.1; } if (ent->effects & EF_BRIGHTLIGHT) { dl = CL_AllocDlight (i); VectorCopy (ent->origin, dl->origin); dl->origin[2] += 16; dl->radius = 400 + (rand()&31); dl->die = cl.time + 0.001; } if (ent->effects & EF_DIMLIGHT) { dl = CL_AllocDlight (i); VectorCopy (ent->origin, dl->origin); dl->radius = 200 + (rand()&31); dl->die = cl.time + 0.001; } #ifdef QUAKE2 if (ent->effects & EF_DARKLIGHT) { dl = CL_AllocDlight (i); VectorCopy (ent->origin, dl->origin); dl->radius = 200.0 + (rand()&31); dl->die = cl.time + 0.001; dl->dark = true; } if (ent->effects & EF_LIGHT) { dl = CL_AllocDlight (i); VectorCopy (ent->origin, dl->origin); dl->radius = 200; dl->die = cl.time + 0.001; } #endif if (ent->model->flags & EF_GIB) R_RocketTrail (oldorg, ent->origin, 2); else if (ent->model->flags & EF_ZOMGIB) R_RocketTrail (oldorg, ent->origin, 4); else if (ent->model->flags & EF_TRACER) R_RocketTrail (oldorg, ent->origin, 3); else if (ent->model->flags & EF_TRACER2) R_RocketTrail (oldorg, ent->origin, 5); else if (ent->model->flags & EF_ROCKET) { R_RocketTrail (oldorg, ent->origin, 0); dl = CL_AllocDlight (i); VectorCopy (ent->origin, dl->origin); dl->radius = 200; dl->die = cl.time + 0.01; } else if (ent->model->flags & EF_GRENADE) R_RocketTrail (oldorg, ent->origin, 1); else if (ent->model->flags & EF_TRACER3) R_RocketTrail (oldorg, ent->origin, 6); ent->forcelink = false; if (i == cl.viewentity && !chase_active->value) continue; #ifdef QUAKE2 if ( ent->effects & EF_NODRAW ) continue; #endif if (cl_numvisedicts < MAX_VISEDICTS) { cl_visedicts[cl_numvisedicts] = ent; cl_numvisedicts++; } } } /* =============== CL_ReadFromServer Read all incoming data from the server =============== */ int CL_ReadFromServer (void) { int ret; cl.oldtime = cl.time; cl.time += host_frametime; do { ret = CL_GetMessage (); if (ret == -1) Host_Error ("CL_ReadFromServer: lost server connection"); if (!ret) break; cl.last_received_message = realtime; CL_ParseServerMessage (); } while (ret && cls.state == ca_connected); if (cl_shownet->value) Con_Printf ("\n"); CL_RelinkEntities (); CL_UpdateTEnts (); // // bring the links up to date // return 0; } /* ================= CL_SendCmd ================= */ void CL_SendCmd (void) { usercmd_t cmd; if (cls.state != ca_connected) return; if (cls.signon == SIGNONS) { // get basic movement from keyboard CL_BaseMove (&cmd); // allow mice or other external controllers to add to the move IN_Move (&cmd); // send the unreliable message CL_SendMove (&cmd); } if (cls.demoplayback) { SZ_Clear (&cls.message); return; } // send the reliable message if (!cls.message.cursize) return; // no message at all if (!NET_CanSendMessage (cls.netcon)) { Con_DPrintf ("CL_WriteToServer: can't send\n"); return; } if (NET_SendMessage (cls.netcon, &cls.message) == -1) Host_Error ("CL_WriteToServer: lost server connection"); SZ_Clear (&cls.message); } // 2001-12-16 M_LOOK cvar by Heffo/Maddes start void Callback_M_Look (cvar_t *var) { if ( !((in_mlook.state & 1) ^ ((int)m_look->value & 1)) && lookspring->value) V_StartPitchDrift(); } // 2001-12-16 M_LOOK cvar by Heffo/Maddes end // 2001-09-18 New cvar system by Maddes (Init) start /* ================= CL_Init_Cvars ================= */ void CL_Init_Cvars (void) { cl_name = Cvar_Get ("_cl_name", "player", CVAR_ARCHIVE|CVAR_ORIGINAL); cl_color = Cvar_Get ("_cl_color", "0", CVAR_ARCHIVE|CVAR_ORIGINAL); cl_upspeed = Cvar_Get ("cl_upspeed", "200", CVAR_ORIGINAL); cl_forwardspeed = Cvar_Get ("cl_forwardspeed", "200", CVAR_ARCHIVE|CVAR_ORIGINAL); cl_backspeed = Cvar_Get ("cl_backspeed", "200", CVAR_ARCHIVE|CVAR_ORIGINAL); cl_sidespeed = Cvar_Get ("cl_sidespeed", "350", CVAR_ORIGINAL); cl_movespeedkey = Cvar_Get ("cl_movespeedkey", "2.0", CVAR_ORIGINAL); cl_yawspeed = Cvar_Get ("cl_yawspeed", "140", CVAR_ORIGINAL); cl_pitchspeed = Cvar_Get ("cl_pitchspeed", "150", CVAR_ORIGINAL); cl_anglespeedkey = Cvar_Get ("cl_anglespeedkey", "1.5", CVAR_ORIGINAL); cl_shownet = Cvar_Get ("cl_shownet", "0", CVAR_ORIGINAL); // 2001-09-18 New cvar system by Maddes start Cvar_SetRangecheck (cl_shownet, Cvar_RangecheckInt, 0, 2); Cvar_Set(cl_shownet, cl_shownet->string); // do rangecheck // 2001-09-18 New cvar system by Maddes end cl_nolerp = Cvar_Get ("cl_nolerp", "0", CVAR_ORIGINAL); lookspring = Cvar_Get ("lookspring", "0", CVAR_ARCHIVE|CVAR_ORIGINAL); lookstrafe = Cvar_Get ("lookstrafe", "0", CVAR_ARCHIVE|CVAR_ORIGINAL); sensitivity = Cvar_Get ("sensitivity", "3", CVAR_ARCHIVE|CVAR_ORIGINAL); m_pitch = Cvar_Get ("m_pitch", "0.022", CVAR_ARCHIVE|CVAR_ORIGINAL); m_yaw = Cvar_Get ("m_yaw", "0.022", CVAR_ARCHIVE|CVAR_ORIGINAL); m_forward = Cvar_Get ("m_forward", "1", CVAR_ARCHIVE|CVAR_ORIGINAL); m_side = Cvar_Get ("m_side", "0.8", CVAR_ARCHIVE|CVAR_ORIGINAL); // 2001-12-16 M_LOOK cvar by Heffo/Maddes start m_look = Cvar_Get ("m_look", "0", CVAR_ARCHIVE); Cvar_SetRangecheck (m_look, Cvar_RangecheckBool, 0, 1); Cvar_SetCallback (m_look, Callback_M_Look); Cvar_Set(m_look, m_look->string); // do rangecheck // 2001-12-16 M_LOOK cvar by Heffo/Maddes end // 2001-11-31 FPS display by QuakeForge/Muff start cl_showfps = Cvar_Get ("cl_showfps", "0", CVAR_NONE); Cvar_SetRangecheck (cl_showfps, Cvar_RangecheckBool, 0, 1); Cvar_Set(cl_showfps, cl_showfps->string); // do rangecheck // 2001-11-31 FPS display by QuakeForge/Muff end // 2001-12-24 Keeping full backwards compatibility by Maddes start cl_compatibility = Cvar_Get ("cl_compatibility", "0", CVAR_ARCHIVE); Cvar_SetRangecheck (cl_compatibility, Cvar_RangecheckBool, 0, 1); Cvar_SetDescription (cl_compatibility, "When set to 1, this client will not request enhanced information from the server (server's entity limit, NVS handshake, etc.) and disables enhanced client messages (precise client aiming, etc.). This is necessary for recording demos that shall run on all Quake executables. Also see SV_COMPATIBILITY."); Cvar_Set(cl_compatibility, cl_compatibility->string); // do rangecheck // 2001-12-24 Keeping full backwards compatibility by Maddes end // 2001-09-20 Configurable entity limits by Maddes start cl_entities_min = Cvar_Get ("cl_entities_min", "0", CVAR_NONE); Cvar_SetRangecheck (cl_entities_min, Cvar_RangecheckInt, MIN_EDICTS, MAX_EDICTS); Cvar_Set(cl_entities_min, cl_entities_min->string); // do rangecheck cl_entities_min_static = Cvar_Get ("cl_entities_min_static", "0", CVAR_NONE); Cvar_SetRangecheck (cl_entities_min_static, Cvar_RangecheckInt, MIN_STATIC_ENTITIES, MAX_EDICTS); Cvar_Set(cl_entities_min_static, cl_entities_min_static->string); // do rangecheck cl_entities_min_temp = Cvar_Get ("cl_entities_min_temp", "0", CVAR_NONE); Cvar_SetRangecheck (cl_entities_min_temp, Cvar_RangecheckInt, MIN_TEMP_ENTITIES, MAX_EDICTS); Cvar_Set(cl_entities_min_temp, cl_entities_min_temp->string); // do rangecheck // 2001-09-20 Configurable entity limits by Maddes end } // 2001-09-18 New cvar system by Maddes (Init) end /* ================= CL_Init ================= */ void CL_Init (void) { SZ_Alloc (&cls.message, 1024); CL_InitInput (); CL_InitTEnts (); // // register our commands // // 2001-09-18 New cvar system by Maddes (Init) start /* cl_name = Cvar_Get ("_cl_name", "player", CVAR_ARCHIVE|CVAR_ORIGINAL); cl_color = Cvar_Get ("_cl_color", "0", CVAR_ARCHIVE|CVAR_ORIGINAL); cl_upspeed = Cvar_Get ("cl_upspeed", "200", CVAR_ORIGINAL); cl_forwardspeed = Cvar_Get ("cl_forwardspeed", "200", CVAR_ARCHIVE|CVAR_ORIGINAL); cl_backspeed = Cvar_Get ("cl_backspeed", "200", CVAR_ARCHIVE|CVAR_ORIGINAL); cl_sidespeed = Cvar_Get ("cl_sidespeed", "350", CVAR_ORIGINAL); cl_movespeedkey = Cvar_Get ("cl_movespeedkey", "2.0", CVAR_ORIGINAL); cl_yawspeed = Cvar_Get ("cl_yawspeed", "140", CVAR_ORIGINAL); cl_pitchspeed = Cvar_Get ("cl_pitchspeed", "150", CVAR_ORIGINAL); cl_anglespeedkey = Cvar_Get ("cl_anglespeedkey", "1.5", CVAR_ORIGINAL); cl_shownet = Cvar_Get ("cl_shownet", "0", CVAR_ORIGINAL); // 2001-09-18 New cvar system by Maddes start Cvar_SetRangecheck (cl_shownet, Cvar_RangecheckInt, 0, 2); Cvar_Set(cl_shownet, cl_shownet->string); // do rangecheck // 2001-09-18 New cvar system by Maddes end cl_nolerp = Cvar_Get ("cl_nolerp", "0", CVAR_ORIGINAL); lookspring = Cvar_Get ("lookspring", "0", CVAR_ARCHIVE|CVAR_ORIGINAL); lookstrafe = Cvar_Get ("lookstrafe", "0", CVAR_ARCHIVE|CVAR_ORIGINAL); sensitivity = Cvar_Get ("sensitivity", "3", CVAR_ARCHIVE|CVAR_ORIGINAL); m_pitch = Cvar_Get ("m_pitch", "0.022", CVAR_ARCHIVE|CVAR_ORIGINAL); m_yaw = Cvar_Get ("m_yaw", "0.022", CVAR_ARCHIVE|CVAR_ORIGINAL); m_forward = Cvar_Get ("m_forward", "1", CVAR_ARCHIVE|CVAR_ORIGINAL); m_side = Cvar_Get ("m_side", "0.8", CVAR_ARCHIVE|CVAR_ORIGINAL); */ // 2001-09-18 New cvar system by Maddes (Init) end // Cvar_RegisterVariable (&cl_autofire); Cmd_AddCommand ("entities", CL_PrintEntities_f); Cmd_AddCommand ("disconnect", CL_Disconnect_f); Cmd_AddCommand ("record", CL_Record_f); Cmd_AddCommand ("stop", CL_Stop_f); Cmd_AddCommand ("playdemo", CL_PlayDemo_f); Cmd_AddCommand ("timedemo", CL_TimeDemo_f); }