common.c

Go to the documentation of this file.
00001 
00006 /*
00007 Copyright (C) 1997-2001 Id Software, Inc.
00008 
00009 This program is free software; you can redistribute it and/or
00010 modify it under the terms of the GNU General Public License
00011 as published by the Free Software Foundation; either version 2
00012 of the License, or (at your option) any later version.
00013 
00014 This program is distributed in the hope that it will be useful,
00015 but WITHOUT ANY WARRANTY; without even the implied warranty of
00016 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00017 
00018 See the GNU General Public License for more details.
00019 
00020 You should have received a copy of the GNU General Public License
00021 along with this program; if not, write to the Free Software
00022 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
00023 
00024 */
00025 
00026 #include "common.h"
00027 #include "../server/server.h"
00028 #include "../shared/parse.h"
00029 #include "../ports/system.h"
00030 #include <setjmp.h>
00031 
00032 #define MAXPRINTMSG 4096
00033 #define MAX_NUM_ARGVS   50
00034 
00035 csi_t csi;
00036 
00037 static int com_argc;
00038 static const char *com_argv[MAX_NUM_ARGVS + 1];
00039 
00040 static jmp_buf abortframe; /* an ERR_DROP occured, exit the entire frame */
00041 
00042 cvar_t *developer;
00043 cvar_t *http_proxy;
00044 cvar_t *http_timeout;
00045 static cvar_t *logfile_active; /* 1 = buffer log, 2 = flush after each print */
00046 cvar_t *sv_dedicated;
00047 #ifndef DEDICATED_ONLY
00048 static cvar_t *cl_maxfps;
00049 cvar_t *s_language;
00050 #endif
00051 cvar_t *sv_gametype;
00052 cvar_t *masterserver_url;
00053 cvar_t *port;
00054 cvar_t* sys_priority;
00055 cvar_t* sys_affinity;
00056 cvar_t* sys_os;
00057 
00058 static qFILE logfile;
00059 
00060 struct memPool_s *com_aliasSysPool;
00061 struct memPool_s *com_cmdSysPool;
00062 struct memPool_s *com_cmodelSysPool;
00063 struct memPool_s *com_cvarSysPool;
00064 struct memPool_s *com_fileSysPool;
00065 struct memPool_s *com_genericPool;
00066 struct memPool_s *com_networkPool;
00067 
00068 struct event {
00069     int when;
00070     event_func *func;
00071     event_check_func *check;
00072     event_clean_func *clean;
00073     void *data;
00074     struct event *next;
00075 };
00076 
00077 static struct event *event_queue = NULL;
00078 
00079 #define TIMER_CHECK_INTERVAL 100
00080 #define TIMER_CHECK_LAG 3
00081 #define TIMER_LATENESS_HIGH 200
00082 #define TIMER_LATENESS_LOW 50
00083 #define TIMER_LATENESS_HISTORY 32
00084 
00085 struct timer {
00086     cvar_t *min_freq;
00087     int interval;
00088     int recent_lateness[TIMER_LATENESS_HISTORY];
00089     int next_lateness;
00090     int total_lateness;
00091     int next_check;
00092     int checks_high;
00093     int checks_low;
00094 
00095     event_func *func;
00096     void *data;
00097 };
00098 
00099 /*
00100 ==============================================================================
00101 TARGETING FUNCTIONS
00102 ==============================================================================
00103 */
00104 
00222 float Com_GrenadeTarget (const vec3_t from, const vec3_t at, float speed, qboolean launched, qboolean rolled, vec3_t v0)
00223 {
00224     const float rollAngle = 3.0; /* angle to throw at for rolling, in degrees. */
00225     vec3_t delta;
00226     float d, h, g, v, alpha, vx, vy;
00227     float k, gd2, len;
00228 
00229     /* calculate target distance and height */
00230     h = at[2] - from[2];
00231     VectorSubtract(at, from, delta);
00232     delta[2] = 0;
00233     d = VectorLength(delta);
00234 
00235     /* check that it's not degenerate */
00236     if (d == 0) {
00237         return 0;
00238     }
00239 
00240     /* precalculate some useful values */
00241     g = GRAVITY;
00242     gd2 = g * d * d;
00243     len = sqrt(h * h + d * d);
00244 
00245     /* are we rolling? */
00246     if (rolled) {
00247         float theta;
00248         alpha = rollAngle * torad;
00249         theta = atan2(d, -h) - 2 * alpha;
00250         k = gd2 / (len * cos(theta) - h);
00251         if (k <= 0) /* impossible shot at any velocity */
00252             return 0;
00253         v = sqrt(k);
00254     } else {
00255         /* firstly try with the maximum speed possible */
00256         v = speed;
00257         k = (v * v * h + gd2) / (v * v * len);
00258 
00259         /* check whether the shot's possible */
00260         if (launched && k >= -1 && k <= 1) {
00261             /* it is possible, so calculate the angle */
00262             alpha = 0.5 * (atan2(d, -h) - acos(k));
00263         } else {
00264             /* calculate the minimum possible velocity that would make it possible */
00265             alpha = 0.5 * atan2(d, -h);
00266             v = sqrt(gd2 / (len - h));
00267         }
00268     }
00269 
00270     /* calculate velocities */
00271     vx = v * cos(alpha);
00272     vy = v * sin(alpha);
00273     VectorNormalize(delta);
00274     VectorScale(delta, vx, v0);
00275     v0[2] = vy;
00276 
00277     /* prevent any rounding errors */
00278     VectorNormalize(v0);
00279     VectorScale(v0, v - DIST_EPSILON, v0);
00280 
00281     /* return time */
00282     return d / vx;
00283 }
00284 
00285 /*
00286 ============================================================================
00287 CLIENT / SERVER interactions
00288 ============================================================================
00289 */
00290 
00291 static char *rd_buffer;
00292 static unsigned int rd_buffersize;
00293 static struct net_stream *rd_stream;
00294 
00301 void Com_BeginRedirect (struct net_stream *stream, char *buffer, int buffersize)
00302 {
00303     if (!buffer || !buffersize)
00304         return;
00305 
00306     rd_stream = stream;
00307     rd_buffer = buffer;
00308     if (buffersize > MAXPRINTMSG)
00309         Com_Error(ERR_DROP, "redirect buffer may not be bigger than MAXPRINTMSG (%i)", MAXPRINTMSG);
00310     rd_buffersize = buffersize;
00311     rd_buffer[0] = '\0';
00312 }
00313 
00318 void Com_EndRedirect (void)
00319 {
00320     NET_OOB_Printf(rd_stream, "print\n%s", rd_buffer);
00321 
00322     rd_stream = NULL;
00323     rd_buffer = NULL;
00324     rd_buffersize = 0;
00325 }
00326 
00332 void Com_MakeTimestamp (char* ts, const size_t tslen)
00333 {
00334     struct tm *t;
00335     time_t aclock;
00336 
00337     time(&aclock);
00338     t = localtime(&aclock);
00339 
00340     Com_sprintf(ts, tslen, "%4i/%02i/%02i %02i:%02i:%02i", t->tm_year + 1900,
00341             t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec);
00342 }
00343 
00348 void Com_vPrintf (const char *fmt, va_list ap)
00349 {
00350     char msg[MAXPRINTMSG];
00351 
00352     Q_vsnprintf(msg, sizeof(msg), fmt, ap);
00353 
00354     /* redirect the output? */
00355     if (rd_buffer) {
00356         if ((strlen(msg) + strlen(rd_buffer)) > (rd_buffersize - 1)) {
00357             NET_OOB_Printf(rd_stream, "print\n%s", rd_buffer);
00358             rd_buffer[0] = '\0';
00359         }
00360         Q_strcat(rd_buffer, msg, sizeof(char) * rd_buffersize);
00361         return;
00362     }
00363 
00364     Con_Print(msg);
00365 
00366     /* also echo to debugging console */
00367     Sys_ConsoleOutput(msg);
00368 
00369     /* logfile */
00370     if (logfile_active && logfile_active->integer) {
00371         if (!logfile.f) {
00372             const char *name = "ufoconsole.log";
00373             if (logfile_active->integer > 2)
00374                 FS_OpenFile(name, &logfile, FILE_APPEND);
00375             else
00376                 FS_OpenFile(name, &logfile, FILE_WRITE);
00377         }
00378         if (logfile.f) {
00379             /* strip color codes */
00380             const char *output = msg;
00381 
00382             /* skip color char */
00383             if (!strncmp(output, COLORED_GREEN, strlen(COLORED_GREEN)))
00384                 output += strlen(COLORED_GREEN);
00385 
00386             if (output[strlen(output) - 1] == '\n') {
00387                 char timestamp[40];
00388                 Com_MakeTimestamp(timestamp, sizeof(timestamp));
00389                 FS_Write(timestamp, strlen(timestamp), &logfile);
00390             }
00391 
00392             FS_Write(output, strlen(output), &logfile);
00393 
00394             if (logfile_active->integer > 1)
00395                 fflush(logfile.f);  /* force it to save every time */
00396         }
00397     }
00398 }
00399 
00400 void Com_Printf (const char* const fmt, ...)
00401 {
00402     va_list ap;
00403 
00404     va_start(ap, fmt);
00405     Com_vPrintf(fmt, ap);
00406     va_end(ap);
00407 }
00408 
00412 void Com_DPrintf (int level, const char *fmt, ...)
00413 {
00414     /* don't confuse non-developers with techie stuff... */
00415     if (!developer)
00416         return;
00417 
00418     if (developer->integer == 1 || (developer->integer & level)) {
00419         va_list ap;
00420 
00421         va_start(ap, fmt);
00422         Com_vPrintf(fmt, ap);
00423         va_end(ap);
00424     }
00425 }
00426 
00431 void Com_Error (int code, const char *fmt, ...)
00432 {
00433     va_list argptr;
00434     static char msg[MAXPRINTMSG];
00435     static qboolean recursive = qfalse;
00436 
00437     if (recursive)
00438         Sys_Error("recursive error after: %s", msg);
00439     recursive = qtrue;
00440 
00441     va_start(argptr, fmt);
00442     Q_vsnprintf(msg, sizeof(msg), fmt, argptr);
00443     va_end(argptr);
00444 
00445     switch (code) {
00446     case ERR_DISCONNECT:
00447         Com_Printf("%s\n", msg);
00448         Cvar_Set("mn_afterdrop", "popup");
00449         CL_Drop();
00450         recursive = qfalse;
00451         Com_Drop();
00452     case ERR_DROP:
00453         Com_Printf("********************\n");
00454         Com_Printf("ERROR: %s\n", msg);
00455         Com_Printf("********************\n");
00456         Sys_Backtrace();
00457         SV_Shutdown("Server crashed.", qfalse);
00458         CL_Drop();
00459         recursive = qfalse;
00460         Com_Drop();
00461     default:
00462         Com_Printf("%s\n", msg);
00463         SV_Shutdown("Server fatal crashed", qfalse);
00464 
00465         /* send an receive net messages a last time */
00466         NET_Wait(0);
00467 
00468         FS_CloseFile(&logfile);
00469 
00470         CL_Shutdown();
00471         Qcommon_Shutdown();
00472         Sys_Error("Shutdown");
00473     }
00474 }
00475 
00476 void Com_Drop (void)
00477 {
00478     longjmp(abortframe, -1);
00479 }
00480 
00485 void Com_Quit (void)
00486 {
00487 #ifdef DEDICATED_ONLY
00488     Com_WriteConfigToFile("dedconfig.cfg");
00489 #else
00490     Com_WriteConfigToFile("config.cfg");
00491 #endif
00492 
00493     SV_Shutdown("Server quit.", qfalse);
00494     SV_Clear();
00495     CL_Shutdown();
00496 
00497     /* send an receive net messages a last time */
00498     NET_Wait(0);
00499     FS_CloseFile(&logfile);
00500     Sys_Quit();
00501 }
00502 
00503 
00508 int Com_ServerState (void)
00509 {
00510     return sv.state;
00511 }
00512 
00517 void Com_SetServerState (int state)
00518 {
00519     Com_DPrintf(DEBUG_ENGINE, "Set server state to %i\n", state);
00520     if (state == ss_dead)
00521         SV_Shutdown("Server shutdown", qfalse);
00522     else if (state == ss_restart)
00523         SV_Shutdown("Server map change", qtrue);
00524     sv.state = state;
00525 }
00526 
00530 unsigned int Com_HashKey (const char *name, int hashsize)
00531 {
00532     int i;
00533     unsigned int v;
00534 
00535     v = 0;
00536     for (i = 0; name[i]; i++) {
00537         const unsigned int c = name[i];
00538         v = (v + i) * 37 + tolower(c);  /* case insensitivity */
00539     }
00540 
00541     return v % hashsize;
00542 }
00543 
00547 int Com_Argc (void)
00548 {
00549     return com_argc;
00550 }
00551 
00555 const char *Com_Argv (int arg)
00556 {
00557     if (arg < 0 || arg >= com_argc || !com_argv[arg])
00558         return "";
00559     return com_argv[arg];
00560 }
00561 
00567 void Com_ClearArgv (int arg)
00568 {
00569     if (arg < 0 || arg >= com_argc || !com_argv[arg])
00570         return;
00571     com_argv[arg] = "";
00572 }
00573 
00574 
00575 void Com_InitArgv (int argc, const char **argv)
00576 {
00577     int i;
00578 
00579     if (argc > MAX_NUM_ARGVS)
00580         Com_Error(ERR_FATAL, "argc > MAX_NUM_ARGVS");
00581     com_argc = argc;
00582     for (i = 0; i < argc; i++) {
00583         if (!argv[i] || strlen(argv[i]) >= MAX_TOKEN_CHARS)
00584             com_argv[i] = "";
00585         else
00586             com_argv[i] = argv[i];
00587     }
00588 }
00589 
00590 #define MACRO_CVAR_ID_LENGTH 6
00591 
00597 const char *Com_MacroExpandString (const char *text)
00598 {
00599     int i, j, count, len;
00600     qboolean inquote;
00601     const char *scan;
00602     static char expanded[MAX_STRING_CHARS];
00603     const char *token, *start, *cvarvalue;
00604     char *pos;
00605 
00606     inquote = qfalse;
00607     scan = text;
00608     if (!text || !*text)
00609         return NULL;
00610 
00611     len = strlen(scan);
00612     if (len >= MAX_STRING_CHARS) {
00613         Com_Printf("Line exceeded %i chars, discarded.\n", MAX_STRING_CHARS);
00614         return NULL;
00615     }
00616 
00617     count = 0;
00618     memset(expanded, 0, sizeof(expanded));
00619     pos = expanded;
00620 
00621     /* also the \0 */
00622     assert(scan[len] == '\0');
00623     for (i = 0; i <= len; i++) {
00624         if (scan[i] == '"')
00625             inquote ^= 1;
00626         /* don't expand inside quotes */
00627         if (inquote || strncmp(&scan[i], "*cvar:", MACRO_CVAR_ID_LENGTH)) {
00628             *pos++ = scan[i];
00629             continue;
00630         }
00631 
00632         /* scan out the complete macro and only parse the cvar name */
00633         start = &scan[i + MACRO_CVAR_ID_LENGTH];
00634         token = Com_Parse(&start);
00635         if (!start)
00636             continue;
00637 
00638         /* skip the macro and the cvar name in the next loop */
00639         i += MACRO_CVAR_ID_LENGTH;
00640         i += strlen(token);
00641         i--;
00642 
00643         /* get the cvar value */
00644         cvarvalue = Cvar_GetString(token);
00645         if (!cvarvalue) {
00646             Com_Printf("Could not get cvar value for cvar: %s\n", token);
00647             return NULL;
00648         }
00649 
00650         j = strlen(cvarvalue);
00651         if (strlen(pos) + j >= MAX_STRING_CHARS) {
00652             Com_Printf("Expanded line exceeded %i chars, discarded.\n", MAX_STRING_CHARS);
00653             return NULL;
00654         }
00655 
00656         /* copy the cvar value into the target buffer */
00657         /* check for overflow is already done - so MAX_STRING_CHARS won't hurt here */
00658         Q_strncpyz(pos, cvarvalue, j + 1);
00659         pos += j;
00660 
00661         if (++count == 100) {
00662             Com_Printf("Macro expansion loop, discarded.\n");
00663             return NULL;
00664         }
00665     }
00666 
00667     if (inquote) {
00668         Com_Printf("Line has unmatched quote, discarded.\n");
00669         return NULL;
00670     }
00671 
00672     if (count)
00673         return expanded;
00674     else
00675         return NULL;
00676 }
00677 
00689 qboolean Com_ConsoleCompleteCommand (const char *s, char *target, size_t bufSize, int *pos, int offset)
00690 {
00691     const char *cmd = NULL, *cvar = NULL, *use = NULL;
00692     char cmdLine[MAXCMDLINE] = "";
00693     char cmdBase[MAXCMDLINE] = "";
00694     qboolean append = qtrue;
00695     char *tmp;
00696 
00697     if (!s[0] || s[0] == ' ')
00698         return qfalse;
00699 
00700     else if (s[0] == '\\' || s[0] == '/') {
00701         /* maybe we are using the same buffers - and we want to keep the slashes */
00702         if (s == target)
00703             offset++;
00704         s++;
00705     }
00706 
00707     assert(bufSize <= MAXCMDLINE);
00708     assert(pos);
00709 
00710     /* don't try to search a command or cvar if we are already in the
00711      * parameter stage */
00712     if (strstr(s, " ")) {
00713         int cntParams;
00714         Q_strncpyz(cmdLine, s, sizeof(cmdLine));
00715         /* remove the last whitespace */
00716         cmdLine[strlen(cmdLine) - 1] = '\0';
00717 
00718         tmp = cmdBase;
00719         while (*s != ' ')
00720             *tmp++ = *s++;
00721         /* get rid of the whitespace */
00722         s++;
00723         /* terminate the string at whitespace position to separate the cmd */
00724         *tmp = '\0';
00725 
00726         /* now strip away that part that is not yet completed */
00727         tmp = strrchr(cmdLine, ' ');
00728         if (tmp)
00729             *tmp = '\0';
00730 
00731         cntParams = Cmd_CompleteCommandParameters(cmdBase, s, &cmd);
00732         if (cntParams > 1)
00733             Com_Printf("\n");
00734         if (cmd) {
00735             /* append the found parameter */
00736             Q_strcat(cmdLine, " ", sizeof(cmdLine));
00737             Q_strcat(cmdLine, cmd, sizeof(cmdLine));
00738             append = qfalse;
00739             use = cmdLine;
00740         } else
00741             return qfalse;
00742     } else {
00743         /* Cmd_GenericCompleteFunction uses one static buffer for output, so backup one completion here if available */
00744         static char cmdBackup[MAX_QPATH];
00745         int cntCvar;
00746         int cntCmd = Cmd_CompleteCommand(s, &cmd);
00747         if (cmd)
00748             Q_strncpyz(cmdBackup, cmd, sizeof(cmdBackup));
00749         cntCvar = Cvar_CompleteVariable(s, &cvar);
00750 
00751         /* complete as much as possible, append only if one single match is found */
00752         if (cntCmd > 0 && !cntCvar) {
00753             use = cmd;
00754             if (cntCmd != 1)
00755                 append = qfalse;
00756         } else if (!cntCmd && cntCvar > 0) {
00757             use = cvar;
00758             if (cntCvar != 1)
00759                 append = qfalse;
00760         } else if (cmd && cvar) {
00761             int maxLength = min(strlen(cmdBackup),strlen(cvar));
00762             int idx = 0;
00763             /* try to find similar content of cvar and cmd match */
00764             Q_strncpyz(cmdLine,cmdBackup,sizeof(cmdLine));
00765             for (; idx < maxLength; idx++) {
00766                 if (cmdBackup[idx] != cvar[idx]) {
00767                     cmdLine[idx] = '\0';
00768                     break;
00769                 }
00770             }
00771             if (idx == maxLength)
00772                 cmdLine[idx] = '\0';
00773             use = cmdLine;
00774             append = qfalse;
00775         }
00776     }
00777 
00778     if (use) {
00779         Q_strncpyz(&target[offset], use, bufSize - offset);
00780         *pos = strlen(target);
00781         if (append)
00782             target[(*pos)++] = ' ';
00783         target[*pos] = '\0';
00784 
00785         return qtrue;
00786     }
00787 
00788     return qfalse;
00789 }
00790 
00791 void Com_SetGameType (void)
00792 {
00793     int i;
00794 
00795     for (i = 0; i < numGTs; i++) {
00796         const gametype_t *gt = &gts[i];
00797         if (!strcmp(gt->id, sv_gametype->string)) {
00798             int j;
00799             const cvarlist_t *list;
00800             if (sv_dedicated->integer)
00801                 Com_Printf("set gametype to: %s\n", gt->id);
00802             for (j = 0, list = gt->cvars; j < gt->num_cvars; j++, list++) {
00803                 Cvar_Set(list->name, list->value);
00804                 if (sv_dedicated->integer)
00805                     Com_Printf("  %s = %s\n", list->name, list->value);
00806             }
00807             /*Com_Printf("Make sure to restart the map if you switched during a game\n");*/
00808             break;
00809         }
00810     }
00811 
00812     if (i == numGTs)
00813         Com_Printf("Can't set the gametype - unknown value for cvar gametype: '%s'\n", sv_gametype->string);
00814 }
00815 
00816 static void Com_GameTypeList_f (void)
00817 {
00818     int i;
00819 
00820     Com_Printf("Available gametypes:\n");
00821     for (i = 0; i < numGTs; i++) {
00822         int j;
00823         const gametype_t *gt = &gts[i];
00824         const cvarlist_t *list;
00825 
00826         Com_Printf("%s\n", gt->id);
00827 
00828         for (j = 0, list = gt->cvars; j < gt->num_cvars; j++, list++)
00829             Com_Printf("  %s = %s\n", list->name, list->value);
00830     }
00831 }
00832 
00833 #ifdef DEBUG
00834 
00837 static void Com_DebugHelp_f (void)
00838 {
00839     Cvar_PrintDebugCvars();
00840 
00841     Cmd_PrintDebugCommands();
00842 }
00843 
00847 static void Com_DebugError_f (void)
00848 {
00849     if (Cmd_Argc() == 3) {
00850         const char *errorType = Cmd_Argv(1);
00851         if (!strcmp(errorType, "ERR_DROP"))
00852             Com_Error(ERR_DROP, "%s", Cmd_Argv(2));
00853         else if (!strcmp(errorType, "ERR_FATAL"))
00854             Com_Error(ERR_FATAL, "%s", Cmd_Argv(2));
00855         else if (!strcmp(errorType, "ERR_DISCONNECT"))
00856             Com_Error(ERR_DISCONNECT, "%s", Cmd_Argv(2));
00857     }
00858     Com_Printf("Usage: %s <ERR_FATAL|ERR_DROP|ERR_DISCONNECT> <msg>\n", Cmd_Argv(0));
00859 }
00860 #endif
00861 
00862 
00863 typedef struct debugLevel_s {
00864     const char *str;
00865     int debugLevel;
00866 } debugLevel_t;
00867 
00868 static const debugLevel_t debugLevels[] = {
00869     {"DEBUG_ALL", DEBUG_ALL},
00870     {"DEBUG_ENGINE", DEBUG_ENGINE},
00871     {"DEBUG_SHARED", DEBUG_SHARED},
00872     {"DEBUG_SYSTEM", DEBUG_SYSTEM},
00873     {"DEBUG_COMMANDS", DEBUG_COMMANDS},
00874     {"DEBUG_CLIENT", DEBUG_CLIENT},
00875     {"DEBUG_EVENTSYS", DEBUG_EVENTSYS},
00876     {"DEBUG_PATHING", DEBUG_PATHING},
00877     {"DEBUG_SERVER", DEBUG_SERVER},
00878     {"DEBUG_GAME", DEBUG_GAME},
00879     {"DEBUG_RENDERER", DEBUG_RENDERER},
00880     {"DEBUG_SOUND", DEBUG_SOUND},
00881 
00882     {NULL, 0}
00883 };
00884 
00885 static void Com_DeveloperSet_f (void)
00886 {
00887     int oldValue = Cvar_GetInteger("developer");
00888     int newValue = oldValue;
00889     int i = 0;
00890 
00891     if (Cmd_Argc() == 2) {
00892         const char *debugLevel = Cmd_Argv(1);
00893         while (debugLevels[i].str) {
00894             if (!strcmp(debugLevel, debugLevels[i].str)) {
00895                 if (oldValue & debugLevels[i].debugLevel)   /* if it's already set... */
00896                     newValue &= ~debugLevels[i].debugLevel; /* ...reset it. */
00897                 else
00898                     newValue |= debugLevels[i].debugLevel;
00899                 break;
00900             }
00901             i++;
00902         }
00903         if (!debugLevels[i].str) {
00904             Com_Printf("No valid debug mode parameter\n");
00905             return;
00906         }
00907         Cvar_SetValue("developer", newValue);
00908         Com_Printf("Currently selected debug print levels\n");
00909         i = 0;
00910         while (debugLevels[i].str) {
00911             if (newValue & debugLevels[i].debugLevel)
00912                 Com_Printf("* %s\n", debugLevels[i].str);
00913             i++;
00914         }
00915     } else {
00916         Com_Printf("Usage: %s <debug_level>\n", Cmd_Argv(0));
00917         Com_Printf("  valid debug_levels are:\n");
00918         while (debugLevels[i].str) {
00919             Com_Printf("  * %s\n", debugLevels[i].str);
00920             i++;
00921         }
00922     }
00923 }
00924 
00925 #ifndef DEDICATED_ONLY
00926 
00929 static qboolean Com_CvarCheckMaxFPS (cvar_t *cvar)
00930 {
00931     /* don't allow setting maxfps too low (or game could stop responding) */
00932     return Cvar_AssertValue(cvar, 10, 1000, qtrue);
00933 }
00934 #endif
00935 
00939 void Com_WriteConfigToFile (const char *filename)
00940 {
00941     qFILE f;
00942 
00943     FS_OpenFile(filename, &f, FILE_WRITE);
00944     if (!f.f) {
00945         Com_Printf("Couldn't write %s.\n", filename);
00946         return;
00947     }
00948 
00949     FS_Printf(&f, "// generated by ufo, do not modify\n");
00950     FS_Printf(&f, "// variables\n");
00951     Cvar_WriteVariables(&f);
00952     FS_Printf(&f, "// aliases\n");
00953     Cmd_WriteAliases(&f);
00954     FS_CloseFile(&f);
00955     Com_Printf("Wrote %s.\n", filename);
00956 }
00957 
00958 
00962 static void Com_WriteConfig_f (void)
00963 {
00964     char filename[MAX_QPATH];
00965 
00966     if (Cmd_Argc() != 2) {
00967         Com_Printf("Usage: %s <filename>\n", Cmd_Argv(0));
00968         return;
00969     }
00970 
00971     Q_strncpyz(filename, Cmd_Argv(1), sizeof(filename));
00972     Com_DefaultExtension(filename, sizeof(filename), ".cfg");
00973     Com_WriteConfigToFile(filename);
00974 }
00975 
00976 static void Cbuf_Execute_timer (int now, void *data)
00977 {
00978     Cbuf_Execute();
00979 }
00980 
00988 void Qcommon_Init (int argc, const char **argv)
00989 {
00990     char *s;
00991 
00992     Sys_InitSignals();
00993 
00994     /* random seed */
00995     srand(time(NULL));
00996 
00997     com_aliasSysPool = Mem_CreatePool("Common: Alias system for commands and enums");
00998     com_cmdSysPool = Mem_CreatePool("Common: Command system");
00999     com_cmodelSysPool = Mem_CreatePool("Common: Collision model");
01000     com_cvarSysPool = Mem_CreatePool("Common: Cvar system");
01001     com_fileSysPool = Mem_CreatePool("Common: File system");
01002     com_genericPool = Mem_CreatePool("Generic");
01003     com_networkPool = Mem_CreatePool("Network");
01004 
01005     if (setjmp(abortframe))
01006         Sys_Error("Error during initialization");
01007 
01008     memset(&csi, 0, sizeof(csi));
01009 
01010     /* prepare enough of the subsystems to handle
01011      * cvar and command buffer management */
01012     Com_InitArgv(argc, argv);
01013 
01014     Swap_Init();
01015     Cbuf_Init();
01016 
01017     Cmd_Init();
01018     Cvar_Init();
01019 
01020     Key_Init();
01021 
01022     /* we need to add the early commands twice, because
01023      * a basedir needs to be set before executing
01024      * config files, but we want other parms to override
01025      * the settings of the config files */
01026     Cbuf_AddEarlyCommands(qfalse);
01027     Cbuf_Execute();
01028 
01029     FS_InitFilesystem(qtrue);
01030 
01031     Cbuf_AddText("exec default.cfg\n");
01032 #ifdef DEDICATED_ONLY
01033     Cbuf_AddText("exec dedconfig.cfg\n");
01034 #else
01035     Cbuf_AddText("exec config.cfg\n");
01036 #endif
01037 
01038     Cbuf_AddEarlyCommands(qtrue);
01039     Cbuf_Execute();
01040 
01041     Com_SetRenderModified(qfalse);
01042     Com_SetUserinfoModified(qfalse);
01043 
01044     /* init commands and vars */
01045     Cmd_AddCommand("saveconfig", Com_WriteConfig_f, "Write the configuration to file");
01046     Cmd_AddCommand("gametypelist", Com_GameTypeList_f, "List all available multiplayer game types");
01047 #ifdef DEBUG
01048     Cmd_AddCommand("debug_help", Com_DebugHelp_f, "Show some debugging help");
01049     Cmd_AddCommand("debug_error", Com_DebugError_f, "Just throw a fatal error to test error shutdown procedures");
01050 #endif
01051     Cmd_AddCommand("setdeveloper", Com_DeveloperSet_f, "Set the developer cvar to only get the debug output you want");
01052 
01053     developer = Cvar_Get("developer", "0", 0, "Activate developer output to logfile and gameconsole");
01054     logfile_active = Cvar_Get("logfile", "1", 0, "0 = deactivate logfile, 1 = write normal logfile, 2 = flush on every new line, 3 = always append to existing file");
01055     sv_gametype = Cvar_Get("sv_gametype", "1on1", CVAR_ARCHIVE | CVAR_SERVERINFO, "Sets the multiplayer gametype - see gametypelist command for a list of all gametypes");
01056     http_proxy = Cvar_Get("http_proxy", "", CVAR_ARCHIVE, "Use this proxy for http transfers");
01057     http_timeout = Cvar_Get("http_timeout", "3", CVAR_ARCHIVE, "Http connection timeout");
01058     port = Cvar_Get("port", DOUBLEQUOTE(PORT_SERVER), CVAR_NOSET, NULL);
01059     masterserver_url = Cvar_Get("masterserver_url", MASTER_SERVER, CVAR_ARCHIVE, "URL of UFO:AI masterserver");
01060 #ifdef DEDICATED_ONLY
01061     sv_dedicated = Cvar_Get("sv_dedicated", "1", CVAR_SERVERINFO | CVAR_NOSET, "Is this a dedicated server?");
01062     /* don't allow to override this from commandline of config */
01063     Cvar_ForceSet("sv_dedicated", "1");
01064 #else
01065     sv_dedicated = Cvar_Get("sv_dedicated", "0", CVAR_SERVERINFO | CVAR_NOSET, "Is this a dedicated server?");
01066 
01067     /* set this to false for client - otherwise Qcommon_Frame would set the initial values to multiplayer */
01068     sv_gametype->modified = qfalse;
01069 
01070     s_language = Cvar_Get("s_language", "", CVAR_ARCHIVE, "Game language - full language string e.g. en_EN.UTF-8");
01071     s_language->modified = qfalse;
01072     cl_maxfps = Cvar_Get("cl_maxfps", "50", CVAR_ARCHIVE, NULL);
01073     Cvar_SetCheckFunction("cl_maxfps", Com_CvarCheckMaxFPS);
01074 #endif
01075 
01076     s = va("UFO: Alien Invasion %s %s %s %s", UFO_VERSION, CPUSTRING, __DATE__, BUILDSTRING);
01077     Cvar_Get("version", s, CVAR_NOSET, "Full version string");
01078     Cvar_Get("ver", UFO_VERSION, CVAR_SERVERINFO | CVAR_NOSET, "Version number");
01079 
01080     if (sv_dedicated->integer)
01081         Cmd_AddCommand("quit", Com_Quit, "Quits the game");
01082 
01083     Mem_Init();
01084     Sys_Init();
01085 
01086     NET_Init();
01087 
01088     curl_global_init(CURL_GLOBAL_NOTHING);
01089     Com_Printf("%s initialized.\n", curl_version());
01090 
01091     SV_Init();
01092 
01093     /* e.g. init the client hunk that is used in script parsing */
01094     CL_Init();
01095 
01096     Com_ParseScripts(sv_dedicated->integer);
01097 #ifndef DEDICATED_ONLY
01098     Cbuf_AddText("exec keys.cfg\n");
01099 #endif
01100 
01101     if (!sv_dedicated->integer)
01102         Cbuf_AddText("init\n");
01103     else
01104         Cbuf_AddText("dedicated_start\n");
01105     Cbuf_Execute();
01106 
01107     FS_ExecAutoexec();
01108 
01109     /* add + commands from command line
01110      * if the user didn't give any commands, run default action */
01111     if (Cbuf_AddLateCommands()) {
01112         /* the user asked for something explicit
01113          * so drop the loading plaque */
01114         SCR_EndLoadingPlaque();
01115     }
01116 
01117     CL_InitAfter();
01118 
01119     /* Check memory integrity */
01120     Mem_CheckGlobalIntegrity();
01121 
01122     /* Touch memory */
01123     Mem_TouchGlobal();
01124 
01125 #ifndef DEDICATED_ONLY
01126     if (!sv_dedicated->integer) {
01127         Schedule_Timer(cl_maxfps, &CL_Frame, NULL);
01128         Schedule_Timer(Cvar_Get("cl_slowfreq", "10", 0, NULL), &CL_SlowFrame, NULL);
01129 
01130         /* now hide the console */
01131         Sys_ShowConsole(qfalse);
01132     }
01133 #endif
01134 
01135     Schedule_Timer(Cvar_Get("sv_freq", "10", CVAR_NOSET, NULL), &SV_Frame, NULL);
01136 
01138     Schedule_Timer(Cvar_Get("cbuf_freq", "10", 0, NULL), &Cbuf_Execute_timer, NULL);
01139 
01140     Com_Printf("====== UFO Initialized ======\n");
01141     Com_Printf("=============================\n");
01142 }
01143 
01144 static void tick_timer (int now, void *data)
01145 {
01146     struct timer *timer = data;
01147     int old_interval = timer->interval;
01148 
01149     /* Compute and store the lateness, updating the total */
01150     const int lateness = Sys_Milliseconds() - now;
01151     timer->total_lateness -= timer->recent_lateness[timer->next_lateness];
01152     timer->recent_lateness[timer->next_lateness] = lateness;
01153     timer->total_lateness += lateness;
01154     timer->next_lateness++;
01155     timer->next_lateness %= TIMER_LATENESS_HISTORY;
01156 
01157     /* Is it time to check the mean yet? */
01158     timer->next_check--;
01159     if (timer->next_check <= 0) {
01160         const int mean = timer->total_lateness / TIMER_LATENESS_HISTORY;
01161 
01162         /* We use a saturating counter to damp the adjustment */
01163 
01164         /* When we stay above the high water mark, increase the interval */
01165         if (mean > TIMER_LATENESS_HIGH)
01166             timer->checks_high = min(TIMER_CHECK_LAG, timer->checks_high + 1);
01167         else
01168             timer->checks_high = max(0, timer->checks_high - 1);
01169 
01170         if (timer->checks_high > TIMER_CHECK_LAG)
01171             timer->interval += 2;
01172 
01173         /* When we stay below the low water mark, decrease the interval */
01174         if (mean < TIMER_LATENESS_LOW)
01175             timer->checks_low = min(TIMER_CHECK_LAG, timer->checks_high + 1);
01176         else
01177             timer->checks_low = max(0, timer->checks_low - 1);
01178 
01179         if (timer->checks_low > TIMER_CHECK_LAG)
01180             timer->interval -= 1;
01181 
01182         /* Note that we slow the timer more quickly than we speed it up,
01183          * so it should tend to settle down in the vicinity of the low
01184          * water mark */
01185 
01186         timer->next_check = TIMER_CHECK_INTERVAL;
01187     }
01188 
01189     timer->interval = max(timer->interval, 1000 / timer->min_freq->integer);
01190 
01191     if (timer->interval != old_interval)
01192         Com_DPrintf(DEBUG_ENGINE, "Adjusted timer on %s to interval %d\n", timer->min_freq->name, timer->interval);
01193 
01194     if (setjmp(abortframe) == 0)
01195         timer->func(now, timer->data);
01196 
01197     /* We correct for the lateness of this frame. We do not correct for
01198      * the time consumed by this frame - that's billed to the lateness
01199      * of future frames (so that the automagic slowdown can work) */
01200     Schedule_Event(now + lateness + timer->interval, &tick_timer, NULL, NULL, timer);
01201 }
01202 
01203 void Schedule_Timer (cvar_t *freq, event_func *func, void *data)
01204 {
01205     struct timer *timer = Mem_PoolAlloc(sizeof(*timer), com_genericPool, 0);
01206     int i;
01207     timer->min_freq = freq;
01208     timer->interval = 1000 / freq->integer;
01209     timer->next_lateness = 0;
01210     timer->total_lateness = 0;
01211     timer->next_check = TIMER_CHECK_INTERVAL;
01212     timer->checks_high = 0;
01213     timer->checks_low = 0;
01214     timer->func = func;
01215     timer->data = data;
01216     for (i = 0; i < TIMER_LATENESS_HISTORY; i++)
01217         timer->recent_lateness[i] = 0;
01218 
01219     Schedule_Event(Sys_Milliseconds() + timer->interval, &tick_timer, NULL, NULL, timer);
01220 }
01221 
01233 void Schedule_Event (int when, event_func *func, event_check_func *check, event_clean_func *clean, void *data)
01234 {
01235     struct event *event = Mem_PoolAlloc(sizeof(*event), com_genericPool, 0);
01236     event->when = when;
01237     event->func = func;
01238     event->check = check;
01239     event->clean = clean;
01240     event->data = data;
01241 
01242     if (!event_queue || event_queue->when > when) {
01243         event->next = event_queue;
01244         event_queue = event;
01245     } else {
01246         struct event *e = event_queue;
01247         while (e->next && e->next->when <= when)
01248             e = e->next;
01249         event->next = e->next;
01250         e->next = event;
01251     }
01252 
01253 #ifdef DEBUG
01254     for (event = event_queue; event && event->next; event = event->next)
01255         if (event->when > event->next->when)
01256             abort();
01257 #endif
01258 }
01259 
01265 static struct event* Dequeue_Event (int now)
01266 {
01267     struct event *event = event_queue;
01268     struct event *prev = NULL;
01269 
01270     while (event && event->when <= now) {
01271         if (event->check == NULL || event->check(now, event->data)) {
01272             if (prev) {
01273                 prev->next = event->next;
01274             } else {
01275                 event_queue = event->next;
01276             }
01277             return event;
01278         }
01279         prev = event;
01280         event = event->next;
01281     }
01282     return NULL;
01283 }
01284 
01291 void CL_FilterEventQueue (event_filter *filter)
01292 {
01293     struct event *event = event_queue;
01294     struct event *prev = NULL;
01295 
01296     assert(filter);
01297 
01298     while (event) {
01299         qboolean keep = filter(event->when, event->func, event->check, event->data);
01300         struct event *freeme = event;
01301 
01302         if (keep) {
01303             prev = event;
01304             event = event->next;
01305             continue;
01306         }
01307 
01308         /* keep == qfalse */
01309         if (prev) {
01310             event = prev->next = event->next;
01311         } else {
01312             event = event_queue = event->next;
01313         }
01314         if (freeme->clean != NULL)
01315             freeme->clean(freeme->data);
01316         Mem_Free(freeme);
01317     }
01318 }
01319 
01328 void Qcommon_Frame (void)
01329 {
01330     int time_to_next;
01331     struct event *event;
01332 
01333     /* an ERR_DROP was thrown */
01334     if (setjmp(abortframe))
01335         return;
01336 
01337     /* If the next event is due... */
01338     event = Dequeue_Event(Sys_Milliseconds());
01339     if (event) {
01340         if (setjmp(abortframe)) {
01341             Mem_Free(event);
01342             return;
01343         }
01344 
01345         /* Dispatch the event */
01346         event->func(event->when, event->data);
01347 
01348         if (setjmp(abortframe))
01349             return;
01350 
01351         Mem_Free(event);
01352     }
01353 
01354     /* Now we spend time_to_next milliseconds working on whatever
01355      * IO is ready (but always try at least once, to make sure IO
01356      * doesn't stall) */
01357     do {
01358         time_to_next = event_queue ? (event_queue->when - Sys_Milliseconds()) : 1000;
01359         if (time_to_next < 0)
01360             time_to_next = 0;
01361 
01362         NET_Wait(time_to_next);
01363     } while (time_to_next > 0);
01364 }
01365 
01373 void Qcommon_Shutdown (void)
01374 {
01375     HTTP_Cleanup();
01376 
01377     FS_Shutdown();
01378     Cvar_Shutdown();
01379     Cmd_Shutdown();
01380     Mem_Shutdown();
01381 }

Generated by  doxygen 1.6.2