// 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();
		}
	}
}
