mirror of
https://github.com/ioquake/jedi-academy.git
synced 2024-11-28 23:12:04 +00:00
61687fff0c
CVE-2011-2764 The FS_CheckFilenameIsNotExecutable function in qcommon/files.c in the ioQuake3 engine 1.36 and earlier, as used in World of Padman, Smokin' Guns, OpenArena, Tremulous, and ioUrbanTerror, does not properly determine dangerous file extensions, which allows remote attackers to execute arbitrary code via a crafted third-party addon that creates a Trojan horse DLL file. CVE-2011-3012 The ioQuake3 engine, as used in World of Padman 1.2 and earlier, Tremulous 1.1.0, and ioUrbanTerror 2007-12-20, does not check for dangerous file extensions before writing to the quake3 directory, which allows remote attackers to execute arbitrary code via a crafted third-party addon that creates a Trojan horse DLL file, a different vulnerability than CVE-2011-2764. bugzilla #3695 from Tim Angus in ioquake3 svn 1405 git 2c0861c1cea44861c5ceba2dc39e601d6bc3f0af * (bug 3695) Not allowing to write file with lib extention (.dll/.so/...) (TsT <tst2006@gmail.com>) from Tim Angus in ioquake3 svn 1499 git 48d8c8876b6ec035b0bb85f4d3c47c9210c3ca30 * s/FS_FilenameIsExecutable/FS_CheckFilenameIsNotExecutable/g * Fix potential buffer under run in FS_CheckFilenameIsNotExecutable from Thilo Schulz in ioquake3 svn 2098 git c4f739b8d03ca203435744c4a96e3561863ccdfe Fix extension name comparison for DLL files from Zack Middleton in ioquake3 git 6c88bf8aeee3c1e5449682f874f91e86cb393ef4 Rename FS_CheckFilenameIsNotExecutable to ..NotImmutable from Harley Laue in ioquake3 git 1b2a6abed996b43eb108486abbda449b3d16e019 Rename FS_CheckFilenameIsNotImmutable to ..IsMutable
1065 lines
26 KiB
C++
1065 lines
26 KiB
C++
//Anything above this #include will be ignored by the compiler
|
|
#include "../qcommon/exe_headers.h"
|
|
|
|
// cl_parse.c -- parse a message received from the server
|
|
|
|
#include "client.h"
|
|
#include "../qcommon/stringed_ingame.h"
|
|
#include "../ghoul2/G2_local.h"
|
|
#ifdef _DONETPROFILE_
|
|
#include "../qcommon/INetProfile.h"
|
|
#endif
|
|
#include "../zlib32/zip.h"
|
|
|
|
#ifdef _XBOX
|
|
#include "../xbox/XBLive.h"
|
|
#include "../xbox/XBoxCommon.h"
|
|
#include "../xbox/XBVoice.h"
|
|
#endif
|
|
|
|
static char hiddenCvarVal[128];
|
|
|
|
char *svc_strings[256] = {
|
|
"svc_bad",
|
|
|
|
"svc_nop",
|
|
"svc_gamestate",
|
|
"svc_configstring",
|
|
"svc_baseline",
|
|
"svc_serverCommand",
|
|
"svc_download",
|
|
"svc_snapshot",
|
|
"svc_setgame",
|
|
"svc_mapchange",
|
|
#ifdef _XBOX
|
|
"svc_newpeer",
|
|
"svc_removepeer",
|
|
"svc_xbInfo",
|
|
#endif
|
|
};
|
|
|
|
void SHOWNET( msg_t *msg, char *s) {
|
|
if ( cl_shownet->integer >= 2) {
|
|
Com_Printf ("%3i:%s\n", msg->readcount-1, s);
|
|
}
|
|
}
|
|
|
|
//void CL_SP_Print(const word ID, byte *Data); //, char* color)
|
|
|
|
/*
|
|
=========================================================================
|
|
|
|
MESSAGE PARSING
|
|
|
|
=========================================================================
|
|
*/
|
|
|
|
/*
|
|
==================
|
|
CL_DeltaEntity
|
|
|
|
Parses deltas from the given base and adds the resulting entity
|
|
to the current frame
|
|
==================
|
|
*/
|
|
void CL_DeltaEntity (msg_t *msg, clSnapshot_t *frame, int newnum, entityState_t *old,
|
|
qboolean unchanged) {
|
|
entityState_t *state;
|
|
|
|
// save the parsed entity state into the big circular buffer so
|
|
// it can be used as the source for a later delta
|
|
state = &cl.parseEntities[cl.parseEntitiesNum & (MAX_PARSE_ENTITIES-1)];
|
|
|
|
if ( unchanged )
|
|
{
|
|
*state = *old;
|
|
}
|
|
else
|
|
{
|
|
MSG_ReadDeltaEntity( msg, old, state, newnum );
|
|
}
|
|
|
|
if ( state->number == (MAX_GENTITIES-1) ) {
|
|
return; // entity was delta removed
|
|
}
|
|
cl.parseEntitiesNum++;
|
|
frame->numEntities++;
|
|
}
|
|
|
|
/*
|
|
==================
|
|
CL_ParsePacketEntities
|
|
|
|
==================
|
|
*/
|
|
void CL_ParsePacketEntities( msg_t *msg, clSnapshot_t *oldframe, clSnapshot_t *newframe) {
|
|
int newnum;
|
|
entityState_t *oldstate;
|
|
int oldindex, oldnum;
|
|
|
|
newframe->parseEntitiesNum = cl.parseEntitiesNum;
|
|
newframe->numEntities = 0;
|
|
|
|
// delta from the entities present in oldframe
|
|
oldindex = 0;
|
|
oldstate = NULL;
|
|
if (!oldframe) {
|
|
oldnum = 99999;
|
|
} else {
|
|
if ( oldindex >= oldframe->numEntities ) {
|
|
oldnum = 99999;
|
|
} else {
|
|
oldstate = &cl.parseEntities[
|
|
(oldframe->parseEntitiesNum + oldindex) & (MAX_PARSE_ENTITIES-1)];
|
|
oldnum = oldstate->number;
|
|
}
|
|
}
|
|
|
|
while ( 1 ) {
|
|
// read the entity index number
|
|
newnum = MSG_ReadBits( msg, GENTITYNUM_BITS );
|
|
|
|
if ( newnum == (MAX_GENTITIES-1) ) {
|
|
break;
|
|
}
|
|
|
|
if ( msg->readcount > msg->cursize ) {
|
|
Com_Error (ERR_DROP,"CL_ParsePacketEntities: end of message");
|
|
}
|
|
|
|
while ( oldnum < newnum ) {
|
|
// one or more entities from the old packet are unchanged
|
|
if ( cl_shownet->integer == 3 ) {
|
|
Com_Printf ("%3i: unchanged: %i\n", msg->readcount, oldnum);
|
|
}
|
|
CL_DeltaEntity( msg, newframe, oldnum, oldstate, qtrue );
|
|
|
|
oldindex++;
|
|
|
|
if ( oldindex >= oldframe->numEntities ) {
|
|
oldnum = 99999;
|
|
} else {
|
|
oldstate = &cl.parseEntities[
|
|
(oldframe->parseEntitiesNum + oldindex) & (MAX_PARSE_ENTITIES-1)];
|
|
oldnum = oldstate->number;
|
|
}
|
|
}
|
|
if (oldnum == newnum) {
|
|
// delta from previous state
|
|
if ( cl_shownet->integer == 3 ) {
|
|
Com_Printf ("%3i: delta: %i\n", msg->readcount, newnum);
|
|
}
|
|
CL_DeltaEntity( msg, newframe, newnum, oldstate, qfalse );
|
|
|
|
oldindex++;
|
|
|
|
if ( oldindex >= oldframe->numEntities ) {
|
|
oldnum = 99999;
|
|
} else {
|
|
oldstate = &cl.parseEntities[
|
|
(oldframe->parseEntitiesNum + oldindex) & (MAX_PARSE_ENTITIES-1)];
|
|
oldnum = oldstate->number;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
if ( oldnum > newnum ) {
|
|
// delta from baseline
|
|
if ( cl_shownet->integer == 3 ) {
|
|
Com_Printf ("%3i: baseline: %i\n", msg->readcount, newnum);
|
|
}
|
|
CL_DeltaEntity( msg, newframe, newnum, &cl.entityBaselines[newnum], qfalse );
|
|
continue;
|
|
}
|
|
|
|
}
|
|
|
|
// any remaining entities in the old frame are copied over
|
|
while ( oldnum != 99999 ) {
|
|
// one or more entities from the old packet are unchanged
|
|
if ( cl_shownet->integer == 3 ) {
|
|
Com_Printf ("%3i: unchanged: %i\n", msg->readcount, oldnum);
|
|
}
|
|
CL_DeltaEntity( msg, newframe, oldnum, oldstate, qtrue );
|
|
|
|
oldindex++;
|
|
|
|
if ( oldindex >= oldframe->numEntities ) {
|
|
oldnum = 99999;
|
|
} else {
|
|
oldstate = &cl.parseEntities[
|
|
(oldframe->parseEntitiesNum + oldindex) & (MAX_PARSE_ENTITIES-1)];
|
|
oldnum = oldstate->number;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
================
|
|
CL_ParseSnapshot
|
|
|
|
If the snapshot is parsed properly, it will be copied to
|
|
cl.snap and saved in cl.snapshots[]. If the snapshot is invalid
|
|
for any reason, no changes to the state will be made at all.
|
|
================
|
|
*/
|
|
void CL_ParseSnapshot( msg_t *msg ) {
|
|
int len;
|
|
clSnapshot_t *old;
|
|
clSnapshot_t newSnap;
|
|
int deltaNum;
|
|
int oldMessageNum;
|
|
int i, packetNum;
|
|
|
|
// get the reliable sequence acknowledge number
|
|
// NOTE: now sent with all server to client messages
|
|
//clc.reliableAcknowledge = MSG_ReadLong( msg );
|
|
|
|
// read in the new snapshot to a temporary buffer
|
|
// we will only copy to cl.snap if it is valid
|
|
Com_Memset (&newSnap, 0, sizeof(newSnap));
|
|
|
|
// we will have read any new server commands in this
|
|
// message before we got to svc_snapshot
|
|
newSnap.serverCommandNum = clc.serverCommandSequence;
|
|
|
|
newSnap.serverTime = MSG_ReadLong( msg );
|
|
|
|
newSnap.messageNum = clc.serverMessageSequence;
|
|
|
|
deltaNum = MSG_ReadByte( msg );
|
|
if ( !deltaNum ) {
|
|
newSnap.deltaNum = -1;
|
|
} else {
|
|
newSnap.deltaNum = newSnap.messageNum - deltaNum;
|
|
}
|
|
newSnap.snapFlags = MSG_ReadByte( msg );
|
|
|
|
// If the frame is delta compressed from data that we
|
|
// no longer have available, we must suck up the rest of
|
|
// the frame, but not use it, then ask for a non-compressed
|
|
// message
|
|
if ( newSnap.deltaNum <= 0 ) {
|
|
newSnap.valid = qtrue; // uncompressed frame
|
|
old = NULL;
|
|
#ifndef _XBOX // No demos on Xbox
|
|
clc.demowaiting = qfalse; // we can start recording now
|
|
#endif
|
|
} else {
|
|
old = &cl.snapshots[newSnap.deltaNum & PACKET_MASK];
|
|
if ( !old->valid ) {
|
|
// should never happen
|
|
Com_Printf ("Delta from invalid frame (not supposed to happen!).\n");
|
|
} else if ( old->messageNum != newSnap.deltaNum ) {
|
|
// The frame that the server did the delta from
|
|
// is too old, so we can't reconstruct it properly.
|
|
Com_Printf ("Delta frame too old.\n");
|
|
} else if ( cl.parseEntitiesNum - old->parseEntitiesNum > MAX_PARSE_ENTITIES-128 ) {
|
|
Com_DPrintf ("Delta parseEntitiesNum too old.\n");
|
|
} else {
|
|
newSnap.valid = qtrue; // valid delta parse
|
|
}
|
|
}
|
|
|
|
// read areamask
|
|
len = MSG_ReadByte( msg );
|
|
|
|
if(len > sizeof(newSnap.areamask))
|
|
{
|
|
Com_Error (ERR_DROP,"CL_ParseSnapshot: Invalid size %d for areamask", len);
|
|
return;
|
|
}
|
|
|
|
MSG_ReadData( msg, &newSnap.areamask, len);
|
|
|
|
// read playerinfo
|
|
SHOWNET( msg, "playerstate" );
|
|
if ( old ) {
|
|
MSG_ReadDeltaPlayerstate( msg, &old->ps, &newSnap.ps );
|
|
if (newSnap.ps.m_iVehicleNum)
|
|
{ //this means we must have written our vehicle's ps too
|
|
MSG_ReadDeltaPlayerstate( msg, &old->vps, &newSnap.vps, qtrue );
|
|
}
|
|
} else {
|
|
MSG_ReadDeltaPlayerstate( msg, NULL, &newSnap.ps );
|
|
if (newSnap.ps.m_iVehicleNum)
|
|
{ //this means we must have written our vehicle's ps too
|
|
MSG_ReadDeltaPlayerstate( msg, NULL, &newSnap.vps, qtrue );
|
|
}
|
|
}
|
|
|
|
// read packet entities
|
|
SHOWNET( msg, "packet entities" );
|
|
CL_ParsePacketEntities( msg, old, &newSnap );
|
|
|
|
// if not valid, dump the entire thing now that it has
|
|
// been properly read
|
|
if ( !newSnap.valid ) {
|
|
return;
|
|
}
|
|
|
|
// clear the valid flags of any snapshots between the last
|
|
// received and this one, so if there was a dropped packet
|
|
// it won't look like something valid to delta from next
|
|
// time we wrap around in the buffer
|
|
oldMessageNum = cl.snap.messageNum + 1;
|
|
|
|
if ( newSnap.messageNum - oldMessageNum >= PACKET_BACKUP ) {
|
|
oldMessageNum = newSnap.messageNum - ( PACKET_BACKUP - 1 );
|
|
}
|
|
for ( ; oldMessageNum < newSnap.messageNum ; oldMessageNum++ ) {
|
|
cl.snapshots[oldMessageNum & PACKET_MASK].valid = qfalse;
|
|
}
|
|
|
|
// copy to the current good spot
|
|
cl.snap = newSnap;
|
|
cl.snap.ping = 999;
|
|
// calculate ping time
|
|
for ( i = 0 ; i < PACKET_BACKUP ; i++ ) {
|
|
packetNum = ( clc.netchan.outgoingSequence - 1 - i ) & PACKET_MASK;
|
|
if ( cl.snap.ps.commandTime >= cl.outPackets[ packetNum ].p_serverTime ) {
|
|
cl.snap.ping = cls.realtime - cl.outPackets[ packetNum ].p_realtime;
|
|
break;
|
|
}
|
|
}
|
|
// save the frame off in the backup array for later delta comparisons
|
|
cl.snapshots[cl.snap.messageNum & PACKET_MASK] = cl.snap;
|
|
|
|
if (cl_shownet->integer == 3) {
|
|
Com_Printf( " snapshot:%i delta:%i ping:%i\n", cl.snap.messageNum,
|
|
cl.snap.deltaNum, cl.snap.ping );
|
|
}
|
|
|
|
cl.newSnapshots = qtrue;
|
|
}
|
|
|
|
|
|
/*
|
|
================
|
|
CL_ParseSetGame
|
|
|
|
rww - Update fs_game, this message is so we can use the ext_data
|
|
*_overrides.txt files for mods.
|
|
================
|
|
*/
|
|
void MSG_CheckNETFPSFOverrides(qboolean psfOverrides);
|
|
void FS_UpdateGamedir(void);
|
|
void CL_ParseSetGame( msg_t *msg )
|
|
{
|
|
char newGameDir[MAX_QPATH];
|
|
int i = 0;
|
|
char next;
|
|
|
|
while (i < MAX_QPATH)
|
|
{
|
|
next = MSG_ReadByte( msg );
|
|
|
|
if (next)
|
|
{ //if next is 0 then we have finished reading to the end of the message
|
|
newGameDir[i] = next;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
i++;
|
|
}
|
|
newGameDir[i] = 0;
|
|
|
|
Cvar_Set("fs_game", newGameDir);
|
|
|
|
//Update the search path for the mod dir
|
|
FS_UpdateGamedir();
|
|
|
|
//Now update the overrides manually
|
|
#ifndef _XBOX // No mods on Xbox
|
|
MSG_CheckNETFPSFOverrides(qfalse);
|
|
MSG_CheckNETFPSFOverrides(qtrue);
|
|
#endif
|
|
}
|
|
|
|
|
|
//=====================================================================
|
|
|
|
int cl_connectedToPureServer;
|
|
int cl_connectedGAME;
|
|
int cl_connectedCGAME;
|
|
int cl_connectedUI;
|
|
|
|
/*
|
|
==================
|
|
CL_SystemInfoChanged
|
|
|
|
The systeminfo configstring has been changed, so parse
|
|
new information out of it. This will happen at every
|
|
gamestate, and possibly during gameplay.
|
|
==================
|
|
*/
|
|
void CL_SystemInfoChanged( void ) {
|
|
char *systemInfo;
|
|
const char *s, *t;
|
|
char key[BIG_INFO_KEY];
|
|
char value[BIG_INFO_VALUE];
|
|
qboolean gameSet;
|
|
|
|
systemInfo = cl.gameState.stringData + cl.gameState.stringOffsets[ CS_SYSTEMINFO ];
|
|
cl.serverId = atoi( Info_ValueForKey( systemInfo, "sv_serverid" ) );
|
|
|
|
// don't set any vars when playing a demo
|
|
#ifndef _XBOX // No demos on Xbox
|
|
if ( clc.demoplaying ) {
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
s = Info_ValueForKey( systemInfo, "sv_cheats" );
|
|
if ( atoi(s) == 0 )
|
|
{
|
|
Cvar_SetCheatState();
|
|
}
|
|
|
|
// check pure server string
|
|
s = Info_ValueForKey( systemInfo, "sv_paks" );
|
|
t = Info_ValueForKey( systemInfo, "sv_pakNames" );
|
|
FS_PureServerSetLoadedPaks( s, t );
|
|
|
|
s = Info_ValueForKey( systemInfo, "sv_referencedPaks" );
|
|
t = Info_ValueForKey( systemInfo, "sv_referencedPakNames" );
|
|
FS_PureServerSetReferencedPaks( s, t );
|
|
|
|
gameSet = qfalse;
|
|
// scan through all the variables in the systeminfo and locally set cvars to match
|
|
s = systemInfo;
|
|
while ( s ) {
|
|
int cvar_flags;
|
|
|
|
Info_NextPair( &s, key, value );
|
|
if ( !key[0] ) {
|
|
break;
|
|
}
|
|
|
|
// ehw!
|
|
if ( !Q_stricmp( key, "fs_game" ) )
|
|
{
|
|
if(FS_CheckDirTraversal(value))
|
|
{
|
|
Com_Printf(S_COLOR_YELLOW "WARNING: Server sent invalid fs_game value %s\n", value);
|
|
continue;
|
|
}
|
|
|
|
gameSet = qtrue;
|
|
}
|
|
|
|
if((cvar_flags = Cvar_Flags(key)) == CVAR_NONEXISTENT)
|
|
Cvar_Get(key, value, CVAR_SERVER_CREATED | CVAR_ROM);
|
|
else
|
|
{
|
|
// If this cvar may not be modified by a server discard the value.
|
|
if(!(cvar_flags & (CVAR_SYSTEMINFO | CVAR_SERVER_CREATED)))
|
|
continue;
|
|
|
|
Cvar_Set(key, value);
|
|
}
|
|
Cvar_Set( key, value );
|
|
}
|
|
// if game folder should not be set and it is set at the client side
|
|
if (!gameSet && *Cvar_VariableString("fs_game") ) {
|
|
Cvar_Set( "fs_game", "" );
|
|
}
|
|
cl_connectedToPureServer = Cvar_VariableValue( "sv_pure" );
|
|
|
|
cl_connectedGAME = atoi(Info_ValueForKey( systemInfo, "vm_game" ));
|
|
cl_connectedCGAME = atoi(Info_ValueForKey( systemInfo, "vm_cgame" ));
|
|
cl_connectedUI = atoi(Info_ValueForKey( systemInfo, "vm_ui" ));
|
|
}
|
|
|
|
void CL_ParseAutomapSymbols ( msg_t* msg )
|
|
{
|
|
int i;
|
|
|
|
clc.rmgAutomapSymbolCount = (unsigned short) MSG_ReadShort ( msg );
|
|
|
|
for ( i = 0; i < clc.rmgAutomapSymbolCount; i ++ )
|
|
{
|
|
clc.rmgAutomapSymbols[i].mType = (int)MSG_ReadByte ( msg );
|
|
clc.rmgAutomapSymbols[i].mSide = (int)MSG_ReadByte ( msg );
|
|
clc.rmgAutomapSymbols[i].mOrigin[0] = (float)MSG_ReadLong ( msg );
|
|
clc.rmgAutomapSymbols[i].mOrigin[1] = (float)MSG_ReadLong ( msg );
|
|
}
|
|
}
|
|
|
|
void CL_ParseRMG ( msg_t* msg )
|
|
{
|
|
clc.rmgHeightMapSize = (unsigned short)MSG_ReadShort ( msg );
|
|
if ( !clc.rmgHeightMapSize )
|
|
{
|
|
return;
|
|
}
|
|
|
|
z_stream zdata;
|
|
int size;
|
|
unsigned char heightmap1[15000];
|
|
|
|
if ( MSG_ReadBits ( msg, 1 ) )
|
|
{
|
|
// Read the heightmap
|
|
memset(&zdata, 0, sizeof(z_stream));
|
|
inflateInit ( &zdata, Z_SYNC_FLUSH );
|
|
|
|
MSG_ReadData ( msg, heightmap1, clc.rmgHeightMapSize );
|
|
|
|
zdata.next_in = heightmap1;
|
|
zdata.avail_in = clc.rmgHeightMapSize;
|
|
zdata.next_out = (unsigned char*)clc.rmgHeightMap;
|
|
zdata.avail_out = MAX_HEIGHTMAP_SIZE;
|
|
inflate (&zdata);
|
|
|
|
clc.rmgHeightMapSize = zdata.total_out;
|
|
|
|
inflateEnd(&zdata);
|
|
}
|
|
else
|
|
{
|
|
MSG_ReadData ( msg, (unsigned char*)clc.rmgHeightMap, clc.rmgHeightMapSize );
|
|
}
|
|
|
|
size = (unsigned short)MSG_ReadShort ( msg );
|
|
|
|
if ( MSG_ReadBits ( msg, 1 ) )
|
|
{
|
|
// Read the flatten map
|
|
memset(&zdata, 0, sizeof(z_stream));
|
|
inflateInit ( &zdata, Z_SYNC_FLUSH );
|
|
|
|
MSG_ReadData ( msg, heightmap1, size );
|
|
|
|
zdata.next_in = heightmap1;
|
|
zdata.avail_in = clc.rmgHeightMapSize;
|
|
zdata.next_out = (unsigned char*)clc.rmgFlattenMap;
|
|
zdata.avail_out = MAX_HEIGHTMAP_SIZE;
|
|
inflate (&zdata );
|
|
inflateEnd(&zdata);
|
|
}
|
|
else
|
|
{
|
|
MSG_ReadData ( msg, (unsigned char*)clc.rmgFlattenMap, size );
|
|
}
|
|
|
|
// Read the seed
|
|
clc.rmgSeed = MSG_ReadLong ( msg );
|
|
|
|
CL_ParseAutomapSymbols ( msg );
|
|
}
|
|
|
|
/*
|
|
==================
|
|
CL_ParseGamestate
|
|
==================
|
|
*/
|
|
void CL_ParseGamestate( msg_t *msg ) {
|
|
int i;
|
|
entityState_t *es;
|
|
int newnum;
|
|
entityState_t nullstate;
|
|
int cmd;
|
|
char *s;
|
|
|
|
Con_Close();
|
|
|
|
clc.connectPacketCount = 0;
|
|
|
|
// wipe local client state
|
|
CL_ClearState();
|
|
|
|
#ifdef _DONETPROFILE_
|
|
int startBytes,endBytes;
|
|
startBytes=msg->readcount;
|
|
#endif
|
|
|
|
// a gamestate always marks a server command sequence
|
|
clc.serverCommandSequence = MSG_ReadLong( msg );
|
|
|
|
// parse all the configstrings and baselines
|
|
cl.gameState.dataCount = 1; // leave a 0 at the beginning for uninitialized configstrings
|
|
while ( 1 ) {
|
|
cmd = MSG_ReadByte( msg );
|
|
|
|
if ( cmd == svc_EOF ) {
|
|
break;
|
|
}
|
|
|
|
if ( cmd == svc_configstring ) {
|
|
int len, start;
|
|
|
|
start = msg->readcount;
|
|
|
|
i = MSG_ReadShort( msg );
|
|
if ( i < 0 || i >= MAX_CONFIGSTRINGS ) {
|
|
Com_Error( ERR_DROP, "configstring > MAX_CONFIGSTRINGS" );
|
|
}
|
|
s = MSG_ReadBigString( msg );
|
|
|
|
if (cl_shownet->integer >= 2)
|
|
{
|
|
Com_Printf("%3i: %d: %s\n", start, i, s);
|
|
}
|
|
|
|
/*
|
|
if (i == CS_SERVERINFO)
|
|
{ //get the special value here
|
|
char *f = strstr(s, "g_debugMelee");
|
|
if (f)
|
|
{
|
|
while (*f && *f != '\\')
|
|
{ //find the \ after it
|
|
f++;
|
|
}
|
|
if (*f == '\\')
|
|
{ //got it
|
|
int i = 0;
|
|
|
|
f++;
|
|
while (*f && *f != '\\' && i < 128)
|
|
{
|
|
hiddenCvarVal[i] = *f;
|
|
i++;
|
|
f++;
|
|
}
|
|
hiddenCvarVal[i] = 0;
|
|
|
|
//resume here
|
|
s = f;
|
|
}
|
|
}
|
|
}
|
|
*/
|
|
|
|
len = strlen( s );
|
|
|
|
if ( len + 1 + cl.gameState.dataCount > MAX_GAMESTATE_CHARS ) {
|
|
Com_Error( ERR_DROP, "MAX_GAMESTATE_CHARS exceeded" );
|
|
}
|
|
|
|
// append it to the gameState string buffer
|
|
cl.gameState.stringOffsets[ i ] = cl.gameState.dataCount;
|
|
Com_Memcpy( cl.gameState.stringData + cl.gameState.dataCount, s, len + 1 );
|
|
cl.gameState.dataCount += len + 1;
|
|
} else if ( cmd == svc_baseline ) {
|
|
newnum = MSG_ReadBits( msg, GENTITYNUM_BITS );
|
|
if ( newnum < 0 || newnum >= MAX_GENTITIES ) {
|
|
Com_Error( ERR_DROP, "Baseline number out of range: %i", newnum );
|
|
}
|
|
Com_Memset (&nullstate, 0, sizeof(nullstate));
|
|
es = &cl.entityBaselines[ newnum ];
|
|
MSG_ReadDeltaEntity( msg, &nullstate, es, newnum );
|
|
} else {
|
|
Com_Error( ERR_DROP, "CL_ParseGamestate: bad command byte" );
|
|
}
|
|
}
|
|
|
|
clc.clientNum = MSG_ReadLong(msg);
|
|
// read the checksum feed
|
|
clc.checksumFeed = MSG_ReadLong( msg );
|
|
|
|
CL_ParseRMG ( msg ); //rwwRMG - get info for it from the server
|
|
|
|
#ifdef _DONETPROFILE_
|
|
endBytes=msg->readcount;
|
|
// ClReadProf().AddField("svc_gamestate",endBytes-startBytes);
|
|
#endif
|
|
|
|
// parse serverId and other cvars
|
|
CL_SystemInfoChanged();
|
|
|
|
// reinitialize the filesystem if the game directory has changed
|
|
if( FS_ConditionalRestart( clc.checksumFeed ) ) {
|
|
// don't set to true because we yet have to start downloading
|
|
// enabling this can cause double loading of a map when connecting to
|
|
// a server which has a different game directory set
|
|
//clc.downloadRestart = qtrue;
|
|
}
|
|
|
|
// This used to call CL_StartHunkUsers, but now we enter the download state before loading the
|
|
// cgame
|
|
CL_InitDownloads();
|
|
|
|
// make sure the game starts
|
|
Cvar_Set( "cl_paused", "0" );
|
|
}
|
|
|
|
|
|
//=====================================================================
|
|
|
|
/*
|
|
=====================
|
|
CL_ParseDownload
|
|
|
|
A download message has been received from the server
|
|
=====================
|
|
*/
|
|
void CL_ParseDownload ( msg_t *msg ) {
|
|
#ifdef _XBOX
|
|
assert(!"Xbox received a download message. Unsupported!");
|
|
#else
|
|
int size;
|
|
unsigned char data[MAX_MSGLEN];
|
|
int block;
|
|
|
|
if (!*clc.downloadTempName) {
|
|
Com_Printf("Server sending download, but no download was requested\n");
|
|
CL_AddReliableCommand("stopdl");
|
|
return;
|
|
}
|
|
|
|
// read the data
|
|
block = (unsigned short)MSG_ReadShort ( msg );
|
|
|
|
if ( !block )
|
|
{
|
|
// block zero is special, contains file size
|
|
clc.downloadSize = MSG_ReadLong ( msg );
|
|
|
|
Cvar_SetValue( "cl_downloadSize", clc.downloadSize );
|
|
|
|
if (clc.downloadSize < 0)
|
|
{
|
|
Com_Error(ERR_DROP, MSG_ReadString( msg ) );
|
|
return;
|
|
}
|
|
}
|
|
|
|
size = (unsigned short)MSG_ReadShort ( msg );
|
|
if (size < 0 || size > sizeof(data))
|
|
{
|
|
Com_Error(ERR_DROP, "CL_ParseDownload: Invalid size %d for download chunk", size);
|
|
return;
|
|
}
|
|
|
|
MSG_ReadData( msg, data, size );
|
|
|
|
if (clc.downloadBlock != block) {
|
|
Com_DPrintf( "CL_ParseDownload: Expected block %d, got %d\n", clc.downloadBlock, block);
|
|
return;
|
|
}
|
|
|
|
// open the file if not opened yet
|
|
if (!clc.download)
|
|
{
|
|
clc.download = FS_SV_FOpenFileWrite( clc.downloadTempName );
|
|
|
|
if (!clc.download) {
|
|
Com_Printf( "Could not create %s\n", clc.downloadTempName );
|
|
CL_AddReliableCommand( "stopdl" );
|
|
CL_NextDownload();
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (size)
|
|
FS_Write( data, size, clc.download );
|
|
|
|
CL_AddReliableCommand( va("nextdl %d", clc.downloadBlock) );
|
|
clc.downloadBlock++;
|
|
|
|
clc.downloadCount += size;
|
|
|
|
// So UI gets access to it
|
|
Cvar_SetValue( "cl_downloadCount", clc.downloadCount );
|
|
|
|
if (!size) { // A zero length block means EOF
|
|
if (clc.download) {
|
|
FS_FCloseFile( clc.download );
|
|
clc.download = 0;
|
|
|
|
// rename the file
|
|
FS_SV_Rename ( clc.downloadTempName, clc.downloadName, qfalse );
|
|
}
|
|
*clc.downloadTempName = *clc.downloadName = 0;
|
|
Cvar_Set( "cl_downloadName", "" );
|
|
|
|
// send intentions now
|
|
// We need this because without it, we would hold the last nextdl and then start
|
|
// loading right away. If we take a while to load, the server is happily trying
|
|
// to send us that last block over and over.
|
|
// Write it twice to help make sure we acknowledge the download
|
|
CL_WritePacket();
|
|
CL_WritePacket();
|
|
|
|
// get another file if needed
|
|
CL_NextDownload ();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
int CL_GetValueForHidden(const char *s)
|
|
{ //string arg here just in case I want to add more sometime and make a lookup table
|
|
return atoi(hiddenCvarVal);
|
|
}
|
|
|
|
/*
|
|
=====================
|
|
CL_ParseCommandString
|
|
|
|
Command strings are just saved off until cgame asks for them
|
|
when it transitions a snapshot
|
|
=====================
|
|
*/
|
|
void CL_ParseCommandString( msg_t *msg ) {
|
|
char *s;
|
|
int seq;
|
|
int index;
|
|
|
|
#ifdef _DONETPROFILE_
|
|
int startBytes,endBytes;
|
|
startBytes=msg->readcount;
|
|
#endif
|
|
seq = MSG_ReadLong( msg );
|
|
s = MSG_ReadString( msg );
|
|
#ifdef _DONETPROFILE_
|
|
endBytes=msg->readcount;
|
|
ClReadProf().AddField("svc_serverCommand",endBytes-startBytes);
|
|
#endif
|
|
// see if we have already executed stored it off
|
|
if ( clc.serverCommandSequence >= seq ) {
|
|
return;
|
|
}
|
|
clc.serverCommandSequence = seq;
|
|
|
|
index = seq & (MAX_RELIABLE_COMMANDS-1);
|
|
/*
|
|
if (s[0] == 'c' && s[1] == 's' && s[2] == ' ' && s[3] == '0' && s[4] == ' ')
|
|
{ //yes.. we seem to have an incoming server info.
|
|
char *f = strstr(s, "g_debugMelee");
|
|
if (f)
|
|
{
|
|
while (*f && *f != '\\')
|
|
{ //find the \ after it
|
|
f++;
|
|
}
|
|
if (*f == '\\')
|
|
{ //got it
|
|
int i = 0;
|
|
|
|
f++;
|
|
while (*f && *f != '\\' && i < 128)
|
|
{
|
|
hiddenCvarVal[i] = *f;
|
|
i++;
|
|
f++;
|
|
}
|
|
hiddenCvarVal[i] = 0;
|
|
|
|
//don't worry about backing over beginning of string I guess,
|
|
//we already know we successfully strstr'd the initial string
|
|
//which exceeds this length.
|
|
//MSG_ReadString appears to just return a static buffer so I
|
|
//can stomp over its contents safely.
|
|
f--;
|
|
*f = '\"';
|
|
f--;
|
|
*f = ' ';
|
|
f--;
|
|
*f = '0';
|
|
f--;
|
|
*f = ' ';
|
|
f--;
|
|
*f = 's';
|
|
f--;
|
|
*f = 'c';
|
|
|
|
//the normal configstring gets to start here...
|
|
s = f;
|
|
}
|
|
}
|
|
}
|
|
*/
|
|
Q_strncpyz( clc.serverCommands[ index ], s, sizeof( clc.serverCommands[ index ] ) );
|
|
}
|
|
|
|
|
|
/*
|
|
=====================
|
|
CL_ParseServerMessage
|
|
=====================
|
|
*/
|
|
void CL_ParseServerMessage( msg_t *msg ) {
|
|
int cmd;
|
|
|
|
if ( cl_shownet->integer == 1 ) {
|
|
Com_Printf ("%i ",msg->cursize);
|
|
} else if ( cl_shownet->integer >= 2 ) {
|
|
Com_Printf ("------------------\n");
|
|
}
|
|
|
|
MSG_Bitstream(msg);
|
|
|
|
// get the reliable sequence acknowledge number
|
|
clc.reliableAcknowledge = MSG_ReadLong( msg );
|
|
//
|
|
if ( clc.reliableAcknowledge < clc.reliableSequence - MAX_RELIABLE_COMMANDS ) {
|
|
clc.reliableAcknowledge = clc.reliableSequence;
|
|
}
|
|
|
|
//
|
|
// parse the message
|
|
//
|
|
while ( 1 ) {
|
|
if ( msg->readcount > msg->cursize ) {
|
|
Com_Error (ERR_DROP,"CL_ParseServerMessage: read past end of server message");
|
|
break;
|
|
}
|
|
|
|
cmd = MSG_ReadByte( msg );
|
|
|
|
if ( cmd == svc_EOF) {
|
|
SHOWNET( msg, "END OF MESSAGE" );
|
|
break;
|
|
}
|
|
|
|
if ( cl_shownet->integer >= 2 ) {
|
|
if ( !svc_strings[cmd] ) {
|
|
Com_Printf( "%3i:BAD CMD %i\n", msg->readcount-1, cmd );
|
|
} else {
|
|
SHOWNET( msg, svc_strings[cmd] );
|
|
}
|
|
}
|
|
|
|
// other commands
|
|
switch ( cmd ) {
|
|
default:
|
|
Com_Error (ERR_DROP,"CL_ParseServerMessage: Illegible server message\n");
|
|
break;
|
|
case svc_nop:
|
|
break;
|
|
case svc_serverCommand:
|
|
CL_ParseCommandString( msg );
|
|
break;
|
|
case svc_gamestate:
|
|
CL_ParseGamestate( msg );
|
|
break;
|
|
case svc_snapshot:
|
|
CL_ParseSnapshot( msg );
|
|
break;
|
|
case svc_setgame:
|
|
CL_ParseSetGame( msg );
|
|
break;
|
|
case svc_download:
|
|
CL_ParseDownload( msg );
|
|
break;
|
|
case svc_mapchange:
|
|
if (cgvm)
|
|
{
|
|
VM_Call( cgvm, CG_MAP_CHANGE );
|
|
}
|
|
break;
|
|
#ifdef _XBOX
|
|
case svc_newpeer:
|
|
{ //jsw// new client to add to our XBonlineInfo
|
|
// We now get the index that we should use to store this from the server
|
|
// That ensures that our playerlist and clientinfo stay in sync!
|
|
int index = MSG_ReadLong(msg);
|
|
|
|
// Sanity check - server shouldn't have us overwriting an active player
|
|
// Unless, we're the server, in which case it will be active. Doh.
|
|
assert( com_sv_running->integer ||
|
|
!xbOnlineInfo.xbPlayerList[index].isActive );
|
|
|
|
// OK. Read directly into the right place
|
|
MSG_ReadData(msg, &xbOnlineInfo.xbPlayerList[index], sizeof(XBPlayerInfo));
|
|
|
|
// Need to do address conversion here, voice needs it
|
|
XNetXnAddrToInAddr( &xbOnlineInfo.xbPlayerList[index].xbAddr,
|
|
Net_GetXNKID(),
|
|
&xbOnlineInfo.xbPlayerList[index].inAddr );
|
|
|
|
// VVFIXME - Search for g_Voice here - lots of stuff we're not doing
|
|
// g_Voice.OnPlayerJoined( index );
|
|
//if(g_Voice.IsPlayerMuted(&xbOnlineInfo.xbPlayerList[index].xuid))
|
|
//{
|
|
// g_Voice.MutePlayer(&xbOnlineInfo.xbPlayerList[index].xbAddr, &xbOnlineInfo.xbPlayerList[index].xuid, false);
|
|
// xbOnlineInfo.xbPlayerList[index].flags |= MUTED_PLAYER;
|
|
//}
|
|
//else if(Cvar_Get(CVAR_XBL_WT_ACTIVE)->integer)
|
|
//{ //reset w/t in case he's on the channel
|
|
// g_Voice.EnableWalkieTalkie();
|
|
//}
|
|
|
|
XBL_PL_CheckHistoryList(&xbOnlineInfo.xbPlayerList[index]);
|
|
break;
|
|
}
|
|
case svc_removepeer:
|
|
{
|
|
// Remove a client from our xbOnlineInfo. Our ordering is the same
|
|
// as the server, so we just get an index.
|
|
int index = MSG_ReadLong(msg);
|
|
|
|
// Sanity check
|
|
assert( xbOnlineInfo.xbPlayerList[index].isActive &&
|
|
index != xbOnlineInfo.localIndex );
|
|
|
|
// Clear out important information
|
|
xbOnlineInfo.xbPlayerList[index].isActive = false;
|
|
g_Voice.OnPlayerDisconnect( &xbOnlineInfo.xbPlayerList[index] );
|
|
XBL_PL_RemoveActivePeer(&xbOnlineInfo.xbPlayerList[index].xuid, index);
|
|
break;
|
|
}
|
|
case svc_xbInfo:
|
|
{ //jsw//get XNADDR list from server
|
|
MSG_ReadData(msg, &xbOnlineInfo, sizeof(XBOnlineInfo));
|
|
|
|
// Immediately convert everyone's XNADDR to an IN_ADDR
|
|
for( int i = 0; i < MAX_ONLINE_PLAYERS; ++i )
|
|
{
|
|
if( xbOnlineInfo.xbPlayerList[i].isActive )
|
|
XNetXnAddrToInAddr( &xbOnlineInfo.xbPlayerList[i].xbAddr,
|
|
Net_GetXNKID(),
|
|
&xbOnlineInfo.xbPlayerList[i].inAddr );
|
|
}
|
|
|
|
//now find which entry we are
|
|
if( logged_on )
|
|
{
|
|
PXONLINE_USER pToUser = XOnlineGetLogonUsers();
|
|
BOOL found = false;
|
|
for( int i = 0; i < MAX_ONLINE_PLAYERS && !found; i++ )
|
|
{
|
|
if( xbOnlineInfo.xbPlayerList[i].isActive && XOnlineAreUsersIdentical(&pToUser->xuid, &xbOnlineInfo.xbPlayerList[i].xuid))
|
|
{
|
|
xbOnlineInfo.localIndex = i;
|
|
found = true;
|
|
}
|
|
}
|
|
|
|
// OK. Time to join the chat! - VVFIXME - need to check mute lists
|
|
g_Voice.JoinSession();
|
|
}
|
|
else
|
|
{
|
|
DWORD dwStatus;
|
|
IN_ADDR localAddr;
|
|
XNetXnAddrToInAddr(Net_GetXNADDR( &dwStatus ), Net_GetXNKID(), &localAddr);
|
|
|
|
if( dwStatus != XNET_GET_XNADDR_NONE )
|
|
{
|
|
int j;
|
|
for( j = 0; j < MAX_ONLINE_PLAYERS; ++j )
|
|
{
|
|
if( xbOnlineInfo.xbPlayerList[j].isActive &&
|
|
localAddr.s_addr == xbOnlineInfo.xbPlayerList[i].inAddr.s_addr )
|
|
break;
|
|
}
|
|
xbOnlineInfo.localIndex = (j == MAX_ONLINE_PLAYERS) ? -1 : j;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
extern int scr_center_y;
|
|
void SCR_CenterPrint (char *str);//, PalIdx_t colour)
|