/* * Copyright(c) 1997-2001 Id Software, Inc. * Copyright(c) 2002 The Quakeforge Project. * Copyright(c) 2006 Quetoo. * * 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. */ #include #include "qcommon.h" #include "mdfour.h" #define MAX_PRINT_MSG 4096 #define MAX_NUM_ARGVS 64 int com_argc; char *com_argv[MAX_NUM_ARGVS + 1]; int realtime; jmp_buf abortframe; // an ERR_DROP occured, exit the entire frame cvar_t *developer; cvar_t *timescale; cvar_t *fixedtime; cvar_t *showtrace; cvar_t *dedicated; int server_state; /* CLIENT / SERVER interactions */ static int rd_target; static char *rd_buffer; static int rd_buffersize; static void (*rd_flush)(int target, char *buffer); /* Com_BeginRedirect */ void Com_BeginRedirect(int target, char *buffer, int buffersize, void(*flush)(int, char*)){ if(!target || !buffer || !buffersize || !flush) return; rd_target = target; rd_buffer = buffer; rd_buffersize = buffersize; rd_flush = flush; *rd_buffer = 0; } /* Com_EndRedirect */ void Com_EndRedirect(void){ rd_flush(rd_target, rd_buffer); rd_target = 0; rd_buffer = NULL; rd_buffersize = 0; rd_flush = NULL; } void Con_Print(char *text); /* Com_Printf Both client and server can use this, and it will output to the apropriate place. */ void Com_Printf(char *fmt, ...){ va_list argptr; char msg[MAX_PRINT_MSG]; va_start(argptr, fmt); vsnprintf(msg, MAX_PRINT_MSG, fmt, argptr); va_end(argptr); if(rd_target){ if((strlen(msg) + strlen(rd_buffer)) > (rd_buffersize - 1)){ rd_flush(rd_target, rd_buffer); *rd_buffer = 0; } strcat(rd_buffer, msg); return; } Con_Print(msg); // also echo to debugging console Sys_ConsoleOutput(msg); } /* Com_DPrintf A Com_Printf that only shows up if the "developer" cvar is set */ void Com_DPrintf(char *fmt, ...){ va_list argptr; char msg[MAX_PRINT_MSG]; if(!developer || !developer->value) return; // don't confuse non-developers with techie stuff... va_start(argptr, fmt); vsnprintf(msg, MAX_PRINT_MSG, fmt, argptr); va_end(argptr); Com_Printf("%s", msg); } /* Com_Error Both client and server can use this, and it will do the apropriate things. */ void Com_Error(int code, char *fmt, ...){ va_list argptr; static char msg[MAX_PRINT_MSG]; static qboolean recursive; if(recursive) Sys_Error("Recursive error: %s", msg); recursive = true; va_start(argptr, fmt); vsnprintf(msg, MAX_PRINT_MSG, fmt, argptr); va_end(argptr); if(code == ERR_DISCONNECT){ CL_Drop(); recursive = false; longjmp(abortframe, -1); } else if(code == ERR_DROP){ Com_Printf("********************\nERROR: %s\n********************\n", msg); SV_Shutdown(va("Server crashed: %s\n", msg), false, false); CL_Drop(); recursive = false; longjmp(abortframe, -1); } else { SV_Shutdown(va("Server fatal crashed: %s\n", msg), false, false); CL_Shutdown(); } Sys_Error("%s", msg); } /* Com_Quit Both client and server can use this, and it will do the apropriate things. */ void Com_Quit(void){ SV_Shutdown("Server quit.\n", false, false); CL_Shutdown(); Sys_Quit(); } /* Com_ServerState */ int Com_ServerState(void){ return server_state; } /* Com_SetServerState */ void Com_SetServerState(int state){ server_state = state; } /* Com_BlockChecksum */ unsigned int Com_BlockChecksum(const void *buffer, int length){ int digest[4]; unsigned int val; mdfour((unsigned char *) digest,(unsigned char *) buffer, length); val = digest[0] ^ digest[1] ^ digest[2] ^ digest[3]; return val; } /* MESSAGE IO FUNCTIONS Handles byte ordering and avoids alignment errors */ vec3_t bytedirs[NUMVERTEXNORMALS] = { #include "anorms.h" }; // writing functions void MSG_WriteChar(sizebuf_t *sb, int c){ byte *buf; buf = SZ_GetSpace(sb, 1); buf[0] = c; } void MSG_WriteByte(sizebuf_t *sb, int c){ byte *buf; buf = SZ_GetSpace(sb, 1); buf[0] = c; } void MSG_WriteShort(sizebuf_t *sb, int c){ byte *buf; buf = SZ_GetSpace(sb, 2); buf[0] = c & 0xff; buf[1] = c >> 8; } void MSG_WriteLong(sizebuf_t *sb, int c){ byte *buf; buf = SZ_GetSpace(sb, 4); buf[0] = c & 0xff; buf[1] = (c >> 8) & 0xff; buf[2] = (c >> 16) & 0xff; buf[3] = c >> 24; } void MSG_WriteFloat(sizebuf_t *sb, float f){ union { float f; int l; } dat; dat.f = f; dat.l = LittleLong(dat.l); SZ_Write(sb, &dat.l, 4); } void MSG_WriteString(sizebuf_t *sb, char *s){ if(!s) SZ_Write(sb, "", 1); else SZ_Write(sb, s, strlen(s) + 1); } void MSG_WriteCoord(sizebuf_t *sb, float f){ MSG_WriteShort(sb, (int)(f * 8)); } void MSG_WritePos(sizebuf_t *sb, vec3_t pos){ MSG_WriteShort(sb, (int)(pos[0] * 8)); MSG_WriteShort(sb, (int)(pos[1] * 8)); MSG_WriteShort(sb, (int)(pos[2] * 8)); } void MSG_WriteAngle(sizebuf_t *sb, float f){ MSG_WriteByte(sb, (int)(f * 256 / 360) & 255); } void MSG_WriteAngle16(sizebuf_t *sb, float f){ MSG_WriteShort(sb, ANGLE2SHORT(f)); } void MSG_WriteDeltaUsercmd(sizebuf_t *buf, usercmd_t *from, usercmd_t *cmd){ int bits; // send the movement message bits = 0; if(cmd->angles[0] != from->angles[0]) bits |= CM_ANGLE1; if(cmd->angles[1] != from->angles[1]) bits |= CM_ANGLE2; if(cmd->angles[2] != from->angles[2]) bits |= CM_ANGLE3; if(cmd->forwardmove != from->forwardmove) bits |= CM_FORWARD; if(cmd->sidemove != from->sidemove) bits |= CM_SIDE; if(cmd->upmove != from->upmove) bits |= CM_UP; if(cmd->buttons != from->buttons) bits |= CM_BUTTONS; if(cmd->impulse != from->impulse) bits |= CM_IMPULSE; MSG_WriteByte(buf, bits); if(bits & CM_ANGLE1) MSG_WriteShort(buf, cmd->angles[0]); if(bits & CM_ANGLE2) MSG_WriteShort(buf, cmd->angles[1]); if(bits & CM_ANGLE3) MSG_WriteShort(buf, cmd->angles[2]); if(bits & CM_FORWARD) MSG_WriteShort(buf, cmd->forwardmove); if(bits & CM_SIDE) MSG_WriteShort(buf, cmd->sidemove); if(bits & CM_UP) MSG_WriteShort(buf, cmd->upmove); if(bits & CM_BUTTONS) MSG_WriteByte(buf, cmd->buttons); if(bits & CM_IMPULSE) MSG_WriteByte(buf, cmd->impulse); MSG_WriteByte(buf, cmd->msec); MSG_WriteByte(buf, 128); //cmd->lightlevel } void MSG_WriteDir(sizebuf_t *sb, vec3_t dir){ int i, best; float d, bestd; if(!dir){ MSG_WriteByte(sb, 0); return; } bestd = 0; best = 0; for(i = 0; i < NUMVERTEXNORMALS; i++){ d = DotProduct(dir, bytedirs[i]); if(d > bestd){ bestd = d; best = i; } } MSG_WriteByte(sb, best); } void MSG_ReadDir(sizebuf_t *sb, vec3_t dir){ int b; b = MSG_ReadByte(sb); if(b >= NUMVERTEXNORMALS) Com_Error(ERR_DROP, "MSF_ReadDir: out of range"); VectorCopy(bytedirs[b], dir); } /* MSG_WriteDeltaEntity Writes part of a packetentities message. Can delta from either a baseline or a previous packet_entity */ void MSG_WriteDeltaEntity(entity_state_t *from, entity_state_t *to, sizebuf_t *msg, qboolean force, qboolean newentity){ int bits; if(!to->number) Com_Error(ERR_FATAL, "Unset entity number"); if(to->number >= MAX_EDICTS) Com_Error(ERR_FATAL, "Entity number >= MAX_EDICTS"); // send an update bits = 0; if(to->number >= 256) bits |= U_NUMBER16; // number8 is implicit otherwise if(to->origin[0] != from->origin[0]) bits |= U_ORIGIN1; if(to->origin[1] != from->origin[1]) bits |= U_ORIGIN2; if(to->origin[2] != from->origin[2]) bits |= U_ORIGIN3; if(to->angles[0] != from->angles[0]) bits |= U_ANGLE1; if(to->angles[1] != from->angles[1]) bits |= U_ANGLE2; if(to->angles[2] != from->angles[2]) bits |= U_ANGLE3; if(to->skinnum != from->skinnum){ if((unsigned)to->skinnum < 256) bits |= U_SKIN8; else if((unsigned)to->skinnum < 0x10000) bits |= U_SKIN16; else bits |=(U_SKIN8 | U_SKIN16); } if(to->frame != from->frame){ if(to->frame < 256) bits |= U_FRAME8; else bits |= U_FRAME16; } if(to->effects != from->effects){ if(to->effects < 256) bits |= U_EFFECTS8; else if(to->effects < 0x8000) bits |= U_EFFECTS16; else bits |= U_EFFECTS8 | U_EFFECTS16; } if(to->renderfx != from->renderfx){ if(to->renderfx < 256) bits |= U_RENDERFX8; else if(to->renderfx < 0x8000) bits |= U_RENDERFX16; else bits |= U_RENDERFX8 | U_RENDERFX16; } if(to->solid != from->solid) bits |= U_SOLID; // event is not delta compressed, just 0 compressed if(to->event) bits |= U_EVENT; if(to->modelindex != from->modelindex) bits |= U_MODEL; if(to->modelindex2 != from->modelindex2) bits |= U_MODEL2; if(to->modelindex3 != from->modelindex3) bits |= U_MODEL3; if(to->modelindex4 != from->modelindex4) bits |= U_MODEL4; if(to->sound != from->sound) bits |= U_SOUND; if(newentity ||(to->renderfx & RF_BEAM)) bits |= U_OLDORIGIN; // // write the message // if(!bits && !force) return; // nothing to send! //---------- if(bits & 0xff000000) bits |= U_MOREBITS3 | U_MOREBITS2 | U_MOREBITS1; else if(bits & 0x00ff0000) bits |= U_MOREBITS2 | U_MOREBITS1; else if(bits & 0x0000ff00) bits |= U_MOREBITS1; MSG_WriteByte(msg, bits&255); if(bits & 0xff000000){ MSG_WriteByte(msg,(bits >> 8)&255); MSG_WriteByte(msg,(bits >> 16)&255); MSG_WriteByte(msg,(bits >> 24)&255); } else if(bits & 0x00ff0000){ MSG_WriteByte(msg,(bits >> 8)&255); MSG_WriteByte(msg,(bits >> 16)&255); } else if(bits & 0x0000ff00){ MSG_WriteByte(msg,(bits >> 8)&255); } //---------- if(bits & U_NUMBER16) MSG_WriteShort(msg, to->number); else MSG_WriteByte(msg, to->number); if(bits & U_MODEL) MSG_WriteByte(msg, to->modelindex); if(bits & U_MODEL2) MSG_WriteByte(msg, to->modelindex2); if(bits & U_MODEL3) MSG_WriteByte(msg, to->modelindex3); if(bits & U_MODEL4) MSG_WriteByte(msg, to->modelindex4); if(bits & U_FRAME8) MSG_WriteByte(msg, to->frame); if(bits & U_FRAME16) MSG_WriteShort(msg, to->frame); if((bits & U_SKIN8) &&(bits & U_SKIN16)) //used for laser colors MSG_WriteLong(msg, to->skinnum); else if(bits & U_SKIN8) MSG_WriteByte(msg, to->skinnum); else if(bits & U_SKIN16) MSG_WriteShort(msg, to->skinnum); if((bits &(U_EFFECTS8 | U_EFFECTS16)) ==(U_EFFECTS8 | U_EFFECTS16)) MSG_WriteLong(msg, to->effects); else if(bits & U_EFFECTS8) MSG_WriteByte(msg, to->effects); else if(bits & U_EFFECTS16) MSG_WriteShort(msg, to->effects); if((bits &(U_RENDERFX8 | U_RENDERFX16)) ==(U_RENDERFX8 | U_RENDERFX16)) MSG_WriteLong(msg, to->renderfx); else if(bits & U_RENDERFX8) MSG_WriteByte(msg, to->renderfx); else if(bits & U_RENDERFX16) MSG_WriteShort(msg, to->renderfx); if(bits & U_ORIGIN1) MSG_WriteCoord(msg, to->origin[0]); if(bits & U_ORIGIN2) MSG_WriteCoord(msg, to->origin[1]); if(bits & U_ORIGIN3) MSG_WriteCoord(msg, to->origin[2]); if(bits & U_ANGLE1) MSG_WriteAngle(msg, to->angles[0]); if(bits & U_ANGLE2) MSG_WriteAngle(msg, to->angles[1]); if(bits & U_ANGLE3) MSG_WriteAngle(msg, to->angles[2]); if(bits & U_OLDORIGIN){ MSG_WriteCoord(msg, to->old_origin[0]); MSG_WriteCoord(msg, to->old_origin[1]); MSG_WriteCoord(msg, to->old_origin[2]); } if(bits & U_SOUND) MSG_WriteByte(msg, to->sound); if(bits & U_EVENT) MSG_WriteByte(msg, to->event); if(bits & U_SOLID) MSG_WriteShort(msg, to->solid); } // reading functions void MSG_BeginReading(sizebuf_t *msg){ msg->readcount = 0; } // returns -1 if no more characters are available int MSG_ReadChar(sizebuf_t *msg_read){ int c; if(msg_read->readcount + 1 > msg_read->cursize) c = -1; else c = (signed char)msg_read->data[msg_read->readcount]; msg_read->readcount++; return c; } int MSG_ReadByte(sizebuf_t *msg_read){ int c; if(msg_read->readcount + 1 > msg_read->cursize) c = -1; else c = (unsigned char)msg_read->data[msg_read->readcount]; msg_read->readcount++; return c; } int MSG_ReadShort(sizebuf_t *msg_read){ int c; if(msg_read->readcount + 2 > msg_read->cursize) c = -1; else c = (short)(msg_read->data[msg_read->readcount] + (msg_read->data[msg_read->readcount + 1] << 8)); msg_read->readcount += 2; return c; } int MSG_ReadLong(sizebuf_t *msg_read){ int c; if(msg_read->readcount + 4 > msg_read->cursize) c = -1; else c = msg_read->data[msg_read->readcount] + (msg_read->data[msg_read->readcount + 1] << 8) + (msg_read->data[msg_read->readcount + 2] << 16) + (msg_read->data[msg_read->readcount + 3] << 24); msg_read->readcount += 4; return c; } float MSG_ReadFloat(sizebuf_t *msg_read){ union { byte b[4]; float f; int l; } dat; if(msg_read->readcount + 4 > msg_read->cursize) dat.f = -1; else { dat.b[0] = msg_read->data[msg_read->readcount]; dat.b[1] = msg_read->data[msg_read->readcount + 1]; dat.b[2] = msg_read->data[msg_read->readcount + 2]; dat.b[3] = msg_read->data[msg_read->readcount + 3]; } msg_read->readcount += 4; dat.l = LittleLong(dat.l); return dat.f; } char *MSG_ReadString(sizebuf_t *msg_read){ static char string[2048]; int l, c; l = 0; do { c = MSG_ReadChar(msg_read); if(c == -1 || c == 0) break; string[l] = c; l++; } while(l < sizeof(string) - 1); string[l] = 0; return string; } char *MSG_ReadStringLine(sizebuf_t *msg_read){ static char string[2048]; int l, c; l = 0; do { c = MSG_ReadChar(msg_read); if(c == -1 || c == 0 || c == '\n') break; string[l] = c; l++; } while(l < sizeof(string) - 1); string[l] = 0; return string; } float MSG_ReadCoord(sizebuf_t *msg_read){ return MSG_ReadShort(msg_read) *(1.0 / 8); } void MSG_ReadPos(sizebuf_t *msg_read, vec3_t pos){ pos[0] = MSG_ReadShort(msg_read) *(1.0 / 8); pos[1] = MSG_ReadShort(msg_read) *(1.0 / 8); pos[2] = MSG_ReadShort(msg_read) *(1.0 / 8); } float MSG_ReadAngle(sizebuf_t *msg_read){ return MSG_ReadChar(msg_read) *(360.0 / 256); } float MSG_ReadAngle16(sizebuf_t *msg_read){ return SHORT2ANGLE(MSG_ReadShort(msg_read)); } void MSG_ReadDeltaUsercmd(sizebuf_t *msg_read, usercmd_t *from, usercmd_t *move){ int bits; memcpy(move, from, sizeof(*move)); bits = MSG_ReadByte(msg_read); // read current angles if(bits & CM_ANGLE1) move->angles[0] = MSG_ReadShort(msg_read); if(bits & CM_ANGLE2) move->angles[1] = MSG_ReadShort(msg_read); if(bits & CM_ANGLE3) move->angles[2] = MSG_ReadShort(msg_read); // read movement if(bits & CM_FORWARD) move->forwardmove = MSG_ReadShort(msg_read); if(bits & CM_SIDE) move->sidemove = MSG_ReadShort(msg_read); if(bits & CM_UP) move->upmove = MSG_ReadShort(msg_read); // read buttons if(bits & CM_BUTTONS) move->buttons = MSG_ReadByte(msg_read); if(bits & CM_IMPULSE) move->impulse = MSG_ReadByte(msg_read); // read time to run command move->msec = MSG_ReadByte(msg_read); // read the light level to satisfy protocol, this is no longer used move->lightlevel = MSG_ReadByte(msg_read); } void MSG_ReadData(sizebuf_t *msg_read, void *data, int len){ int i; for(i = 0; i < len; i++) ((byte *)data)[i] = MSG_ReadByte(msg_read); } /* Memory chunk management */ void SZ_Init(sizebuf_t *buf, byte *data, int length){ memset(buf, 0, sizeof(*buf)); buf->data = data; buf->maxsize = length; } void SZ_Clear(sizebuf_t *buf){ buf->cursize = 0; buf->overflowed = false; } void *SZ_GetSpace(sizebuf_t *buf, int length){ void *data; if(buf->cursize + length > buf->maxsize){ if(!buf->allowoverflow) Com_Error(ERR_FATAL, "SZ_GetSpace: overflow without allowoverflow set"); if(length > buf->maxsize) Com_Error(ERR_FATAL, "SZ_GetSpace: %i is > full buffer size", length); Com_Printf("SZ_GetSpace: overflow\n"); SZ_Clear(buf); buf->overflowed = true; } data = buf->data + buf->cursize; buf->cursize += length; return data; } void SZ_Write(sizebuf_t *buf, void *data, int length){ memcpy(SZ_GetSpace(buf, length), data, length); } void SZ_Print(sizebuf_t *buf, char *data){ int len; len = strlen(data) + 1; if(buf->cursize){ if(buf->data[buf->cursize - 1]) memcpy((byte *)SZ_GetSpace(buf, len), data, len); // no trailing 0 else memcpy((byte *)SZ_GetSpace(buf, len - 1) - 1, data, len); // write over trailing 0 } else memcpy((byte *)SZ_GetSpace(buf, len), data, len); } int COM_Argc(void){ return com_argc; } char *COM_Argv(int arg){ if(arg < 0 || arg >= com_argc || !com_argv[arg]) return ""; return com_argv[arg]; } void COM_ClearArgv(int arg){ if(arg < 0 || arg >= com_argc || !com_argv[arg]) return; com_argv[arg] = ""; } void COM_InitArgv(int argc, char **argv){ int i; if(argc > MAX_NUM_ARGVS) Com_Error(ERR_FATAL, "argc > MAX_NUM_ARGVS"); com_argc = argc; for(i = 0; i < argc; i++){ if(!argv[i] || strlen(argv[i]) >= MAX_TOKEN_CHARS) com_argv[i] = ""; else com_argv[i] = argv[i]; } } char *CopyString(char *in){ char *out; out = Z_Malloc(strlen(in) + 1); strcpy(out, in); return out; } void Info_Print(char *s){ char key[512]; char value[512]; char *o; int l; if(*s == '\\') s++; while(*s){ o = key; while(*s && *s != '\\') *o++ = *s++; l = o - key; if(l < 20){ memset(o, ' ', 20 - l); key[20] = 0; } else *o = 0; Com_Printf("%s", key); if(!*s){ Com_Printf("MISSING VALUE\n"); return; } o = value; s++; while(*s && *s != '\\') *o++ = *s++; *o = 0; if(*s) s++; Com_Printf("%s\n", value); } } /* HUNK MANAGEMENT */ byte *hunk = NULL; cvar_t *hunkmegs; int hunkbytes; int hunkofs; void Hunk_Init(void){ if(hunk) // free the old hunk free(hunk); hunkmegs = Cvar_Get("hunkmegs", "16", CVAR_LATCH); hunkbytes = hunkmegs->value * 1024 * 1024; if(!(hunk = malloc(hunkbytes))) // malloc the new one Sys_Error("Unable to allocate hunk"); hunkofs = 0; } void *Hunk_Begin(){ return hunk + hunkofs; } int Hunk_End(void *buf){ return hunk + hunkofs - (byte *)buf; } void *Hunk_Alloc(int size){ byte *b; size = (size + 31) & ~31; // round to cacheline if(hunkofs + size > hunkbytes) Sys_Error("Hunk_Alloc overflow"); b = hunk + hunkofs; hunkofs += size; return b; } void Hunk_FreeAll(void){ memset(hunk, 0, hunkbytes); hunkofs = 0; } void Hunk_Shutdown(void){ if(hunk) // free the hunk free(hunk); } /* ZONE MEMORY ALLOCATION */ #define Z_MAGIC 0x1d1d typedef struct zhead_s { struct zhead_s *prev, *next; short magic; short tag; // for group free int size; } zhead_t; zhead_t z_chain; int z_count, z_bytes; /* Z_Free */ void Z_Free(void *ptr){ zhead_t *z; z =((zhead_t *)ptr) - 1; if(z->magic != Z_MAGIC){ printf("free: %p failed\n", ptr); abort(); Com_Error(ERR_FATAL, "Z_Free: bad magic"); } z->prev->next = z->next; z->next->prev = z->prev; z_count--; z_bytes -= z->size; free(z); } /* Z_Stats_f */ void Z_Stats_f(void){ Com_Printf("%i bytes in %i blocks\n", z_bytes, z_count); } /* Z_FreeTags */ void Z_FreeTags(int tag){ zhead_t *z, *next; for(z = z_chain.next; z != &z_chain; z = next){ next = z->next; if(z->tag == tag) Z_Free((void *)(z + 1)); } } /* Z_TagMalloc */ void *Z_TagMalloc(int size, int tag){ zhead_t *z; size = size + sizeof(zhead_t); z = malloc(size); if(!z) Com_Error(ERR_FATAL, "Z_TagMalloc: failed on allocation of %i bytes", size); memset(z, 0, size); z_count++; z_bytes += size; z->magic = Z_MAGIC; z->tag = tag; z->size = size; z->next = z_chain.next; z->prev = &z_chain; z_chain.next->prev = z; z_chain.next = z; return(void *)(z + 1); } /* Z_Malloc */ void *Z_Malloc(int size){ return Z_TagMalloc(size, 0); } float frand(void){ return(rand()&32767)*(1.0 / 32767); } float crand(void){ return(rand()&32767)*(2.0 / 32767) - 1; } void Key_Init(void); /* Qcommon_Init */ void Qcommon_Init(int argc, char **argv){ char *s; if(setjmp(abortframe)) Sys_Error("Error during initialization"); z_chain.next = z_chain.prev = &z_chain; // prepare enough of the subsystems to handle // cvar and command buffer management COM_InitArgv(argc, argv); Swap_Init(); Cbuf_Init(); Cmd_Init(); Cvar_Init(); Key_Init(); // we need to add the early commands twice, because // a basedir needs to be set before execing config files, // but we want other parms to override the settings of // the config files Cbuf_AddEarlyCommands(false); Cbuf_Execute(); FS_InitFilesystem(); #include "binds.h" Cbuf_AddText(DEFAULT_BINDS); Cbuf_AddText("exec quetoo.cfg\n"); Cbuf_AddEarlyCommands(true); Cbuf_Execute(); // init commands and vars Cmd_AddCommand("z_stats", Z_Stats_f); developer = Cvar_Get("developer", "0", 0); timescale = Cvar_Get("timescale", "1", 0); fixedtime = Cvar_Get("fixedtime", "0", 0); showtrace = Cvar_Get("showtrace", "0", 0); #ifndef BUILD_CLIENT dedicated = Cvar_Get("dedicated", "1", CVAR_NOSET); #else dedicated = Cvar_Get("dedicated", "0", CVAR_NOSET); #endif s = va("Quetoo %s %s %s", VERSION, __DATE__, BUILDHOST); Cvar_Get("version", s, CVAR_SERVERINFO | CVAR_NOSET); if(dedicated->value) Cmd_AddCommand("quit", Com_Quit); else Hunk_Init(); NET_Init(); Netchan_Init(); SV_Init(); CL_Init(); Com_Printf("Quetoo initialized.\n"); // add + commands from command line if(!Cbuf_AddLateCommands() && dedicated->value){ //nothing specified, q2dm1 Cbuf_AddText("map q2dm1\n"); Cbuf_Execute(); } } /* Qcommon_Frame */ void Qcommon_Frame(volatile int msec){ char *s; if(setjmp(abortframe)) return; // an ERR_DROP was thrown if(fixedtime->value) msec = fixedtime->value; else if(timescale->value){ msec *= timescale->value; if(msec < 1) msec = 1; } if(showtrace->value){ extern int c_traces, c_brush_traces; extern int c_pointcontents; Com_Printf("%4i traces %4i points\n", c_traces, c_pointcontents); c_traces = 0; c_brush_traces = 0; c_pointcontents = 0; } do { s = Sys_ConsoleInput(); if(s) Cbuf_AddText(va("%s\n", s)); } while(s); Cbuf_Execute(); SV_Frame(msec); CL_Frame(msec); } /* Qcommon_Shutdown */ void Qcommon_Shutdown(void){ Hunk_Shutdown(); }