diff options
Diffstat (limited to 'source/cl_parse.c')
-rw-r--r-- | source/cl_parse.c | 1661 |
1 files changed, 0 insertions, 1661 deletions
diff --git a/source/cl_parse.c b/source/cl_parse.c deleted file mode 100644 index 75ed182..0000000 --- a/source/cl_parse.c +++ /dev/null @@ -1,1661 +0,0 @@ -/* -Copyright (C) 1997-2001 Id Software, Inc. - -This program 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. - -This program 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 this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -*/ -// cl_parse.c -- parse a message received from the server - -#include "cl_local.h" - -//============================================================================= - -/* -=============== -CL_CheckOrDownloadFile - -Returns qtrue if the file exists, otherwise it attempts -to start a download from the server. -=============== -*/ -qboolean CL_CheckOrDownloadFile( const char *path ) { - qhandle_t f; - size_t len; - ssize_t ret; - - len = strlen( path ); - if( len < 1 || len >= MAX_QPATH - || !Q_ispath( path[0] ) - || !Q_ispath( path[ len - 1 ] ) - || strchr( path, '\\' ) - || strchr( path, ':' ) - || !strchr( path, '/' ) - || strstr( path, ".." ) ) - { - Com_Printf( "Refusing to download file with invalid path.\n" ); - return qtrue; - } - - if( FS_FileExists( path ) ) { - // it exists, no need to download - return qtrue; - } - -#if USE_CURL - if( HTTP_QueueDownload( path ) ) { - //we return true so that the precache check keeps feeding us more files. - //since we have multiple HTTP connections we want to minimize latency - //and be constantly sending requests, not one at a time. - return qtrue; - } -#endif - - memcpy( cls.download.name, path, len + 1 ); - - // download to a temp name, and only rename - // to the real name when done, so if interrupted - // a runt file wont be left - memcpy( cls.download.temp, path, len ); - memcpy( cls.download.temp + len, ".tmp", 5 ); - -//ZOID - // check to see if we already have a tmp for this file, if so, try to resume - // open the file if not opened yet - ret = FS_FOpenFile( cls.download.temp, &f, FS_MODE_RDWR ); - if( ret >= 0 ) { // it exists - cls.download.file = f; - // give the server an offset to start the download - Com_Printf( "Resuming %s\n", cls.download.name ); - CL_ClientCommand( va( "download \"%s\" %d", cls.download.name, (int)ret ) ); - } else if( ret == Q_ERR_NOENT ) { // it doesn't exist - Com_Printf( "Downloading %s\n", cls.download.name ); - CL_ClientCommand( va( "download \"%s\"", cls.download.name ) ); - } else { // error happened - Com_EPrintf( "Couldn't open %s for appending: %s\n", cls.download.temp, - Q_ErrorString( ret ) ); - return qtrue; - } - - return qfalse; -} - -/* -=============== -CL_Download_f - -Request a download from the server -=============== -*/ -void CL_Download_f( void ) { - char *path; - - if( cls.state < ca_connected ) { - Com_Printf( "Must be connected to a server.\n" ); - return; - } - if( !allow_download->integer ) { - Com_Printf( "Downloading is disabled.\n" ); - return; - } - - if( Cmd_Argc() != 2 ) { - Com_Printf( "Usage: download <filename>\n" ); - return; - } - - if( cls.download.temp[0] ) { - Com_Printf( "Already downloading.\n" ); - if( cls.serverProtocol == PROTOCOL_VERSION_Q2PRO ) { - Com_Printf( "Try using 'stopdl' command to abort the download.\n" ); - } - return; - } - - path = Cmd_Argv( 1 ); - - if( FS_FileExists( path ) ) { - Com_Printf( "%s already exists.\n", path ); - return; - } - - CL_CheckOrDownloadFile( path ); -} - - -/* -===================== -CL_ParseDownload - -A download message has been received from the server -===================== -*/ -static void CL_ParseDownload( void ) { - int size, percent; - qerror_t ret; - - if( !cls.download.temp[0] ) { - Com_Error( ERR_DROP, "%s: no download requested", __func__ ); - } - - // read the data - size = MSG_ReadShort(); - percent = MSG_ReadByte(); - if( size == -1 ) { - if( !percent ) { - Com_Printf( "Server was unable to send this file.\n" ); - } else { - Com_Printf( "Server stopped the download.\n" ); - } - if( cls.download.file ) { - // if here, we tried to resume a file but the server said no - FS_FCloseFile( cls.download.file ); - } - goto another; - } - - if( size < 0 ) { - Com_Error( ERR_DROP, "%s: bad size: %d", __func__, size ); - } - - if( msg_read.readcount + size > msg_read.cursize ) { - Com_Error( ERR_DROP, "%s: read past end of message", __func__ ); - } - - // open the file if not opened yet - if( !cls.download.file ) { - ret = FS_FOpenFile( cls.download.temp, &cls.download.file, FS_MODE_WRITE ); - if( !cls.download.file ) { - msg_read.readcount += size; - Com_EPrintf( "Couldn't open %s for writing: %s\n", - cls.download.temp, Q_ErrorString( ret ) ); - goto another; - } - } - - FS_Write( msg_read.data + msg_read.readcount, size, cls.download.file ); - msg_read.readcount += size; - - if( percent != 100 ) { - // request next block - // change display routines by zoid - cls.download.percent = percent; - - CL_ClientCommand( "nextdl" ); - } else { - FS_FCloseFile( cls.download.file ); - - // rename the temp file to it's final name - ret = FS_RenameFile( cls.download.temp, cls.download.name ); - if( ret ) { - Com_EPrintf( "Couldn't rename %s to %s: %s\n", - cls.download.temp, cls.download.name, Q_ErrorString( ret ) ); - } - - Com_Printf( "Downloaded successfully.\n" ); - -another: - // get another file if needed - memset( &cls.download, 0, sizeof( cls.download ) ); - CL_RequestNextDownload(); - } -} - -/* -===================================================================== - - DELTA FRAME PARSING - -===================================================================== -*/ - - -/* -================== -CL_ParseDeltaEntity -================== -*/ -static inline void CL_ParseDeltaEntity( server_frame_t *frame, - int newnum, - entity_state_t *old, - int bits ) -{ - entity_state_t *state; - - if( frame->numEntities >= MAX_PACKET_ENTITIES ) { - Com_Error( ERR_DROP, "%s: MAX_PACKET_ENTITIES exceeded", __func__ ); - } - - state = &cl.entityStates[cl.numEntityStates & PARSE_ENTITIES_MASK]; - cl.numEntityStates++; - frame->numEntities++; - -#ifdef _DEBUG - if( cl_shownet->integer > 2 && bits ) { - MSG_ShowDeltaEntityBits( bits ); - Com_Printf( "\n" ); - } -#endif - - MSG_ParseDeltaEntity( old, state, newnum, bits, cl.esFlags ); -} - -/* -================== -CL_ParsePacketEntities -================== -*/ -static void CL_ParsePacketEntities( server_frame_t *oldframe, - server_frame_t *frame ) -{ - int newnum; - int bits; - entity_state_t *oldstate; - int oldindex, oldnum; - int i; - - frame->firstEntity = cl.numEntityStates; - frame->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 { - i = oldframe->firstEntity + oldindex; - oldstate = &cl.entityStates[i & PARSE_ENTITIES_MASK]; - oldnum = oldstate->number; - } - } - - while( 1 ) { - newnum = MSG_ParseEntityBits( &bits ); - if( newnum < 0 || newnum >= MAX_EDICTS ) { - Com_Error( ERR_DROP, "%s: bad number: %d", __func__, newnum ); - } - - if( msg_read.readcount > msg_read.cursize ) { - Com_Error( ERR_DROP, "%s: read past end of message", __func__ ); - } - - if( !newnum ) { - break; - } - - while( oldnum < newnum ) { - // one or more entities from the old packet are unchanged - SHOWNET( 2, " unchanged: %i\n", oldnum ); - CL_ParseDeltaEntity( frame, oldnum, oldstate, 0 ); - - oldindex++; - - if( oldindex >= oldframe->numEntities ) { - oldnum = 99999; - } else { - i = oldframe->firstEntity + oldindex; - oldstate = &cl.entityStates[i & PARSE_ENTITIES_MASK]; - oldnum = oldstate->number; - } - } - - if( bits & U_REMOVE ) { - // the entity present in oldframe is not in the current frame - SHOWNET( 2, " remove: %i\n", newnum ); - if( oldnum != newnum ) { - Com_DPrintf( "U_REMOVE: oldnum != newnum\n" ); - } - if( !oldframe ) { - Com_Error( ERR_DROP, "U_REMOVE: NULL oldframe" ); - } - - oldindex++; - - if( oldindex >= oldframe->numEntities ) { - oldnum = 99999; - } else { - i = oldframe->firstEntity + oldindex; - oldstate = &cl.entityStates[i & PARSE_ENTITIES_MASK]; - oldnum = oldstate->number; - } - continue; - } - - if( oldnum == newnum ) { - // delta from previous state - SHOWNET( 2, " delta: %i ", newnum ); - CL_ParseDeltaEntity( frame, newnum, oldstate, bits ); - - oldindex++; - - if( oldindex >= oldframe->numEntities ) { - oldnum = 99999; - } else { - i = oldframe->firstEntity + oldindex; - oldstate = &cl.entityStates[i & PARSE_ENTITIES_MASK]; - oldnum = oldstate->number; - } - continue; - } - - if( oldnum > newnum ) { - // delta from baseline - SHOWNET( 2, " baseline: %i ", newnum ); - CL_ParseDeltaEntity( frame, newnum, &cl.baselines[newnum], bits ); - continue; - } - - } - - // any remaining entities in the old frame are copied over - while( oldnum != 99999 ) { - // one or more entities from the old packet are unchanged - SHOWNET( 3, " unchanged: %i\n", oldnum ); - CL_ParseDeltaEntity( frame, oldnum, oldstate, 0 ); - - oldindex++; - - if( oldindex >= oldframe->numEntities ) { - oldnum = 99999; - } else { - i = oldframe->firstEntity + oldindex; - oldstate = &cl.entityStates[i & PARSE_ENTITIES_MASK]; - oldnum = oldstate->number; - } - } -} - -/* -================ -CL_SetActiveState -================ -*/ -static void CL_SetActiveState( void ) { - cl.serverdelta = cl.frame.number; - cl.time = cl.servertime = 0; // set time, needed for demos - cls.state = ca_active; - cl.oldframe.valid = qfalse; - cl.frameflags = 0; - cl.putaway = qfalse; - if( cls.netchan ) { - cl.initialSeq = cls.netchan->outgoing_sequence; - } - - if( !cls.demo.playback ) { - VectorScale( cl.frame.ps.pmove.origin, 0.125f, cl.predicted_origin ); - VectorCopy( cl.frame.ps.viewangles, cl.predicted_angles ); - } - - SCR_EndLoadingPlaque (); // get rid of loading plaque - SCR_LagClear(); - - if( !cls.demo.playback ) { - EXEC_TRIGGER( cl_beginmapcmd ); - Cmd_ExecTrigger( "#cl_enterlevel" ); - } - - Cvar_Set( "cl_paused", "0" ); -} - -/* -================ -CL_ParseFrame -================ -*/ -static void CL_ParseFrame( int extrabits ) { - uint32_t bits, extraflags; - int currentframe, deltaframe, - delta, surpressed; - server_frame_t frame, *oldframe; - player_state_t *from; - int length; - - memset( &frame, 0, sizeof( frame ) ); - - cl.frameflags = 0; - - surpressed = 0; - extraflags = 0; - if( cls.serverProtocol > PROTOCOL_VERSION_DEFAULT ) { - bits = MSG_ReadLong(); - - currentframe = bits & FRAMENUM_MASK; - delta = bits >> FRAMENUM_BITS; - - if( delta == 31 ) { - deltaframe = -1; - } else { - deltaframe = currentframe - delta; - } - - bits = MSG_ReadByte(); - - surpressed = bits & SURPRESSCOUNT_MASK; - if( cls.serverProtocol == PROTOCOL_VERSION_Q2PRO ) { - cl.frameflags |= surpressed; - } else if( surpressed ) { - cl.frameflags |= FF_SURPRESSED; - } - extraflags = ( extrabits << 4 ) | ( bits >> SURPRESSCOUNT_BITS ); - } else { - currentframe = MSG_ReadLong(); - deltaframe = MSG_ReadLong(); - - // BIG HACK to let old demos continue to work - if( cls.serverProtocol != PROTOCOL_VERSION_OLD ) { - surpressed = MSG_ReadByte(); - if( surpressed ) { - cl.frameflags |= FF_SURPRESSED; - } - } - } - - frame.number = currentframe; - frame.delta = deltaframe; - - if( cls.netchan && cls.netchan->dropped ) { - cl.frameflags |= FF_SERVERDROP; - } - - /* 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( deltaframe > 0 ) { - oldframe = &cl.frames[deltaframe & UPDATE_MASK]; - from = &oldframe->ps; - if( deltaframe == currentframe ) { - // old buggy q2 servers still cause this on map change - Com_DPrintf( "%s: delta from current frame\n", __func__ ); - cl.frameflags |= FF_BADFRAME; - } else if( oldframe->number != deltaframe ) { - // The frame that the server did the delta from - // is too old, so we can't reconstruct it properly. - Com_DPrintf( "%s: delta frame was never received or too old\n", __func__ ); - cl.frameflags |= FF_OLDFRAME; - } else if( !oldframe->valid ) { - // should never happen - Com_DPrintf( "%s: delta from invalid frame\n", __func__ ); - cl.frameflags |= FF_BADFRAME; - } else if( cl.numEntityStates - oldframe->firstEntity > - MAX_PARSE_ENTITIES - MAX_PACKET_ENTITIES ) - { - Com_DPrintf( "%s: delta entities too old\n", __func__ ); - cl.frameflags |= FF_OLDENT; - } else { - frame.valid = qtrue; // valid delta parse - } - if( !frame.valid && cl.frame.valid && cls.demo.playback ) { - Com_DPrintf( "%s: recovering broken demo\n", __func__ ); - oldframe = &cl.frame; - from = &oldframe->ps; - frame.valid = qtrue; - } - } else { - oldframe = NULL; - from = NULL; - frame.valid = qtrue; // uncompressed frame - //if( !cls.demowaiting ) { - cl.frameflags |= FF_NODELTA; - //} - //cls.demowaiting = qfalse; // we can start recording now - } - - // read areabits - length = MSG_ReadByte(); - if( length ) { - if( length < 0 || msg_read.readcount + length > msg_read.cursize ) { - Com_Error( ERR_DROP, "%s: read past end of message", __func__ ); - } - if( length > sizeof( frame.areabits ) ) { - Com_Error( ERR_DROP, "%s: invalid areabits length", __func__ ); - } - memcpy( frame.areabits, msg_read.data + msg_read.readcount, length ); - msg_read.readcount += length; - frame.areabytes = length; - } else { - frame.areabytes = 0; - } - - if( cls.serverProtocol <= PROTOCOL_VERSION_DEFAULT ) { - if( MSG_ReadByte() != svc_playerinfo ) { - Com_Error( ERR_DROP, "%s: not playerinfo", __func__ ); - } - } - - SHOWNET( 2, "%3"PRIz":playerinfo\n", msg_read.readcount - 1 ); - - // parse playerstate - bits = MSG_ReadShort(); - if( cls.serverProtocol > PROTOCOL_VERSION_DEFAULT ) { - MSG_ParseDeltaPlayerstate_Enhanced( from, &frame.ps, bits, extraflags ); -#ifdef _DEBUG - if( cl_shownet->integer > 2 ) { - MSG_ShowDeltaPlayerstateBits_Enhanced( bits ); - Com_Printf( "\n" ); - } -#endif - if( cls.serverProtocol == PROTOCOL_VERSION_Q2PRO ) { - // parse clientNum - if( extraflags & EPS_CLIENTNUM ) { - frame.clientNum = MSG_ReadByte(); - } else if( oldframe ) { - frame.clientNum = oldframe->clientNum; - } - } else { - frame.clientNum = cl.clientNum; - } - } else { - MSG_ParseDeltaPlayerstate_Default( from, &frame.ps, bits ); -#ifdef _DEBUG - if( cl_shownet->integer > 2 ) { - MSG_ShowDeltaPlayerstateBits_Default( bits ); - Com_Printf( "\n" ); - } -#endif - frame.clientNum = cl.clientNum; - } - if( !frame.ps.fov ) { - // fail out early to prevent spurious errors later - Com_Error( ERR_DROP, "%s: bad fov", __func__ ); - } - - // parse packetentities - if( cls.serverProtocol <= PROTOCOL_VERSION_DEFAULT ) { - if( MSG_ReadByte() != svc_packetentities ) { - Com_Error( ERR_DROP, "%s: not packetentities", __func__ ); - } - } - - SHOWNET( 2, "%3"PRIz":packetentities\n", msg_read.readcount - 1 ); - - CL_ParsePacketEntities( oldframe, &frame ); - - // save the frame off in the backup array for later delta comparisons - cl.frames[currentframe & UPDATE_MASK] = frame; - -#ifdef _DEBUG - if( cl_shownet->integer > 2 ) { - int rtt = 0; - if( cls.netchan ) { - int seq = cls.netchan->incoming_acknowledged & CMD_MASK; - rtt = cls.realtime - cl.history[seq].sent; - } - Com_Printf( "%3"PRIz":frame:%d delta:%d rtt:%d\n", - msg_read.readcount - 1, frame.number, frame.delta, rtt ); - } -#endif - - if( !frame.valid ) { - cl.frame.valid = qfalse; - return; // do not change anything - } - - cl.oldframe = cl.frame; - cl.frame = frame; - - // getting a valid frame message ends the connection process - if( cls.state == ca_precached ) { - CL_SetActiveState(); - } - - CL_DeltaFrame(); - - CL_CheckPredictionError(); -} - - -/* -===================================================================== - - SERVER CONNECTING MESSAGES - -===================================================================== -*/ - -static void CL_ParseConfigstring( int index ) { - size_t len, maxlen; - char *string; - - if( index < 0 || index >= MAX_CONFIGSTRINGS ) { - Com_Error( ERR_DROP, "%s: bad index: %d", __func__, index ); - } - - string = cl.configstrings[index]; - maxlen = CS_SIZE( index ); - len = MSG_ReadString( string, maxlen ); - - SHOWNET( 2, " %d \"%s\"\n", index, string ); - - if( len >= maxlen ) { - Com_WPrintf( - "%s: index %d overflowed: %"PRIz" > %"PRIz"\n", - __func__, index, len, maxlen - 1 ); - len = maxlen - 1; - } - - if( cls.demo.recording && cls.demo.paused ) { - Q_SetBit( cl.dcs, index ); - } - - // do something apropriate - if( index == CS_MAXCLIENTS ) { - cl.maxclients = atoi( string ); - return; - } - if( index == CS_MODELS + 1 ) { - if( len <= 9 ) { - Com_Error( ERR_DROP, "%s: bad world model: %s", __func__, string ); - } - memcpy( cl.mapname, string + 5, len - 9 ); // skip "maps/" - cl.mapname[len - 9] = 0; // cut off ".bsp" - return; - } -#if USE_LIGHTSTYLES - if (index >= CS_LIGHTS && index < CS_LIGHTS+MAX_LIGHTSTYLES) { - CL_SetLightStyle( index - CS_LIGHTS, string, len ); - return; - } -#endif - - if( cls.state < ca_precached ) { - return; - } - if (index >= CS_MODELS+2 && index < CS_MODELS+MAX_MODELS) { - cl.model_draw[index-CS_MODELS] = R_RegisterModel (string); - if (*string == '*') - cl.model_clip[index-CS_MODELS] = BSP_InlineModel (cl.bsp, string); - else - cl.model_clip[index-CS_MODELS] = NULL; - } else if (index >= CS_SOUNDS && index < CS_SOUNDS+MAX_MODELS) { - cl.sound_precache[index-CS_SOUNDS] = S_RegisterSound (string); - } else if (index >= CS_IMAGES && index < CS_IMAGES+MAX_MODELS) { - cl.image_precache[index-CS_IMAGES] = R_RegisterPic (string); - } else if (index >= CS_PLAYERSKINS && index < CS_PLAYERSKINS+MAX_CLIENTS) { - CL_LoadClientinfo( &cl.clientinfo[index - CS_PLAYERSKINS], string ); - } else if( index == CS_AIRACCEL && !cl.pmp.qwmode ) { - cl.pmp.airaccelerate = atoi( string ) ? qtrue : qfalse; - } -} - -static void CL_ParseBaseline( int index, int bits ) { - if( index < 1 || index >= MAX_EDICTS ) { - Com_Error( ERR_DROP, "%s: bad index: %d", __func__, index ); - } -#ifdef _DEBUG - if( cl_shownet->integer > 2 ) { - MSG_ShowDeltaEntityBits( bits ); - Com_Printf( "\n" ); - } -#endif - MSG_ParseDeltaEntity( NULL, &cl.baselines[index], index, bits, cl.esFlags ); -} - -/* -================== -CL_ParseGamestate - -Instead of wasting space for svc_configstring and svc_spawnbaseline -bytes, entire game state is compressed into a single stream. -================== -*/ -static void CL_ParseGamestate( void ) { - int index, bits; - - while( msg_read.readcount < msg_read.cursize ) { - index = MSG_ReadShort(); - if( index == MAX_CONFIGSTRINGS ) { - break; - } - CL_ParseConfigstring( index ); - } - - while( msg_read.readcount < msg_read.cursize ) { - index = MSG_ParseEntityBits( &bits ); - if( !index ) { - break; - } - CL_ParseBaseline( index, bits ); - } -} - -/* -================== -CL_ParseServerData -================== -*/ -static void CL_ParseServerData( void ) { - char levelname[MAX_QPATH]; - int i, protocol, attractloop; - size_t len; - - Cbuf_Execute( &cl_cmdbuf ); // make sure any stuffed commands are done - - // wipe the client_state_t struct - CL_ClearState(); - - // parse protocol version number - protocol = MSG_ReadLong(); - cl.servercount = MSG_ReadLong(); - attractloop = MSG_ReadByte(); - - Com_DPrintf( "Serverdata packet received " - "(protocol=%d, servercount=%d, attractloop=%d)\n", - protocol, cl.servercount, attractloop ); - - // check protocol - if( cls.serverProtocol != protocol ) { - if( !cls.demo.playback ) { - Com_Error( ERR_DROP, "Requested protocol version %d, but server returned %d.", - cls.serverProtocol, protocol ); - } - // BIG HACK to let demos from release work with the 3.0x patch!!! - if( protocol < PROTOCOL_VERSION_OLD || protocol > PROTOCOL_VERSION_Q2PRO ) { - Com_Error( ERR_DROP, "Demo uses unsupported protocol version %d.", protocol ); - } - cls.serverProtocol = protocol; - } - - // game directory - len = MSG_ReadString( cl.gamedir, sizeof( cl.gamedir ) ); - if( len >= sizeof( cl.gamedir ) ) { - Com_Error( ERR_DROP, "Oversize gamedir string" ); - } - - // never allow demos to change gamedir - // do not change gamedir if connected to local sever either, - // as it was already done by SV_InitGame, and changing it - // here will not work since server is now running - if( !cls.demo.playback && !sv_running->integer ) { - // pretend it has been set by user, so that 'changed' hook - // gets called and filesystem is restarted - Cvar_UserSet( "game", cl.gamedir ); - - // protect it from modifications while we are connected - fs_game->flags |= CVAR_ROM; - } - - // parse player entity number - cl.clientNum = MSG_ReadShort(); - - // get the full level name - MSG_ReadString( levelname, sizeof( levelname ) ); - - // setup default pmove parameters - PmoveInit( &cl.pmp ); - - // setup default frame times - cl.frametime = 100; - cl.framefrac = 0.01f; - - if( cls.serverProtocol == PROTOCOL_VERSION_R1Q2 ) { - i = MSG_ReadByte(); - if( i ) { - Com_Error( ERR_DROP, "'Enhanced' R1Q2 servers are not supported" ); - } - i = MSG_ReadShort(); - // for some reason, R1Q2 servers always report the highest protocol - // version they support, while still using the lower version - // client specified in the 'connect' packet. oh well... - if( !R1Q2_SUPPORTED( i ) ) { - Com_WPrintf( - "R1Q2 server reports unsupported protocol version %d.\n" - "Assuming it really uses our current client version %d.\n" - "Things will break if it does not!\n", i, PROTOCOL_VERSION_R1Q2_CURRENT ); - clamp( i, PROTOCOL_VERSION_R1Q2_MINIMUM, PROTOCOL_VERSION_R1Q2_CURRENT ); - } - Com_DPrintf( "Using minor R1Q2 protocol version %d\n", i ); - cls.protocolVersion = i; - MSG_ReadByte(); // used to be advanced deltas - i = MSG_ReadByte(); - if( i ) { - Com_DPrintf( "R1Q2 strafejump hack enabled\n" ); - cl.pmp.strafehack = qtrue; - } - if( cls.protocolVersion >= PROTOCOL_VERSION_R1Q2_LONG_SOLID ) { - cl.esFlags |= MSG_ES_LONGSOLID; - } - cl.pmp.speedmult = 2; - } else if( cls.serverProtocol == PROTOCOL_VERSION_Q2PRO ) { - i = MSG_ReadShort(); - if( !Q2PRO_SUPPORTED( i ) ) { - Com_Error( ERR_DROP, - "Q2PRO server reports unsupported protocol version %d.\n" - "Current client version is %d.", i, PROTOCOL_VERSION_Q2PRO_CURRENT ); - } - Com_DPrintf( "Using minor Q2PRO protocol version %d\n", i ); - cls.protocolVersion = i; - MSG_ReadByte(); // used to be gametype - i = MSG_ReadByte(); - if( i ) { - Com_DPrintf( "Q2PRO strafejump hack enabled\n" ); - cl.pmp.strafehack = qtrue; - } - i = MSG_ReadByte(); //atu QWMod - if( i ) { - Com_DPrintf( "Q2PRO QW mode enabled\n" ); - PmoveEnableQW( &cl.pmp ); - } - cl.esFlags |= MSG_ES_UMASK; - if( cls.protocolVersion >= PROTOCOL_VERSION_Q2PRO_LONG_SOLID ) { - cl.esFlags |= MSG_ES_LONGSOLID; - } - if( cls.protocolVersion >= PROTOCOL_VERSION_Q2PRO_WATERJUMP_HACK ) { - i = MSG_ReadByte(); - if( i ) { - Com_DPrintf( "Q2PRO waterjump hack enabled\n" ); - cl.pmp.waterhack = qtrue; - } - } - cl.pmp.speedmult = 2; - cl.pmp.flyhack = qtrue; // fly hack is unconditionally enabled - cl.pmp.flyfriction = 4; - } - - if( cl.clientNum == -1 ) { - // tell the server to advance to the next map / cinematic - CL_ClientCommand( va( "nextserver %i\n", cl.servercount ) ); - } else { - // seperate the printfs so the server message can have a color - Con_Printf( - "\n\n" - "\35\36\36\36\36\36\36\36\36\36\36\36" - "\36\36\36\36\36\36\36\36\36\36\36\36" - "\36\36\36\36\36\36\36\36\36\36\36\37" - "\n\n" ); - - Com_SetColor( COLOR_ALT ); - Com_Printf( "%s\n", levelname ); - Com_SetColor( COLOR_NONE ); - - // make sure clientNum is in range - if( cl.clientNum < 0 || cl.clientNum >= MAX_CLIENTS ) { - cl.clientNum = CLIENTNUM_NONE; - } - } - -} - -/* -================ -CL_LoadClientinfo - -================ -*/ -void CL_LoadClientinfo( clientinfo_t *ci, const char *s ) { - int i; - char *t; - char model_name[MAX_QPATH]; - char skin_name[MAX_QPATH]; - char model_filename[MAX_QPATH]; - char skin_filename[MAX_QPATH]; - char weapon_filename[MAX_QPATH]; - char icon_filename[MAX_QPATH]; - - // isolate the player's name - strcpy( ci->name, s ); - t = strchr( s, '\\' ); - if( t ) { - ci->name[ t - s ] = 0; - s = t + 1; - } - - strcpy( model_name, s ); - - // isolate the model name - t = strchr( model_name, '/' ); - if( !t ) - t = strchr( model_name, '\\' ); - if( !t ) - t = model_name; - if( t == model_name ) { - strcpy( model_name, "male" ); - strcpy( skin_name, "grunt" ); - } else { - *t = 0; - - // apply restictions on skins - if( cl_noskins->integer == 2 && !Q_stricmp( model_name, "female" ) ) { - strcpy( model_name, "female" ); - strcpy( skin_name, "athena" ); - } else if( cl_noskins->integer ) { - strcpy( model_name, "male" ); - strcpy( skin_name, "grunt" ); - } else { - // isolate the skin name - strcpy( skin_name, t + 1 ); - } - } - - // model file - Q_concat( model_filename, sizeof( model_filename ), - "players/", model_name, "/tris.md2", NULL ); - ci->model = R_RegisterModel( model_filename ); - if( !ci->model && Q_stricmp( model_name, "male" ) ) { - strcpy( model_name, "male" ); - strcpy( model_filename, "players/male/tris.md2" ); - ci->model = R_RegisterModel( model_filename ); - } - - // skin file - Q_concat( skin_filename, sizeof( skin_filename ), - "players/", model_name, "/", skin_name, ".pcx", NULL ); - ci->skin = R_RegisterSkin( skin_filename ); - - // if we don't have the skin and the model was female, - // see if athena skin exists - if( !ci->skin && !Q_stricmp( model_name, "female" ) ) { - strcpy( skin_name, "athena" ); - strcpy( skin_filename, "players/female/athena.pcx" ); - ci->skin = R_RegisterSkin( skin_filename ); - } - - // if we don't have the skin and the model wasn't male, - // see if the male has it (this is for CTF's skins) - if( !ci->skin && Q_stricmp( model_name, "male" ) ) { - // change model to male - strcpy( model_name, "male" ); - strcpy( model_filename, "players/male/tris.md2" ); - ci->model = R_RegisterModel( model_filename ); - - // see if the skin exists for the male model - Q_concat( skin_filename, sizeof( skin_filename ), - "players/male/", skin_name, ".pcx", NULL ); - ci->skin = R_RegisterSkin( skin_filename ); - } - - // if we still don't have a skin, it means that the male model - // didn't have it, so default to grunt - if( !ci->skin ) { - // see if the skin exists for the male model - strcpy( skin_name, "grunt" ); - strcpy( skin_filename, "players/male/grunt.pcx" ); - ci->skin = R_RegisterSkin( skin_filename ); - } - - // weapon file - for( i = 0; i < cl.numWeaponModels; i++ ) { - Q_concat( weapon_filename, sizeof( weapon_filename ), - "players/", model_name, "/", cl.weaponModels[i], NULL ); - ci->weaponmodel[i] = R_RegisterModel( weapon_filename ); - if( !ci->weaponmodel[i] && Q_stricmp( model_name, "male" ) ) { - // try male - Q_concat( weapon_filename, sizeof( weapon_filename ), - "players/male/", cl.weaponModels[i], NULL ); - ci->weaponmodel[i] = R_RegisterModel( weapon_filename ); - } - } - - // icon file - Q_concat( icon_filename, sizeof( icon_filename ), - "/players/", model_name, "/", skin_name, "_i.pcx", NULL ); - ci->icon = R_RegisterPic( icon_filename ); - - strcpy( ci->model_name, model_name ); - strcpy( ci->skin_name, skin_name ); - - // must have loaded all data types to be valid - if( !ci->skin || !ci->icon || !ci->model || !ci->weaponmodel[0] ) { - ci->skin = 0; - ci->icon = 0; - ci->model = 0; - ci->weaponmodel[0] = 0; - ci->model_name[0] = 0; - ci->skin_name[0] = 0; - } -} - -/* -===================================================================== - -ACTION MESSAGES - -===================================================================== -*/ - -tent_params_t te; -mz_params_t mz; - -static void CL_ParseTEntParams( void ) { - te.type = MSG_ReadByte(); - - switch( te.type ) { - case TE_BLOOD: - case TE_GUNSHOT: - case TE_SPARKS: - case TE_BULLET_SPARKS: - case TE_SCREEN_SPARKS: - case TE_SHIELD_SPARKS: - case TE_SHOTGUN: - case TE_BLASTER: - case TE_GREENBLOOD: - case TE_BLASTER2: - case TE_FLECHETTE: - case TE_HEATBEAM_SPARKS: - case TE_HEATBEAM_STEAM: - case TE_MOREBLOOD: - case TE_ELECTRIC_SPARKS: - MSG_ReadPos( te.pos1 ); - MSG_ReadDir( te.dir ); - break; - - case TE_SPLASH: - case TE_LASER_SPARKS: - case TE_WELDING_SPARKS: - case TE_TUNNEL_SPARKS: - te.count = MSG_ReadByte(); - MSG_ReadPos( te.pos1 ); - MSG_ReadDir( te.dir ); - te.color = MSG_ReadByte(); - break; - - case TE_BLUEHYPERBLASTER: - case TE_RAILTRAIL: - case TE_BUBBLETRAIL: - case TE_DEBUGTRAIL: - case TE_BUBBLETRAIL2: - case TE_BFG_LASER: - MSG_ReadPos( te.pos1 ); - MSG_ReadPos( te.pos2 ); - break; - - case TE_GRENADE_EXPLOSION: - case TE_GRENADE_EXPLOSION_WATER: - case TE_EXPLOSION2: - case TE_PLASMA_EXPLOSION: - case TE_ROCKET_EXPLOSION: - case TE_ROCKET_EXPLOSION_WATER: - case TE_EXPLOSION1: - case TE_EXPLOSION1_NP: - case TE_EXPLOSION1_BIG: - case TE_BFG_EXPLOSION: - case TE_BFG_BIGEXPLOSION: - case TE_BOSSTPORT: - case TE_PLAIN_EXPLOSION: - case TE_CHAINFIST_SMOKE: - case TE_TRACKER_EXPLOSION: - case TE_TELEPORT_EFFECT: - case TE_DBALL_GOAL: - case TE_WIDOWSPLASH: - case TE_NUKEBLAST: - MSG_ReadPos( te.pos1 ); - break; - - case TE_PARASITE_ATTACK: - case TE_MEDIC_CABLE_ATTACK: - case TE_HEATBEAM: - case TE_MONSTER_HEATBEAM: - te.entity1 = MSG_ReadShort(); - MSG_ReadPos( te.pos1 ); - MSG_ReadPos( te.pos2 ); - break; - - case TE_GRAPPLE_CABLE: - te.entity1 = MSG_ReadShort(); - MSG_ReadPos( te.pos1 ); - MSG_ReadPos( te.pos2 ); - MSG_ReadPos( te.offset ); - break; - - case TE_LIGHTNING: - te.entity1 = MSG_ReadShort(); - te.entity2 = MSG_ReadShort(); - MSG_ReadPos( te.pos1 ); - MSG_ReadPos( te.pos2 ); - break; - - case TE_FLASHLIGHT: - MSG_ReadPos( te.pos1 ); - te.entity1 = MSG_ReadShort(); - break; - - case TE_FORCEWALL: - MSG_ReadPos( te.pos1 ); - MSG_ReadPos( te.pos2 ); - te.color = MSG_ReadByte(); - break; - - case TE_STEAM: - te.entity1 = MSG_ReadShort(); - te.count = MSG_ReadByte(); - MSG_ReadPos( te.pos1 ); - MSG_ReadDir( te.dir ); - te.color = MSG_ReadByte(); - te.entity2 = MSG_ReadShort(); - if( te.entity1 != -1 ) { - te.time = MSG_ReadLong(); - } - break; - - case TE_WIDOWBEAMOUT: - te.entity1 = MSG_ReadShort(); - MSG_ReadPos( te.pos1 ); - break; - - default: - Com_Error( ERR_DROP, "%s: bad type", __func__ ); - } - - CL_ParseTEnt(); -} - -static void CL_ParseMuzzleFlashParams( void ) { - int entity, weapon; - - entity = MSG_ReadShort(); - if( entity < 1 || entity >= MAX_EDICTS ) - Com_Error( ERR_DROP, "%s: bad entity", __func__ ); - - weapon = MSG_ReadByte(); - mz.silenced = weapon & MZ_SILENCED; - mz.weapon = weapon & ~MZ_SILENCED; - mz.entity = entity; - - CL_ParseMuzzleFlash(); -} - -static void CL_ParseMuzzleFlashParams2( void ) { - int entity; - - entity = MSG_ReadShort(); - if( entity < 1 || entity >= MAX_EDICTS ) - Com_Error( ERR_DROP, "%s: bad entity", __func__ ); - - mz.weapon = MSG_ReadByte(); - mz.entity = entity; - - CL_ParseMuzzleFlash2(); -} - -/* -================== -CL_ParseStartSoundPacket -================== -*/ -static void CL_ParseStartSoundPacket( void ) { - vec3_t pos_v; - float *pos; - int channel, ent; - int sound_num; - float volume; - float attenuation; - int flags; - float ofs; - - flags = MSG_ReadByte(); - sound_num = MSG_ReadByte(); - if( sound_num == -1 ) { - Com_Error( ERR_DROP, "%s: read past end of message", __func__ ); - } - - if( flags & SND_VOLUME ) - volume = MSG_ReadByte() / 255.0; - else - volume = DEFAULT_SOUND_PACKET_VOLUME; - - if( flags & SND_ATTENUATION ) - attenuation = MSG_ReadByte() / 64.0; - else - attenuation = DEFAULT_SOUND_PACKET_ATTENUATION; - - if( flags & SND_OFFSET ) - ofs = MSG_ReadByte() / 1000.0; - else - ofs = 0; - - if( flags & SND_ENT ) { - // entity relative - channel = MSG_ReadShort(); - ent = channel >> 3; - if( ent < 0 || ent >= MAX_EDICTS ) - Com_Error( ERR_DROP, "%s: bad ent: %d", __func__, ent ); - channel &= 7; - } else { - ent = 0; - channel = 0; - } - - if( flags & SND_POS ) { - // positioned in space - MSG_ReadPos( pos_v ); - pos = pos_v; - } else { - if( !( flags & SND_ENT ) ) { - Com_Error( ERR_DROP, "%s: neither SND_ENT nor SND_POS set", __func__ ); - } - if( cl_entities[ent].serverframe != cl.frame.number ) { - if( cl_entities[ent].serverframe ) { - Com_DPrintf( "SERVER BUG: sound on entity %d last seen %d frames ago\n", - ent, cl.frame.number - cl_entities[ent].serverframe ); - } else { - Com_DPrintf( "SERVER BUG: sound on entity %d never seen before\n", ent ); - } - } - // use entity number - pos = NULL; - } - - SHOWNET( 2, " %s\n", cl.configstrings[CS_SOUNDS+sound_num] ); - - if( cl.sound_precache[sound_num] ) { - S_StartSound( pos, ent, channel, cl.sound_precache[sound_num], - volume, attenuation, ofs ); - } -} - -/* -===================== -CL_ParseReconnect -===================== -*/ -static void CL_ParseReconnect( void ) { - if( cls.demo.playback ) { - return; - } - - S_StopAllSounds(); - - if( cls.demo.recording ) - CL_Stop_f(); - - Com_Printf( "Server disconnected, reconnecting\n" ); - - if( cls.download.file ) { - FS_FCloseFile( cls.download.file ); - } - memset( &cls.download, 0, sizeof( cls.download ) ); - - EXEC_TRIGGER( cl_changemapcmd ); - - CL_ClearState(); - cls.state = ca_challenging; - cls.connect_time = cls.realtime - CONNECT_DELAY; - cls.connect_count = 0; - - CL_CheckForResend(); -} - -#if USE_AUTOREPLY -/* -==================== -CL_CheckForVersion -==================== -*/ -static void CL_CheckForVersion( const char *string ) { - char *p; - - p = strstr( string, ": " ); - if( !p ) { - return; - } - - if( strncmp( p + 2, "!version", 8 ) ) { - return; - } - - if( cl.reply_time && cls.realtime - cl.reply_time < 120000 ) { - return; - } - - cl.reply_time = cls.realtime; - cl.reply_delta = 1024 + ( rand() & 1023 ); -} -#endif - - -/* -===================== -CL_ParsePrint -===================== -*/ -static void CL_ParsePrint( void ) { - int level; - char string[MAX_STRING_CHARS]; - const char *fmt; - - level = MSG_ReadByte(); - MSG_ReadString( string, sizeof( string ) ); - - SHOWNET( 2, " %i \"%s\"\n", level, string ); - - if( level != PRINT_CHAT ) { - Com_Printf( "%s", string ); - if( !cls.demo.playback ) { - COM_strclr( string ); - Cmd_ExecTrigger( string ); - } - return; - } - -#if USE_AUTOREPLY - if( !cls.demo.playback ) { - CL_CheckForVersion( string ); - } -#endif - - // disable notify - if( !cl_chat_notify->integer ) { - Con_SkipNotify( qtrue ); - } - - // filter text - if( cl_chat_filter->integer ) { - COM_strclr( string ); - fmt = "%s\n"; - } else { - fmt = "%s"; - } - - Com_LPrintf( PRINT_TALK, fmt, string ); - - Con_SkipNotify( qfalse ); - -#if USE_CHATHUD - SCR_AddToChatHUD( string ); -#endif - - // play sound - if( cl_chat_sound->string[0] ) { - S_StartLocalSound_( cl_chat_sound->string ); - } - -} - -/* -===================== -CL_ParseCenterPrint -===================== -*/ -static void CL_ParseCenterPrint( void ) { - char string[MAX_STRING_CHARS]; - - MSG_ReadString( string, sizeof( string ) ); - SHOWNET( 2, " \"%s\"\n", string ); - SCR_CenterPrint( string ); - - if( !cls.demo.playback ) { - COM_strclr( string ); - Cmd_ExecTrigger( string ); - } -} - -/* -===================== -CL_ParseStuffText -===================== -*/ -static void CL_ParseStuffText( void ) { - char s[MAX_STRING_CHARS]; - - MSG_ReadString( s, sizeof( s ) ); - SHOWNET( 2, " \"%s\"\n", s ); - Cbuf_AddText( &cl_cmdbuf, s ); -} - -/* -===================== -CL_ParseLayout -===================== -*/ -static void CL_ParseLayout( void ) { - MSG_ReadString( cl.layout, sizeof( cl.layout ) ); - SHOWNET( 2, " \"%s\"\n", cl.layout ); - cl.putaway = qfalse; -} - -/* -================ -CL_ParseInventory -================ -*/ -static void CL_ParseInventory( void ) { - int i; - - for( i = 0; i < MAX_ITEMS; i++ ) { - cl.inventory[i] = MSG_ReadShort(); - } - cl.putaway = qfalse; -} - -static void CL_ParseZPacket( void ) { -#if USE_ZLIB - sizebuf_t temp; - byte buffer[MAX_MSGLEN]; - size_t inlen, outlen; - - if( msg_read.data != msg_read_buffer ) { - Com_Error( ERR_DROP, "%s: recursively entered", __func__ ); - } - - inlen = MSG_ReadShort(); - if( msg_read.readcount + inlen > msg_read.cursize ) { - Com_Error( ERR_DROP, "%s: read past end of message", __func__ ); - } - - outlen = MSG_ReadShort(); - if( outlen > MAX_MSGLEN ) { - Com_Error( ERR_DROP, "%s: invalid output length", __func__ ); - } - - inflateReset( &cls.z ); - - cls.z.next_in = msg_read.data + msg_read.readcount; - cls.z.avail_in = ( uInt )inlen; - cls.z.next_out = buffer; - cls.z.avail_out = ( uInt )outlen; - if( inflate( &cls.z, Z_FINISH ) != Z_STREAM_END ) { - Com_Error( ERR_DROP, "%s: inflate() failed: %s", __func__, cls.z.msg ); - } - - msg_read.readcount += inlen; - - temp = msg_read; - SZ_Init( &msg_read, buffer, outlen ); - msg_read.cursize = outlen; - - CL_ParseServerMessage(); - - msg_read = temp; -#else - Com_Error( ERR_DROP, "Compressed server packet received, " - "but no zlib support linked in." ); -#endif -} - -static void CL_ParseSetting( void ) { - uint32_t index, value; - - index = MSG_ReadLong(); - value = MSG_ReadLong(); - - switch( index ) { -#if USE_FPS - case SVS_FPS: - if( !value ) { - value = 10; - } - cl.frametime = 1000 / value; - cl.framefrac = value * 0.001f; - break; -#endif - default: - break; - } -} - -/* -===================== -CL_ParseServerMessage -===================== -*/ -void CL_ParseServerMessage( void ) { - int cmd, extrabits; - size_t readcount; - int index, bits; - -#ifdef _DEBUG - if( cl_shownet->integer == 1 ) { - Com_Printf( "%"PRIz" ", msg_read.cursize ); - } else if( cl_shownet->integer > 1 ) { - Com_Printf( "------------------\n" ); - } -#endif - -// -// parse the message -// - while( 1 ) { - if( msg_read.readcount > msg_read.cursize ) { - Com_Error( ERR_DROP, "%s: read past end of server message", __func__ ); - } - - readcount = msg_read.readcount; - - if( ( cmd = MSG_ReadByte() ) == -1 ) { - SHOWNET( 1, "%3"PRIz":END OF MESSAGE\n", msg_read.readcount - 1 ); - break; - } - - extrabits = cmd >> SVCMD_BITS; - cmd &= SVCMD_MASK; - -#ifdef _DEBUG - if( cl_shownet->integer > 1 ) { - MSG_ShowSVC( cmd ); - } -#endif - - // other commands - switch( cmd ) { - default: - badbyte: - Com_Error( ERR_DROP, "%s: illegible server message: %d", __func__, cmd ); - break; - - case svc_nop: - break; - - case svc_disconnect: - Com_Error( ERR_DISCONNECT, "Server disconnected" ); - break; - - case svc_reconnect: - CL_ParseReconnect(); - return; - - case svc_print: - CL_ParsePrint(); - break; - - case svc_centerprint: - CL_ParseCenterPrint(); - break; - - case svc_stufftext: - CL_ParseStuffText(); - break; - - case svc_serverdata: - CL_ParseServerData(); - continue; - - case svc_configstring: - index = MSG_ReadShort(); - CL_ParseConfigstring( index ); - break; - - case svc_sound: - CL_ParseStartSoundPacket(); - break; - - case svc_spawnbaseline: - index = MSG_ParseEntityBits( &bits ); - CL_ParseBaseline( index, bits ); - break; - - case svc_temp_entity: - CL_ParseTEntParams(); - break; - - case svc_muzzleflash: - CL_ParseMuzzleFlashParams(); - break; - - case svc_muzzleflash2: - CL_ParseMuzzleFlashParams2(); - break; - - case svc_download: - CL_ParseDownload(); - continue; - - case svc_frame: - CL_ParseFrame( extrabits ); - continue; - - case svc_inventory: - CL_ParseInventory(); - break; - - case svc_layout: - CL_ParseLayout(); - break; - - case svc_zpacket: - if( cls.serverProtocol < PROTOCOL_VERSION_R1Q2 ) { - goto badbyte; - } - CL_ParseZPacket(); - continue; - - case svc_gamestate: - if( cls.serverProtocol != PROTOCOL_VERSION_Q2PRO ) { - goto badbyte; - } - CL_ParseGamestate(); - continue; - - case svc_setting: - if( cls.serverProtocol < PROTOCOL_VERSION_R1Q2 ) { - goto badbyte; - } - CL_ParseSetting(); - continue; - } - - // copy protocol invariant stuff - if( cls.demo.recording && !cls.demo.paused ) { - size_t len = msg_read.readcount - readcount; - - // with modern servers, it is easily possible to overflow - // the small protocol 34 demo frame... attempt to preserve - // reliable messages at least, which should come first - if( cls.demo.buffer.cursize + len < cls.demo.buffer.maxsize ) { - SZ_Write( &cls.demo.buffer, msg_read.data + readcount, len ); - } - } - } - -// -// if recording demos, write the message out -// - if( cls.demo.recording && !cls.demo.paused ) { - CL_WriteDemoMessage( &cls.demo.buffer ); - } -} - |