📄 g_main.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
===========================================================================
*/
//
#include "g_local.h"
level_locals_t level;
typedef struct {
vmCvar_t *vmCvar;
char *cvarName;
char *defaultString;
int cvarFlags;
int modificationCount; // for tracking changes
qboolean trackChange; // track this variable, and announce if changed
qboolean teamShader; // track and if changed, update shader state
} cvarTable_t;
gentity_t g_entities[MAX_GENTITIES];
gclient_t g_clients[MAX_CLIENTS];
vmCvar_t g_gametype;
vmCvar_t g_dmflags;
vmCvar_t g_fraglimit;
vmCvar_t g_timelimit;
vmCvar_t g_capturelimit;
vmCvar_t g_friendlyFire;
vmCvar_t g_password;
vmCvar_t g_needpass;
vmCvar_t g_maxclients;
vmCvar_t g_maxGameClients;
vmCvar_t g_dedicated;
vmCvar_t g_speed;
vmCvar_t g_gravity;
vmCvar_t g_cheats;
vmCvar_t g_knockback;
vmCvar_t g_quadfactor;
vmCvar_t g_forcerespawn;
vmCvar_t g_inactivity;
vmCvar_t g_debugMove;
vmCvar_t g_debugDamage;
vmCvar_t g_debugAlloc;
vmCvar_t g_weaponRespawn;
vmCvar_t g_weaponTeamRespawn;
vmCvar_t g_motd;
vmCvar_t g_synchronousClients;
vmCvar_t g_warmup;
vmCvar_t g_doWarmup;
vmCvar_t g_restarted;
vmCvar_t g_log;
vmCvar_t g_logSync;
vmCvar_t g_blood;
vmCvar_t g_podiumDist;
vmCvar_t g_podiumDrop;
vmCvar_t g_allowVote;
vmCvar_t g_teamAutoJoin;
vmCvar_t g_teamForceBalance;
vmCvar_t g_banIPs;
vmCvar_t g_filterBan;
vmCvar_t g_smoothClients;
vmCvar_t pmove_fixed;
vmCvar_t pmove_msec;
vmCvar_t g_rankings;
vmCvar_t g_listEntity;
#ifdef MISSIONPACK
vmCvar_t g_obeliskHealth;
vmCvar_t g_obeliskRegenPeriod;
vmCvar_t g_obeliskRegenAmount;
vmCvar_t g_obeliskRespawnDelay;
vmCvar_t g_cubeTimeout;
vmCvar_t g_redteam;
vmCvar_t g_blueteam;
vmCvar_t g_singlePlayer;
vmCvar_t g_enableDust;
vmCvar_t g_enableBreath;
vmCvar_t g_proxMineTimeout;
#endif
// bk001129 - made static to avoid aliasing
static cvarTable_t gameCvarTable[] = {
// don't override the cheat state set by the system
{ &g_cheats, "sv_cheats", "", 0, 0, qfalse },
// noset vars
{ NULL, "gamename", GAMEVERSION , CVAR_SERVERINFO | CVAR_ROM, 0, qfalse },
{ NULL, "gamedate", __DATE__ , CVAR_ROM, 0, qfalse },
{ &g_restarted, "g_restarted", "0", CVAR_ROM, 0, qfalse },
{ NULL, "sv_mapname", "", CVAR_SERVERINFO | CVAR_ROM, 0, qfalse },
// latched vars
{ &g_gametype, "g_gametype", "0", CVAR_SERVERINFO | CVAR_USERINFO | CVAR_LATCH, 0, qfalse },
{ &g_maxclients, "sv_maxclients", "8", CVAR_SERVERINFO | CVAR_LATCH | CVAR_ARCHIVE, 0, qfalse },
{ &g_maxGameClients, "g_maxGameClients", "0", CVAR_SERVERINFO | CVAR_LATCH | CVAR_ARCHIVE, 0, qfalse },
// change anytime vars
{ &g_dmflags, "dmflags", "0", CVAR_SERVERINFO | CVAR_ARCHIVE, 0, qtrue },
{ &g_fraglimit, "fraglimit", "20", CVAR_SERVERINFO | CVAR_ARCHIVE | CVAR_NORESTART, 0, qtrue },
{ &g_timelimit, "timelimit", "0", CVAR_SERVERINFO | CVAR_ARCHIVE | CVAR_NORESTART, 0, qtrue },
{ &g_capturelimit, "capturelimit", "8", CVAR_SERVERINFO | CVAR_ARCHIVE | CVAR_NORESTART, 0, qtrue },
{ &g_synchronousClients, "g_synchronousClients", "0", CVAR_SYSTEMINFO, 0, qfalse },
{ &g_friendlyFire, "g_friendlyFire", "0", CVAR_ARCHIVE, 0, qtrue },
{ &g_teamAutoJoin, "g_teamAutoJoin", "0", CVAR_ARCHIVE },
{ &g_teamForceBalance, "g_teamForceBalance", "0", CVAR_ARCHIVE },
{ &g_warmup, "g_warmup", "20", CVAR_ARCHIVE, 0, qtrue },
{ &g_doWarmup, "g_doWarmup", "0", 0, 0, qtrue },
{ &g_log, "g_log", "games.log", CVAR_ARCHIVE, 0, qfalse },
{ &g_logSync, "g_logSync", "0", CVAR_ARCHIVE, 0, qfalse },
{ &g_password, "g_password", "", CVAR_USERINFO, 0, qfalse },
{ &g_banIPs, "g_banIPs", "", CVAR_ARCHIVE, 0, qfalse },
{ &g_filterBan, "g_filterBan", "1", CVAR_ARCHIVE, 0, qfalse },
{ &g_needpass, "g_needpass", "0", CVAR_SERVERINFO | CVAR_ROM, 0, qfalse },
{ &g_dedicated, "dedicated", "0", 0, 0, qfalse },
{ &g_speed, "g_speed", "320", 0, 0, qtrue },
{ &g_gravity, "g_gravity", "800", 0, 0, qtrue },
{ &g_knockback, "g_knockback", "1000", 0, 0, qtrue },
{ &g_quadfactor, "g_quadfactor", "3", 0, 0, qtrue },
{ &g_weaponRespawn, "g_weaponrespawn", "5", 0, 0, qtrue },
{ &g_weaponTeamRespawn, "g_weaponTeamRespawn", "30", 0, 0, qtrue },
{ &g_forcerespawn, "g_forcerespawn", "20", 0, 0, qtrue },
{ &g_inactivity, "g_inactivity", "0", 0, 0, qtrue },
{ &g_debugMove, "g_debugMove", "0", 0, 0, qfalse },
{ &g_debugDamage, "g_debugDamage", "0", 0, 0, qfalse },
{ &g_debugAlloc, "g_debugAlloc", "0", 0, 0, qfalse },
{ &g_motd, "g_motd", "", 0, 0, qfalse },
{ &g_blood, "com_blood", "1", 0, 0, qfalse },
{ &g_podiumDist, "g_podiumDist", "80", 0, 0, qfalse },
{ &g_podiumDrop, "g_podiumDrop", "70", 0, 0, qfalse },
{ &g_allowVote, "g_allowVote", "1", CVAR_ARCHIVE, 0, qfalse },
{ &g_listEntity, "g_listEntity", "0", 0, 0, qfalse },
#ifdef MISSIONPACK
{ &g_obeliskHealth, "g_obeliskHealth", "2500", 0, 0, qfalse },
{ &g_obeliskRegenPeriod, "g_obeliskRegenPeriod", "1", 0, 0, qfalse },
{ &g_obeliskRegenAmount, "g_obeliskRegenAmount", "15", 0, 0, qfalse },
{ &g_obeliskRespawnDelay, "g_obeliskRespawnDelay", "10", CVAR_SERVERINFO, 0, qfalse },
{ &g_cubeTimeout, "g_cubeTimeout", "30", 0, 0, qfalse },
{ &g_redteam, "g_redteam", "Stroggs", CVAR_ARCHIVE | CVAR_SERVERINFO | CVAR_USERINFO , 0, qtrue, qtrue },
{ &g_blueteam, "g_blueteam", "Pagans", CVAR_ARCHIVE | CVAR_SERVERINFO | CVAR_USERINFO , 0, qtrue, qtrue },
{ &g_singlePlayer, "ui_singlePlayerActive", "", 0, 0, qfalse, qfalse },
{ &g_enableDust, "g_enableDust", "0", CVAR_SERVERINFO, 0, qtrue, qfalse },
{ &g_enableBreath, "g_enableBreath", "0", CVAR_SERVERINFO, 0, qtrue, qfalse },
{ &g_proxMineTimeout, "g_proxMineTimeout", "20000", 0, 0, qfalse },
#endif
{ &g_smoothClients, "g_smoothClients", "1", 0, 0, qfalse},
{ &pmove_fixed, "pmove_fixed", "0", CVAR_SYSTEMINFO, 0, qfalse},
{ &pmove_msec, "pmove_msec", "8", CVAR_SYSTEMINFO, 0, qfalse},
{ &g_rankings, "g_rankings", "0", 0, 0, qfalse}
};
// bk001129 - made static to avoid aliasing
static int gameCvarTableSize = sizeof( gameCvarTable ) / sizeof( gameCvarTable[0] );
void G_InitGame( int levelTime, int randomSeed, int restart );
void G_RunFrame( int levelTime );
void G_ShutdownGame( int restart );
void CheckExitRules( void );
/*
================
vmMain
This is the only way control passes into the module.
This must be the very first function compiled into the .q3vm file
================
*/
int vmMain( int command, int arg0, int arg1, int arg2, int arg3, int arg4, int arg5, int arg6, int arg7, int arg8, int arg9, int arg10, int arg11 ) {
switch ( command ) {
case GAME_INIT:
G_InitGame( arg0, arg1, arg2 );
return 0;
case GAME_SHUTDOWN:
G_ShutdownGame( arg0 );
return 0;
case GAME_CLIENT_CONNECT:
return (int)ClientConnect( arg0, arg1, arg2 );
case GAME_CLIENT_THINK:
ClientThink( arg0 );
return 0;
case GAME_CLIENT_USERINFO_CHANGED:
ClientUserinfoChanged( arg0 );
return 0;
case GAME_CLIENT_DISCONNECT:
ClientDisconnect( arg0 );
return 0;
case GAME_CLIENT_BEGIN:
ClientBegin( arg0 );
return 0;
case GAME_CLIENT_COMMAND:
ClientCommand( arg0 );
return 0;
case GAME_RUN_FRAME:
G_RunFrame( arg0 );
return 0;
case GAME_CONSOLE_COMMAND:
return ConsoleCommand();
case BOTAI_START_FRAME:
return BotAIStartFrame( arg0 );
}
return -1;
}
void QDECL G_Printf( const char *fmt, ... ) {
va_list argptr;
char text[1024];
va_start (argptr, fmt);
vsprintf (text, fmt, argptr);
va_end (argptr);
trap_Printf( text );
}
void QDECL G_Error( const char *fmt, ... ) {
va_list argptr;
char text[1024];
va_start (argptr, fmt);
vsprintf (text, fmt, argptr);
va_end (argptr);
trap_Error( text );
}
/*
================
G_FindTeams
Chain together all entities with a matching team field.
Entity teams are used for item groups and multi-entity mover groups.
All but the first will have the FL_TEAMSLAVE flag set and teammaster field set
All but the last will have the teamchain field set to the next one
================
*/
void G_FindTeams( void ) {
gentity_t *e, *e2;
int i, j;
int c, c2;
c = 0;
c2 = 0;
for ( i=1, e=g_entities+i ; i < level.num_entities ; i++,e++ ){
if (!e->inuse)
continue;
if (!e->team)
continue;
if (e->flags & FL_TEAMSLAVE)
continue;
e->teammaster = e;
c++;
c2++;
for (j=i+1, e2=e+1 ; j < level.num_entities ; j++,e2++)
{
if (!e2->inuse)
continue;
if (!e2->team)
continue;
if (e2->flags & FL_TEAMSLAVE)
continue;
if (!strcmp(e->team, e2->team))
{
c2++;
e2->teamchain = e->teamchain;
e->teamchain = e2;
e2->teammaster = e;
e2->flags |= FL_TEAMSLAVE;
// make sure that targets only point at the master
if ( e2->targetname ) {
e->targetname = e2->targetname;
e2->targetname = NULL;
}
}
}
}
G_Printf ("%i teams with %i entities\n", c, c2);
}
void G_RemapTeamShaders() {
#ifdef MISSIONPACK
char string[1024];
float f = level.time * 0.001;
Com_sprintf( string, sizeof(string), "team_icon/%s_red", g_redteam.string );
AddRemap("textures/ctf2/redteam01", string, f);
AddRemap("textures/ctf2/redteam02", string, f);
Com_sprintf( string, sizeof(string), "team_icon/%s_blue", g_blueteam.string );
AddRemap("textures/ctf2/blueteam01", string, f);
AddRemap("textures/ctf2/blueteam02", string, f);
trap_SetConfigstring(CS_SHADERSTATE, BuildShaderStateConfig());
#endif
}
/*
=================
G_RegisterCvars
=================
*/
void G_RegisterCvars( void ) {
int i;
cvarTable_t *cv;
qboolean remapped = qfalse;
for ( i = 0, cv = gameCvarTable ; i < gameCvarTableSize ; i++, cv++ ) {
trap_Cvar_Register( cv->vmCvar, cv->cvarName,
cv->defaultString, cv->cvarFlags );
if ( cv->vmCvar )
cv->modificationCount = cv->vmCvar->modificationCount;
if (cv->teamShader) {
remapped = qtrue;
}
}
if (remapped) {
G_RemapTeamShaders();
}
// check some things
if ( g_gametype.integer < 0 || g_gametype.integer >= GT_MAX_GAME_TYPE ) {
G_Printf( "g_gametype %i is out of range, defaulting to 0\n", g_gametype.integer );
trap_Cvar_Set( "g_gametype", "0" );
}
level.warmupModificationCount = g_warmup.modificationCount;
}
/*
=================
G_UpdateCvars
=================
*/
void G_UpdateCvars( void ) {
int i;
cvarTable_t *cv;
qboolean remapped = qfalse;
for ( i = 0, cv = gameCvarTable ; i < gameCvarTableSize ; i++, cv++ ) {
if ( cv->vmCvar ) {
trap_Cvar_Update( cv->vmCvar );
if ( cv->modificationCount != cv->vmCvar->modificationCount ) {
cv->modificationCount = cv->vmCvar->modificationCount;
if ( cv->trackChange ) {
trap_SendServerCommand( -1, va("print \"Server: %s changed to %s\n\"",
cv->cvarName, cv->vmCvar->string ) );
}
if (cv->teamShader) {
remapped = qtrue;
}
}
}
}
if (remapped) {
G_RemapTeamShaders();
}
}
/*
============
G_InitGame
============
*/
void G_InitGame( int levelTime, int randomSeed, int restart ) {
int i;
G_Printf ("------- Game Initialization -------\n");
G_Printf ("gamename: %s\n", GAMEVERSION);
G_Printf ("gamedate: %s\n", __DATE__);
srand( randomSeed );
G_RegisterCvars();
G_ProcessIPBans();
G_InitMemory();
// set some level globals
memset( &level, 0, sizeof( level ) );
level.time = levelTime;
level.startTime = levelTime;
level.snd_fry = G_SoundIndex("sound/player/fry.wav"); // FIXME standing in lava / slime
if ( g_gametype.integer != GT_SINGLE_PLAYER && g_log.string[0] ) {
if ( g_logSync.integer ) {
trap_FS_FOpenFile( g_log.string, &level.logFile, FS_APPEND_SYNC );
} else {
trap_FS_FOpenFile( g_log.string, &level.logFile, FS_APPEND );
}
if ( !level.logFile ) {
G_Printf( "WARNING: Couldn't open logfile: %s\n", g_log.string );
} else {
char serverinfo[MAX_INFO_STRING];
trap_GetServerinfo( serverinfo, sizeof( serverinfo ) );
G_LogPrintf("------------------------------------------------------------\n" );
G_LogPrintf("InitGame: %s\n", serverinfo );
}
} else {
G_Printf( "Not logging to disk.\n" );
}
G_InitWorldSession();
// initialize all entities for this game
memset( g_entities, 0, MAX_GENTITIES * sizeof(g_entities[0]) );
level.gentities = g_entities;
// initialize all clients for this game
level.maxclients = g_maxclients.integer;
memset( g_clients, 0, MAX_CLIENTS * sizeof(g_clients[0]) );
level.clients = g_clients;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -