mirror of
https://github.com/blendogames/thirtyflightsofloving.git
synced 2024-11-14 16:40:57 +00:00
6a59000846
Renamed "sp2" sprite structs for SP2 to have sp2 in their name.
1043 lines
26 KiB
C
1043 lines
26 KiB
C
/*
|
|
===========================================================================
|
|
Copyright (C) 1997-2001 Id Software, Inc.
|
|
|
|
This file is part of Quake 2 source code.
|
|
|
|
Quake 2 source code is free software; you can redistribute it
|
|
and/or modify it under the terms of the GNU General Public License as
|
|
published by the Free Software Foundation; either version 2 of the License,
|
|
or (at your option) any later version.
|
|
|
|
Quake 2 source code is distributed in the hope that it will be
|
|
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with Quake 2 source code; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
===========================================================================
|
|
*/
|
|
|
|
// cl_parse.c -- parse a message received from the server
|
|
|
|
#include "client.h"
|
|
|
|
char *svc_strings[256] =
|
|
{
|
|
"svc_bad",
|
|
|
|
"svc_muzzleflash",
|
|
"svc_muzzlflash2",
|
|
"svc_temp_entity",
|
|
"svc_layout",
|
|
"svc_inventory",
|
|
|
|
"svc_nop",
|
|
"svc_disconnect",
|
|
"svc_reconnect",
|
|
"svc_sound",
|
|
"svc_print",
|
|
"svc_stufftext",
|
|
"svc_serverdata",
|
|
"svc_configstring",
|
|
"svc_spawnbaseline",
|
|
"svc_centerprint",
|
|
"svc_download",
|
|
"svc_playerinfo",
|
|
"svc_packetentities",
|
|
"svc_deltapacketentities",
|
|
"svc_frame",
|
|
"svc_fog" // Knightmare added
|
|
};
|
|
|
|
//=============================================================================
|
|
|
|
/*
|
|
======================
|
|
CL_RegisterSounds
|
|
======================
|
|
*/
|
|
void CL_RegisterSounds (void)
|
|
{
|
|
int i;
|
|
|
|
S_BeginRegistration ();
|
|
CL_RegisterTEntSounds ();
|
|
|
|
// Knightmare- 1/2/2002- ULTRA-CHEESY HACK for old demos or
|
|
// connected to server using old protocol
|
|
// Changed config strings require different offsets
|
|
if ( LegacyProtocol() )
|
|
{
|
|
for (i=1; i < OLD_MAX_SOUNDS; i++)
|
|
{
|
|
if (!cl.configstrings[OLD_CS_SOUNDS+i][0])
|
|
break;
|
|
cl.sound_precache[i] = S_RegisterSound (cl.configstrings[OLD_CS_SOUNDS+i]);
|
|
Sys_SendKeyEvents (); // pump message loop
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
for (i=1; i < MAX_SOUNDS; i++)
|
|
{
|
|
if (!cl.configstrings[CS_SOUNDS+i][0])
|
|
break;
|
|
cl.sound_precache[i] = S_RegisterSound (cl.configstrings[CS_SOUNDS+i]);
|
|
Sys_SendKeyEvents (); // pump message loop
|
|
}
|
|
}
|
|
//end Knightmare
|
|
S_EndRegistration ();
|
|
}
|
|
|
|
/*
|
|
=====================================================================
|
|
|
|
SERVER CONNECTING MESSAGES
|
|
|
|
=====================================================================
|
|
*/
|
|
|
|
/*
|
|
==================
|
|
CL_ParseServerData
|
|
==================
|
|
*/
|
|
void CL_ParseServerData (void)
|
|
{
|
|
extern cvar_t *fs_gamedirvar;
|
|
char *str;
|
|
int i;
|
|
|
|
Com_DPrintf ("Serverdata packet received.\n");
|
|
//
|
|
// wipe the client_state_t struct
|
|
//
|
|
CL_ClearState ();
|
|
cls.state = ca_connected;
|
|
|
|
// parse protocol version number
|
|
i = MSG_ReadLong (&net_message);
|
|
cls.serverProtocol = i;
|
|
|
|
// BIG HACK to let demos from release work with the 3.0x patch!!!
|
|
// Knightmare- also allow connectivity with servers using the old protocol
|
|
// if (Com_ServerState() && (i < PROTOCOL_VERSION) /*== 35*/)
|
|
if ( LegacyProtocol() ) {} // do nothing
|
|
/* else if (i == OLD_PROTOCOL_VERSION)
|
|
Cvar_ForceSet ("cl_servertrick", "1");
|
|
else if (i == PROTOCOL_VERSION)
|
|
Cvar_ForceSet ("cl_servertrick", "0"); // force this off for local games
|
|
else if (i != PROTOCOL_VERSION) */
|
|
else if ( (i != PROTOCOL_VERSION) && (i != OLD_PROTOCOL_VERSION) )
|
|
Com_Error (ERR_DROP, "Server returned version %i, not %i or %i", i, PROTOCOL_VERSION, OLD_PROTOCOL_VERSION);
|
|
|
|
cl.servercount = MSG_ReadLong (&net_message);
|
|
cl.attractloop = MSG_ReadByte (&net_message);
|
|
|
|
// game directory
|
|
str = MSG_ReadString (&net_message);
|
|
strncpy (cl.gamedir, str, sizeof(cl.gamedir)-1);
|
|
|
|
// set gamedir
|
|
if ( ( (*str && (!fs_gamedirvar->string || !*fs_gamedirvar->string || strcmp(fs_gamedirvar->string, str)))
|
|
|| (!*str && (fs_gamedirvar->string && *fs_gamedirvar->string)) ) // was fs_gamedirvar->string || *fs_gamedirvar->string
|
|
&& !cl.attractloop ) // Knightmare- don't allow demos to change this
|
|
Cvar_Set("game", str);
|
|
|
|
// parse player entity number
|
|
cl.playernum = MSG_ReadShort (&net_message);
|
|
|
|
// get the full level name
|
|
str = MSG_ReadString (&net_message);
|
|
|
|
if (cl.playernum == -1)
|
|
{ // playing a cinematic or showing a pic, not a level
|
|
SCR_PlayCinematic (str);
|
|
}
|
|
else
|
|
{
|
|
// seperate the printfs so the server message can have a color
|
|
Com_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\36\37\n\n");
|
|
con.ormask = 128;
|
|
Com_Printf ("%c"S_COLOR_SHADOW S_COLOR_ALT"%s\n", 2, str);
|
|
con.ormask = 0;
|
|
|
|
// need to prep refresh at next oportunity
|
|
cl.refresh_prepped = false;
|
|
}
|
|
}
|
|
|
|
/*
|
|
==================
|
|
CL_ParseBaseline
|
|
==================
|
|
*/
|
|
void CL_ParseBaseline (void)
|
|
{
|
|
entity_state_t *es;
|
|
int bits;
|
|
int newnum;
|
|
entity_state_t nullstate;
|
|
|
|
memset (&nullstate, 0, sizeof(nullstate));
|
|
|
|
newnum = CL_ParseEntityBits (&bits);
|
|
es = &cl_entities[newnum].baseline;
|
|
CL_ParseDelta (&nullstate, es, newnum, bits);
|
|
}
|
|
|
|
|
|
/*
|
|
================
|
|
CL_LoadClientinfo
|
|
|
|
================
|
|
*/
|
|
void CL_LoadClientinfo (clientinfo_t *ci, 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];
|
|
|
|
strncpy(ci->cinfo, s, sizeof(ci->cinfo));
|
|
ci->cinfo[sizeof(ci->cinfo)-1] = 0;
|
|
|
|
// isolate the player's name
|
|
strncpy(ci->name, s, sizeof(ci->name));
|
|
ci->name[sizeof(ci->name)-1] = 0;
|
|
t = strstr (s, "\\");
|
|
if (t)
|
|
{
|
|
ci->name[t-s] = 0;
|
|
s = t+1;
|
|
}
|
|
|
|
// if (cl_noskins->value || *s == 0)
|
|
if (cl_noskins->integer || *s == 0)
|
|
{
|
|
Com_sprintf (model_filename, sizeof(model_filename), "players/male/tris.md2");
|
|
Com_sprintf (weapon_filename, sizeof(weapon_filename), "players/male/weapon.md2");
|
|
Com_sprintf (skin_filename, sizeof(skin_filename), "players/male/grunt.pcx");
|
|
Com_sprintf (ci->iconname, sizeof(ci->iconname), "/players/male/grunt_i.pcx");
|
|
ci->model = R_RegisterModel (model_filename);
|
|
memset(ci->weaponmodel, 0, sizeof(ci->weaponmodel));
|
|
ci->weaponmodel[0] = R_RegisterModel (weapon_filename);
|
|
ci->skin = R_RegisterSkin (skin_filename);
|
|
ci->icon = R_DrawFindPic (ci->iconname);
|
|
}
|
|
else
|
|
{
|
|
// isolate the model name
|
|
// strncpy (model_name, s);
|
|
Q_strncpyz (model_name, s, sizeof(model_name));
|
|
t = strstr(model_name, "/");
|
|
if (!t)
|
|
t = strstr(model_name, "\\");
|
|
if (!t)
|
|
t = model_name;
|
|
*t = 0;
|
|
|
|
// isolate the skin name
|
|
// strncpy (skin_name, s + strlen(model_name) + 1);
|
|
Q_strncpyz (skin_name, s + strlen(model_name) + 1, sizeof(skin_name));
|
|
|
|
// model file
|
|
Com_sprintf (model_filename, sizeof(model_filename), "players/%s/tris.md2", model_name);
|
|
ci->model = R_RegisterModel (model_filename);
|
|
if (!ci->model)
|
|
{
|
|
// strncpy(model_name, "male");
|
|
Q_strncpyz(model_name, "male", sizeof(model_name));
|
|
Com_sprintf (model_filename, sizeof(model_filename), "players/male/tris.md2");
|
|
ci->model = R_RegisterModel (model_filename);
|
|
}
|
|
|
|
// skin file
|
|
Com_sprintf (skin_filename, sizeof(skin_filename), "players/%s/%s.pcx", model_name, skin_name);
|
|
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
|
|
// strncpy(model_name, "male");
|
|
Q_strncpyz(model_name, "male", sizeof(model_name));
|
|
Com_sprintf (model_filename, sizeof(model_filename), "players/male/tris.md2");
|
|
ci->model = R_RegisterModel (model_filename);
|
|
|
|
// see if the skin exists for the male model
|
|
Com_sprintf (skin_filename, sizeof(skin_filename), "players/%s/%s.pcx", model_name, skin_name);
|
|
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
|
|
Com_sprintf (skin_filename, sizeof(skin_filename), "players/%s/grunt.pcx", model_name, skin_name);
|
|
ci->skin = R_RegisterSkin (skin_filename);
|
|
}
|
|
|
|
// weapon file
|
|
for (i = 0; i < num_cl_weaponmodels; i++) {
|
|
Com_sprintf (weapon_filename, sizeof(weapon_filename), "players/%s/%s", model_name, cl_weaponmodels[i]);
|
|
ci->weaponmodel[i] = R_RegisterModel(weapon_filename);
|
|
if (!ci->weaponmodel[i] && strcmp(model_name, "cyborg") == 0) {
|
|
// try male
|
|
Com_sprintf (weapon_filename, sizeof(weapon_filename), "players/male/%s", cl_weaponmodels[i]);
|
|
ci->weaponmodel[i] = R_RegisterModel(weapon_filename);
|
|
}
|
|
// if (!cl_vwep->value)
|
|
if (!cl_vwep->integer)
|
|
break; // only one when vwep is off
|
|
}
|
|
|
|
// icon file
|
|
Com_sprintf (ci->iconname, sizeof(ci->iconname), "/players/%s/%s_i.pcx", model_name, skin_name);
|
|
ci->icon = R_DrawFindPic (ci->iconname);
|
|
}
|
|
|
|
// must have loaded all data types to be valud
|
|
if (!ci->skin || !ci->icon || !ci->model || !ci->weaponmodel[0])
|
|
{
|
|
ci->skin = NULL;
|
|
ci->icon = NULL;
|
|
ci->model = NULL;
|
|
ci->weaponmodel[0] = NULL;
|
|
return;
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
CL_ParseClientinfo
|
|
|
|
Load the skin, icon, and model for a client
|
|
================
|
|
*/
|
|
void CL_ParseClientinfo (int player)
|
|
{
|
|
char *s;
|
|
clientinfo_t *ci;
|
|
|
|
// Knightmare- 1/2/2002- GROSS HACK for old demos or
|
|
// connected to server using old protocol
|
|
// Changed config strings require different offsets
|
|
if ( LegacyProtocol() )
|
|
s = cl.configstrings[player+OLD_CS_PLAYERSKINS];
|
|
else
|
|
s = cl.configstrings[player+CS_PLAYERSKINS];
|
|
//end Knightmare
|
|
|
|
ci = &cl.clientinfo[player];
|
|
|
|
CL_LoadClientinfo (ci, s);
|
|
}
|
|
|
|
|
|
/*
|
|
================
|
|
CL_MissionPackCDTrack
|
|
Returns correct OGG track number for mission packs.
|
|
This assumes that the standard Q2 CD was ripped
|
|
as track02-track11, and the Rogue CD as track12-track21.
|
|
================
|
|
*/
|
|
int CL_MissionPackCDTrack (int tracknum)
|
|
{
|
|
// if (FS_ModType("rogue") || cl_rogue_music->value)
|
|
if (FS_ModType("rogue") || cl_rogue_music->integer)
|
|
{
|
|
if (tracknum >= 2 && tracknum <= 11)
|
|
return tracknum + 10;
|
|
else
|
|
return tracknum;
|
|
}
|
|
// an out-of-order mix from Q2 and Rogue CDs
|
|
// else if (FS_ModType("xatrix") || cl_xatrix_music->value)
|
|
else if (FS_ModType("xatrix") || cl_xatrix_music->integer)
|
|
{
|
|
switch(tracknum)
|
|
{
|
|
case 2: return 9; break;
|
|
case 3: return 13; break;
|
|
case 4: return 14; break;
|
|
case 5: return 7; break;
|
|
case 6: return 16; break;
|
|
case 7: return 2; break;
|
|
case 8: return 15; break;
|
|
case 9: return 3; break;
|
|
case 10: return 4; break;
|
|
case 11: return 18; break;
|
|
default: return tracknum; break;
|
|
}
|
|
}
|
|
else
|
|
return tracknum;
|
|
}
|
|
|
|
|
|
/*
|
|
=================
|
|
CL_PlayBackgroundTrack
|
|
=================
|
|
*/
|
|
#ifdef OGG_SUPPORT
|
|
|
|
#include "snd_ogg.h"
|
|
|
|
void CL_PlayBackgroundTrack (void)
|
|
{
|
|
char name[MAX_QPATH];
|
|
int track;
|
|
|
|
if (!cl.refresh_prepped)
|
|
return;
|
|
|
|
// using a named audio track intead of numbered
|
|
if (strlen(cl.configstrings[CS_CDTRACK]) > 2)
|
|
{
|
|
Com_sprintf (name, sizeof(name), "music/%s.ogg", cl.configstrings[CS_CDTRACK]);
|
|
if (FS_LoadFile(name, NULL) != -1)
|
|
{
|
|
CDAudio_Stop();
|
|
S_StartBackgroundTrack(name, name);
|
|
return;
|
|
}
|
|
}
|
|
|
|
track = atoi(cl.configstrings[CS_CDTRACK]);
|
|
|
|
if (track == 0)
|
|
{ // Stop any playing track
|
|
CDAudio_Stop();
|
|
S_StopBackgroundTrack();
|
|
return;
|
|
}
|
|
|
|
// If an OGG file exists play it, otherwise fall back to CD audio
|
|
Com_sprintf (name, sizeof(name), "music/track%02i.ogg", CL_MissionPackCDTrack(track));
|
|
// if ( (FS_LoadFile(name, NULL) != -1) && cl_ogg_music->value )
|
|
if ( (FS_LoadFile(name, NULL) != -1) && cl_ogg_music->integer )
|
|
S_StartBackgroundTrack(name, name);
|
|
else
|
|
CDAudio_Play(track, true);
|
|
}
|
|
|
|
#else
|
|
|
|
void CL_PlayBackgroundTrack (void)
|
|
{
|
|
CDAudio_Play (atoi(cl.configstrings[CS_CDTRACK]), true);
|
|
}
|
|
|
|
#endif // OGG_SUPPORT
|
|
|
|
/*
|
|
================
|
|
CL_ParseConfigString
|
|
================
|
|
*/
|
|
void CL_ParseConfigString (void)
|
|
{
|
|
int i;
|
|
int max_models, max_sounds, max_images, cs_lights, cs_sounds, cs_images, cs_playerskins;
|
|
char *s;
|
|
char olds[MAX_QPATH];
|
|
size_t length;
|
|
|
|
// Knightmare- hack for connected to server using old protocol
|
|
// Changed config strings require different parsing
|
|
if ( LegacyProtocol() )
|
|
{
|
|
max_models = OLD_MAX_MODELS;
|
|
max_sounds = OLD_MAX_SOUNDS;
|
|
max_images = OLD_MAX_IMAGES;
|
|
cs_lights = OLD_CS_LIGHTS;
|
|
cs_sounds = OLD_CS_SOUNDS;
|
|
cs_images = OLD_CS_IMAGES;
|
|
cs_playerskins = OLD_CS_PLAYERSKINS;
|
|
}
|
|
else
|
|
{
|
|
max_models = MAX_MODELS;
|
|
max_sounds = MAX_SOUNDS;
|
|
max_images = MAX_IMAGES;
|
|
cs_lights = CS_LIGHTS;
|
|
cs_sounds = CS_SOUNDS;
|
|
cs_images = CS_IMAGES;
|
|
cs_playerskins = CS_PLAYERSKINS;
|
|
}
|
|
|
|
i = MSG_ReadShort (&net_message);
|
|
if (i < 0 || i >= MAX_CONFIGSTRINGS)
|
|
Com_Error (ERR_DROP, "configstring > MAX_CONFIGSTRINGS");
|
|
s = MSG_ReadString(&net_message);
|
|
|
|
Q_strncpyz (olds, cl.configstrings[i], sizeof(olds));
|
|
|
|
// check length
|
|
length = strlen(s);
|
|
if ( length >= (sizeof(cl.configstrings[0]) * (MAX_CONFIGSTRINGS - i)) - 1 )
|
|
Com_Error (ERR_DROP, "CL_ParseConfigString: string %d exceeds available buffer space!", i);
|
|
|
|
if (i >= CS_STATUSBAR && i < CS_AIRACCEL) { // allow writes to statusbar strings to overflow
|
|
strncpy (cl.configstrings[i], s, (sizeof(cl.configstrings[i]) * (CS_AIRACCEL - i))-1 );
|
|
cl.configstrings[CS_AIRACCEL-1][MAX_QPATH-1] = 0; // null terminate end of section
|
|
}
|
|
else {
|
|
if (length >= MAX_QPATH)
|
|
Com_Printf(S_COLOR_YELLOW"CL_ParseConfigString: string %d of length %d exceeds MAX_QPATH.\n", i, (int)length);
|
|
Q_strncpyz (cl.configstrings[i], s, sizeof(cl.configstrings[i]));
|
|
}
|
|
|
|
// do something apropriate
|
|
if (i >= cs_lights && i < cs_lights+MAX_LIGHTSTYLES)
|
|
CL_SetLightstyle (i - cs_lights);
|
|
else if (i == CS_CDTRACK)
|
|
{
|
|
if (cl.refresh_prepped)
|
|
CL_PlayBackgroundTrack ();
|
|
}
|
|
else if (i == CS_MAXCLIENTS) // from R1Q2
|
|
{
|
|
if (!cl.attractloop)
|
|
cl.maxclients = atoi(cl.configstrings[CS_MAXCLIENTS]);
|
|
}
|
|
else if (i >= CS_MODELS && i < CS_MODELS+max_models)
|
|
{
|
|
if (cl.refresh_prepped)
|
|
{
|
|
cl.model_draw[i-CS_MODELS] = R_RegisterModel (cl.configstrings[i]);
|
|
if (cl.configstrings[i][0] == '*')
|
|
cl.model_clip[i-CS_MODELS] = CM_InlineModel (cl.configstrings[i]);
|
|
else
|
|
cl.model_clip[i-CS_MODELS] = NULL;
|
|
}
|
|
}
|
|
else if (i >= cs_sounds && i < cs_sounds+max_sounds) // Knightmare- was MAX_MODELS
|
|
{
|
|
if (cl.refresh_prepped)
|
|
cl.sound_precache[i-cs_sounds] = S_RegisterSound (cl.configstrings[i]);
|
|
}
|
|
else if (i >= cs_images && i < cs_images+max_images) // Knightmare- was MAX_IMAGES
|
|
{
|
|
if (cl.refresh_prepped)
|
|
cl.image_precache[i-cs_images] = R_DrawFindPic (cl.configstrings[i]);
|
|
}
|
|
else if (i >= cs_playerskins && i < cs_playerskins+MAX_CLIENTS)
|
|
{
|
|
// from R1Q2- a hack to avoid parsing non-skins from mods that overload CS_PLAYERSKINS
|
|
if ( (i-cs_playerskins) < cl.maxclients ) {
|
|
if (cl.refresh_prepped && strcmp(olds, s))
|
|
CL_ParseClientinfo (i-cs_playerskins);
|
|
}
|
|
else {
|
|
Com_DPrintf ("CL_ParseConfigString: Ignoring out-of-range playerskin %d (%s)\n", i, MakePrintable(s, 0));
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
=====================================================================
|
|
|
|
ACTION MESSAGES
|
|
|
|
=====================================================================
|
|
*/
|
|
|
|
/*
|
|
==================
|
|
CL_ParseStartSoundPacket
|
|
==================
|
|
*/
|
|
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 (&net_message);
|
|
|
|
// Knightmare- 12/23/2001
|
|
// read sound indices as bytes only if playing old demos or
|
|
// connected to server using old protocol; otherwise, read as shorts
|
|
if ( LegacyProtocol() )
|
|
sound_num = MSG_ReadByte (&net_message);
|
|
else
|
|
sound_num = MSG_ReadShort (&net_message);
|
|
//end Knightmare
|
|
|
|
if (flags & SND_VOLUME)
|
|
volume = MSG_ReadByte (&net_message) / 255.0;
|
|
else
|
|
volume = DEFAULT_SOUND_PACKET_VOLUME;
|
|
|
|
if (flags & SND_ATTENUATION)
|
|
attenuation = MSG_ReadByte (&net_message) / 64.0;
|
|
else
|
|
attenuation = DEFAULT_SOUND_PACKET_ATTENUATION;
|
|
|
|
if (flags & SND_OFFSET)
|
|
ofs = MSG_ReadByte (&net_message) / 1000.0;
|
|
else
|
|
ofs = 0;
|
|
|
|
if (flags & SND_ENT)
|
|
{ // entity reletive
|
|
channel = MSG_ReadShort(&net_message);
|
|
ent = channel>>3;
|
|
if (ent > MAX_EDICTS)
|
|
Com_Error (ERR_DROP,"CL_ParseStartSoundPacket: ent = %i", ent);
|
|
|
|
channel &= 7;
|
|
}
|
|
else
|
|
{
|
|
ent = 0;
|
|
channel = 0;
|
|
}
|
|
|
|
if (flags & SND_POS)
|
|
{ // positioned in space
|
|
MSG_ReadPos (&net_message, pos_v);
|
|
|
|
pos = pos_v;
|
|
}
|
|
else // use entity number
|
|
pos = NULL;
|
|
|
|
if (!cl.sound_precache[sound_num])
|
|
return;
|
|
|
|
S_StartSound (pos, ent, channel, cl.sound_precache[sound_num], volume, attenuation, ofs);
|
|
}
|
|
|
|
|
|
/*
|
|
==================
|
|
CL_ParsePrint
|
|
==================
|
|
*/
|
|
void CL_ParsePrint (void)
|
|
{
|
|
int i;
|
|
char *s;
|
|
|
|
i = MSG_ReadByte (&net_message);
|
|
s = MSG_ReadString (&net_message);
|
|
|
|
// Chat Ignore from R1Q2/Q2Pro
|
|
// Also filter private messages
|
|
if ( (i == PRINT_CHAT) || (strstr(s, "(private message)") != NULL) ) {
|
|
if ( CL_CheckForChatIgnore(s) )
|
|
return;
|
|
}
|
|
|
|
if (i == PRINT_CHAT)
|
|
{
|
|
// Chat Ignore from R1Q2/Q2Pro
|
|
// if ( CL_CheckForChatIgnore(s) )
|
|
// return;
|
|
|
|
S_StartLocalSound ("misc/talk.wav");
|
|
// con.ormask = 128; // made redundant by color code
|
|
|
|
// Set advertise timer for !version, from R1Q2
|
|
if ( (strstr(s, "!kmq2_version") || strstr(s, "!version")) &&
|
|
( (cls.lastAdvertiseTime == 0) || (cls.realtime > cls.lastAdvertiseTime + 300000) ) )
|
|
cls.advertiseTime = cls.realtime + (int)(random() * 1500);
|
|
|
|
Com_Printf (S_COLOR_ALT"%s", s); // add green flag
|
|
}
|
|
else
|
|
Com_Printf ("%s", s);
|
|
con.ormask = 0;
|
|
}
|
|
|
|
|
|
void SHOWNET(char *s)
|
|
{
|
|
// if (cl_shownet->value >= 2)
|
|
if (cl_shownet->integer >= 2)
|
|
Com_Printf ("%3i:%s\n", net_message.readcount-1, s);
|
|
}
|
|
|
|
|
|
/*
|
|
=====================
|
|
CL_ParseStuffText
|
|
Catches stuffed quit or error commands from the server.
|
|
Shutting down suddenly in this way can hang some SMP systems.
|
|
This simply disconnects, same effect as kicking player.
|
|
=====================
|
|
*/
|
|
/*qboolean CL_ParseStuffText (char *stufftext)
|
|
{
|
|
char *parsetext = stufftext;
|
|
|
|
// skip leading spaces
|
|
while (*parsetext == ' ') parsetext++;
|
|
if (strncmp(parsetext, "quit", 4))
|
|
{
|
|
Com_Printf("server stuffed quit command, disconnecting...\n");
|
|
CL_Disconnect ();
|
|
return false;
|
|
}
|
|
if (strncmp(parsetext, "error", 5))
|
|
{
|
|
Com_Printf("server stuffed error command, disconnecting...\n");
|
|
CL_Disconnect ();
|
|
return false;
|
|
}
|
|
return true;
|
|
}*/
|
|
|
|
|
|
/*
|
|
=====================
|
|
CL_FilterStuffText
|
|
Catches malicious stuffed commands from the server.
|
|
Simply disconnects when the stuffed command is quit
|
|
or error, same effect as kicking the player.
|
|
Uses list of malicious commands from xian.
|
|
=====================
|
|
*/
|
|
qboolean CL_FilterStuffText (char *stufftext, size_t textSize)
|
|
{
|
|
int i, quotes, stuffLen, execLen, cmdLen, textLen;
|
|
char *parsetext = stufftext;
|
|
char *s, *execname, *p;
|
|
char *bad_stuffcmds[] =
|
|
{
|
|
"sensitivity",
|
|
"unbindall",
|
|
"unbind",
|
|
"bind",
|
|
// "exec",
|
|
"kill",
|
|
"rate",
|
|
"cl_maxfps",
|
|
"r_maxfps",
|
|
"net_maxfps",
|
|
// "quit",
|
|
// "error",
|
|
0
|
|
};
|
|
|
|
stuffLen = (int)strlen(stufftext);
|
|
|
|
// nothing to filter?
|
|
if ( !stufftext || (stuffLen == 0) )
|
|
return true;
|
|
|
|
do
|
|
{
|
|
if ( ((parsetext - stufftext) >= (textSize-1)) || ((parsetext - stufftext) >= stuffLen) )
|
|
break;
|
|
|
|
// skip ;
|
|
if (*parsetext == ';') parsetext++;
|
|
|
|
if ( ((parsetext - stufftext) >= (textSize-1)) || ((parsetext - stufftext) >= stuffLen) )
|
|
break;
|
|
|
|
// skip leading spaces
|
|
while ( (*parsetext == ' ') && ((parsetext - stufftext) < (textSize-1)) && ((parsetext - stufftext) < stuffLen) )
|
|
parsetext++;
|
|
|
|
if ( ((parsetext - stufftext) >= (textSize-1)) || ((parsetext - stufftext) >= stuffLen) )
|
|
break;
|
|
|
|
// handle quit and error stuffs specially
|
|
if (!strncmp(parsetext, "quit", 4) || !strncmp(parsetext, "error", 5))
|
|
{
|
|
Com_Printf(S_COLOR_YELLOW"CL_FilterStuffText: Server stuffed 'quit' or 'error' command, disconnecting...\n");
|
|
CL_Disconnect ();
|
|
return false;
|
|
}
|
|
|
|
// don't allow stuffing of renderer cvars
|
|
if ( !strncmp(parsetext, "gl_", 3) || !strncmp(parsetext, "r_", 2) )
|
|
return false;
|
|
|
|
// the Generations mod stuffs exec g*.cfg for classes, so limit exec stuffs to .cfg files
|
|
if ( !strncmp(parsetext, "exec", 4) )
|
|
{
|
|
s = parsetext;
|
|
execname = COM_Parse (&s);
|
|
if (!s) {
|
|
Com_Printf(S_COLOR_YELLOW"CL_FilterStuffText: Server stuffed 'exec' command with no file\n");
|
|
return false; // catch case of no text after 'exec'
|
|
}
|
|
|
|
execname = COM_Parse (&s);
|
|
execLen = (int)strlen(execname);
|
|
|
|
if ( (execLen > 1) && (execname[execLen-1] == ';') ) // catch token ending with ;
|
|
execLen--;
|
|
|
|
if ( (execLen < 5) || (strncmp(execname+execLen-4, ".cfg", 4) != 0) ) {
|
|
Com_Printf(S_COLOR_YELLOW"CL_FilterStuffText: Server stuffed 'exec' command for non-cfg file\n");
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// code by xian- cycle through list of malicious commands
|
|
i = 0;
|
|
while (bad_stuffcmds[i] != NULL)
|
|
{
|
|
cmdLen = (int)strlen(bad_stuffcmds[i]);
|
|
if ( Q_strncmp(parsetext, bad_stuffcmds[i], cmdLen) == 0 )
|
|
return false;
|
|
i++;
|
|
}
|
|
|
|
// find a ; for next pass
|
|
quotes = 0;
|
|
textLen = (int)strlen(parsetext);
|
|
p = NULL;
|
|
for (i = 0; i < textLen; i++)
|
|
{
|
|
if (parsetext[i] == '"')
|
|
quotes++;
|
|
// don't break if in a quoted string
|
|
if ( !(quotes & 1) && (parsetext[i] == ';') )
|
|
p = &parsetext[i];
|
|
}
|
|
parsetext = p;
|
|
} while (parsetext != NULL);
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
// Knightmare- server-controlled fog
|
|
/*
|
|
=====================
|
|
CL_ParseFog
|
|
=====================
|
|
*/
|
|
// Fog is sent like this:
|
|
// gi.WriteByte (svc_fog); // svc_fog = 21
|
|
// gi.WriteByte (fog_enable); // 1 = on, 0 = off
|
|
// gi.WriteByte (fog_model); // 0, 1, or 2
|
|
// gi.WriteByte (fog_density); // 1-100
|
|
// gi.WriteShort (fog_near); // >0, <fog_far
|
|
// gi.WriteShort (fog_far); // >fog_near-64, <5000
|
|
// gi.WriteByte (fog_red); // 0-255
|
|
// gi.WriteByte (fog_green); // 0-255
|
|
// gi.WriteByte (fog_blue); // 0-255
|
|
// gi.unicast (player_ent, true);
|
|
|
|
void CL_ParseFog (void)
|
|
{
|
|
qboolean fogenable;
|
|
int model, density, start, end,
|
|
red, green, blue, temp;
|
|
|
|
temp = MSG_ReadByte (&net_message);
|
|
fogenable = (temp > 0) ? true:false;
|
|
model = MSG_ReadByte (&net_message);
|
|
density = MSG_ReadByte (&net_message);
|
|
start = MSG_ReadShort (&net_message);
|
|
end = MSG_ReadShort (&net_message);
|
|
red = MSG_ReadByte (&net_message);
|
|
green = MSG_ReadByte (&net_message);
|
|
blue = MSG_ReadByte (&net_message);
|
|
|
|
// R_SetFogVars (fogenable, model, density, start, end, red, green, blue);
|
|
V_SetFogInfo (fogenable, model, density, start, end, red, green, blue);
|
|
}
|
|
|
|
|
|
/*
|
|
=====================
|
|
CL_ParseServerMessage
|
|
=====================
|
|
*/
|
|
void CL_ParseServerMessage (void)
|
|
{
|
|
int cmd;
|
|
char *s;
|
|
// int i;
|
|
|
|
//
|
|
// if recording demos, copy the message out
|
|
//
|
|
// if (cl_shownet->value == 1)
|
|
if (cl_shownet->integer == 1)
|
|
Com_Printf ("%i ",net_message.cursize);
|
|
// else if (cl_shownet->value >= 2)
|
|
else if (cl_shownet->integer >= 2)
|
|
Com_Printf ("------------------\n");
|
|
|
|
|
|
//
|
|
// parse the message
|
|
//
|
|
while (1)
|
|
{
|
|
if (net_message.readcount > net_message.cursize)
|
|
{
|
|
Com_Error (ERR_DROP,"CL_ParseServerMessage: Bad server message");
|
|
break;
|
|
}
|
|
|
|
cmd = MSG_ReadByte (&net_message);
|
|
|
|
if (cmd == -1)
|
|
{
|
|
SHOWNET("END OF MESSAGE");
|
|
break;
|
|
}
|
|
|
|
// if (cl_shownet->value >= 2)
|
|
if (cl_shownet->integer >= 2)
|
|
{
|
|
if (!svc_strings[cmd])
|
|
Com_Printf ("%3i:BAD CMD %i\n", net_message.readcount-1,cmd);
|
|
else
|
|
SHOWNET(svc_strings[cmd]);
|
|
}
|
|
|
|
// other commands
|
|
switch (cmd)
|
|
{
|
|
default:
|
|
Com_Error (ERR_DROP,"CL_ParseServerMessage: Illegible server message\n");
|
|
break;
|
|
|
|
case svc_nop:
|
|
// Com_Printf ("svc_nop\n");
|
|
break;
|
|
|
|
case svc_disconnect:
|
|
Com_Error (ERR_DISCONNECT,"Server disconnected\n");
|
|
break;
|
|
|
|
case svc_reconnect:
|
|
Com_Printf ("Server disconnected, reconnecting\n");
|
|
if (cls.download) {
|
|
//ZOID, close download
|
|
fclose (cls.download);
|
|
cls.download = NULL;
|
|
}
|
|
cls.state = ca_connecting;
|
|
cls.connect_time = -99999; // CL_CheckForResend() will fire immediately
|
|
break;
|
|
|
|
case svc_print:
|
|
/* i = MSG_ReadByte (&net_message);
|
|
if (i == PRINT_CHAT)
|
|
{
|
|
S_StartLocalSound ("misc/talk.wav");
|
|
// con.ormask = 128; // Knightmare- made redundant by color code
|
|
Com_Printf (S_COLOR_ALT"%s", MSG_ReadString (&net_message)); // Knightmare- add green flag
|
|
}
|
|
else
|
|
Com_Printf ("%s", MSG_ReadString (&net_message));
|
|
con.ormask = 0;*/
|
|
CL_ParsePrint ();
|
|
break;
|
|
|
|
case svc_centerprint:
|
|
SCR_CenterPrint (MSG_ReadString (&net_message));
|
|
break;
|
|
|
|
case svc_stufftext:
|
|
s = MSG_ReadString (&net_message);
|
|
// Knightmare- filter malicious stufftext
|
|
if ( !CL_FilterStuffText(s, MSG_STRING_SIZE) ) {
|
|
Com_Printf(S_COLOR_YELLOW"CL_ParseServerMessage: Malicious stufftext from server: %s\n", s);
|
|
break;
|
|
}
|
|
Com_DPrintf ("stufftext: %s\n", s);
|
|
Cbuf_AddText (s);
|
|
break;
|
|
|
|
case svc_serverdata:
|
|
Cbuf_Execute (); // make sure any stuffed commands are done
|
|
CL_ParseServerData ();
|
|
break;
|
|
|
|
case svc_configstring:
|
|
CL_ParseConfigString ();
|
|
break;
|
|
|
|
case svc_sound:
|
|
CL_ParseStartSoundPacket();
|
|
break;
|
|
|
|
case svc_spawnbaseline:
|
|
CL_ParseBaseline ();
|
|
break;
|
|
|
|
case svc_temp_entity:
|
|
CL_ParseTEnt ();
|
|
break;
|
|
|
|
case svc_muzzleflash:
|
|
CL_ParseMuzzleFlash ();
|
|
break;
|
|
|
|
case svc_muzzleflash2:
|
|
CL_ParseMuzzleFlash2 ();
|
|
break;
|
|
|
|
case svc_download:
|
|
CL_ParseDownload ();
|
|
break;
|
|
|
|
case svc_frame:
|
|
CL_ParseFrame ();
|
|
break;
|
|
|
|
case svc_inventory:
|
|
CL_ParseInventory ();
|
|
break;
|
|
|
|
case svc_fog: // Knightmare added
|
|
CL_ParseFog ();
|
|
break;
|
|
|
|
case svc_layout:
|
|
s = MSG_ReadString (&net_message);
|
|
strncpy (cl.layout, s, sizeof(cl.layout)-1);
|
|
break;
|
|
|
|
case svc_playerinfo:
|
|
case svc_packetentities:
|
|
case svc_deltapacketentities:
|
|
Com_Error (ERR_DROP, "Out of place frame data");
|
|
break;
|
|
}
|
|
}
|
|
|
|
CL_AddNetgraph ();
|
|
|
|
//
|
|
// we don't know if it is ok to save a demo message until
|
|
// after we have parsed the frame
|
|
//
|
|
if (cls.demorecording && !cls.demowaiting)
|
|
CL_WriteDemoMessage ();
|
|
|
|
}
|