📄 common.c
字号:
/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
This file is part of Quake III Arena source code.
Quake III Arena source code 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.
Quake III Arena source code 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 Foobar; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
// common.c -- misc functions used in client and server
#include "../game/q_shared.h"
#include "qcommon.h"
#include <setjmp.h>
#ifdef __linux__
#include <netinet/in.h>
#else
#if defined(MACOS_X)
#include <netinet/in.h>
#else
#include <winsock.h>
#endif
#endif
int demo_protocols[] =
{ 66, 67, 68, 0 };
#define MAX_NUM_ARGVS 50
#define MIN_DEDICATED_COMHUNKMEGS 1
#define MIN_COMHUNKMEGS 56
#ifdef MACOS_X
#define DEF_COMHUNKMEGS "64"
#define DEF_COMZONEMEGS "24"
#else
#define DEF_COMHUNKMEGS "56"
#define DEF_COMZONEMEGS "16"
#endif
int com_argc;
char *com_argv[MAX_NUM_ARGVS+1];
jmp_buf abortframe; // an ERR_DROP occured, exit the entire frame
FILE *debuglogfile;
static fileHandle_t logfile;
fileHandle_t com_journalFile; // events are written here
fileHandle_t com_journalDataFile; // config files are written here
cvar_t *com_viewlog;
cvar_t *com_speeds;
cvar_t *com_developer;
cvar_t *com_dedicated;
cvar_t *com_timescale;
cvar_t *com_fixedtime;
cvar_t *com_dropsim; // 0.0 to 1.0, simulated packet drops
cvar_t *com_journal;
cvar_t *com_maxfps;
cvar_t *com_timedemo;
cvar_t *com_sv_running;
cvar_t *com_cl_running;
cvar_t *com_logfile; // 1 = buffer log, 2 = flush after each print
cvar_t *com_showtrace;
cvar_t *com_version;
cvar_t *com_blood;
cvar_t *com_buildScript; // for automated data building scripts
cvar_t *com_introPlayed;
cvar_t *cl_paused;
cvar_t *sv_paused;
cvar_t *com_cameraMode;
#if defined(_WIN32) && defined(_DEBUG)
cvar_t *com_noErrorInterrupt;
#endif
// com_speeds times
int time_game;
int time_frontend; // renderer frontend time
int time_backend; // renderer backend time
int com_frameTime;
int com_frameMsec;
int com_frameNumber;
qboolean com_errorEntered;
qboolean com_fullyInitialized;
char com_errorMessage[MAXPRINTMSG];
void Com_WriteConfig_f( void );
void CIN_CloseAllVideos();
//============================================================================
static char *rd_buffer;
static int rd_buffersize;
static void (*rd_flush)( char *buffer );
void Com_BeginRedirect (char *buffer, int buffersize, void (*flush)( char *) )
{
if (!buffer || !buffersize || !flush)
return;
rd_buffer = buffer;
rd_buffersize = buffersize;
rd_flush = flush;
*rd_buffer = 0;
}
void Com_EndRedirect (void)
{
if ( rd_flush ) {
rd_flush(rd_buffer);
}
rd_buffer = NULL;
rd_buffersize = 0;
rd_flush = NULL;
}
/*
=============
Com_Printf
Both client and server can use this, and it will output
to the apropriate place.
A raw string should NEVER be passed as fmt, because of "%f" type crashers.
=============
*/
void QDECL Com_Printf( const char *fmt, ... ) {
va_list argptr;
char msg[MAXPRINTMSG];
static qboolean opening_qconsole = qfalse;
va_start (argptr,fmt);
Q_vsnprintf (msg, sizeof(msg), fmt, argptr);
va_end (argptr);
if ( rd_buffer ) {
if ((strlen (msg) + strlen(rd_buffer)) > (rd_buffersize - 1)) {
rd_flush(rd_buffer);
*rd_buffer = 0;
}
Q_strcat(rd_buffer, rd_buffersize, msg);
// TTimo nooo .. that would defeat the purpose
//rd_flush(rd_buffer);
//*rd_buffer = 0;
return;
}
// echo to console if we're not a dedicated server
if ( com_dedicated && !com_dedicated->integer ) {
CL_ConsolePrint( msg );
}
// echo to dedicated console and early console
Sys_Print( msg );
// logfile
if ( com_logfile && com_logfile->integer ) {
// TTimo: only open the qconsole.log if the filesystem is in an initialized state
// also, avoid recursing in the qconsole.log opening (i.e. if fs_debug is on)
if ( !logfile && FS_Initialized() && !opening_qconsole) {
struct tm *newtime;
time_t aclock;
opening_qconsole = qtrue;
time( &aclock );
newtime = localtime( &aclock );
logfile = FS_FOpenFileWrite( "qconsole.log" );
Com_Printf( "logfile opened on %s\n", asctime( newtime ) );
if ( com_logfile->integer > 1 ) {
// force it to not buffer so we get valid
// data even if we are crashing
FS_ForceFlush(logfile);
}
opening_qconsole = qfalse;
}
if ( logfile && FS_Initialized()) {
FS_Write(msg, strlen(msg), logfile);
}
}
}
/*
================
Com_DPrintf
A Com_Printf that only shows up if the "developer" cvar is set
================
*/
void QDECL Com_DPrintf( const char *fmt, ...) {
va_list argptr;
char msg[MAXPRINTMSG];
if ( !com_developer || !com_developer->integer ) {
return; // don't confuse non-developers with techie stuff...
}
va_start (argptr,fmt);
Q_vsnprintf (msg, sizeof(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 QDECL Com_Error( int code, const char *fmt, ... ) {
va_list argptr;
static int lastErrorTime;
static int errorCount;
int currentTime;
#if defined(_WIN32) && defined(_DEBUG)
if ( code != ERR_DISCONNECT && code != ERR_NEED_CD ) {
if (!com_noErrorInterrupt->integer) {
__asm {
int 0x03
}
}
}
#endif
// when we are running automated scripts, make sure we
// know if anything failed
if ( com_buildScript && com_buildScript->integer ) {
code = ERR_FATAL;
}
// make sure we can get at our local stuff
FS_PureServerSetLoadedPaks( "", "" );
// if we are getting a solid stream of ERR_DROP, do an ERR_FATAL
currentTime = Sys_Milliseconds();
if ( currentTime - lastErrorTime < 100 ) {
if ( ++errorCount > 3 ) {
code = ERR_FATAL;
}
} else {
errorCount = 0;
}
lastErrorTime = currentTime;
if ( com_errorEntered ) {
Sys_Error( "recursive error after: %s", com_errorMessage );
}
com_errorEntered = qtrue;
va_start (argptr,fmt);
vsprintf (com_errorMessage,fmt,argptr);
va_end (argptr);
if ( code != ERR_DISCONNECT && code != ERR_NEED_CD ) {
Cvar_Set("com_errorMessage", com_errorMessage);
}
if ( code == ERR_SERVERDISCONNECT ) {
CL_Disconnect( qtrue );
CL_FlushMemory( );
com_errorEntered = qfalse;
longjmp (abortframe, -1);
} else if ( code == ERR_DROP || code == ERR_DISCONNECT ) {
Com_Printf ("********************\nERROR: %s\n********************\n", com_errorMessage);
SV_Shutdown (va("Server crashed: %s\n", com_errorMessage));
CL_Disconnect( qtrue );
CL_FlushMemory( );
com_errorEntered = qfalse;
longjmp (abortframe, -1);
} else if ( code == ERR_NEED_CD ) {
SV_Shutdown( "Server didn't have CD\n" );
if ( com_cl_running && com_cl_running->integer ) {
CL_Disconnect( qtrue );
CL_FlushMemory( );
com_errorEntered = qfalse;
CL_CDDialog();
} else {
Com_Printf("Server didn't have CD\n" );
}
longjmp (abortframe, -1);
} else {
CL_Shutdown ();
SV_Shutdown (va("Server fatal crashed: %s\n", com_errorMessage));
}
Com_Shutdown ();
Sys_Error ("%s", com_errorMessage);
}
/*
=============
Com_Quit_f
Both client and server can use this, and it will
do the apropriate things.
=============
*/
void Com_Quit_f( void ) {
// don't try to shutdown if we are in a recursive error
if ( !com_errorEntered ) {
SV_Shutdown ("Server quit\n");
CL_Shutdown ();
Com_Shutdown ();
FS_Shutdown(qtrue);
}
Sys_Quit ();
}
/*
============================================================================
COMMAND LINE FUNCTIONS
+ characters seperate the commandLine string into multiple console
command lines.
All of these are valid:
quake3 +set test blah +map test
quake3 set test blah+map test
quake3 set test blah + map test
============================================================================
*/
#define MAX_CONSOLE_LINES 32
int com_numConsoleLines;
char *com_consoleLines[MAX_CONSOLE_LINES];
/*
==================
Com_ParseCommandLine
Break it up into multiple console lines
==================
*/
void Com_ParseCommandLine( char *commandLine ) {
int inq = 0;
com_consoleLines[0] = commandLine;
com_numConsoleLines = 1;
while ( *commandLine ) {
if (*commandLine == '"') {
inq = !inq;
}
// look for a + seperating character
// if commandLine came from a file, we might have real line seperators
if ( (*commandLine == '+' && !inq) || *commandLine == '\n' || *commandLine == '\r' ) {
if ( com_numConsoleLines == MAX_CONSOLE_LINES ) {
return;
}
com_consoleLines[com_numConsoleLines] = commandLine + 1;
com_numConsoleLines++;
*commandLine = 0;
}
commandLine++;
}
}
/*
===================
Com_SafeMode
Check for "safe" on the command line, which will
skip loading of q3config.cfg
===================
*/
qboolean Com_SafeMode( void ) {
int i;
for ( i = 0 ; i < com_numConsoleLines ; i++ ) {
Cmd_TokenizeString( com_consoleLines[i] );
if ( !Q_stricmp( Cmd_Argv(0), "safe" )
|| !Q_stricmp( Cmd_Argv(0), "cvar_restart" ) ) {
com_consoleLines[i][0] = 0;
return qtrue;
}
}
return qfalse;
}
/*
===============
Com_StartupVariable
Searches for command line parameters that are set commands.
If match is not NULL, only that cvar will be looked for.
That is necessary because cddir and basedir need to be set
before the filesystem is started, but all other sets shouls
be after execing the config and default.
===============
*/
void Com_StartupVariable( const char *match ) {
int i;
char *s;
cvar_t *cv;
for (i=0 ; i < com_numConsoleLines ; i++) {
Cmd_TokenizeString( com_consoleLines[i] );
if ( strcmp( Cmd_Argv(0), "set" ) ) {
continue;
}
s = Cmd_Argv(1);
if ( !match || !strcmp( s, match ) ) {
Cvar_Set( s, Cmd_Argv(2) );
cv = Cvar_Get( s, "", 0 );
cv->flags |= CVAR_USER_CREATED;
// com_consoleLines[i] = 0;
}
}
}
/*
=================
Com_AddStartupCommands
Adds command line parameters as script statements
Commands are seperated by + signs
Returns qtrue if any late commands were added, which
will keep the demoloop from immediately starting
=================
*/
qboolean Com_AddStartupCommands( void ) {
int i;
qboolean added;
added = qfalse;
// quote every token, so args with semicolons can work
for (i=0 ; i < com_numConsoleLines ; i++) {
if ( !com_consoleLines[i] || !com_consoleLines[i][0] ) {
continue;
}
// set commands won't override menu startup
if ( Q_stricmpn( com_consoleLines[i], "set", 3 ) ) {
added = qtrue;
}
Cbuf_AddText( com_consoleLines[i] );
Cbuf_AddText( "\n" );
}
return added;
}
//============================================================================
void Info_Print( const 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)
{
Com_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);
}
}
/*
============
Com_StringContains
============
*/
char *Com_StringContains(char *str1, char *str2, int casesensitive) {
int len, i, j;
len = strlen(str1) - strlen(str2);
for (i = 0; i <= len; i++, str1++) {
for (j = 0; str2[j]; j++) {
if (casesensitive) {
if (str1[j] != str2[j]) {
break;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -