aboutsummaryrefslogtreecommitdiffstats
path: root/QW/client/cl_parse.c
diff options
context:
space:
mode:
Diffstat (limited to 'QW/client/cl_parse.c')
-rw-r--r--QW/client/cl_parse.c1383
1 files changed, 1383 insertions, 0 deletions
diff --git a/QW/client/cl_parse.c b/QW/client/cl_parse.c
new file mode 100644
index 0000000..267e523
--- /dev/null
+++ b/QW/client/cl_parse.c
@@ -0,0 +1,1383 @@
+/*
+Copyright (C) 1996-1997 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 "quakedef.h"
+
+char *svc_strings[] =
+{
+ "svc_bad",
+ "svc_nop",
+ "svc_disconnect",
+ "svc_updatestat",
+ "svc_version", // [long] server version
+ "svc_setview", // [short] entity number
+ "svc_sound", // <see code>
+ "svc_time", // [float] server time
+ "svc_print", // [string] null terminated string
+ "svc_stufftext", // [string] stuffed into client's console buffer
+ // the string should be \n terminated
+ "svc_setangle", // [vec3] set the view angle to this absolute value
+
+ "svc_serverdata", // [long] version ...
+ "svc_lightstyle", // [byte] [string]
+ "svc_updatename", // [byte] [string]
+ "svc_updatefrags", // [byte] [short]
+ "svc_clientdata", // <shortbits + data>
+ "svc_stopsound", // <see code>
+ "svc_updatecolors", // [byte] [byte]
+ "svc_particle", // [vec3] <variable>
+ "svc_damage", // [byte] impact [byte] blood [vec3] from
+
+ "svc_spawnstatic",
+ "OBSOLETE svc_spawnbinary",
+ "svc_spawnbaseline",
+
+ "svc_temp_entity", // <variable>
+ "svc_setpause",
+ "svc_signonnum",
+ "svc_centerprint",
+ "svc_killedmonster",
+ "svc_foundsecret",
+ "svc_spawnstaticsound",
+ "svc_intermission",
+ "svc_finale",
+
+ "svc_cdtrack",
+ "svc_sellscreen",
+
+ "svc_smallkick",
+ "svc_bigkick",
+
+ "svc_updateping",
+ "svc_updateentertime",
+
+ "svc_updatestatlong",
+ "svc_muzzleflash",
+ "svc_updateuserinfo",
+ "svc_download",
+ "svc_playerinfo",
+ "svc_nails",
+ "svc_choke",
+ "svc_modellist",
+ "svc_soundlist",
+ "svc_packetentities",
+ "svc_deltapacketentities",
+ "svc_maxspeed",
+ "svc_entgravity",
+
+ "svc_setinfo",
+ "svc_serverinfo",
+ "svc_updatepl",
+ "NEW PROTOCOL",
+ "NEW PROTOCOL",
+ "NEW PROTOCOL",
+ "NEW PROTOCOL",
+ "NEW PROTOCOL",
+ "NEW PROTOCOL",
+ "NEW PROTOCOL",
+ "NEW PROTOCOL",
+ "NEW PROTOCOL",
+ "NEW PROTOCOL",
+ "NEW PROTOCOL",
+ "NEW PROTOCOL",
+ "NEW PROTOCOL"
+};
+
+int oldparsecountmod;
+int parsecountmod;
+double parsecounttime;
+
+int cl_spikeindex, cl_playerindex, cl_flagindex;
+
+//=============================================================================
+
+int packet_latency[NET_TIMINGS];
+
+int CL_CalcNet (void)
+{
+ int a, i;
+ frame_t *frame;
+ int lost;
+ char st[80];
+
+ for (i=cls.netchan.outgoing_sequence-UPDATE_BACKUP+1
+ ; i <= cls.netchan.outgoing_sequence
+ ; i++)
+ {
+ frame = &cl.frames[i&UPDATE_MASK];
+ if (frame->receivedtime == -1)
+ packet_latency[i&NET_TIMINGSMASK] = 9999; // dropped
+ else if (frame->receivedtime == -2)
+ packet_latency[i&NET_TIMINGSMASK] = 10000; // choked
+ else if (frame->invalid)
+ packet_latency[i&NET_TIMINGSMASK] = 9998; // invalid delta
+ else
+ packet_latency[i&NET_TIMINGSMASK] = (frame->receivedtime - frame->senttime)*20;
+ }
+
+ lost = 0;
+ for (a=0 ; a<NET_TIMINGS ; a++)
+ {
+ i = (cls.netchan.outgoing_sequence-a) & NET_TIMINGSMASK;
+ if (packet_latency[i] == 9999)
+ lost++;
+ }
+ return lost * 100 / NET_TIMINGS;
+}
+
+//=============================================================================
+
+/*
+===============
+CL_CheckOrDownloadFile
+
+Returns true if the file exists, otherwise it attempts
+to start a download from the server.
+===============
+*/
+qboolean CL_CheckOrDownloadFile (char *filename)
+{
+ FILE *f;
+
+ if (strstr (filename, ".."))
+ {
+ Con_Printf ("Refusing to download a path with ..\n");
+ return true;
+ }
+
+ COM_FOpenFile (filename, &f);
+ if (f)
+ { // it exists, no need to download
+ fclose (f);
+ return true;
+ }
+
+ //ZOID - can't download when recording
+ if (cls.demorecording) {
+ Con_Printf("Unable to download %s in record mode.\n", cls.downloadname);
+ return true;
+ }
+ //ZOID - can't download when playback
+ if (cls.demoplayback)
+ return true;
+
+ strcpy (cls.downloadname, filename);
+ Con_Printf ("Downloading %s...\n", cls.downloadname);
+
+ // download to a temp name, and only rename
+ // to the real name when done, so if interrupted
+ // a runt file wont be left
+ COM_StripExtension (cls.downloadname, cls.downloadtempname);
+ strcat (cls.downloadtempname, ".tmp");
+
+ MSG_WriteByte (&cls.netchan.message, clc_stringcmd);
+ MSG_WriteString (&cls.netchan.message, va("download %s", cls.downloadname));
+
+ cls.downloadnumber++;
+
+ return false;
+}
+
+/*
+=================
+Model_NextDownload
+=================
+*/
+void Model_NextDownload (void)
+{
+ char *s;
+ int i;
+ extern char gamedirfile[];
+
+ if (cls.downloadnumber == 0)
+ {
+ Con_Printf ("Checking models...\n");
+ cls.downloadnumber = 1;
+ }
+
+ cls.downloadtype = dl_model;
+ for (
+ ; cl.model_name[cls.downloadnumber][0]
+ ; cls.downloadnumber++)
+ {
+ s = cl.model_name[cls.downloadnumber];
+ if (s[0] == '*')
+ continue; // inline brush model
+ if (!CL_CheckOrDownloadFile(s))
+ return; // started a download
+ }
+
+ for (i=1 ; i<MAX_MODELS ; i++)
+ {
+ if (!cl.model_name[i][0])
+ break;
+
+ cl.model_precache[i] = Mod_ForName (cl.model_name[i], false);
+
+ if (!cl.model_precache[i])
+ {
+ Con_Printf ("\nThe required model file '%s' could not be found or downloaded.\n\n"
+ , cl.model_name[i]);
+ Con_Printf ("You may need to download or purchase a %s client "
+ "pack in order to play on this server.\n\n", gamedirfile);
+ CL_Disconnect ();
+ return;
+ }
+ }
+
+ // all done
+ cl.worldmodel = cl.model_precache[1];
+ R_NewMap ();
+ Hunk_Check (); // make sure nothing is hurt
+
+ // done with modellist, request first of static signon messages
+ MSG_WriteByte (&cls.netchan.message, clc_stringcmd);
+// MSG_WriteString (&cls.netchan.message, va("prespawn %i 0 %i", cl.servercount, cl.worldmodel->checksum2));
+ MSG_WriteString (&cls.netchan.message, va(prespawn_name, cl.servercount, cl.worldmodel->checksum2));
+}
+
+/*
+=================
+Sound_NextDownload
+=================
+*/
+void Sound_NextDownload (void)
+{
+ char *s;
+ int i;
+
+ if (cls.downloadnumber == 0)
+ {
+ Con_Printf ("Checking sounds...\n");
+ cls.downloadnumber = 1;
+ }
+
+ cls.downloadtype = dl_sound;
+ for (
+ ; cl.sound_name[cls.downloadnumber][0]
+ ; cls.downloadnumber++)
+ {
+ s = cl.sound_name[cls.downloadnumber];
+ if (!CL_CheckOrDownloadFile(va("sound/%s",s)))
+ return; // started a download
+ }
+
+ for (i=1 ; i<MAX_SOUNDS ; i++)
+ {
+ if (!cl.sound_name[i][0])
+ break;
+ cl.sound_precache[i] = S_PrecacheSound (cl.sound_name[i]);
+ }
+
+ // done with sounds, request models now
+ memset (cl.model_precache, 0, sizeof(cl.model_precache));
+ cl_playerindex = -1;
+ cl_spikeindex = -1;
+ cl_flagindex = -1;
+ MSG_WriteByte (&cls.netchan.message, clc_stringcmd);
+// MSG_WriteString (&cls.netchan.message, va("modellist %i 0", cl.servercount));
+ MSG_WriteString (&cls.netchan.message, va(modellist_name, cl.servercount, 0));
+}
+
+
+/*
+======================
+CL_RequestNextDownload
+======================
+*/
+void CL_RequestNextDownload (void)
+{
+ switch (cls.downloadtype)
+ {
+ case dl_single:
+ break;
+ case dl_skin:
+ Skin_NextDownload ();
+ break;
+ case dl_model:
+ Model_NextDownload ();
+ break;
+ case dl_sound:
+ Sound_NextDownload ();
+ break;
+ case dl_none:
+ default:
+ Con_DPrintf("Unknown download type.\n");
+ }
+}
+
+/*
+=====================
+CL_ParseDownload
+
+A download message has been received from the server
+=====================
+*/
+void CL_ParseDownload (void)
+{
+ int size, percent;
+ byte name[1024];
+ int r;
+
+
+ // read the data
+ size = MSG_ReadShort ();
+ percent = MSG_ReadByte ();
+
+ if (cls.demoplayback) {
+ if (size > 0)
+ msg_readcount += size;
+ return; // not in demo playback
+ }
+
+ if (size == -1)
+ {
+ Con_Printf ("File not found.\n");
+ if (cls.download)
+ {
+ Con_Printf ("cls.download shouldn't have been set\n");
+ fclose (cls.download);
+ cls.download = NULL;
+ }
+ CL_RequestNextDownload ();
+ return;
+ }
+
+ // open the file if not opened yet
+ if (!cls.download)
+ {
+ if (strncmp(cls.downloadtempname,"skins/",6))
+ sprintf (name, "%s/%s", com_gamedir, cls.downloadtempname);
+ else
+ sprintf (name, "qw/%s", cls.downloadtempname);
+
+ COM_CreatePath (name);
+
+ cls.download = fopen (name, "wb");
+ if (!cls.download)
+ {
+ msg_readcount += size;
+ Con_Printf ("Failed to open %s\n", cls.downloadtempname);
+ CL_RequestNextDownload ();
+ return;
+ }
+ }
+
+ fwrite (net_message.data + msg_readcount, 1, size, cls.download);
+ msg_readcount += size;
+
+ if (percent != 100)
+ {
+// change display routines by zoid
+ // request next block
+#if 0
+ Con_Printf (".");
+ if (10*(percent/10) != cls.downloadpercent)
+ {
+ cls.downloadpercent = 10*(percent/10);
+ Con_Printf ("%i%%", cls.downloadpercent);
+ }
+#endif
+ cls.downloadpercent = percent;
+
+ MSG_WriteByte (&cls.netchan.message, clc_stringcmd);
+ SZ_Print (&cls.netchan.message, "nextdl");
+ }
+ else
+ {
+ char oldn[MAX_OSPATH];
+ char newn[MAX_OSPATH];
+
+#if 0
+ Con_Printf ("100%%\n");
+#endif
+
+ fclose (cls.download);
+
+ // rename the temp file to it's final name
+ if (strcmp(cls.downloadtempname, cls.downloadname)) {
+ if (strncmp(cls.downloadtempname,"skins/",6)) {
+ sprintf (oldn, "%s/%s", com_gamedir, cls.downloadtempname);
+ sprintf (newn, "%s/%s", com_gamedir, cls.downloadname);
+ } else {
+ sprintf (oldn, "qw/%s", cls.downloadtempname);
+ sprintf (newn, "qw/%s", cls.downloadname);
+ }
+ r = rename (oldn, newn);
+ if (r)
+ Con_Printf ("failed to rename.\n");
+ }
+
+ cls.download = NULL;
+ cls.downloadpercent = 0;
+
+ // get another file if needed
+
+ CL_RequestNextDownload ();
+ }
+}
+
+static byte *upload_data;
+static int upload_pos;
+static int upload_size;
+
+void CL_NextUpload(void)
+{
+ byte buffer[1024];
+ int r;
+ int percent;
+ int size;
+
+ if (!upload_data)
+ return;
+
+ r = upload_size - upload_pos;
+ if (r > 768)
+ r = 768;
+ memcpy(buffer, upload_data + upload_pos, r);
+ MSG_WriteByte (&cls.netchan.message, clc_upload);
+ MSG_WriteShort (&cls.netchan.message, r);
+
+ upload_pos += r;
+ size = upload_size;
+ if (!size)
+ size = 1;
+ percent = upload_pos*100/size;
+ MSG_WriteByte (&cls.netchan.message, percent);
+ SZ_Write (&cls.netchan.message, buffer, r);
+
+Con_DPrintf ("UPLOAD: %6d: %d written\n", upload_pos - r, r);
+
+ if (upload_pos != upload_size)
+ return;
+
+ Con_Printf ("Upload completed\n");
+
+ free(upload_data);
+ upload_data = 0;
+ upload_pos = upload_size = 0;
+}
+
+void CL_StartUpload (byte *data, int size)
+{
+ if (cls.state < ca_onserver)
+ return; // gotta be connected
+
+ // override
+ if (upload_data)
+ free(upload_data);
+
+Con_DPrintf("Upload starting of %d...\n", size);
+
+ upload_data = malloc(size);
+ memcpy(upload_data, data, size);
+ upload_size = size;
+ upload_pos = 0;
+
+ CL_NextUpload();
+}
+
+qboolean CL_IsUploading(void)
+{
+ if (upload_data)
+ return true;
+ return false;
+}
+
+void CL_StopUpload(void)
+{
+ if (upload_data)
+ free(upload_data);
+ upload_data = NULL;
+}
+
+/*
+=====================================================================
+
+ SERVER CONNECTING MESSAGES
+
+=====================================================================
+*/
+
+/*
+==================
+CL_ParseServerData
+==================
+*/
+void CL_ParseServerData (void)
+{
+ char *str;
+ FILE *f;
+ char fn[MAX_OSPATH];
+ qboolean cflag = false;
+ extern char gamedirfile[MAX_OSPATH];
+ int protover;
+
+ Con_DPrintf ("Serverdata packet received.\n");
+//
+// wipe the client_state_t struct
+//
+ CL_ClearState ();
+
+// parse protocol version number
+// allow 2.2 and 2.29 demos to play
+ protover = MSG_ReadLong ();
+ if (protover != PROTOCOL_VERSION &&
+ !(cls.demoplayback && (protover == 26 || protover == 27 || protover == 28)))
+ Host_EndGame ("Server returned version %i, not %i\nYou probably need to upgrade.\nCheck http://www.quakeworld.net/", protover, PROTOCOL_VERSION);
+
+ cl.servercount = MSG_ReadLong ();
+
+ // game directory
+ str = MSG_ReadString ();
+
+ if (stricmp(gamedirfile, str)) {
+ // save current config
+ Host_WriteConfiguration ();
+ cflag = true;
+ }
+
+ COM_Gamedir(str);
+
+ //ZOID--run the autoexec.cfg in the gamedir
+ //if it exists
+ if (cflag) {
+ sprintf(fn, "%s/%s", com_gamedir, "config.cfg");
+ if ((f = fopen(fn, "r")) != NULL) {
+ fclose(f);
+ Cbuf_AddText ("cl_warncmd 0\n");
+ Cbuf_AddText("exec config.cfg\n");
+ Cbuf_AddText("exec frontend.cfg\n");
+ Cbuf_AddText ("cl_warncmd 1\n");
+ }
+ }
+
+ // parse player slot, high bit means spectator
+ cl.playernum = MSG_ReadByte ();
+ if (cl.playernum & 128)
+ {
+ cl.spectator = true;
+ cl.playernum &= ~128;
+ }
+
+ // get the full level name
+ str = MSG_ReadString ();
+ strncpy (cl.levelname, str, sizeof(cl.levelname)-1);
+
+ // get the movevars
+ movevars.gravity = MSG_ReadFloat();
+ movevars.stopspeed = MSG_ReadFloat();
+ movevars.maxspeed = MSG_ReadFloat();
+ movevars.spectatormaxspeed = MSG_ReadFloat();
+ movevars.accelerate = MSG_ReadFloat();
+ movevars.airaccelerate = MSG_ReadFloat();
+ movevars.wateraccelerate = MSG_ReadFloat();
+ movevars.friction = MSG_ReadFloat();
+ movevars.waterfriction = MSG_ReadFloat();
+ movevars.entgravity = MSG_ReadFloat();
+
+ // 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\36\37\n\n");
+ Con_Printf ("%c%s\n", 2, str);
+
+ // ask for the sound list next
+ memset(cl.sound_name, 0, sizeof(cl.sound_name));
+ MSG_WriteByte (&cls.netchan.message, clc_stringcmd);
+// MSG_WriteString (&cls.netchan.message, va("soundlist %i 0", cl.servercount));
+ MSG_WriteString (&cls.netchan.message, va(soundlist_name, cl.servercount, 0));
+
+ // now waiting for downloads, etc
+ cls.state = ca_onserver;
+}
+
+/*
+==================
+CL_ParseSoundlist
+==================
+*/
+void CL_ParseSoundlist (void)
+{
+ int numsounds;
+ char *str;
+ int n;
+
+// precache sounds
+// memset (cl.sound_precache, 0, sizeof(cl.sound_precache));
+
+ numsounds = MSG_ReadByte();
+
+ for (;;) {
+ str = MSG_ReadString ();
+ if (!str[0])
+ break;
+ numsounds++;
+ if (numsounds == MAX_SOUNDS)
+ Host_EndGame ("Server sent too many sound_precache");
+ strcpy (cl.sound_name[numsounds], str);
+ }
+
+ n = MSG_ReadByte();
+
+ if (n) {
+ MSG_WriteByte (&cls.netchan.message, clc_stringcmd);
+// MSG_WriteString (&cls.netchan.message, va("soundlist %i %i", cl.servercount, n));
+ MSG_WriteString (&cls.netchan.message, va(soundlist_name, cl.servercount, n));
+ return;
+ }
+
+ cls.downloadnumber = 0;
+ cls.downloadtype = dl_sound;
+ Sound_NextDownload ();
+}
+
+/*
+==================
+CL_ParseModellist
+==================
+*/
+void CL_ParseModellist (void)
+{
+ int nummodels;
+ char *str;
+ int n;
+
+// precache models and note certain default indexes
+ nummodels = MSG_ReadByte();
+
+ for (;;)
+ {
+ str = MSG_ReadString ();
+ if (!str[0])
+ break;
+ nummodels++;
+ if (nummodels==MAX_MODELS)
+ Host_EndGame ("Server sent too many model_precache");
+ strcpy (cl.model_name[nummodels], str);
+
+ if (!strcmp(cl.model_name[nummodels],"progs/spike.mdl"))
+ cl_spikeindex = nummodels;
+ if (!strcmp(cl.model_name[nummodels],"progs/player.mdl"))
+ cl_playerindex = nummodels;
+ if (!strcmp(cl.model_name[nummodels],"progs/flag.mdl"))
+ cl_flagindex = nummodels;
+ }
+
+ n = MSG_ReadByte();
+
+ if (n) {
+ MSG_WriteByte (&cls.netchan.message, clc_stringcmd);
+// MSG_WriteString (&cls.netchan.message, va("modellist %i %i", cl.servercount, n));
+ MSG_WriteString (&cls.netchan.message, va(modellist_name, cl.servercount, n));
+ return;
+ }
+
+ cls.downloadnumber = 0;
+ cls.downloadtype = dl_model;
+ Model_NextDownload ();
+}
+
+/*
+==================
+CL_ParseBaseline
+==================
+*/
+void CL_ParseBaseline (entity_state_t *es)
+{
+ int i;
+
+ es->modelindex = MSG_ReadByte ();
+ es->frame = MSG_ReadByte ();
+ es->colormap = MSG_ReadByte();
+ es->skinnum = MSG_ReadByte();
+ for (i=0 ; i<3 ; i++)
+ {
+ es->origin[i] = MSG_ReadCoord ();
+ es->angles[i] = MSG_ReadAngle ();
+ }
+}
+
+
+
+/*
+=====================
+CL_ParseStatic
+
+Static entities are non-interactive world objects
+like torches
+=====================
+*/
+void CL_ParseStatic (void)
+{
+ entity_t *ent;
+ int i;
+ entity_state_t es;
+
+ CL_ParseBaseline (&es);
+
+ i = cl.num_statics;
+ if (i >= MAX_STATIC_ENTITIES)
+ Host_EndGame ("Too many static entities");
+ ent = &cl_static_entities[i];
+ cl.num_statics++;
+
+// copy it to the current state
+ ent->model = cl.model_precache[es.modelindex];
+ ent->frame = es.frame;
+ ent->colormap = vid.colormap;
+ ent->skinnum = es.skinnum;
+
+ VectorCopy (es.origin, ent->origin);
+ VectorCopy (es.angles, ent->angles);
+
+ R_AddEfrags (ent);
+}
+
+/*
+===================
+CL_ParseStaticSound
+===================
+*/
+void CL_ParseStaticSound (void)
+{
+ vec3_t org;
+ int sound_num, vol, atten;
+ int i;
+
+ for (i=0 ; i<3 ; i++)
+ org[i] = MSG_ReadCoord ();
+ sound_num = MSG_ReadByte ();
+ vol = MSG_ReadByte ();
+ atten = MSG_ReadByte ();
+
+ S_StaticSound (cl.sound_precache[sound_num], org, vol, atten);
+}
+
+
+
+/*
+=====================================================================
+
+ACTION MESSAGES
+
+=====================================================================
+*/
+
+/*
+==================
+CL_ParseStartSoundPacket
+==================
+*/
+void CL_ParseStartSoundPacket(void)
+{
+ vec3_t pos;
+ int channel, ent;
+ int sound_num;
+ int volume;
+ float attenuation;
+ int i;
+
+ channel = MSG_ReadShort();
+
+ if (channel & SND_VOLUME)
+ volume = MSG_ReadByte ();
+ else
+ volume = DEFAULT_SOUND_PACKET_VOLUME;
+
+ if (channel & SND_ATTENUATION)
+ attenuation = MSG_ReadByte () / 64.0;
+ else
+ attenuation = DEFAULT_SOUND_PACKET_ATTENUATION;
+
+ sound_num = MSG_ReadByte ();
+
+ for (i=0 ; i<3 ; i++)
+ pos[i] = MSG_ReadCoord ();
+
+ ent = (channel>>3)&1023;
+ channel &= 7;
+
+ if (ent > MAX_EDICTS)
+ Host_EndGame ("CL_ParseStartSoundPacket: ent = %i", ent);
+
+ S_StartSound (ent, channel, cl.sound_precache[sound_num], pos, volume/255.0, attenuation);
+}
+
+
+/*
+==================
+CL_ParseClientdata
+
+Server information pertaining to this client only, sent every frame
+==================
+*/
+void CL_ParseClientdata (void)
+{
+ int i;
+ float latency;
+ frame_t *frame;
+
+// calculate simulated time of message
+ oldparsecountmod = parsecountmod;
+
+ i = cls.netchan.incoming_acknowledged;
+ cl.parsecount = i;
+ i &= UPDATE_MASK;
+ parsecountmod = i;
+ frame = &cl.frames[i];
+ parsecounttime = cl.frames[i].senttime;
+
+ frame->receivedtime = realtime;
+
+// calculate latency
+ latency = frame->receivedtime - frame->senttime;
+
+ if (latency < 0 || latency > 1.0)
+ {
+// Con_Printf ("Odd latency: %5.2f\n", latency);
+ }
+ else
+ {
+ // drift the average latency towards the observed latency
+ if (latency < cls.latency)
+ cls.latency = latency;
+ else
+ cls.latency += 0.001; // drift up, so correction are needed
+ }
+}
+
+/*
+=====================
+CL_NewTranslation
+=====================
+*/
+void CL_NewTranslation (int slot)
+{
+#ifdef GLQUAKE
+ if (slot > MAX_CLIENTS)
+ Sys_Error ("CL_NewTranslation: slot > MAX_CLIENTS");
+
+ R_TranslatePlayerSkin(slot);
+#else
+
+ int i, j;
+ int top, bottom;
+ byte *dest, *source;
+ player_info_t *player;
+ char s[512];
+
+ if (slot > MAX_CLIENTS)
+ Sys_Error ("CL_NewTranslation: slot > MAX_CLIENTS");
+
+ player = &cl.players[slot];
+
+ strcpy(s, Info_ValueForKey(player->userinfo, "skin"));
+ COM_StripExtension(s, s);
+ if (player->skin && !stricmp(s, player->skin->name))
+ player->skin = NULL;
+
+ if (player->_topcolor != player->topcolor ||
+ player->_bottomcolor != player->bottomcolor || !player->skin) {
+ player->_topcolor = player->topcolor;
+ player->_bottomcolor = player->bottomcolor;
+
+ dest = player->translations;
+ source = vid.colormap;
+ memcpy (dest, vid.colormap, sizeof(player->translations));
+ top = player->topcolor;
+ if (top > 13 || top < 0)
+ top = 13;
+ top *= 16;
+ bottom = player->bottomcolor;
+ if (bottom > 13 || bottom < 0)
+ bottom = 13;
+ bottom *= 16;
+
+ for (i=0 ; i<VID_GRADES ; i++, dest += 256, source+=256)
+ {
+ if (top < 128) // the artists made some backwards ranges. sigh.
+ memcpy (dest + TOP_RANGE, source + top, 16);
+ else
+ for (j=0 ; j<16 ; j++)
+ dest[TOP_RANGE+j] = source[top+15-j];
+
+ if (bottom < 128)
+ memcpy (dest + BOTTOM_RANGE, source + bottom, 16);
+ else
+ for (j=0 ; j<16 ; j++)
+ dest[BOTTOM_RANGE+j] = source[bottom+15-j];
+ }
+ }
+#endif
+}
+
+/*
+==============
+CL_UpdateUserinfo
+==============
+*/
+void CL_ProcessUserInfo (int slot, player_info_t *player)
+{
+ strncpy (player->name, Info_ValueForKey (player->userinfo, "name"), sizeof(player->name)-1);
+ player->topcolor = atoi(Info_ValueForKey (player->userinfo, "topcolor"));
+ player->bottomcolor = atoi(Info_ValueForKey (player->userinfo, "bottomcolor"));
+ if (Info_ValueForKey (player->userinfo, "*spectator")[0])
+ player->spectator = true;
+ else
+ player->spectator = false;
+
+ if (cls.state == ca_active)
+ Skin_Find (player);
+
+ Sbar_Changed ();
+ CL_NewTranslation (slot);
+}
+
+/*
+==============
+CL_UpdateUserinfo
+==============
+*/
+void CL_UpdateUserinfo (void)
+{
+ int slot;
+ player_info_t *player;
+
+ slot = MSG_ReadByte ();
+ if (slot >= MAX_CLIENTS)
+ Host_EndGame ("CL_ParseServerMessage: svc_updateuserinfo > MAX_SCOREBOARD");
+
+ player = &cl.players[slot];
+ player->userid = MSG_ReadLong ();
+ strncpy (player->userinfo, MSG_ReadString(), sizeof(player->userinfo)-1);
+
+ CL_ProcessUserInfo (slot, player);
+}
+
+/*
+==============
+CL_SetInfo
+==============
+*/
+void CL_SetInfo (void)
+{
+ int slot;
+ player_info_t *player;
+ char key[MAX_MSGLEN];
+ char value[MAX_MSGLEN];
+
+ slot = MSG_ReadByte ();
+ if (slot >= MAX_CLIENTS)
+ Host_EndGame ("CL_ParseServerMessage: svc_setinfo > MAX_SCOREBOARD");
+
+ player = &cl.players[slot];
+
+ strncpy (key, MSG_ReadString(), sizeof(key) - 1);
+ key[sizeof(key) - 1] = 0;
+ strncpy (value, MSG_ReadString(), sizeof(value) - 1);
+ key[sizeof(value) - 1] = 0;
+
+ Con_DPrintf("SETINFO %s: %s=%s\n", player->name, key, value);
+
+ Info_SetValueForKey (player->userinfo, key, value, MAX_INFO_STRING);
+
+ CL_ProcessUserInfo (slot, player);
+}
+
+/*
+==============
+CL_ServerInfo
+==============
+*/
+void CL_ServerInfo (void)
+{
+ int slot;
+ player_info_t *player;
+ char key[MAX_MSGLEN];
+ char value[MAX_MSGLEN];
+
+ strncpy (key, MSG_ReadString(), sizeof(key) - 1);
+ key[sizeof(key) - 1] = 0;
+ strncpy (value, MSG_ReadString(), sizeof(value) - 1);
+ key[sizeof(value) - 1] = 0;
+
+ Con_DPrintf("SERVERINFO: %s=%s\n", key, value);
+
+ Info_SetValueForKey (cl.serverinfo, key, value, MAX_SERVERINFO_STRING);
+}
+
+/*
+=====================
+CL_SetStat
+=====================
+*/
+void CL_SetStat (int stat, int value)
+{
+ int j;
+ if (stat < 0 || stat >= MAX_CL_STATS)
+ Sys_Error ("CL_SetStat: %i is invalid", stat);
+
+ Sbar_Changed ();
+
+ if (stat == STAT_ITEMS)
+ { // set flash times
+ Sbar_Changed ();
+ for (j=0 ; j<32 ; j++)
+ if ( (value & (1<<j)) && !(cl.stats[stat] & (1<<j)))
+ cl.item_gettime[j] = cl.time;
+ }
+
+ cl.stats[stat] = value;
+}
+
+/*
+==============
+CL_MuzzleFlash
+==============
+*/
+void CL_MuzzleFlash (void)
+{
+ vec3_t fv, rv, uv;
+ dlight_t *dl;
+ int i;
+ player_state_t *pl;
+
+ i = MSG_ReadShort ();
+
+ if ((unsigned)(i-1) >= MAX_CLIENTS)
+ return;
+
+#ifdef GLQUAKE
+ // don't draw our own muzzle flash in gl if flashblending
+ if (i-1 == cl.playernum && gl_flashblend.value)
+ return;
+#endif
+
+ pl = &cl.frames[parsecountmod].playerstate[i-1];
+
+ dl = CL_AllocDlight (i);
+ VectorCopy (pl->origin, dl->origin);
+ AngleVectors (pl->viewangles, fv, rv, uv);
+
+ VectorMA (dl->origin, 18, fv, dl->origin);
+ dl->radius = 200 + (rand()&31);
+ dl->minlight = 32;
+ dl->die = cl.time + 0.1;
+ dl->color[0] = 0.2;
+ dl->color[1] = 0.1;
+ dl->color[2] = 0.05;
+ dl->color[3] = 0.7;
+}
+
+
+#define SHOWNET(x) if(cl_shownet.value==2)Con_Printf ("%3i:%s\n", msg_readcount-1, x);
+/*
+=====================
+CL_ParseServerMessage
+=====================
+*/
+int received_framecount;
+void CL_ParseServerMessage (void)
+{
+ int cmd;
+ char *s;
+ int i, j;
+
+ received_framecount = host_framecount;
+ cl.last_servermessage = realtime;
+ CL_ClearProjectiles ();
+
+//
+// if recording demos, copy the message out
+//
+ if (cl_shownet.value == 1)
+ Con_Printf ("%i ",net_message.cursize);
+ else if (cl_shownet.value == 2)
+ Con_Printf ("------------------\n");
+
+
+ CL_ParseClientdata ();
+
+//
+// parse the message
+//
+ while (1)
+ {
+ if (msg_badread)
+ {
+ Host_EndGame ("CL_ParseServerMessage: Bad server message");
+ break;
+ }
+
+ cmd = MSG_ReadByte ();
+
+ if (cmd == -1)
+ {
+ msg_readcount++; // so the EOM showner has the right value
+ SHOWNET("END OF MESSAGE");
+ break;
+ }
+
+ SHOWNET(svc_strings[cmd]);
+
+ // other commands
+ switch (cmd)
+ {
+ default:
+ Host_EndGame ("CL_ParseServerMessage: Illegible server message");
+ break;
+
+ case svc_nop:
+// Con_Printf ("svc_nop\n");
+ break;
+
+ case svc_disconnect:
+ if (cls.state == ca_connected)
+ Host_EndGame ("Server disconnected\n"
+ "Server version may not be compatible");
+ else
+ Host_EndGame ("Server disconnected");
+ break;
+
+ case svc_print:
+ i = MSG_ReadByte ();
+ if (i == PRINT_CHAT)
+ {
+ S_LocalSound ("misc/talk.wav");
+ con_ormask = 128;
+ }
+ Con_Printf ("%s", MSG_ReadString ());
+ con_ormask = 0;
+ break;
+
+ case svc_centerprint:
+ SCR_CenterPrint (MSG_ReadString ());
+ break;
+
+ case svc_stufftext:
+ s = MSG_ReadString ();
+ Con_DPrintf ("stufftext: %s\n", s);
+ Cbuf_AddText (s);
+ break;
+
+ case svc_damage:
+ V_ParseDamage ();
+ break;
+
+ case svc_serverdata:
+ Cbuf_Execute (); // make sure any stuffed commands are done
+ CL_ParseServerData ();
+ vid.recalc_refdef = true; // leave full screen intermission
+ break;
+
+ case svc_setangle:
+ for (i=0 ; i<3 ; i++)
+ cl.viewangles[i] = MSG_ReadAngle ();
+// cl.viewangles[PITCH] = cl.viewangles[ROLL] = 0;
+ break;
+
+ case svc_lightstyle:
+ i = MSG_ReadByte ();
+ if (i >= MAX_LIGHTSTYLES)
+ Sys_Error ("svc_lightstyle > MAX_LIGHTSTYLES");
+ Q_strcpy (cl_lightstyle[i].map, MSG_ReadString());
+ cl_lightstyle[i].length = Q_strlen(cl_lightstyle[i].map);
+ break;
+
+ case svc_sound:
+ CL_ParseStartSoundPacket();
+ break;
+
+ case svc_stopsound:
+ i = MSG_ReadShort();
+ S_StopSound(i>>3, i&7);
+ break;
+
+ case svc_updatefrags:
+ Sbar_Changed ();
+ i = MSG_ReadByte ();
+ if (i >= MAX_CLIENTS)
+ Host_EndGame ("CL_ParseServerMessage: svc_updatefrags > MAX_SCOREBOARD");
+ cl.players[i].frags = MSG_ReadShort ();
+ break;
+
+ case svc_updateping:
+ i = MSG_ReadByte ();
+ if (i >= MAX_CLIENTS)
+ Host_EndGame ("CL_ParseServerMessage: svc_updateping > MAX_SCOREBOARD");
+ cl.players[i].ping = MSG_ReadShort ();
+ break;
+
+ case svc_updatepl:
+ i = MSG_ReadByte ();
+ if (i >= MAX_CLIENTS)
+ Host_EndGame ("CL_ParseServerMessage: svc_updatepl > MAX_SCOREBOARD");
+ cl.players[i].pl = MSG_ReadByte ();
+ break;
+
+ case svc_updateentertime:
+ // time is sent over as seconds ago
+ i = MSG_ReadByte ();
+ if (i >= MAX_CLIENTS)
+ Host_EndGame ("CL_ParseServerMessage: svc_updateentertime > MAX_SCOREBOARD");
+ cl.players[i].entertime = realtime - MSG_ReadFloat ();
+ break;
+
+ case svc_spawnbaseline:
+ i = MSG_ReadShort ();
+ CL_ParseBaseline (&cl_baselines[i]);
+ break;
+ case svc_spawnstatic:
+ CL_ParseStatic ();
+ break;
+ case svc_temp_entity:
+ CL_ParseTEnt ();
+ break;
+
+ case svc_killedmonster:
+ cl.stats[STAT_MONSTERS]++;
+ break;
+
+ case svc_foundsecret:
+ cl.stats[STAT_SECRETS]++;
+ break;
+
+ case svc_updatestat:
+ i = MSG_ReadByte ();
+ j = MSG_ReadByte ();
+ CL_SetStat (i, j);
+ break;
+ case svc_updatestatlong:
+ i = MSG_ReadByte ();
+ j = MSG_ReadLong ();
+ CL_SetStat (i, j);
+ break;
+
+ case svc_spawnstaticsound:
+ CL_ParseStaticSound ();
+ break;
+
+ case svc_cdtrack:
+ cl.cdtrack = MSG_ReadByte ();
+ CDAudio_Play ((byte)cl.cdtrack, true);
+ break;
+
+ case svc_intermission:
+ cl.intermission = 1;
+ cl.completed_time = realtime;
+ vid.recalc_refdef = true; // go to full screen
+ for (i=0 ; i<3 ; i++)
+ cl.simorg[i] = MSG_ReadCoord ();
+ for (i=0 ; i<3 ; i++)
+ cl.simangles[i] = MSG_ReadAngle ();
+ VectorCopy (vec3_origin, cl.simvel);
+ break;
+
+ case svc_finale:
+ cl.intermission = 2;
+ cl.completed_time = realtime;
+ vid.recalc_refdef = true; // go to full screen
+ SCR_CenterPrint (MSG_ReadString ());
+ break;
+
+ case svc_sellscreen:
+ Cmd_ExecuteString ("help");
+ break;
+
+ case svc_smallkick:
+ cl.punchangle = -2;
+ break;
+ case svc_bigkick:
+ cl.punchangle = -4;
+ break;
+
+ case svc_muzzleflash:
+ CL_MuzzleFlash ();
+ break;
+
+ case svc_updateuserinfo:
+ CL_UpdateUserinfo ();
+ break;
+
+ case svc_setinfo:
+ CL_SetInfo ();
+ break;
+
+ case svc_serverinfo:
+ CL_ServerInfo ();
+ break;
+
+ case svc_download:
+ CL_ParseDownload ();
+ break;
+
+ case svc_playerinfo:
+ CL_ParsePlayerinfo ();
+ break;
+
+ case svc_nails:
+ CL_ParseProjectiles ();
+ break;
+
+ case svc_chokecount: // some preceding packets were choked
+ i = MSG_ReadByte ();
+ for (j=0 ; j<i ; j++)
+ cl.frames[ (cls.netchan.incoming_acknowledged-1-j)&UPDATE_MASK ].receivedtime = -2;
+ break;
+
+ case svc_modellist:
+ CL_ParseModellist ();
+ break;
+
+ case svc_soundlist:
+ CL_ParseSoundlist ();
+ break;
+
+ case svc_packetentities:
+ CL_ParsePacketEntities (false);
+ break;
+
+ case svc_deltapacketentities:
+ CL_ParsePacketEntities (true);
+ break;
+
+ case svc_maxspeed :
+ movevars.maxspeed = MSG_ReadFloat();
+ break;
+
+ case svc_entgravity :
+ movevars.entgravity = MSG_ReadFloat();
+ break;
+
+ case svc_setpause:
+ cl.paused = MSG_ReadByte ();
+ if (cl.paused)
+ CDAudio_Pause ();
+ else
+ CDAudio_Resume ();
+ break;
+
+ }
+ }
+
+ CL_SetSolidEntities ();
+}
+
+