mirror of
https://github.com/UberGames/RPG-X2.git
synced 2024-11-27 22:52:33 +00:00
03e1822a02
Most of them game logic entities such as target_boolean or target_warp.
934 lines
22 KiB
C
934 lines
22 KiB
C
// Copyright (C) 1999-2000 Id Software, Inc.
|
|
//
|
|
// cg_servercmds.c -- reliably sequenced text commands sent by the server
|
|
// these are processed at snapshot transition time, so there will definately
|
|
// be a valid snapshot this frame
|
|
|
|
#include "cg_local.h"
|
|
|
|
#define MAX_LOCAL_ENTITIES 512
|
|
extern localEntity_t cg_localEntities[MAX_LOCAL_ENTITIES];
|
|
|
|
/*
|
|
=================
|
|
CG_ParseScores
|
|
|
|
=================
|
|
*/
|
|
static void CG_ParseScores( void ) {
|
|
int i, powerups, eliminated;
|
|
|
|
cg.numScores = atoi( CG_Argv( 1 ) );
|
|
if ( cg.numScores > MAX_CLIENTS ) {
|
|
cg.numScores = MAX_CLIENTS;
|
|
}
|
|
|
|
cg.teamScores[0] = atoi( CG_Argv( 2 ) );
|
|
cg.teamScores[1] = atoi( CG_Argv( 3 ) );
|
|
|
|
memset( cg.scores, 0, sizeof( cg.scores ) );
|
|
for ( i = 0 ; i < cg.numScores ; i++ ) {
|
|
cg.scores[i].client = atoi( CG_Argv( i * 11+ 4 ) );
|
|
cg.scores[i].score = atoi( CG_Argv( i * 11+ 5 ) );
|
|
cg.scores[i].ping = atoi( CG_Argv( i * 11+ 6 ) );
|
|
cg.scores[i].time = atoi( CG_Argv( i * 11+ 7 ) );
|
|
cg.scores[i].scoreFlags = atoi( CG_Argv( i * 11+ 8 ) );
|
|
powerups = atoi( CG_Argv( i * 11+ 9 ) );
|
|
// cg.scores[i].faveTarget = atoi( CG_Argv( i * 12+ 10) );
|
|
// cg.scores[i].faveTargetKills = atoi( CG_Argv( i * 12+ 11) );
|
|
cg.scores[i].worstEnemy = atoi( CG_Argv( i * 11+ 10) );
|
|
cg.scores[i].worstEnemyKills= atoi( CG_Argv( i * 11+ 11) );
|
|
cg.scores[i].faveWeapon = atoi( CG_Argv( i * 11+ 12) );
|
|
cg.scores[i].killedCnt = atoi( CG_Argv( i * 11+ 13) );
|
|
eliminated = atoi( CG_Argv( i * 11+ 14) );
|
|
|
|
if ( cg.scores[i].client < 0 || cg.scores[i].client >= MAX_CLIENTS ) {
|
|
cg.scores[i].client = 0;
|
|
}
|
|
cgs.clientinfo[ cg.scores[i].client ].score = cg.scores[i].score;
|
|
cgs.clientinfo[ cg.scores[i].client ].powerups = powerups;
|
|
cgs.clientinfo[ cg.scores[i].client ].eliminated = eliminated;
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
=================
|
|
CG_ParseTeamInfo
|
|
|
|
=================
|
|
*/
|
|
static void CG_ParseTeamInfo( void ) {
|
|
int i;
|
|
int client;
|
|
|
|
numSortedTeamPlayers = atoi( CG_Argv( 1 ) );
|
|
|
|
for ( i = 0 ; i < numSortedTeamPlayers ; i++ ) {
|
|
client = atoi( CG_Argv( i * 2 + 2 ) ); //6
|
|
|
|
sortedTeamPlayers[i] = client;
|
|
|
|
cgs.clientinfo[ client ].location = atoi( CG_Argv( i * 2 + 3 ) ); //6 +
|
|
/*cgs.clientinfo[ client ].health = atoi( CG_Argv( i * 6 + 4 ) );
|
|
cgs.clientinfo[ client ].armor = atoi( CG_Argv( i * 6 + 5 ) );
|
|
cgs.clientinfo[ client ].curWeapon = atoi( CG_Argv( i * 6 + 6 ) );
|
|
cgs.clientinfo[ client ].powerups = atoi( CG_Argv( i * 6 + 7 ) );*/
|
|
}
|
|
}
|
|
|
|
/*
|
|
=================
|
|
CG_ParseHealthInfo
|
|
|
|
=================
|
|
*/
|
|
static void CG_ParseHealthInfo( void ) {
|
|
int i;
|
|
int client;
|
|
int numHealthInfoClients = 0;
|
|
|
|
numHealthInfoClients = atoi( CG_Argv( 1 ) );
|
|
|
|
for ( i = 0 ; i < numHealthInfoClients ; i++ ) {
|
|
client = atoi( CG_Argv( i * 2 + 2 ) );
|
|
|
|
cgs.clientinfo[ client ].health = atoi( CG_Argv( i * 2 + 3 ) );
|
|
}
|
|
}
|
|
/*
|
|
================
|
|
CG_ParseServerinfo
|
|
|
|
This is called explicitly when the gamestate is first received,
|
|
and whenever the server updates any serverinfo flagged cvars
|
|
================
|
|
*/
|
|
void CG_ParseServerinfo( void ) {
|
|
const char *info;
|
|
char *mapname;
|
|
|
|
info = CG_ConfigString( CS_SERVERINFO );
|
|
cgs.gametype = atoi( Info_ValueForKey( info, "g_gametype" ) );
|
|
cgs.pModAssimilation = atoi( Info_ValueForKey( info, "g_pModAssimilation" ) );
|
|
cgs.pModDisintegration = atoi( Info_ValueForKey( info, "g_pModDisintegration" ) );
|
|
cgs.pModActionHero = atoi( Info_ValueForKey( info, "g_pModActionHero" ) );
|
|
cgs.pModSpecialties = atoi( Info_ValueForKey( info, "g_pModSpecialties" ) );
|
|
cgs.pModElimination = atoi( Info_ValueForKey( info, "g_pModElimination" ) );
|
|
cgs.dmflags = atoi( Info_ValueForKey( info, "dmflags" ) );
|
|
cgs.teamflags = atoi( Info_ValueForKey( info, "teamflags" ) );
|
|
cgs.fraglimit = atoi( Info_ValueForKey( info, "fraglimit" ) );
|
|
cgs.capturelimit = atoi( Info_ValueForKey( info, "capturelimit" ) );
|
|
cgs.timelimit = atoi( Info_ValueForKey( info, "timelimit" ) );
|
|
cgs.maxclients = atoi( Info_ValueForKey( info, "sv_maxclients" ) );
|
|
cgs.ForceClassColor = atoi( Info_ValueForKey( info, "rpg_forceClassColor" ) );
|
|
mapname = Info_ValueForKey( info, "mapname" );
|
|
Com_sprintf( cgs.mapname, sizeof( cgs.mapname ), "maps/%s.bsp", mapname );
|
|
|
|
//RPG-X: TiM - new Rankset
|
|
Q_strncpyz( cgs.rankSet, Info_ValueForKey( info, "rpg_rankSet"), sizeof(cgs.rankSet) );
|
|
//RPG-X: TiM - new Class set
|
|
Q_strncpyz( cgs.classSet, Info_ValueForKey( info, "rpg_classSet" ), sizeof( cgs.classSet ) );
|
|
|
|
//scannable panels
|
|
cgs.scannablePanels = atoi( Info_ValueForKey( info, "rpg_scannablePanels" ) );
|
|
}
|
|
|
|
/*
|
|
==================
|
|
CG_ParseWarmup
|
|
==================
|
|
*/
|
|
static void CG_ParseWarmup( void ) {
|
|
const char *info;
|
|
int warmup;
|
|
|
|
info = CG_ConfigString( CS_WARMUP );
|
|
|
|
warmup = atoi( info );
|
|
cg.warmupCount = -1;
|
|
|
|
if ( warmup == 0 && cg.warmup ) {
|
|
|
|
} else if ( warmup > 0 && cg.warmup <= 0 ) {
|
|
trap_S_StartLocalSound( cgs.media.countPrepareSound, CHAN_ANNOUNCER );
|
|
}
|
|
|
|
cg.warmup = warmup;
|
|
}
|
|
|
|
/*
|
|
================
|
|
CG_SetConfigValues
|
|
|
|
Called on load to set the initial values from configure strings
|
|
================
|
|
*/
|
|
void CG_SetConfigValues( void ) {
|
|
const char *s;
|
|
|
|
cgs.scores1 = atoi( CG_ConfigString( CS_SCORES1 ) );
|
|
cgs.scores2 = atoi( CG_ConfigString( CS_SCORES2 ) );
|
|
cgs.levelStartTime = atoi( CG_ConfigString( CS_LEVEL_START_TIME ) );
|
|
s = CG_ConfigString( CS_FLAGSTATUS );
|
|
cgs.redflag = s[0] - '0';
|
|
cgs.blueflag = s[1] - '0';
|
|
cg.warmup = atoi( CG_ConfigString( CS_WARMUP ) );
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
CG_ClientShakeCamera
|
|
|
|
TiM: Parses the cam shake
|
|
config string, and inputs the useful
|
|
data.
|
|
=====================
|
|
*/
|
|
void CG_ClientShakeCamera ( void ) {
|
|
float intensity;
|
|
int duration;
|
|
char *str;
|
|
|
|
str = (char *)CG_ConfigString( CS_CAMERA_SHAKE );
|
|
intensity = (float)atoi( COM_Parse( &str ) )/10.0f;
|
|
duration = atoi( COM_Parse( &str ) ); //This is an offset so if a player somehow received
|
|
//the string halfway thru the cycle (ie just connected etc)
|
|
//This way, it'll only do it as much as is left for the rest of the players
|
|
cg.shake_serverIndex = duration; //Back up the index for later
|
|
duration -= ( cg.time - cgs.levelStartTime ); //This is the actual duration, based off of length, and the time the command was received
|
|
|
|
CG_CameraShake( intensity, duration, qtrue );
|
|
}
|
|
|
|
/*
|
|
==================
|
|
CG_ParseClassData
|
|
==================
|
|
*/
|
|
/*void CG_ParseClassData( void )
|
|
{
|
|
char *str;
|
|
int i;
|
|
char *val;
|
|
char *lineChar;
|
|
char *lineCharEnd;
|
|
int colorBits;
|
|
int classBits;
|
|
|
|
str = (char *)CG_ConfigString( CS_CLASS_DATA );
|
|
|
|
if ( !str[0] )
|
|
return;
|
|
|
|
memset( &cgs.classData, 0, sizeof( cgs.classData ) );
|
|
|
|
for ( i = 0; i < MAX_CLASSES; i++ ) {
|
|
val = Info_ValueForKey( str, va( "c%i", i ) );
|
|
|
|
if (!val[0])
|
|
break;
|
|
|
|
//First slash = consoleName, so skip that
|
|
lineChar = strstr( val, "|");
|
|
lineChar++;
|
|
|
|
//next line should be formal name
|
|
lineCharEnd = strstr( lineChar, "|" );
|
|
lineCharEnd--;
|
|
|
|
val = lineChar;
|
|
val[ strlen(lineChar) - strlen(lineCharEnd) + 1] = '\0';
|
|
|
|
Q_strncpyz( cgs.classData[i].formalName, val, sizeof( cgs.classData[i].formalName ) );
|
|
|
|
//CG_Printf( S_COLOR_RED "%s\n", cgs.classData[i].formalName );
|
|
|
|
//--Next is color
|
|
|
|
lineChar = lineChar + (strlen(lineChar) - strlen(lineCharEnd))+2;
|
|
|
|
lineCharEnd = strstr( lineChar, "|" );
|
|
lineCharEnd--;
|
|
|
|
val = lineChar;
|
|
val[ strlen(lineChar) - strlen(lineCharEnd)+1] = '\0';
|
|
|
|
colorBits=atoi( val );
|
|
cgs.classData[i].radarColor[0] = colorBits & 255;
|
|
cgs.classData[i].radarColor[1] = (colorBits >> 8) & 255;
|
|
cgs.classData[i].radarColor[2] = (colorBits >> 16) & 255;
|
|
|
|
//CG_Printf( S_COLOR_RED "%i\n", colorBits );
|
|
|
|
//cgs.classData[i].showRanks = (colorBits >> 25) & 1;
|
|
|
|
//--Next is Rank Icon Color
|
|
lineChar = lineCharEnd+2;
|
|
|
|
classBits = atoi( lineChar );
|
|
|
|
cgs.classData[i].isMedic = ( classBits >> 1 ) & 1;
|
|
cgs.classData[i].showRanks = ( classBits >> 2 ) & 1;
|
|
cgs.classData[i].iconColor = ( classBits >> 4 ) & 15;
|
|
|
|
//CG_Printf( S_COLOR_RED "%i\n", classBits );
|
|
}
|
|
}*/
|
|
|
|
/*
|
|
================
|
|
CG_ConfigStringModified
|
|
|
|
================
|
|
*/
|
|
static void CG_ConfigStringModified( void ) {
|
|
const char *str;
|
|
int num;
|
|
|
|
num = atoi( CG_Argv( 1 ) );
|
|
|
|
// get the gamestate from the client system, which will have the
|
|
// new configstring already integrated
|
|
trap_GetGameState( &cgs.gameState );
|
|
|
|
// look up the individual string that was modified
|
|
str = CG_ConfigString( num );
|
|
|
|
// do something with it if necessary
|
|
if ( num == CS_MUSIC ) {
|
|
CG_StartMusic();
|
|
} else if ( num == CS_CAMERA_SHAKE ) { //RPG-X : TiM - Camera Shake
|
|
CG_ClientShakeCamera();
|
|
} else if ( num == CS_SERVERINFO ) {
|
|
CG_ParseServerinfo();
|
|
} else if ( num == CS_WARMUP ) {
|
|
CG_ParseWarmup();
|
|
} else if ( num == CS_SCORES1 ) {
|
|
cgs.scores1 = atoi( str );
|
|
} else if ( num == CS_SCORES2 ) {
|
|
cgs.scores2 = atoi( str );
|
|
} else if ( num == CS_WARMUP ) {
|
|
CG_ParseWarmup();
|
|
} else if ( num == CS_LEVEL_START_TIME ) {
|
|
cgs.levelStartTime = atoi( str );
|
|
} else if ( num == CS_VOTE_TIME ) {
|
|
cgs.voteTime = atoi( str );
|
|
cgs.voteModified = qtrue;
|
|
} else if ( num == CS_VOTE_YES ) {
|
|
cgs.voteYes = atoi( str );
|
|
cgs.voteModified = qtrue;
|
|
} else if ( num == CS_VOTE_NO ) {
|
|
cgs.voteNo = atoi( str );
|
|
cgs.voteModified = qtrue;
|
|
} else if ( num == CS_VOTE_STRING ) {
|
|
Q_strncpyz( cgs.voteString, str, sizeof( cgs.voteString ) );
|
|
} else if ( num == CS_INTERMISSION ) {
|
|
cg.intermissionStarted = atoi( str );
|
|
} else if ( num >= CS_MODELS && num < CS_MODELS+MAX_MODELS ) {
|
|
cgs.gameModels[ num-CS_MODELS ] = trap_R_RegisterModel( str );
|
|
} else if ( num >= CS_SOUNDS && num < CS_SOUNDS+MAX_SOUNDS ) {
|
|
if ( str[0] != '*' ) { // player specific sounds don't register here
|
|
cgs.gameSounds[ num-CS_SOUNDS] = trap_S_RegisterSound( str );
|
|
}
|
|
} else if ( num >= CS_PLAYERS && num < CS_PLAYERS+MAX_CLIENTS ) {
|
|
CG_NewClientInfo( num - CS_PLAYERS );
|
|
} else if ( num >= CS_DECOYS && num < CS_DECOYS+MAX_DECOYS ) {
|
|
CG_NewDecoyInfo( num - CS_DECOYS );
|
|
} else if ( num == CS_FLAGSTATUS ) {
|
|
// format is rb where its red/blue, 0 is at base, 1 is taken, 2 is dropped
|
|
cgs.redflag = str[0] - '0';
|
|
cgs.blueflag = str[1] - '0';
|
|
}
|
|
#ifdef XTRA
|
|
else if(num == CS_SHADERSTATE) {
|
|
CG_ShaderStateChanged();
|
|
}
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
/*
|
|
=======================
|
|
CG_AddToTeamChat
|
|
|
|
=======================
|
|
*/
|
|
static void CG_AddToTeamChat( const char *str ) {
|
|
int len;
|
|
char *p, *ls;
|
|
int lastcolor;
|
|
int chatHeight;
|
|
|
|
if (cg_teamChatHeight.integer < TEAMCHAT_HEIGHT) {
|
|
chatHeight = cg_teamChatHeight.integer;
|
|
} else {
|
|
chatHeight = TEAMCHAT_HEIGHT;
|
|
}
|
|
|
|
if (chatHeight <= 0 || cg_teamChatTime.integer <= 0) {
|
|
// team chat disabled, dump into normal chat
|
|
cgs.teamChatPos = cgs.teamLastChatPos = 0;
|
|
return;
|
|
}
|
|
|
|
len = 0;
|
|
|
|
p = cgs.teamChatMsgs[cgs.teamChatPos % chatHeight];
|
|
*p = 0;
|
|
|
|
lastcolor = '7';
|
|
|
|
ls = NULL;
|
|
while (*str) {
|
|
if (len > TEAMCHAT_WIDTH - 1) {
|
|
if (ls) {
|
|
str -= (p - ls);
|
|
str++;
|
|
p -= (p - ls);
|
|
}
|
|
*p = 0;
|
|
|
|
cgs.teamChatMsgTimes[cgs.teamChatPos % chatHeight] = cg.time;
|
|
|
|
cgs.teamChatPos++;
|
|
p = cgs.teamChatMsgs[cgs.teamChatPos % chatHeight];
|
|
*p = 0;
|
|
*p++ = Q_COLOR_ESCAPE;
|
|
*p++ = lastcolor;
|
|
len = 0;
|
|
ls = NULL;
|
|
}
|
|
|
|
if ( Q_IsColorString( str ) ) {
|
|
*p++ = *str++;
|
|
lastcolor = *str;
|
|
*p++ = *str++;
|
|
continue;
|
|
}
|
|
if (*str == ' ') {
|
|
ls = p;
|
|
}
|
|
*p++ = *str++;
|
|
len++;
|
|
}
|
|
*p = 0;
|
|
|
|
cgs.teamChatMsgTimes[cgs.teamChatPos % chatHeight] = cg.time;
|
|
cgs.teamChatPos++;
|
|
|
|
if (cgs.teamChatPos - cgs.teamLastChatPos > chatHeight)
|
|
cgs.teamLastChatPos = cgs.teamChatPos - chatHeight;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
===============
|
|
CG_MapRestart
|
|
|
|
The server has issued a map_restart, so the next snapshot
|
|
is completely new and should not be interpolated to.
|
|
|
|
A tournement restart will clear everything, but doesn't
|
|
require a reload of all the media
|
|
===============
|
|
*/
|
|
static void CG_MapRestart( void ) {
|
|
if ( cg_showmiss.integer ) {
|
|
CG_Printf( "CG_MapRestart\n" );
|
|
}
|
|
|
|
CG_InitLocalEntities();
|
|
CG_InitMarkPolys();
|
|
|
|
// make sure the "3 frags left" warnings play again
|
|
cg.fraglimitWarnings = 0;
|
|
|
|
cg.timelimitWarnings = 0;
|
|
|
|
cg.intermissionStarted = qfalse;
|
|
|
|
cgs.voteTime = 0;
|
|
|
|
CG_StartMusic();
|
|
|
|
// we really should clear more parts of cg here and stop sounds
|
|
|
|
// play the "fight" sound if this is a restart without warmup
|
|
if ( cg.warmup == 0 /* && cgs.gametype == GT_TOURNAMENT */)
|
|
{
|
|
trap_S_StartLocalSound( cgs.media.countFightSound, CHAN_ANNOUNCER );
|
|
}
|
|
}
|
|
|
|
/***********************
|
|
CG_EncodeIDFile
|
|
|
|
The server detected that
|
|
we have a pure copy of
|
|
the ID, so it's sent us
|
|
the IP it received so we
|
|
can byte encrypt it into
|
|
an ID and save it to file
|
|
***********************/
|
|
|
|
static void CG_EncodeIDFile( void )
|
|
{
|
|
unsigned int playerID;
|
|
char *IP;
|
|
char strSubnet[3];
|
|
int intSubnet[4];
|
|
int i, j;
|
|
|
|
memset(strSubnet, 0, sizeof(strSubnet));
|
|
|
|
IP = (char *)CG_Argv( 1 );
|
|
//IP = "143.163.12.2";
|
|
|
|
//TiM - Scooter's IP List
|
|
//Double-check we're not spawning an ID off of these
|
|
if( Q_stricmp( IP, "localhost" ) //localhost
|
|
&& Q_strncmp( IP, "10.", 3 ) //class A
|
|
&& Q_strncmp( IP, "172.16.", 7 ) //class B
|
|
&& Q_strncmp( IP, "192.168.", 8 ) //class C
|
|
&& Q_strncmp( IP, "127.", 4 ) //loopback
|
|
&& Q_strncmp( IP, "169.254.", 8 ) //link-local
|
|
)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//check we don't already have an ID
|
|
if ( (unsigned)atoul( sv_securityCode.string ) != SECURITY_PID )
|
|
return;
|
|
|
|
i = 0;
|
|
j = 0;
|
|
while ( *IP )
|
|
{
|
|
if( *IP != '.' )
|
|
{
|
|
if ( i < 3 )
|
|
strSubnet[i++] = *IP;
|
|
}
|
|
else
|
|
{
|
|
if ( j < 4 )
|
|
intSubnet[j++] = atoi( strSubnet );
|
|
|
|
i=0;
|
|
memset( strSubnet, 0, 3 );
|
|
}
|
|
|
|
IP++;
|
|
}
|
|
|
|
//the final cell
|
|
intSubnet[j++] = atoi( strSubnet );
|
|
|
|
//calculate the key
|
|
playerID = ( intSubnet[3] << 24 ) | ( intSubnet[2] << 16 ) | ( intSubnet[1] << 8 ) | intSubnet[0];
|
|
|
|
//CG_Printf( "%i %i %i %i - %u\n", intSubnet[0], intSubnet[1], intSubnet[2], intSubnet[3], playerID );
|
|
|
|
//encode the information into the id key file
|
|
{
|
|
fileHandle_t f;
|
|
//unsigned char buffer[SECURITY_SIZE];
|
|
int fileLen;
|
|
rpgxSecurityFile_t sF;
|
|
|
|
fileLen = trap_FS_FOpenFile( SECURITY_FILE, &f, FS_READ );
|
|
|
|
if ( !f || fileLen != SECURITY_SIZE )
|
|
{
|
|
CG_Error( "ERROR: Could not validate %s file.\n", SECURITY_FILE );
|
|
return;
|
|
}
|
|
|
|
trap_FS_Read( &sF, SECURITY_SIZE, f );
|
|
|
|
trap_FS_FCloseFile( f );
|
|
|
|
if ( !sF.ID || sF.ID != SECURITY_ID )
|
|
{
|
|
CG_Error( "ERROR: %s was loaded, but it wasn't valid.\n", SECURITY_FILE );
|
|
return;
|
|
}
|
|
|
|
//ensure the hash is valid
|
|
if ( sF.hash != atoul( sv_securityHash.string ) )
|
|
{
|
|
CG_Error( "ERROR: %s was loaded, but the hash wasn't valid.\n", SECURITY_FILE );
|
|
return;
|
|
}
|
|
|
|
//okay, reopen the file for writing, and input the new ID
|
|
f = 0;
|
|
|
|
fileLen = trap_FS_FOpenFile( SECURITY_FILE, &f, FS_WRITE );
|
|
if ( !f )
|
|
{
|
|
CG_Error( "ERROR: Could not validate %s file for writing.\n", SECURITY_FILE );
|
|
return;
|
|
}
|
|
|
|
//copy over the new key
|
|
sF.playerID = playerID;
|
|
|
|
trap_FS_Write( &sF, SECURITY_SIZE, f );
|
|
trap_FS_FCloseFile( f );
|
|
}
|
|
|
|
trap_Cvar_Set( "sv_SecurityCode", va( "%u", playerID ) );
|
|
}
|
|
|
|
|
|
/*
|
|
==================
|
|
ConcatArgs
|
|
==================
|
|
*/
|
|
char *ConcatArgs2( int start ) {
|
|
int i, c, tlen;
|
|
static char line[MAX_STRING_CHARS];
|
|
int len;
|
|
char arg[MAX_STRING_CHARS];
|
|
|
|
len = 0;
|
|
c = trap_Argc();
|
|
for ( i = start ; i < c ; i++ ) {
|
|
trap_Argv( i, arg, sizeof( arg ) );
|
|
tlen = strlen( arg );
|
|
if ( len + tlen >= MAX_STRING_CHARS - 1 ) {
|
|
break;
|
|
}
|
|
memcpy( line + len, arg, tlen );
|
|
len += tlen;
|
|
if ( i != c - 1 ) {
|
|
line[len] = ' ';
|
|
len++;
|
|
}
|
|
}
|
|
|
|
line[len] = 0;
|
|
|
|
return line;
|
|
}
|
|
|
|
#ifdef XTRA
|
|
/*
|
|
=====================
|
|
CG_ShaderStateChanged
|
|
=====================
|
|
*/
|
|
void CG_ShaderStateChanged(void) {
|
|
char originalShader[MAX_QPATH];
|
|
char newShader[MAX_QPATH];
|
|
char timeOffset[16];
|
|
const char *o;
|
|
char *n,*t;
|
|
|
|
o = CG_ConfigString( CS_SHADERSTATE );
|
|
|
|
if(!o) return;
|
|
|
|
while (o && *o) {
|
|
n = strstr(o, "=");
|
|
if (n && *n) {
|
|
strncpy(originalShader, o, n-o);
|
|
originalShader[n-o] = 0;
|
|
n++;
|
|
t = strstr(n, ":");
|
|
if (t && *t) {
|
|
strncpy(newShader, n, t-n);
|
|
newShader[t-n] = 0;
|
|
} else {
|
|
break;
|
|
}
|
|
t++;
|
|
o = strstr(t, "@");
|
|
if (o) {
|
|
strncpy(timeOffset, t, o-t);
|
|
timeOffset[o-t] = 0;
|
|
o++;
|
|
trap_R_RemapShader( originalShader, newShader, timeOffset );
|
|
}
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
/*
|
|
=================
|
|
CG_ServerCommand
|
|
|
|
The string has been tokenized and can be retrieved with
|
|
Cmd_Argc() / Cmd_Argv()
|
|
=================
|
|
*/
|
|
static void CG_ServerCommand( void ) {
|
|
const char *cmd;
|
|
|
|
cmd = CG_Argv(0);
|
|
|
|
if ( !cmd[0] ) {
|
|
// server claimed the command
|
|
return;
|
|
}
|
|
|
|
//RPG-X | Phenix | 13/02/2005
|
|
// Play a insult to the n00b when moved into n00b class
|
|
if ( !strcmp( cmd, "playN00bInsult") ) {
|
|
trap_S_StartLocalSound( cgs.media.N00bSound[(rand()%N00bSoundCount)], CHAN_LOCAL_SOUND );
|
|
CG_CenterPrint( "Welcome to the n00b Class", SCREEN_HEIGHT * 0.25, BIGCHAR_WIDTH );
|
|
return;
|
|
}
|
|
|
|
// RPG-X | Phenix | 08/06/05
|
|
if ( !strcmp( cmd, "servermsg") ) {
|
|
trap_S_StartLocalSound( cgs.media.AdminMsgSound, CHAN_LOCAL_SOUND );
|
|
cg.adminMsgTime = cg.time + 10000;
|
|
Q_strncpyz( cg.adminMsgMsg, ConcatArgs2(1), sizeof( cg.adminMsgMsg ) );
|
|
//CG_CenterPrint( cg.adminMsgMsg, SCREEN_HEIGHT * 0.25, BIGCHAR_WIDTH );
|
|
return;
|
|
}
|
|
|
|
if( !strcmp( cmd, "servercprint") ) {
|
|
trap_S_StartLocalSound( cgs.media.AdminMsgSound, CHAN_LOCAL_SOUND );
|
|
//cg.adminMsgTime = cg.time + 10000;
|
|
Q_strncpyz( cg.adminMsgMsg, ConcatArgs2(1), sizeof( cg.adminMsgMsg ) );
|
|
CG_CenterPrint( cg.adminMsgMsg, SCREEN_HEIGHT * 0.25, BIGCHAR_WIDTH );
|
|
return;
|
|
}
|
|
|
|
if ( !strcmp( cmd, "cp" ) ) {
|
|
CG_CenterPrint( CG_Argv(1), SCREEN_HEIGHT * 0.25, BIGCHAR_WIDTH );
|
|
return;
|
|
}
|
|
|
|
if ( !strcmp( cmd, "cs" ) ) {
|
|
CG_ConfigStringModified();
|
|
return;
|
|
}
|
|
|
|
if ( !strcmp( cmd, "print" ) ) {
|
|
CG_Printf( "%s", CG_Argv(1) );
|
|
return;
|
|
}
|
|
|
|
if ( !strcmp( cmd, "chat" ) ) {
|
|
trap_S_StartLocalSound( cgs.media.talkSound, CHAN_LOCAL_SOUND );
|
|
CG_Printf( "%s\n", CG_Argv(1) );
|
|
return;
|
|
}
|
|
|
|
if ( !strcmp( cmd, "pc" ) ) {
|
|
trap_Cvar_Set("ui_playerClass", CG_Argv(1));
|
|
return;
|
|
}
|
|
|
|
if ( !strcmp( cmd, "prank" ) ) {
|
|
trap_Cvar_Set("ui_playerRank", CG_Argv(1));
|
|
return;
|
|
}
|
|
|
|
/*if ( !strcmp( cmd, "cr" ) ) {
|
|
trap_Cvar_VariableStringBuffer( "ui_playerclass", pClass, sizeof(pClass) );
|
|
trap_Cvar_VariableStringBuffer( "ui_playerrank", pRank, sizeof(pRank) );
|
|
|
|
if ( !strcmp( pClass, "maker" ) || !strcmp( pClass, "alphaomega22" ) ) {
|
|
trap_SendClientCommand( "class command" );
|
|
trap_SendClientCommand( va( "rank %s", pRank) );
|
|
}
|
|
|
|
trap_SendClientCommand( va( "class %s", pClass) );
|
|
trap_SendClientCommand( va( "rank %s", pRank) );
|
|
return;
|
|
}*/
|
|
|
|
if ( !strcmp( cmd, "tchat" ) ) {
|
|
trap_S_StartLocalSound( cgs.media.talkSound, CHAN_LOCAL_SOUND );
|
|
CG_AddToTeamChat( CG_Argv(1) );
|
|
CG_Printf( "%s\n", CG_Argv(1) );
|
|
return;
|
|
}
|
|
|
|
if ( !strcmp( cmd, "scores" ) ) {
|
|
CG_ParseScores();
|
|
return;
|
|
}
|
|
|
|
if ( !strcmp( cmd, "awards" ) ) {
|
|
AW_SPPostgameMenu_f();
|
|
return;
|
|
}
|
|
|
|
if ( !strcmp( cmd, "tinfo" ) ) {
|
|
CG_ParseTeamInfo();
|
|
return;
|
|
}
|
|
|
|
if ( !strcmp( cmd, "hinfo" ) ) {
|
|
CG_ParseHealthInfo();
|
|
return;
|
|
}
|
|
|
|
if ( !strcmp( cmd, "map_restart" ) ) {
|
|
CG_MapRestart();
|
|
return;
|
|
}
|
|
|
|
//TiM: Purge all active effects
|
|
if ( !strcmp( cmd, "cg_flushFX" ) ) {
|
|
int i;
|
|
|
|
for ( i = 0; i < MAX_LOCAL_ENTITIES; i ++ ) {
|
|
cg_localEntities[i].endTime = cg.time;
|
|
}
|
|
return;
|
|
}
|
|
|
|
/*if ( !strcmp( cmd, "cg_flushAngles" ) ) {
|
|
//CG_ResetPlayerEntity( &cg.predictedPlayerEntity ); //&cg_entities[ cg.predictedPlayerState.clientNum ]
|
|
cg_entities[cg.predictedPlayerState.clientNum].pe.torso.yawAngle = cg_entities[cg.predictedPlayerState.clientNum].lerpAngles[YAW];
|
|
cg_entities[cg.predictedPlayerState.clientNum].pe.legs.yawAngle = cg_entities[cg.predictedPlayerState.clientNum].lerpAngles[YAW];
|
|
return;
|
|
}*/
|
|
|
|
// loaddeferred can be both a servercmd and a consolecmd
|
|
if ( !strcmp( cmd, "loaddeferred" ) ) { // FIXME: spelled wrong, but not changing for demo
|
|
CG_LoadDeferredPlayers();
|
|
return;
|
|
}
|
|
|
|
// clientLevelShot is sent before taking a special screenshot for
|
|
// the menu system during development
|
|
if ( !strcmp( cmd, "clientLevelShot" ) ) {
|
|
cg.levelShot = qtrue;
|
|
return;
|
|
}
|
|
|
|
//TiM - client received a command from a turbolift ent
|
|
//Show the decks UI
|
|
if ( !strcmp( cmd, "lift" ) ) {
|
|
trap_SendConsoleCommand( va( "ui_turbolift %i", CG_Argv( 1 ) ) );
|
|
return;
|
|
}
|
|
|
|
//The server motd thingzor
|
|
//RPG-X | Marcin | 15/12/2008
|
|
if ( !strcmp( cmd, "motd" ) ) {
|
|
trap_SendConsoleCommand( "ui_motd_reset\n" );
|
|
trap_SendConsoleCommand( "ui_motd\n" );
|
|
return;
|
|
}
|
|
|
|
//RPG-X | Marcin | 15/12/2008
|
|
if ( !strcmp( cmd, "motd_line" ) ) {
|
|
trap_SendConsoleCommand( va( "ui_motd_line \"%s\"\n", CG_Argv( 1 ) ) );
|
|
return;
|
|
}
|
|
|
|
if ( !strcmp( cmd, "configID" ) )
|
|
{
|
|
CG_EncodeIDFile();
|
|
return;
|
|
}
|
|
|
|
if ( !strcmp( cmd, "changeClientInfo" ) )
|
|
{
|
|
//create local copy of the args
|
|
//due to the way CG_Argv works
|
|
char arg1[64];
|
|
char arg2[64];
|
|
|
|
Q_strncpyz( arg1, CG_Argv(1), sizeof(arg1) );
|
|
Q_strncpyz( arg2, CG_Argv(2), sizeof(arg2) );
|
|
|
|
trap_Cvar_Set( arg1, arg2 );
|
|
return;
|
|
}
|
|
|
|
if ( !strcmp( cmd, "playSnd" ) )
|
|
{
|
|
trap_SendConsoleCommand( va( "play %s", CG_Argv(1) ) );
|
|
return;
|
|
}
|
|
|
|
if ( !strcmp( cmd, "cg_connect" ) ) {
|
|
trap_SendConsoleCommand( va( "connect %s", CG_Argv(1) ) );
|
|
return;
|
|
}
|
|
|
|
#ifdef XTRA
|
|
if ( Q_stricmp (cmd, "remapShader") == 0 )
|
|
{
|
|
if (trap_Argc() == 4)
|
|
{
|
|
char shader1[MAX_QPATH];
|
|
char shader2[MAX_QPATH];
|
|
char shader3[MAX_QPATH];
|
|
|
|
Q_strncpyz(shader1, CG_Argv(1), sizeof(shader1));
|
|
Q_strncpyz(shader2, CG_Argv(2), sizeof(shader2));
|
|
Q_strncpyz(shader3, CG_Argv(3), sizeof(shader3));
|
|
|
|
trap_R_RemapShader(shader1, shader2, shader3);
|
|
}
|
|
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
if(!strcmp(cmd, "ui_transporter")) {
|
|
trap_SendConsoleCommand(va("ui_transporter %s", CG_Argv(1)));
|
|
return;
|
|
}
|
|
|
|
if(!strcmp(cmd, "ui_trdata")) {
|
|
trap_SendConsoleCommand(va("ui_trdata \"%s\"", CG_Argv(1)));
|
|
return;
|
|
}
|
|
|
|
if(!strcmp(cmd, "holo_data")) {
|
|
trap_SendConsoleCommand(va("holo_data \"%s\"", CG_Argv(1)));
|
|
return;
|
|
}
|
|
|
|
if(!strcmp(cmd, "ui_holodeck")) {
|
|
trap_SendClientCommand(va("ui_holodeck %i", CG_Argv(1)));
|
|
return;
|
|
}
|
|
|
|
#ifdef XTRA
|
|
if(!strcmp(cmd, "sqlkey")) {
|
|
trap_SendClientCommand(va("sqlkey %i", CG_Argv(1)));
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
CG_Printf( "Unknown client game command: %s\n", cmd );
|
|
}
|
|
|
|
|
|
/*
|
|
====================
|
|
CG_ExecuteNewServerCommands
|
|
|
|
Execute all of the server commands that were received along
|
|
with this this snapshot.
|
|
====================
|
|
*/
|
|
void CG_ExecuteNewServerCommands( int latestSequence ) {
|
|
while ( cgs.serverCommandSequence < latestSequence ) {
|
|
if ( trap_GetServerCommand( ++cgs.serverCommandSequence ) ) {
|
|
CG_ServerCommand();
|
|
}
|
|
}
|
|
}
|