DP_SND_SOUND7_WIP1
[xonotic/darkplaces.git] / cl_parse.c
1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13 See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18
19 */
20 // cl_parse.c  -- parse a message received from the server
21
22 #include "quakedef.h"
23 #include "cdaudio.h"
24 #include "cl_collision.h"
25 #include "csprogs.h"
26 #include "libcurl.h"
27 #include "utf8lib.h"
28 #include "menu.h"
29 #include "cl_video.h"
30
31 const char *svc_strings[128] =
32 {
33         "svc_bad",
34         "svc_nop",
35         "svc_disconnect",
36         "svc_updatestat",
37         "svc_version",          // [int] server version
38         "svc_setview",          // [short] entity number
39         "svc_sound",                    // <see code>
40         "svc_time",                     // [float] server time
41         "svc_print",                    // [string] null terminated string
42         "svc_stufftext",                // [string] stuffed into client's console buffer
43                                                 // the string should be \n terminated
44         "svc_setangle",         // [vec3] set the view angle to this absolute value
45
46         "svc_serverinfo",               // [int] version
47                                                 // [string] signon string
48                                                 // [string]..[0]model cache [string]...[0]sounds cache
49                                                 // [string]..[0]item cache
50         "svc_lightstyle",               // [byte] [string]
51         "svc_updatename",               // [byte] [string]
52         "svc_updatefrags",      // [byte] [short]
53         "svc_clientdata",               // <shortbits + data>
54         "svc_stopsound",                // <see code>
55         "svc_updatecolors",     // [byte] [byte]
56         "svc_particle",         // [vec3] <variable>
57         "svc_damage",                   // [byte] impact [byte] blood [vec3] from
58
59         "svc_spawnstatic",
60         "OBSOLETE svc_spawnbinary",
61         "svc_spawnbaseline",
62
63         "svc_temp_entity",              // <variable>
64         "svc_setpause",
65         "svc_signonnum",
66         "svc_centerprint",
67         "svc_killedmonster",
68         "svc_foundsecret",
69         "svc_spawnstaticsound",
70         "svc_intermission",
71         "svc_finale",                   // [string] music [string] text
72         "svc_cdtrack",                  // [byte] track [byte] looptrack
73         "svc_sellscreen",
74         "svc_cutscene",
75         "svc_showlmp",  // [string] iconlabel [string] lmpfile [short] x [short] y
76         "svc_hidelmp",  // [string] iconlabel
77         "svc_skybox", // [string] skyname
78         "", // 38
79         "", // 39
80         "", // 40
81         "", // 41
82         "", // 42
83         "", // 43
84         "", // 44
85         "", // 45
86         "", // 46
87         "", // 47
88         "", // 48
89         "", // 49
90         "svc_downloaddata", //                          50              // [int] start [short] size [variable length] data
91         "svc_updatestatubyte", //                       51              // [byte] stat [byte] value
92         "svc_effect", //                        52              // [vector] org [byte] modelindex [byte] startframe [byte] framecount [byte] framerate
93         "svc_effect2", //                       53              // [vector] org [short] modelindex [short] startframe [byte] framecount [byte] framerate
94         "svc_sound2", //                        54              // short soundindex instead of byte
95         "svc_spawnbaseline2", //        55              // short modelindex instead of byte
96         "svc_spawnstatic2", //          56              // short modelindex instead of byte
97         "svc_entities", //                      57              // [int] deltaframe [int] thisframe [float vector] eye [variable length] entitydata
98         "svc_csqcentities", //          58              // [short] entnum [variable length] entitydata ... [short] 0x0000
99         "svc_spawnstaticsound2", //     59              // [coord3] [short] samp [byte] vol [byte] aten
100         "svc_trailparticles", //        60              // [short] entnum [short] effectnum [vector] start [vector] end
101         "svc_pointparticles", //        61              // [short] effectnum [vector] start [vector] velocity [short] count
102         "svc_pointparticles1", //       62              // [short] effectnum [vector] start, same as svc_pointparticles except velocity is zero and count is 1
103 };
104
105 const char *qw_svc_strings[128] =
106 {
107         "qw_svc_bad",                                   // 0
108         "qw_svc_nop",                                   // 1
109         "qw_svc_disconnect",                    // 2
110         "qw_svc_updatestat",                    // 3    // [byte] [byte]
111         "",                                                             // 4
112         "qw_svc_setview",                               // 5    // [short] entity number
113         "qw_svc_sound",                                 // 6    // <see code>
114         "",                                                             // 7
115         "qw_svc_print",                                 // 8    // [byte] id [string] null terminated string
116         "qw_svc_stufftext",                             // 9    // [string] stuffed into client's console buffer
117         "qw_svc_setangle",                              // 10   // [angle3] set the view angle to this absolute value
118         "qw_svc_serverdata",                    // 11   // [long] protocol ...
119         "qw_svc_lightstyle",                    // 12   // [byte] [string]
120         "",                                                             // 13
121         "qw_svc_updatefrags",                   // 14   // [byte] [short]
122         "",                                                             // 15
123         "qw_svc_stopsound",                             // 16   // <see code>
124         "",                                                             // 17
125         "",                                                             // 18
126         "qw_svc_damage",                                // 19
127         "qw_svc_spawnstatic",                   // 20
128         "",                                                             // 21
129         "qw_svc_spawnbaseline",                 // 22
130         "qw_svc_temp_entity",                   // 23   // variable
131         "qw_svc_setpause",                              // 24   // [byte] on / off
132         "",                                                             // 25
133         "qw_svc_centerprint",                   // 26   // [string] to put in center of the screen
134         "qw_svc_killedmonster",                 // 27
135         "qw_svc_foundsecret",                   // 28
136         "qw_svc_spawnstaticsound",              // 29   // [coord3] [byte] samp [byte] vol [byte] aten
137         "qw_svc_intermission",                  // 30           // [vec3_t] origin [vec3_t] angle
138         "qw_svc_finale",                                // 31           // [string] text
139         "qw_svc_cdtrack",                               // 32           // [byte] track
140         "qw_svc_sellscreen",                    // 33
141         "qw_svc_smallkick",                             // 34           // set client punchangle to 2
142         "qw_svc_bigkick",                               // 35           // set client punchangle to 4
143         "qw_svc_updateping",                    // 36           // [byte] [short]
144         "qw_svc_updateentertime",               // 37           // [byte] [float]
145         "qw_svc_updatestatlong",                // 38           // [byte] [long]
146         "qw_svc_muzzleflash",                   // 39           // [short] entity
147         "qw_svc_updateuserinfo",                // 40           // [byte] slot [long] uid
148         "qw_svc_download",                              // 41           // [short] size [size bytes]
149         "qw_svc_playerinfo",                    // 42           // variable
150         "qw_svc_nails",                                 // 43           // [byte] num [48 bits] xyzpy 12 12 12 4 8
151         "qw_svc_chokecount",                    // 44           // [byte] packets choked
152         "qw_svc_modellist",                             // 45           // [strings]
153         "qw_svc_soundlist",                             // 46           // [strings]
154         "qw_svc_packetentities",                // 47           // [...]
155         "qw_svc_deltapacketentities",   // 48           // [...]
156         "qw_svc_maxspeed",                              // 49           // maxspeed change, for prediction
157         "qw_svc_entgravity",                    // 50           // gravity change, for prediction
158         "qw_svc_setinfo",                               // 51           // setinfo on a client
159         "qw_svc_serverinfo",                    // 52           // serverinfo
160         "qw_svc_updatepl",                              // 53           // [byte] [byte]
161 };
162
163 //=============================================================================
164
165 cvar_t cl_worldmessage = {CVAR_READONLY, "cl_worldmessage", "", "title of current level"};
166 cvar_t cl_worldname = {CVAR_READONLY, "cl_worldname", "", "name of current worldmodel"};
167 cvar_t cl_worldnamenoextension = {CVAR_READONLY, "cl_worldnamenoextension", "", "name of current worldmodel without extension"};
168 cvar_t cl_worldbasename = {CVAR_READONLY, "cl_worldbasename", "", "name of current worldmodel without maps/ prefix or extension"};
169
170 cvar_t developer_networkentities = {0, "developer_networkentities", "0", "prints received entities, value is 0-4 (higher for more info)"};
171 cvar_t cl_gameplayfix_soundsmovewithentities = {0, "cl_gameplayfix_soundsmovewithentities", "1", "causes sounds made by lifts, players, projectiles, and any other entities, to move with the entity, so for example a rocket noise follows the rocket rather than staying at the starting position"};
172 cvar_t cl_sound_wizardhit = {0, "cl_sound_wizardhit", "wizard/hit.wav", "sound to play during TE_WIZSPIKE (empty cvar disables sound)"};
173 cvar_t cl_sound_hknighthit = {0, "cl_sound_hknighthit", "hknight/hit.wav", "sound to play during TE_KNIGHTSPIKE (empty cvar disables sound)"};
174 cvar_t cl_sound_tink1 = {0, "cl_sound_tink1", "weapons/tink1.wav", "sound to play with 80% chance during TE_SPIKE/TE_SUPERSPIKE (empty cvar disables sound)"};
175 cvar_t cl_sound_ric1 = {0, "cl_sound_ric1", "weapons/ric1.wav", "sound to play with 5% chance during TE_SPIKE/TE_SUPERSPIKE (empty cvar disables sound)"};
176 cvar_t cl_sound_ric2 = {0, "cl_sound_ric2", "weapons/ric2.wav", "sound to play with 5% chance during TE_SPIKE/TE_SUPERSPIKE (empty cvar disables sound)"};
177 cvar_t cl_sound_ric3 = {0, "cl_sound_ric3", "weapons/ric3.wav", "sound to play with 10% chance during TE_SPIKE/TE_SUPERSPIKE (empty cvar disables sound)"};
178 cvar_t cl_readpicture_force = {0, "cl_readpicture_force", "0", "when enabled, the low quality pictures read by ReadPicture() are preferred over the high quality pictures on the file system"};
179
180 #define RIC_GUNSHOT             1
181 #define RIC_GUNSHOTQUAD 2
182 cvar_t cl_sound_ric_gunshot = {0, "cl_sound_ric_gunshot", "0", "specifies if and when the related cl_sound_ric and cl_sound_tink sounds apply to TE_GUNSHOT/TE_GUNSHOTQUAD, 0 = no sound, 1 = TE_GUNSHOT, 2 = TE_GUNSHOTQUAD, 3 = TE_GUNSHOT and TE_GUNSHOTQUAD"};
183 cvar_t cl_sound_r_exp3 = {0, "cl_sound_r_exp3", "weapons/r_exp3.wav", "sound to play during TE_EXPLOSION and related effects (empty cvar disables sound)"};
184 cvar_t cl_serverextension_download = {0, "cl_serverextension_download", "0", "indicates whether the server supports the download command"};
185 cvar_t cl_joinbeforedownloadsfinish = {CVAR_SAVE, "cl_joinbeforedownloadsfinish", "1", "if non-zero the game will begin after the map is loaded before other downloads finish"};
186 cvar_t cl_nettimesyncfactor = {CVAR_SAVE, "cl_nettimesyncfactor", "0", "rate at which client time adapts to match server time, 1 = instantly, 0.125 = slowly, 0 = not at all (bounding still applies)"};
187 cvar_t cl_nettimesyncboundmode = {CVAR_SAVE, "cl_nettimesyncboundmode", "6", "method of restricting client time to valid values, 0 = no correction, 1 = tight bounding (jerky with packet loss), 2 = loose bounding (corrects it if out of bounds), 3 = leniant bounding (ignores temporary errors due to varying framerate), 4 = slow adjustment method from Quake3, 5 = slighttly nicer version of Quake3 method, 6 = bounding + Quake3"};
188 cvar_t cl_nettimesyncboundtolerance = {CVAR_SAVE, "cl_nettimesyncboundtolerance", "0.25", "how much error is tolerated by bounding check, as a fraction of frametime, 0.25 = up to 25% margin of error tolerated, 1 = use only new time, 0 = use only old time (same effect as setting cl_nettimesyncfactor to 1)"};
189 cvar_t cl_iplog_name = {CVAR_SAVE, "cl_iplog_name", "darkplaces_iplog.txt", "name of iplog file containing player addresses for iplog_list command and automatic ip logging when parsing status command"};
190
191 static qboolean QW_CL_CheckOrDownloadFile(const char *filename);
192 static void QW_CL_RequestNextDownload(void);
193 static void QW_CL_NextUpload(void);
194 void QW_CL_StartUpload(unsigned char *data, int size);
195 //static qboolean QW_CL_IsUploading(void);
196 static void QW_CL_StopUpload(void);
197 void CL_VM_UpdateIntermissionState(int intermission);
198 qboolean CL_VM_Event_Sound(int sound_num, float volume, int channel, float attenuation, int ent, vec3_t pos);
199
200 /*
201 ==================
202 CL_ParseStartSoundPacket
203 ==================
204 */
205 void CL_ParseStartSoundPacket(int largesoundindex)
206 {
207         vec3_t  pos;
208         int     channel, ent;
209         int     sound_num;
210         int     volume;
211         int     field_mask;
212         float   attenuation;
213
214         if (cls.protocol == PROTOCOL_QUAKEWORLD)
215         {
216                 channel = MSG_ReadShort();
217
218                 if (channel & (1<<15))
219                         volume = MSG_ReadByte ();
220                 else
221                         volume = DEFAULT_SOUND_PACKET_VOLUME;
222
223                 if (channel & (1<<14))
224                         attenuation = MSG_ReadByte () / 64.0;
225                 else
226                         attenuation = DEFAULT_SOUND_PACKET_ATTENUATION;
227
228                 ent = (channel>>3)&1023;
229                 channel &= 7;
230
231                 sound_num = MSG_ReadByte ();
232         }
233         else
234         {
235                 field_mask = MSG_ReadByte();
236
237                 if (field_mask & SND_VOLUME)
238                         volume = MSG_ReadByte ();
239                 else
240                         volume = DEFAULT_SOUND_PACKET_VOLUME;
241
242                 if (field_mask & SND_ATTENUATION)
243                         attenuation = MSG_ReadByte () / 64.0;
244                 else
245                         attenuation = DEFAULT_SOUND_PACKET_ATTENUATION;
246
247                 if (field_mask & SND_LARGEENTITY)
248                 {
249                         ent = (unsigned short) MSG_ReadShort ();
250                         channel = MSG_ReadChar ();
251                 }
252                 else
253                 {
254                         channel = (unsigned short) MSG_ReadShort ();
255                         ent = channel >> 3;
256                         channel &= 7;
257                 }
258
259                 if (largesoundindex || (field_mask & SND_LARGESOUND) || cls.protocol == PROTOCOL_NEHAHRABJP2 || cls.protocol == PROTOCOL_NEHAHRABJP3)
260                         sound_num = (unsigned short) MSG_ReadShort ();
261                 else
262                         sound_num = MSG_ReadByte ();
263         }
264
265         channel = CHAN_NET2ENGINE(channel);
266
267         MSG_ReadVector(pos, cls.protocol);
268
269         if (sound_num >= MAX_SOUNDS)
270         {
271                 Con_Printf("CL_ParseStartSoundPacket: sound_num (%i) >= MAX_SOUNDS (%i)\n", sound_num, MAX_SOUNDS);
272                 return;
273         }
274
275         if (ent >= MAX_EDICTS)
276         {
277                 Con_Printf("CL_ParseStartSoundPacket: ent = %i", ent);
278                 return;
279         }
280
281         if (ent >= cl.max_entities)
282                 CL_ExpandEntities(ent);
283
284         if( !CL_VM_Event_Sound(sound_num, volume / 255.0f, channel, attenuation, ent, pos) )
285                 S_StartSound (ent, channel, cl.sound_precache[sound_num], pos, volume/255.0f, attenuation);
286 }
287
288 /*
289 ==================
290 CL_KeepaliveMessage
291
292 When the client is taking a long time to load stuff, send keepalive messages
293 so the server doesn't disconnect.
294 ==================
295 */
296
297 static unsigned char olddata[NET_MAXMESSAGE];
298 void CL_KeepaliveMessage (qboolean readmessages)
299 {
300         float time;
301         static double nextmsg = -1;
302         static double nextupdate = -1;
303 #if 0
304         static double lasttime = -1;
305 #endif
306         int oldreadcount;
307         qboolean oldbadread;
308         sizebuf_t old;
309
310         if(cls.state != ca_dedicated)
311         {
312                 if((time = Sys_DoubleTime()) >= nextupdate)
313                 {
314                         SCR_UpdateLoadingScreenIfShown();
315                         nextupdate = time + 2;
316                 }
317         }
318
319         // no need if server is local and definitely not if this is a demo
320         if (!cls.netcon || cls.protocol == PROTOCOL_QUAKEWORLD || cls.signon >= SIGNONS)
321                 return;
322
323         if (readmessages)
324         {
325                 // read messages from server, should just be nops
326                 oldreadcount = msg_readcount;
327                 oldbadread = msg_badread;
328                 old = net_message;
329                 memcpy(olddata, net_message.data, net_message.cursize);
330
331                 NetConn_ClientFrame();
332
333                 msg_readcount = oldreadcount;
334                 msg_badread = oldbadread;
335                 net_message = old;
336                 memcpy(net_message.data, olddata, net_message.cursize);
337         }
338
339 #if 0
340         if((time = Sys_DoubleTime()) >= lasttime + 1)
341         {
342                 Con_Printf("long delta: %f\n", time - lasttime);
343         }
344         lasttime = Sys_DoubleTime();
345 #endif
346
347         if (cls.netcon && (time = Sys_DoubleTime()) >= nextmsg)
348         {
349                 sizebuf_t       msg;
350                 unsigned char           buf[4];
351                 nextmsg = time + 5;
352                 // write out a nop
353                 // LordHavoc: must use unreliable because reliable could kill the sigon message!
354                 Con_Print("--> client to server keepalive\n");
355                 memset(&msg, 0, sizeof(msg));
356                 msg.data = buf;
357                 msg.maxsize = sizeof(buf);
358                 MSG_WriteChar(&msg, clc_nop);
359                 NetConn_SendUnreliableMessage(cls.netcon, &msg, cls.protocol, 10000, false);
360         }
361 }
362
363 void CL_ParseEntityLump(char *entdata)
364 {
365         const char *data;
366         char key[128], value[MAX_INPUTLINE];
367         FOG_clear(); // LordHavoc: no fog until set
368         // LordHavoc: default to the map's sky (q3 shader parsing sets this)
369         R_SetSkyBox(cl.worldmodel->brush.skybox);
370         data = entdata;
371         if (!data)
372                 return;
373         if (!COM_ParseToken_Simple(&data, false, false))
374                 return; // error
375         if (com_token[0] != '{')
376                 return; // error
377         while (1)
378         {
379                 if (!COM_ParseToken_Simple(&data, false, false))
380                         return; // error
381                 if (com_token[0] == '}')
382                         break; // end of worldspawn
383                 if (com_token[0] == '_')
384                         strlcpy (key, com_token + 1, sizeof (key));
385                 else
386                         strlcpy (key, com_token, sizeof (key));
387                 while (key[strlen(key)-1] == ' ') // remove trailing spaces
388                         key[strlen(key)-1] = 0;
389                 if (!COM_ParseToken_Simple(&data, false, false))
390                         return; // error
391                 strlcpy (value, com_token, sizeof (value));
392                 if (!strcmp("sky", key))
393                         R_SetSkyBox(value);
394                 else if (!strcmp("skyname", key)) // non-standard, introduced by QuakeForge... sigh.
395                         R_SetSkyBox(value);
396                 else if (!strcmp("qlsky", key)) // non-standard, introduced by QuakeLives (EEK)
397                         R_SetSkyBox(value);
398                 else if (!strcmp("fog", key))
399                 {
400                         FOG_clear(); // so missing values get good defaults
401                         r_refdef.fog_start = 0;
402                         r_refdef.fog_alpha = 1;
403                         r_refdef.fog_end = 16384;
404                         r_refdef.fog_height = 1<<30;
405                         r_refdef.fog_fadedepth = 128;
406 #if _MSC_VER >= 1400
407 #define sscanf sscanf_s
408 #endif
409                         sscanf(value, "%f %f %f %f %f %f %f %f %f", &r_refdef.fog_density, &r_refdef.fog_red, &r_refdef.fog_green, &r_refdef.fog_blue, &r_refdef.fog_alpha, &r_refdef.fog_start, &r_refdef.fog_end, &r_refdef.fog_height, &r_refdef.fog_fadedepth);
410                 }
411                 else if (!strcmp("fog_density", key))
412                         r_refdef.fog_density = atof(value);
413                 else if (!strcmp("fog_red", key))
414                         r_refdef.fog_red = atof(value);
415                 else if (!strcmp("fog_green", key))
416                         r_refdef.fog_green = atof(value);
417                 else if (!strcmp("fog_blue", key))
418                         r_refdef.fog_blue = atof(value);
419                 else if (!strcmp("fog_alpha", key))
420                         r_refdef.fog_alpha = atof(value);
421                 else if (!strcmp("fog_start", key))
422                         r_refdef.fog_start = atof(value);
423                 else if (!strcmp("fog_end", key))
424                         r_refdef.fog_end = atof(value);
425                 else if (!strcmp("fog_height", key))
426                         r_refdef.fog_height = atof(value);
427                 else if (!strcmp("fog_fadedepth", key))
428                         r_refdef.fog_fadedepth = atof(value);
429                 else if (!strcmp("fog_heighttexture", key))
430                 {
431                         FOG_clear(); // so missing values get good defaults
432 #if _MSC_VER >= 1400
433                         sscanf_s(value, "%f %f %f %f %f %f %f %f %f %s", &r_refdef.fog_density, &r_refdef.fog_red, &r_refdef.fog_green, &r_refdef.fog_blue, &r_refdef.fog_alpha, &r_refdef.fog_start, &r_refdef.fog_end, &r_refdef.fog_height, &r_refdef.fog_fadedepth, r_refdef.fog_height_texturename, (unsigned int)sizeof(r_refdef.fog_height_texturename));
434 #else
435                         sscanf(value, "%f %f %f %f %f %f %f %f %f %63s", &r_refdef.fog_density, &r_refdef.fog_red, &r_refdef.fog_green, &r_refdef.fog_blue, &r_refdef.fog_alpha, &r_refdef.fog_start, &r_refdef.fog_end, &r_refdef.fog_height, &r_refdef.fog_fadedepth, r_refdef.fog_height_texturename);
436 #endif
437                         r_refdef.fog_height_texturename[63] = 0;
438                 }
439         }
440 }
441
442 extern void CL_Locs_Reload_f(void);
443 extern void CL_VM_Init (void);
444 static const vec3_t defaultmins = {-4096, -4096, -4096};
445 static const vec3_t defaultmaxs = {4096, 4096, 4096};
446 static void CL_SetupWorldModel(void)
447 {
448         // update the world model
449         cl.entities[0].render.model = cl.worldmodel = CL_GetModelByIndex(1);
450         CL_UpdateRenderEntity(&cl.entities[0].render);
451
452         // make sure the cl.worldname and related cvars are set up now that we know the world model name
453         // set up csqc world for collision culling
454         if (cl.worldmodel)
455         {
456                 strlcpy(cl.worldname, cl.worldmodel->name, sizeof(cl.worldname));
457                 FS_StripExtension(cl.worldname, cl.worldnamenoextension, sizeof(cl.worldnamenoextension));
458                 strlcpy(cl.worldbasename, !strncmp(cl.worldnamenoextension, "maps/", 5) ? cl.worldnamenoextension + 5 : cl.worldnamenoextension, sizeof(cl.worldbasename));
459                 Cvar_SetQuick(&cl_worldmessage, cl.worldmessage);
460                 Cvar_SetQuick(&cl_worldname, cl.worldname);
461                 Cvar_SetQuick(&cl_worldnamenoextension, cl.worldnamenoextension);
462                 Cvar_SetQuick(&cl_worldbasename, cl.worldbasename);
463                 World_SetSize(&cl.world, cl.worldname, cl.worldmodel->normalmins, cl.worldmodel->normalmaxs);
464         }
465         else
466         {
467                 Cvar_SetQuick(&cl_worldmessage, cl.worldmessage);
468                 Cvar_SetQuick(&cl_worldnamenoextension, "");
469                 Cvar_SetQuick(&cl_worldbasename, "");
470                 World_SetSize(&cl.world, "", defaultmins, defaultmaxs);
471         }
472         World_Start(&cl.world);
473
474         // load or reload .loc file for team chat messages
475         CL_Locs_Reload_f();
476
477         // make sure we send enough keepalives
478         CL_KeepaliveMessage(false);
479
480         // reset particles and other per-level things
481         R_Modules_NewMap();
482
483         // make sure we send enough keepalives
484         CL_KeepaliveMessage(false);
485
486         // load the team chat beep if possible
487         cl.foundtalk2wav = FS_FileExists("sound/misc/talk2.wav");
488
489         // check memory integrity
490         Mem_CheckSentinelsGlobal();
491
492         // make menu know
493         MR_NewMap();
494
495         // load the csqc now
496         if (cl.loadcsqc)
497         {
498                 cl.loadcsqc = false;
499
500                 CL_VM_Init();
501         }
502 }
503
504 static qboolean QW_CL_CheckOrDownloadFile(const char *filename)
505 {
506         qfile_t *file;
507
508         // see if the file already exists
509         file = FS_OpenVirtualFile(filename, true);
510         if (file)
511         {
512                 FS_Close(file);
513                 return true;
514         }
515
516         // download messages in a demo would be bad
517         if (cls.demorecording)
518         {
519                 Con_Printf("Unable to download \"%s\" when recording.\n", filename);
520                 return true;
521         }
522
523         // don't try to download when playing a demo
524         if (!cls.netcon)
525                 return true;
526
527         strlcpy(cls.qw_downloadname, filename, sizeof(cls.qw_downloadname));
528         Con_Printf("Downloading %s\n", filename);
529
530         if (!cls.qw_downloadmemory)
531         {
532                 cls.qw_downloadmemory = NULL;
533                 cls.qw_downloadmemorycursize = 0;
534                 cls.qw_downloadmemorymaxsize = 1024*1024; // start out with a 1MB buffer
535         }
536
537         MSG_WriteByte(&cls.netcon->message, qw_clc_stringcmd);
538         MSG_WriteString(&cls.netcon->message, va("download %s", filename));
539
540         cls.qw_downloadnumber++;
541         cls.qw_downloadpercent = 0;
542         cls.qw_downloadmemorycursize = 0;
543
544         return false;
545 }
546
547 static void QW_CL_ProcessUserInfo(int slot);
548 static void QW_CL_RequestNextDownload(void)
549 {
550         int i;
551
552         // clear name of file that just finished
553         cls.qw_downloadname[0] = 0;
554
555         switch (cls.qw_downloadtype)
556         {
557         case dl_single:
558                 break;
559         case dl_skin:
560                 if (cls.qw_downloadnumber == 0)
561                         Con_Printf("Checking skins...\n");
562                 for (;cls.qw_downloadnumber < cl.maxclients;cls.qw_downloadnumber++)
563                 {
564                         if (!cl.scores[cls.qw_downloadnumber].name[0])
565                                 continue;
566                         // check if we need to download the file, and return if so
567                         if (!QW_CL_CheckOrDownloadFile(va("skins/%s.pcx", cl.scores[cls.qw_downloadnumber].qw_skin)))
568                                 return;
569                 }
570
571                 cls.qw_downloadtype = dl_none;
572
573                 // load any newly downloaded skins
574                 for (i = 0;i < cl.maxclients;i++)
575                         QW_CL_ProcessUserInfo(i);
576
577                 // if we're still in signon stages, request the next one
578                 if (cls.signon != SIGNONS)
579                 {
580                         cls.signon = SIGNONS-1;
581                         // we'll go to SIGNONS when the first entity update is received
582                         MSG_WriteByte(&cls.netcon->message, qw_clc_stringcmd);
583                         MSG_WriteString(&cls.netcon->message, va("begin %i", cl.qw_servercount));
584                 }
585                 break;
586         case dl_model:
587                 if (cls.qw_downloadnumber == 0)
588                 {
589                         Con_Printf("Checking models...\n");
590                         cls.qw_downloadnumber = 1;
591                 }
592
593                 for (;cls.qw_downloadnumber < MAX_MODELS && cl.model_name[cls.qw_downloadnumber][0];cls.qw_downloadnumber++)
594                 {
595                         // skip submodels
596                         if (cl.model_name[cls.qw_downloadnumber][0] == '*')
597                                 continue;
598                         if (!strcmp(cl.model_name[cls.qw_downloadnumber], "progs/spike.mdl"))
599                                 cl.qw_modelindex_spike = cls.qw_downloadnumber;
600                         if (!strcmp(cl.model_name[cls.qw_downloadnumber], "progs/player.mdl"))
601                                 cl.qw_modelindex_player = cls.qw_downloadnumber;
602                         if (!strcmp(cl.model_name[cls.qw_downloadnumber], "progs/flag.mdl"))
603                                 cl.qw_modelindex_flag = cls.qw_downloadnumber;
604                         if (!strcmp(cl.model_name[cls.qw_downloadnumber], "progs/s_explod.spr"))
605                                 cl.qw_modelindex_s_explod = cls.qw_downloadnumber;
606                         // check if we need to download the file, and return if so
607                         if (!QW_CL_CheckOrDownloadFile(cl.model_name[cls.qw_downloadnumber]))
608                                 return;
609                 }
610
611                 cls.qw_downloadtype = dl_none;
612
613                 // touch all of the precached models that are still loaded so we can free
614                 // anything that isn't needed
615                 if (!sv.active)
616                         Mod_ClearUsed();
617                 for (i = 1;i < MAX_MODELS && cl.model_name[i][0];i++)
618                         Mod_FindName(cl.model_name[i], cl.model_name[i][0] == '*' ? cl.model_name[1] : NULL);
619                 // precache any models used by the client (this also marks them used)
620                 cl.model_bolt = Mod_ForName("progs/bolt.mdl", false, false, NULL);
621                 cl.model_bolt2 = Mod_ForName("progs/bolt2.mdl", false, false, NULL);
622                 cl.model_bolt3 = Mod_ForName("progs/bolt3.mdl", false, false, NULL);
623                 cl.model_beam = Mod_ForName("progs/beam.mdl", false, false, NULL);
624
625                 // we purge the models and sounds later in CL_SignonReply
626                 //Mod_PurgeUnused();
627
628                 // now we try to load everything that is new
629
630                 // world model
631                 cl.model_precache[1] = Mod_ForName(cl.model_name[1], false, false, NULL);
632                 if (cl.model_precache[1]->Draw == NULL)
633                         Con_Printf("Map %s could not be found or downloaded\n", cl.model_name[1]);
634
635                 // normal models
636                 for (i = 2;i < MAX_MODELS && cl.model_name[i][0];i++)
637                         if ((cl.model_precache[i] = Mod_ForName(cl.model_name[i], false, false, cl.model_name[i][0] == '*' ? cl.model_name[1] : NULL))->Draw == NULL)
638                                 Con_Printf("Model %s could not be found or downloaded\n", cl.model_name[i]);
639
640                 // check memory integrity
641                 Mem_CheckSentinelsGlobal();
642
643                 // now that we have a world model, set up the world entity, renderer
644                 // modules and csqc
645                 CL_SetupWorldModel();
646
647                 // add pmodel/emodel CRCs to userinfo
648                 CL_SetInfo("pmodel", va("%i", FS_CRCFile("progs/player.mdl", NULL)), true, true, true, true);
649                 CL_SetInfo("emodel", va("%i", FS_CRCFile("progs/eyes.mdl", NULL)), true, true, true, true);
650
651                 // done checking sounds and models, send a prespawn command now
652                 MSG_WriteByte(&cls.netcon->message, qw_clc_stringcmd);
653                 MSG_WriteString(&cls.netcon->message, va("prespawn %i 0 %i", cl.qw_servercount, cl.model_precache[1]->brush.qw_md4sum2));
654
655                 if (cls.qw_downloadmemory)
656                 {
657                         Mem_Free(cls.qw_downloadmemory);
658                         cls.qw_downloadmemory = NULL;
659                 }
660
661                 // done loading
662                 cl.loadfinished = true;
663                 break;
664         case dl_sound:
665                 if (cls.qw_downloadnumber == 0)
666                 {
667                         Con_Printf("Checking sounds...\n");
668                         cls.qw_downloadnumber = 1;
669                 }
670
671                 for (;cl.sound_name[cls.qw_downloadnumber][0];cls.qw_downloadnumber++)
672                 {
673                         // check if we need to download the file, and return if so
674                         if (!QW_CL_CheckOrDownloadFile(va("sound/%s", cl.sound_name[cls.qw_downloadnumber])))
675                                 return;
676                 }
677
678                 cls.qw_downloadtype = dl_none;
679
680                 // clear sound usage flags for purging of unused sounds
681                 S_ClearUsed();
682
683                 // precache any sounds used by the client
684                 cl.sfx_wizhit = S_PrecacheSound(cl_sound_wizardhit.string, false, true);
685                 cl.sfx_knighthit = S_PrecacheSound(cl_sound_hknighthit.string, false, true);
686                 cl.sfx_tink1 = S_PrecacheSound(cl_sound_tink1.string, false, true);
687                 cl.sfx_ric1 = S_PrecacheSound(cl_sound_ric1.string, false, true);
688                 cl.sfx_ric2 = S_PrecacheSound(cl_sound_ric2.string, false, true);
689                 cl.sfx_ric3 = S_PrecacheSound(cl_sound_ric3.string, false, true);
690                 cl.sfx_r_exp3 = S_PrecacheSound(cl_sound_r_exp3.string, false, true);
691
692                 // sounds used by the game
693                 for (i = 1;i < MAX_SOUNDS && cl.sound_name[i][0];i++)
694                         cl.sound_precache[i] = S_PrecacheSound(cl.sound_name[i], true, true);
695
696                 // we purge the models and sounds later in CL_SignonReply
697                 //S_PurgeUnused();
698
699                 // check memory integrity
700                 Mem_CheckSentinelsGlobal();
701
702                 // done with sound downloads, next we check models
703                 MSG_WriteByte(&cls.netcon->message, qw_clc_stringcmd);
704                 MSG_WriteString(&cls.netcon->message, va("modellist %i %i", cl.qw_servercount, 0));
705                 break;
706         case dl_none:
707         default:
708                 Con_Printf("Unknown download type.\n");
709         }
710 }
711
712 static void QW_CL_ParseDownload(void)
713 {
714         int size = (signed short)MSG_ReadShort();
715         int percent = MSG_ReadByte();
716
717         //Con_Printf("download %i %i%% (%i/%i)\n", size, percent, cls.qw_downloadmemorycursize, cls.qw_downloadmemorymaxsize);
718
719         // skip the download fragment if playing a demo
720         if (!cls.netcon)
721         {
722                 if (size > 0)
723                         msg_readcount += size;
724                 return;
725         }
726
727         if (size == -1)
728         {
729                 Con_Printf("File not found.\n");
730                 QW_CL_RequestNextDownload();
731                 return;
732         }
733
734         if (msg_readcount + (unsigned short)size > net_message.cursize)
735                 Host_Error("corrupt download message\n");
736
737         // make sure the buffer is big enough to include this new fragment
738         if (!cls.qw_downloadmemory || cls.qw_downloadmemorymaxsize < cls.qw_downloadmemorycursize + size)
739         {
740                 unsigned char *old;
741                 while (cls.qw_downloadmemorymaxsize < cls.qw_downloadmemorycursize + size)
742                         cls.qw_downloadmemorymaxsize *= 2;
743                 old = cls.qw_downloadmemory;
744                 cls.qw_downloadmemory = (unsigned char *)Mem_Alloc(cls.permanentmempool, cls.qw_downloadmemorymaxsize);
745                 if (old)
746                 {
747                         memcpy(cls.qw_downloadmemory, old, cls.qw_downloadmemorycursize);
748                         Mem_Free(old);
749                 }
750         }
751
752         // read the fragment out of the packet
753         MSG_ReadBytes(size, cls.qw_downloadmemory + cls.qw_downloadmemorycursize);
754         cls.qw_downloadmemorycursize += size;
755         cls.qw_downloadspeedcount += size;
756
757         cls.qw_downloadpercent = percent;
758
759         if (percent != 100)
760         {
761                 // request next fragment
762                 MSG_WriteByte(&cls.netcon->message, qw_clc_stringcmd);
763                 MSG_WriteString(&cls.netcon->message, "nextdl");
764         }
765         else
766         {
767                 // finished file
768                 Con_Printf("Downloaded \"%s\"\n", cls.qw_downloadname);
769
770                 FS_WriteFile(cls.qw_downloadname, cls.qw_downloadmemory, cls.qw_downloadmemorycursize);
771
772                 cls.qw_downloadpercent = 0;
773
774                 // start downloading the next file (or join the game)
775                 QW_CL_RequestNextDownload();
776         }
777 }
778
779 static void QW_CL_ParseModelList(void)
780 {
781         int n;
782         int nummodels = MSG_ReadByte();
783         char *str;
784
785         // parse model precache list
786         for (;;)
787         {
788                 str = MSG_ReadString();
789                 if (!str[0])
790                         break;
791                 nummodels++;
792                 if (nummodels==MAX_MODELS)
793                         Host_Error("Server sent too many model precaches");
794                 if (strlen(str) >= MAX_QPATH)
795                         Host_Error("Server sent a precache name of %i characters (max %i)", (int)strlen(str), MAX_QPATH - 1);
796                 strlcpy(cl.model_name[nummodels], str, sizeof (cl.model_name[nummodels]));
797         }
798
799         n = MSG_ReadByte();
800         if (n)
801         {
802                 MSG_WriteByte(&cls.netcon->message, qw_clc_stringcmd);
803                 MSG_WriteString(&cls.netcon->message, va("modellist %i %i", cl.qw_servercount, n));
804                 return;
805         }
806
807         cls.signon = 2;
808         cls.qw_downloadnumber = 0;
809         cls.qw_downloadtype = dl_model;
810         QW_CL_RequestNextDownload();
811 }
812
813 static void QW_CL_ParseSoundList(void)
814 {
815         int n;
816         int numsounds = MSG_ReadByte();
817         char *str;
818
819         // parse sound precache list
820         for (;;)
821         {
822                 str = MSG_ReadString();
823                 if (!str[0])
824                         break;
825                 numsounds++;
826                 if (numsounds==MAX_SOUNDS)
827                         Host_Error("Server sent too many sound precaches");
828                 if (strlen(str) >= MAX_QPATH)
829                         Host_Error("Server sent a precache name of %i characters (max %i)", (int)strlen(str), MAX_QPATH - 1);
830                 strlcpy(cl.sound_name[numsounds], str, sizeof (cl.sound_name[numsounds]));
831         }
832
833         n = MSG_ReadByte();
834
835         if (n)
836         {
837                 MSG_WriteByte(&cls.netcon->message, qw_clc_stringcmd);
838                 MSG_WriteString(&cls.netcon->message, va("soundlist %i %i", cl.qw_servercount, n));
839                 return;
840         }
841
842         cls.signon = 2;
843         cls.qw_downloadnumber = 0;
844         cls.qw_downloadtype = dl_sound;
845         QW_CL_RequestNextDownload();
846 }
847
848 static void QW_CL_Skins_f(void)
849 {
850         cls.qw_downloadnumber = 0;
851         cls.qw_downloadtype = dl_skin;
852         QW_CL_RequestNextDownload();
853 }
854
855 static void QW_CL_Changing_f(void)
856 {
857         if (cls.qw_downloadmemory)  // don't change when downloading
858                 return;
859
860         S_StopAllSounds();
861         cl.intermission = 0;
862         cls.signon = 1; // not active anymore, but not disconnected
863         Con_Printf("\nChanging map...\n");
864 }
865
866 void QW_CL_NextUpload(void)
867 {
868         int r, percent, size;
869
870         if (!cls.qw_uploaddata)
871                 return;
872
873         r = cls.qw_uploadsize - cls.qw_uploadpos;
874         if (r > 768)
875                 r = 768;
876         size = min(1, cls.qw_uploadsize);
877         percent = (cls.qw_uploadpos+r)*100/size;
878
879         MSG_WriteByte(&cls.netcon->message, qw_clc_upload);
880         MSG_WriteShort(&cls.netcon->message, r);
881         MSG_WriteByte(&cls.netcon->message, percent);
882         SZ_Write(&cls.netcon->message, cls.qw_uploaddata + cls.qw_uploadpos, r);
883
884         Con_DPrintf("UPLOAD: %6d: %d written\n", cls.qw_uploadpos, r);
885
886         cls.qw_uploadpos += r;
887
888         if (cls.qw_uploadpos < cls.qw_uploadsize)
889                 return;
890
891         Con_Printf("Upload completed\n");
892
893         QW_CL_StopUpload();
894 }
895
896 void QW_CL_StartUpload(unsigned char *data, int size)
897 {
898         // do nothing in demos or if not connected
899         if (!cls.netcon)
900                 return;
901
902         // abort existing upload if in progress
903         QW_CL_StopUpload();
904
905         Con_DPrintf("Starting upload of %d bytes...\n", size);
906
907         cls.qw_uploaddata = (unsigned char *)Mem_Alloc(cls.permanentmempool, size);
908         memcpy(cls.qw_uploaddata, data, size);
909         cls.qw_uploadsize = size;
910         cls.qw_uploadpos = 0;
911
912         QW_CL_NextUpload();
913 }
914
915 #if 0
916 qboolean QW_CL_IsUploading(void)
917 {
918         return cls.qw_uploaddata != NULL;
919 }
920 #endif
921
922 void QW_CL_StopUpload(void)
923 {
924         if (cls.qw_uploaddata)
925                 Mem_Free(cls.qw_uploaddata);
926         cls.qw_uploaddata = NULL;
927         cls.qw_uploadsize = 0;
928         cls.qw_uploadpos = 0;
929 }
930
931 static void QW_CL_ProcessUserInfo(int slot)
932 {
933         int topcolor, bottomcolor;
934         char temp[2048];
935         InfoString_GetValue(cl.scores[slot].qw_userinfo, "name", cl.scores[slot].name, sizeof(cl.scores[slot].name));
936         InfoString_GetValue(cl.scores[slot].qw_userinfo, "topcolor", temp, sizeof(temp));topcolor = atoi(temp);
937         InfoString_GetValue(cl.scores[slot].qw_userinfo, "bottomcolor", temp, sizeof(temp));bottomcolor = atoi(temp);
938         cl.scores[slot].colors = topcolor * 16 + bottomcolor;
939         InfoString_GetValue(cl.scores[slot].qw_userinfo, "*spectator", temp, sizeof(temp));
940         cl.scores[slot].qw_spectator = temp[0] != 0;
941         InfoString_GetValue(cl.scores[slot].qw_userinfo, "team", cl.scores[slot].qw_team, sizeof(cl.scores[slot].qw_team));
942         InfoString_GetValue(cl.scores[slot].qw_userinfo, "skin", cl.scores[slot].qw_skin, sizeof(cl.scores[slot].qw_skin));
943         if (!cl.scores[slot].qw_skin[0])
944                 strlcpy(cl.scores[slot].qw_skin, "base", sizeof(cl.scores[slot].qw_skin));
945         // TODO: skin cache
946 }
947
948 static void QW_CL_UpdateUserInfo(void)
949 {
950         int slot;
951         slot = MSG_ReadByte();
952         if (slot >= cl.maxclients)
953         {
954                 Con_Printf("svc_updateuserinfo >= cl.maxclients\n");
955                 MSG_ReadLong();
956                 MSG_ReadString();
957                 return;
958         }
959         cl.scores[slot].qw_userid = MSG_ReadLong();
960         strlcpy(cl.scores[slot].qw_userinfo, MSG_ReadString(), sizeof(cl.scores[slot].qw_userinfo));
961
962         QW_CL_ProcessUserInfo(slot);
963 }
964
965 static void QW_CL_SetInfo(void)
966 {
967         int slot;
968         char key[2048];
969         char value[2048];
970         slot = MSG_ReadByte();
971         strlcpy(key, MSG_ReadString(), sizeof(key));
972         strlcpy(value, MSG_ReadString(), sizeof(value));
973         if (slot >= cl.maxclients)
974         {
975                 Con_Printf("svc_setinfo >= cl.maxclients\n");
976                 return;
977         }
978         InfoString_SetValue(cl.scores[slot].qw_userinfo, sizeof(cl.scores[slot].qw_userinfo), key, value);
979
980         QW_CL_ProcessUserInfo(slot);
981 }
982
983 static void QW_CL_ServerInfo(void)
984 {
985         char key[2048];
986         char value[2048];
987         char temp[32];
988         strlcpy(key, MSG_ReadString(), sizeof(key));
989         strlcpy(value, MSG_ReadString(), sizeof(value));
990         Con_DPrintf("SERVERINFO: %s=%s\n", key, value);
991         InfoString_SetValue(cl.qw_serverinfo, sizeof(cl.qw_serverinfo), key, value);
992         InfoString_GetValue(cl.qw_serverinfo, "teamplay", temp, sizeof(temp));
993         cl.qw_teamplay = atoi(temp);
994 }
995
996 static void QW_CL_ParseNails(void)
997 {
998         int i, j;
999         int numnails = MSG_ReadByte();
1000         vec_t *v;
1001         unsigned char bits[6];
1002         for (i = 0;i < numnails;i++)
1003         {
1004                 for (j = 0;j < 6;j++)
1005                         bits[j] = MSG_ReadByte();
1006                 if (cl.qw_num_nails > 255)
1007                         continue;
1008                 v = cl.qw_nails[cl.qw_num_nails++];
1009                 v[0] = ( ( bits[0] + ((bits[1]&15)<<8) ) <<1) - 4096;
1010                 v[1] = ( ( (bits[1]>>4) + (bits[2]<<4) ) <<1) - 4096;
1011                 v[2] = ( ( bits[3] + ((bits[4]&15)<<8) ) <<1) - 4096;
1012                 v[3] = -360*(bits[4]>>4)/16;
1013                 v[4] = 360*bits[5]/256;
1014                 v[5] = 0;
1015         }
1016 }
1017
1018 static void CL_UpdateItemsAndWeapon(void)
1019 {
1020         int j;
1021         // check for important changes
1022
1023         // set flash times
1024         if (cl.olditems != cl.stats[STAT_ITEMS])
1025                 for (j = 0;j < 32;j++)
1026                         if ((cl.stats[STAT_ITEMS] & (1<<j)) && !(cl.olditems & (1<<j)))
1027                                 cl.item_gettime[j] = cl.time;
1028         cl.olditems = cl.stats[STAT_ITEMS];
1029
1030         // GAME_NEXUIZ hud needs weapon change time
1031         if (cl.activeweapon != cl.stats[STAT_ACTIVEWEAPON])
1032                 cl.weapontime = cl.time;
1033         cl.activeweapon = cl.stats[STAT_ACTIVEWEAPON];
1034 }
1035
1036 #define LOADPROGRESSWEIGHT_SOUND            1.0
1037 #define LOADPROGRESSWEIGHT_MODEL            4.0
1038 #define LOADPROGRESSWEIGHT_WORLDMODEL      30.0
1039 #define LOADPROGRESSWEIGHT_WORLDMODEL_INIT  2.0
1040
1041 void CL_BeginDownloads(qboolean aborteddownload)
1042 {
1043         // quakeworld works differently
1044         if (cls.protocol == PROTOCOL_QUAKEWORLD)
1045                 return;
1046
1047         // this would be a good place to do curl downloads
1048         if(Curl_Have_forthismap())
1049         {
1050                 Curl_Register_predownload(); // come back later
1051                 return;
1052         }
1053
1054         // if we got here...
1055         // curl is done, so let's start with the business
1056         if(!cl.loadbegun)
1057                 SCR_PushLoadingScreen(false, "Loading precaches", 1);
1058         cl.loadbegun = true;
1059
1060         // if already downloading something from the previous level, don't stop it
1061         if (cls.qw_downloadname[0])
1062                 return;
1063
1064         if (cl.downloadcsqc)
1065         {
1066                 size_t progsize;
1067                 cl.downloadcsqc = false;
1068                 if (cls.netcon
1069                  && !sv.active
1070                  && csqc_progname.string
1071                  && csqc_progname.string[0]
1072                  && csqc_progcrc.integer >= 0
1073                  && cl_serverextension_download.integer
1074                  && (FS_CRCFile(csqc_progname.string, &progsize) != csqc_progcrc.integer || ((int)progsize != csqc_progsize.integer && csqc_progsize.integer != -1))
1075                  && !FS_FileExists(va("dlcache/%s.%i.%i", csqc_progname.string, csqc_progsize.integer, csqc_progcrc.integer)))
1076                 {
1077                         Con_Printf("Downloading new CSQC code to dlcache/%s.%i.%i\n", csqc_progname.string, csqc_progsize.integer, csqc_progcrc.integer);
1078                         if(cl_serverextension_download.integer == 2 && FS_HasZlib())
1079                                 Cmd_ForwardStringToServer(va("download %s deflate", csqc_progname.string));
1080                         else
1081                                 Cmd_ForwardStringToServer(va("download %s", csqc_progname.string));
1082                         return;
1083                 }
1084         }
1085
1086         if (cl.loadmodel_current < cl.loadmodel_total)
1087         {
1088                 // loading models
1089                 if(cl.loadmodel_current == 1)
1090                 {
1091                         // worldmodel counts as 16 models (15 + world model setup), for better progress bar
1092                         SCR_PushLoadingScreen(false, "Loading precached models",
1093                                 (
1094                                         (cl.loadmodel_total - 1) * LOADPROGRESSWEIGHT_MODEL
1095                                 +       LOADPROGRESSWEIGHT_WORLDMODEL
1096                                 +       LOADPROGRESSWEIGHT_WORLDMODEL_INIT
1097                                 ) / (
1098                                         (cl.loadmodel_total - 1) * LOADPROGRESSWEIGHT_MODEL
1099                                 +       LOADPROGRESSWEIGHT_WORLDMODEL
1100                                 +       LOADPROGRESSWEIGHT_WORLDMODEL_INIT
1101                                 +       cl.loadsound_total * LOADPROGRESSWEIGHT_SOUND
1102                                 )
1103                         );
1104                         SCR_BeginLoadingPlaque();
1105                 }
1106                 for (;cl.loadmodel_current < cl.loadmodel_total;cl.loadmodel_current++)
1107                 {
1108                         SCR_PushLoadingScreen(false, cl.model_name[cl.loadmodel_current],
1109                                 (
1110                                         (cl.loadmodel_current == 1) ? LOADPROGRESSWEIGHT_WORLDMODEL : LOADPROGRESSWEIGHT_MODEL
1111                                 ) / (
1112                                         (cl.loadmodel_total - 1) * LOADPROGRESSWEIGHT_MODEL
1113                                 +       LOADPROGRESSWEIGHT_WORLDMODEL
1114                                 +       LOADPROGRESSWEIGHT_WORLDMODEL_INIT
1115                                 )
1116                         );
1117                         if (cl.model_precache[cl.loadmodel_current] && cl.model_precache[cl.loadmodel_current]->Draw)
1118                         {
1119                                 SCR_PopLoadingScreen(false);
1120                                 if(cl.loadmodel_current == 1)
1121                                 {
1122                                         SCR_PushLoadingScreen(false, cl.model_name[cl.loadmodel_current], 1.0 / cl.loadmodel_total);
1123                                         SCR_PopLoadingScreen(false);
1124                                 }
1125                                 continue;
1126                         }
1127                         CL_KeepaliveMessage(true);
1128
1129                         // if running a local game, calling Mod_ForName is a completely wasted effort...
1130                         if (sv.active)
1131                                 cl.model_precache[cl.loadmodel_current] = sv.models[cl.loadmodel_current];
1132                         else
1133                         {
1134                                 if(cl.loadmodel_current == 1)
1135                                 {
1136                                         // they'll be soon loaded, but make sure we apply freshly downloaded shaders from a curled pk3
1137                                         Mod_FreeQ3Shaders();
1138                                 }
1139                                 cl.model_precache[cl.loadmodel_current] = Mod_ForName(cl.model_name[cl.loadmodel_current], false, false, cl.model_name[cl.loadmodel_current][0] == '*' ? cl.model_name[1] : NULL);
1140                         }
1141                         SCR_PopLoadingScreen(false);
1142                         if (cl.model_precache[cl.loadmodel_current] && cl.model_precache[cl.loadmodel_current]->Draw && cl.loadmodel_current == 1)
1143                         {
1144                                 // we now have the worldmodel so we can set up the game world
1145                                 SCR_PushLoadingScreen(true, "world model setup",
1146                                         (
1147                                                 LOADPROGRESSWEIGHT_WORLDMODEL_INIT
1148                                         ) / (
1149                                                 (cl.loadmodel_total - 1) * LOADPROGRESSWEIGHT_MODEL
1150                                         +       LOADPROGRESSWEIGHT_WORLDMODEL
1151                                         +       LOADPROGRESSWEIGHT_WORLDMODEL_INIT
1152                                         )
1153                                 );
1154                                 CL_SetupWorldModel();
1155                                 SCR_PopLoadingScreen(true);
1156                                 if (!cl.loadfinished && cl_joinbeforedownloadsfinish.integer)
1157                                 {
1158                                         cl.loadfinished = true;
1159                                         // now issue the spawn to move on to signon 2 like normal
1160                                         if (cls.netcon)
1161                                                 Cmd_ForwardStringToServer("prespawn");
1162                                 }
1163                         }
1164                 }
1165                 SCR_PopLoadingScreen(false);
1166                 // finished loading models
1167         }
1168
1169         if (cl.loadsound_current < cl.loadsound_total)
1170         {
1171                 // loading sounds
1172                 if(cl.loadsound_current == 1)
1173                         SCR_PushLoadingScreen(false, "Loading precached sounds",
1174                                 (
1175                                         cl.loadsound_total * LOADPROGRESSWEIGHT_SOUND
1176                                 ) / (
1177                                         (cl.loadmodel_total - 1) * LOADPROGRESSWEIGHT_MODEL
1178                                 +       LOADPROGRESSWEIGHT_WORLDMODEL
1179                                 +       LOADPROGRESSWEIGHT_WORLDMODEL_INIT
1180                                 +       cl.loadsound_total * LOADPROGRESSWEIGHT_SOUND
1181                                 )
1182                         );
1183                 for (;cl.loadsound_current < cl.loadsound_total;cl.loadsound_current++)
1184                 {
1185                         SCR_PushLoadingScreen(false, cl.sound_name[cl.loadsound_current], 1.0 / cl.loadsound_total);
1186                         if (cl.sound_precache[cl.loadsound_current] && S_IsSoundPrecached(cl.sound_precache[cl.loadsound_current]))
1187                         {
1188                                 SCR_PopLoadingScreen(false);
1189                                 continue;
1190                         }
1191                         CL_KeepaliveMessage(true);
1192                         cl.sound_precache[cl.loadsound_current] = S_PrecacheSound(cl.sound_name[cl.loadsound_current], false, true);
1193                         SCR_PopLoadingScreen(false);
1194                 }
1195                 SCR_PopLoadingScreen(false);
1196                 // finished loading sounds
1197         }
1198
1199         if(gamemode == GAME_NEXUIZ || gamemode == GAME_XONOTIC)
1200                 Cvar_SetValueQuick(&cl_serverextension_download, false);
1201                 // in Nexuiz/Xonotic, the built in download protocol is kinda broken (misses lots
1202                 // of dependencies) anyway, and can mess around with the game directory;
1203                 // until this is fixed, only support pk3 downloads via curl, and turn off
1204                 // individual file downloads other than for CSQC
1205                 // on the other end of the download protocol, GAME_NEXUIZ/GAME_XONOTIC enforces writing
1206                 // to dlcache only
1207                 // idea: support download of pk3 files using this protocol later
1208
1209         // note: the reason these loops skip already-loaded things is that it
1210         // enables this command to be issued during the game if desired
1211
1212         if (cl.downloadmodel_current < cl.loadmodel_total)
1213         {
1214                 // loading models
1215
1216                 for (;cl.downloadmodel_current < cl.loadmodel_total;cl.downloadmodel_current++)
1217                 {
1218                         if (aborteddownload)
1219                         {
1220
1221                                 if (cl.downloadmodel_current == 1)
1222                                 {
1223                                         // the worldmodel failed, but we need to set up anyway
1224                                         Mod_FreeQ3Shaders();
1225                                         CL_SetupWorldModel();
1226                                         if (!cl.loadfinished && cl_joinbeforedownloadsfinish.integer)
1227                                         {
1228                                                 cl.loadfinished = true;
1229                                                 // now issue the spawn to move on to signon 2 like normal
1230                                                 if (cls.netcon)
1231                                                         Cmd_ForwardStringToServer("prespawn");
1232                                         }
1233                                 }
1234                                 aborteddownload = false;
1235                                 continue;
1236                         }
1237                         if (cl.model_precache[cl.downloadmodel_current] && cl.model_precache[cl.downloadmodel_current]->Draw)
1238                                 continue;
1239                         CL_KeepaliveMessage(true);
1240                         if (cl.model_name[cl.downloadmodel_current][0] != '*' && strcmp(cl.model_name[cl.downloadmodel_current], "null") && !FS_FileExists(cl.model_name[cl.downloadmodel_current]))
1241                         {
1242                                 if (cl.downloadmodel_current == 1)
1243                                         Con_Printf("Map %s not found\n", cl.model_name[cl.downloadmodel_current]);
1244                                 else
1245                                         Con_Printf("Model %s not found\n", cl.model_name[cl.downloadmodel_current]);
1246                                 // regarding the * check: don't try to download submodels
1247                                 if (cl_serverextension_download.integer && cls.netcon && cl.model_name[cl.downloadmodel_current][0] != '*' && !sv.active)
1248                                 {
1249                                         Cmd_ForwardStringToServer(va("download %s", cl.model_name[cl.downloadmodel_current]));
1250                                         // we'll try loading again when the download finishes
1251                                         return;
1252                                 }
1253                         }
1254
1255                         if(cl.downloadmodel_current == 1)
1256                         {
1257                                 // they'll be soon loaded, but make sure we apply freshly downloaded shaders from a curled pk3
1258                                 Mod_FreeQ3Shaders();
1259                         }
1260
1261                         cl.model_precache[cl.downloadmodel_current] = Mod_ForName(cl.model_name[cl.downloadmodel_current], false, false, cl.model_name[cl.downloadmodel_current][0] == '*' ? cl.model_name[1] : NULL);
1262                         if (cl.downloadmodel_current == 1)
1263                         {
1264                                 // we now have the worldmodel so we can set up the game world
1265                                 // or maybe we do not have it (cl_serverextension_download 0)
1266                                 // then we need to continue loading ANYWAY!
1267                                 CL_SetupWorldModel();
1268                                 if (!cl.loadfinished && cl_joinbeforedownloadsfinish.integer)
1269                                 {
1270                                         cl.loadfinished = true;
1271                                         // now issue the spawn to move on to signon 2 like normal
1272                                         if (cls.netcon)
1273                                                 Cmd_ForwardStringToServer("prespawn");
1274                                 }
1275                         }
1276                 }
1277
1278                 // finished loading models
1279         }
1280
1281         if (cl.downloadsound_current < cl.loadsound_total)
1282         {
1283                 // loading sounds
1284
1285                 for (;cl.downloadsound_current < cl.loadsound_total;cl.downloadsound_current++)
1286                 {
1287                         char soundname[MAX_QPATH];
1288                         if (aborteddownload)
1289                         {
1290                                 aborteddownload = false;
1291                                 continue;
1292                         }
1293                         if (cl.sound_precache[cl.downloadsound_current] && S_IsSoundPrecached(cl.sound_precache[cl.downloadsound_current]))
1294                                 continue;
1295                         CL_KeepaliveMessage(true);
1296                         dpsnprintf(soundname, sizeof(soundname), "sound/%s", cl.sound_name[cl.downloadsound_current]);
1297                         if (!FS_FileExists(soundname) && !FS_FileExists(cl.sound_name[cl.downloadsound_current]))
1298                         {
1299                                 Con_Printf("Sound %s not found\n", soundname);
1300                                 if (cl_serverextension_download.integer && cls.netcon && !sv.active)
1301                                 {
1302                                         Cmd_ForwardStringToServer(va("download %s", soundname));
1303                                         // we'll try loading again when the download finishes
1304                                         return;
1305                                 }
1306                         }
1307                         cl.sound_precache[cl.downloadsound_current] = S_PrecacheSound(cl.sound_name[cl.downloadsound_current], false, true);
1308                 }
1309
1310                 // finished loading sounds
1311         }
1312
1313         SCR_PopLoadingScreen(false);
1314
1315         if (!cl.loadfinished)
1316         {
1317                 cl.loadfinished = true;
1318
1319                 // check memory integrity
1320                 Mem_CheckSentinelsGlobal();
1321
1322                 // now issue the spawn to move on to signon 2 like normal
1323                 if (cls.netcon)
1324                         Cmd_ForwardStringToServer("prespawn");
1325         }
1326 }
1327
1328 void CL_BeginDownloads_f(void)
1329 {
1330         // prevent cl_begindownloads from being issued multiple times in one match
1331         // to prevent accidentally cancelled downloads
1332         if(cl.loadbegun)
1333                 Con_Printf("cl_begindownloads is only valid once per match\n");
1334         else
1335                 CL_BeginDownloads(false);
1336 }
1337
1338 void CL_StopDownload(int size, int crc)
1339 {
1340         if (cls.qw_downloadmemory && cls.qw_downloadmemorycursize == size && CRC_Block(cls.qw_downloadmemory, cls.qw_downloadmemorycursize) == crc)
1341         {
1342                 int existingcrc;
1343                 size_t existingsize;
1344                 const char *extension;
1345
1346                 if(cls.qw_download_deflate)
1347                 {
1348                         unsigned char *out;
1349                         size_t inflated_size;
1350                         out = FS_Inflate(cls.qw_downloadmemory, cls.qw_downloadmemorycursize, &inflated_size, tempmempool);
1351                         Mem_Free(cls.qw_downloadmemory);
1352                         if(out)
1353                         {
1354                                 Con_Printf("Inflated download: new size: %u (%g%%)\n", (unsigned)inflated_size, 100.0 - 100.0*(cls.qw_downloadmemorycursize / (float)inflated_size));
1355                                 cls.qw_downloadmemory = out;
1356                                 cls.qw_downloadmemorycursize = inflated_size;
1357                         }
1358                         else
1359                         {
1360                                 cls.qw_downloadmemory = NULL;
1361                                 cls.qw_downloadmemorycursize = 0;
1362                                 Con_Printf("Cannot inflate download, possibly corrupt or zlib not present\n");
1363                         }
1364                 }
1365
1366                 if(!cls.qw_downloadmemory)
1367                 {
1368                         Con_Printf("Download \"%s\" is corrupt (see above!)\n", cls.qw_downloadname);
1369                 }
1370                 else
1371                 {
1372                         crc = CRC_Block(cls.qw_downloadmemory, cls.qw_downloadmemorycursize);
1373                         size = cls.qw_downloadmemorycursize;
1374                         // finished file
1375                         // save to disk only if we don't already have it
1376                         // (this is mainly for playing back demos)
1377                         existingcrc = FS_CRCFile(cls.qw_downloadname, &existingsize);
1378                         if (existingsize || gamemode == GAME_NEXUIZ || gamemode == GAME_XONOTIC || !strcmp(cls.qw_downloadname, csqc_progname.string))
1379                                 // let csprogs ALWAYS go to dlcache, to prevent "viral csprogs"; also, never put files outside dlcache for Nexuiz/Xonotic
1380                         {
1381                                 if ((int)existingsize != size || existingcrc != crc)
1382                                 {
1383                                         // we have a mismatching file, pick another name for it
1384                                         char name[MAX_QPATH*2];
1385                                         dpsnprintf(name, sizeof(name), "dlcache/%s.%i.%i", cls.qw_downloadname, size, crc);
1386                                         if (!FS_FileExists(name))
1387                                         {
1388                                                 Con_Printf("Downloaded \"%s\" (%i bytes, %i CRC)\n", name, size, crc);
1389                                                 FS_WriteFile(name, cls.qw_downloadmemory, cls.qw_downloadmemorycursize);
1390                                         }
1391                                 }
1392                         }
1393                         else
1394                         {
1395                                 // we either don't have it or have a mismatching file...
1396                                 // so it's time to accept the file
1397                                 // but if we already have a mismatching file we need to rename
1398                                 // this new one, and if we already have this file in renamed form,
1399                                 // we do nothing
1400                                 Con_Printf("Downloaded \"%s\" (%i bytes, %i CRC)\n", cls.qw_downloadname, size, crc);
1401                                 FS_WriteFile(cls.qw_downloadname, cls.qw_downloadmemory, cls.qw_downloadmemorycursize);
1402                                 extension = FS_FileExtension(cls.qw_downloadname);
1403                                 if (!strcasecmp(extension, "pak") || !strcasecmp(extension, "pk3"))
1404                                         FS_Rescan();
1405                         }
1406                 }
1407         }
1408         else if (cls.qw_downloadmemory && size)
1409         {
1410                 Con_Printf("Download \"%s\" is corrupt (%i bytes, %i CRC, should be %i bytes, %i CRC), discarding\n", cls.qw_downloadname, size, crc, (int)cls.qw_downloadmemorycursize, (int)CRC_Block(cls.qw_downloadmemory, cls.qw_downloadmemorycursize));
1411                 CL_BeginDownloads(true);
1412         }
1413
1414         if (cls.qw_downloadmemory)
1415                 Mem_Free(cls.qw_downloadmemory);
1416         cls.qw_downloadmemory = NULL;
1417         cls.qw_downloadname[0] = 0;
1418         cls.qw_downloadmemorymaxsize = 0;
1419         cls.qw_downloadmemorycursize = 0;
1420         cls.qw_downloadpercent = 0;
1421 }
1422
1423 void CL_ParseDownload(void)
1424 {
1425         int i, start, size;
1426         static unsigned char data[NET_MAXMESSAGE];
1427         start = MSG_ReadLong();
1428         size = (unsigned short)MSG_ReadShort();
1429
1430         // record the start/size information to ack in the next input packet
1431         for (i = 0;i < CL_MAX_DOWNLOADACKS;i++)
1432         {
1433                 if (!cls.dp_downloadack[i].start && !cls.dp_downloadack[i].size)
1434                 {
1435                         cls.dp_downloadack[i].start = start;
1436                         cls.dp_downloadack[i].size = size;
1437                         break;
1438                 }
1439         }
1440
1441         MSG_ReadBytes(size, data);
1442
1443         if (!cls.qw_downloadname[0])
1444         {
1445                 if (size > 0)
1446                         Con_Printf("CL_ParseDownload: received %i bytes with no download active\n", size);
1447                 return;
1448         }
1449
1450         if (start + size > cls.qw_downloadmemorymaxsize)
1451                 Host_Error("corrupt download message\n");
1452
1453         // only advance cursize if the data is at the expected position
1454         // (gaps are unacceptable)
1455         memcpy(cls.qw_downloadmemory + start, data, size);
1456         cls.qw_downloadmemorycursize = start + size;
1457         cls.qw_downloadpercent = (int)floor((start+size) * 100.0 / cls.qw_downloadmemorymaxsize);
1458         cls.qw_downloadpercent = bound(0, cls.qw_downloadpercent, 100);
1459         cls.qw_downloadspeedcount += size;
1460 }
1461
1462 void CL_DownloadBegin_f(void)
1463 {
1464         int size = atoi(Cmd_Argv(1));
1465
1466         if (size < 0 || size > 1<<30 || FS_CheckNastyPath(Cmd_Argv(2), false))
1467         {
1468                 Con_Printf("cl_downloadbegin: received bogus information\n");
1469                 CL_StopDownload(0, 0);
1470                 return;
1471         }
1472
1473         if (cls.qw_downloadname[0])
1474                 Con_Printf("Download of %s aborted\n", cls.qw_downloadname);
1475
1476         CL_StopDownload(0, 0);
1477
1478         // we're really beginning a download now, so initialize stuff
1479         strlcpy(cls.qw_downloadname, Cmd_Argv(2), sizeof(cls.qw_downloadname));
1480         cls.qw_downloadmemorymaxsize = size;
1481         cls.qw_downloadmemory = (unsigned char *) Mem_Alloc(cls.permanentmempool, cls.qw_downloadmemorymaxsize);
1482         cls.qw_downloadnumber++;
1483
1484         cls.qw_download_deflate = false;
1485         if(Cmd_Argc() >= 4)
1486         {
1487                 if(!strcmp(Cmd_Argv(3), "deflate"))
1488                         cls.qw_download_deflate = true;
1489                 // check further encodings here
1490         }
1491
1492         Cmd_ForwardStringToServer("sv_startdownload");
1493 }
1494
1495 void CL_StopDownload_f(void)
1496 {
1497         Curl_CancelAll();
1498         if (cls.qw_downloadname[0])
1499         {
1500                 Con_Printf("Download of %s aborted\n", cls.qw_downloadname);
1501                 CL_StopDownload(0, 0);
1502         }
1503         CL_BeginDownloads(true);
1504 }
1505
1506 void CL_DownloadFinished_f(void)
1507 {
1508         if (Cmd_Argc() < 3)
1509         {
1510                 Con_Printf("Malformed cl_downloadfinished command\n");
1511                 return;
1512         }
1513         CL_StopDownload(atoi(Cmd_Argv(1)), atoi(Cmd_Argv(2)));
1514         CL_BeginDownloads(false);
1515 }
1516
1517 static void CL_SendPlayerInfo(void)
1518 {
1519         MSG_WriteByte (&cls.netcon->message, clc_stringcmd);
1520         MSG_WriteString (&cls.netcon->message, va("name \"%s\"", cl_name.string));
1521
1522         MSG_WriteByte (&cls.netcon->message, clc_stringcmd);
1523         MSG_WriteString (&cls.netcon->message, va("color %i %i", cl_color.integer >> 4, cl_color.integer & 15));
1524
1525         MSG_WriteByte (&cls.netcon->message, clc_stringcmd);
1526         MSG_WriteString (&cls.netcon->message, va("rate %i", cl_rate.integer));
1527
1528         if (cl_pmodel.integer)
1529         {
1530                 MSG_WriteByte (&cls.netcon->message, clc_stringcmd);
1531                 MSG_WriteString (&cls.netcon->message, va("pmodel %i", cl_pmodel.integer));
1532         }
1533         if (*cl_playermodel.string)
1534         {
1535                 MSG_WriteByte (&cls.netcon->message, clc_stringcmd);
1536                 MSG_WriteString (&cls.netcon->message, va("playermodel %s", cl_playermodel.string));
1537         }
1538         if (*cl_playerskin.string)
1539         {
1540                 MSG_WriteByte (&cls.netcon->message, clc_stringcmd);
1541                 MSG_WriteString (&cls.netcon->message, va("playerskin %s", cl_playerskin.string));
1542         }
1543 }
1544
1545 /*
1546 =====================
1547 CL_SignonReply
1548
1549 An svc_signonnum has been received, perform a client side setup
1550 =====================
1551 */
1552 static void CL_SignonReply (void)
1553 {
1554         Con_DPrintf("CL_SignonReply: %i\n", cls.signon);
1555
1556         switch (cls.signon)
1557         {
1558         case 1:
1559                 if (cls.netcon)
1560                 {
1561                         // send player info before we begin downloads
1562                         // (so that the server can see the player name while downloading)
1563                         CL_SendPlayerInfo();
1564
1565                         // execute cl_begindownloads next frame
1566                         // (after any commands added by svc_stufftext have been executed)
1567                         // when done with downloads the "prespawn" will be sent
1568                         Cbuf_AddText("\ncl_begindownloads\n");
1569
1570                         //MSG_WriteByte (&cls.netcon->message, clc_stringcmd);
1571                         //MSG_WriteString (&cls.netcon->message, "prespawn");
1572                 }
1573                 else // playing a demo...  make sure loading occurs as soon as possible
1574                         CL_BeginDownloads(false);
1575                 break;
1576
1577         case 2:
1578                 if (cls.netcon)
1579                 {
1580                         // LordHavoc: quake sent the player info here but due to downloads
1581                         // it is sent earlier instead
1582                         // CL_SendPlayerInfo();
1583
1584                         // LordHavoc: changed to begin a loading stage and issue this when done
1585                         MSG_WriteByte (&cls.netcon->message, clc_stringcmd);
1586                         MSG_WriteString (&cls.netcon->message, "spawn");
1587                 }
1588                 break;
1589
1590         case 3:
1591                 if (cls.netcon)
1592                 {
1593                         MSG_WriteByte (&cls.netcon->message, clc_stringcmd);
1594                         MSG_WriteString (&cls.netcon->message, "begin");
1595                 }
1596                 break;
1597
1598         case 4:
1599                 // after the level has been loaded, we shouldn't need the shaders, and
1600                 // if they are needed again they will be automatically loaded...
1601                 // we also don't need the unused models or sounds from the last level
1602                 Mod_FreeQ3Shaders();
1603                 Mod_PurgeUnused();
1604                 S_PurgeUnused();
1605
1606                 Con_ClearNotify();
1607                 if (COM_CheckParm("-profilegameonly"))
1608                         Sys_AllowProfiling(true);
1609                 break;
1610         }
1611 }
1612
1613 /*
1614 ==================
1615 CL_ParseServerInfo
1616 ==================
1617 */
1618 void CL_ParseServerInfo (void)
1619 {
1620         char *str;
1621         int i;
1622         protocolversion_t protocol;
1623         int nummodels, numsounds;
1624
1625         // if we start loading a level and a video is still playing, stop it
1626         CL_VideoStop();
1627
1628         Con_DPrint("Serverinfo packet received.\n");
1629         Collision_Cache_Reset(true);
1630
1631         // if server is active, we already began a loading plaque
1632         if (!sv.active)
1633         {
1634                 SCR_BeginLoadingPlaque();
1635                 S_StopAllSounds();
1636                 // free q3 shaders so that any newly downloaded shaders will be active
1637                 Mod_FreeQ3Shaders();
1638         }
1639
1640         // check memory integrity
1641         Mem_CheckSentinelsGlobal();
1642
1643         // clear cl_serverextension cvars
1644         Cvar_SetValueQuick(&cl_serverextension_download, 0);
1645
1646 //
1647 // wipe the client_state_t struct
1648 //
1649         CL_ClearState ();
1650
1651 // parse protocol version number
1652         i = MSG_ReadLong ();
1653         protocol = Protocol_EnumForNumber(i);
1654         if (protocol == PROTOCOL_UNKNOWN)
1655         {
1656                 Host_Error("CL_ParseServerInfo: Server is unrecognized protocol number (%i)", i);
1657                 return;
1658         }
1659         // hack for unmarked Nehahra movie demos which had a custom protocol
1660         if (protocol == PROTOCOL_QUAKEDP && cls.demoplayback && gamemode == GAME_NEHAHRA)
1661                 protocol = PROTOCOL_NEHAHRAMOVIE;
1662         cls.protocol = protocol;
1663         Con_DPrintf("Server protocol is %s\n", Protocol_NameForEnum(cls.protocol));
1664
1665         cl.num_entities = 1;
1666
1667         if (protocol == PROTOCOL_QUAKEWORLD)
1668         {
1669                 char gamedir[1][MAX_QPATH];
1670
1671                 cl.qw_servercount = MSG_ReadLong();
1672
1673                 str = MSG_ReadString();
1674                 Con_Printf("server gamedir is %s\n", str);
1675                 strlcpy(gamedir[0], str, sizeof(gamedir[0]));
1676
1677                 // change gamedir if needed
1678                 if (!FS_ChangeGameDirs(1, gamedir, true, false))
1679                         Host_Error("CL_ParseServerInfo: unable to switch to server specified gamedir");
1680
1681                 cl.gametype = GAME_DEATHMATCH;
1682                 cl.maxclients = 32;
1683
1684                 // parse player number
1685                 i = MSG_ReadByte();
1686                 // cl.qw_spectator is an unneeded flag, cl.scores[cl.playerentity].qw_spectator works better (it can be updated by the server during the game)
1687                 //cl.qw_spectator = (i & 128) != 0;
1688                 cl.realplayerentity = cl.playerentity = cl.viewentity = (i & 127) + 1;
1689                 cl.scores = (scoreboard_t *)Mem_Alloc(cls.levelmempool, cl.maxclients*sizeof(*cl.scores));
1690
1691                 // get the full level name
1692                 str = MSG_ReadString ();
1693                 strlcpy (cl.worldmessage, str, sizeof(cl.worldmessage));
1694
1695                 // get the movevars that are defined in the qw protocol
1696                 cl.movevars_gravity            = MSG_ReadFloat();
1697                 cl.movevars_stopspeed          = MSG_ReadFloat();
1698                 cl.movevars_maxspeed           = MSG_ReadFloat();
1699                 cl.movevars_spectatormaxspeed  = MSG_ReadFloat();
1700                 cl.movevars_accelerate         = MSG_ReadFloat();
1701                 cl.movevars_airaccelerate      = MSG_ReadFloat();
1702                 cl.movevars_wateraccelerate    = MSG_ReadFloat();
1703                 cl.movevars_friction           = MSG_ReadFloat();
1704                 cl.movevars_waterfriction      = MSG_ReadFloat();
1705                 cl.movevars_entgravity         = MSG_ReadFloat();
1706
1707                 // other movevars not in the protocol...
1708                 cl.movevars_wallfriction = 0;
1709                 cl.movevars_timescale = 1;
1710                 cl.movevars_jumpvelocity = 270;
1711                 cl.movevars_edgefriction = 1;
1712                 cl.movevars_maxairspeed = 30;
1713                 cl.movevars_stepheight = 18;
1714                 cl.movevars_airaccel_qw = 1;
1715                 cl.movevars_airaccel_sideways_friction = 0;
1716
1717                 // seperate the printfs so the server message can have a color
1718                 Con_Printf("\n\n<===================================>\n\n\2%s\n", str);
1719
1720                 // check memory integrity
1721                 Mem_CheckSentinelsGlobal();
1722
1723                 if (cls.netcon)
1724                 {
1725                         MSG_WriteByte(&cls.netcon->message, qw_clc_stringcmd);
1726                         MSG_WriteString(&cls.netcon->message, va("soundlist %i %i", cl.qw_servercount, 0));
1727                 }
1728
1729                 cl.loadbegun = false;
1730                 cl.loadfinished = false;
1731
1732                 cls.state = ca_connected;
1733                 cls.signon = 1;
1734
1735                 // note: on QW protocol we can't set up the gameworld until after
1736                 // downloads finish...
1737                 // (we don't even know the name of the map yet)
1738                 // this also means cl_autodemo does not work on QW protocol...
1739
1740                 strlcpy(cl.worldname, "", sizeof(cl.worldname));
1741                 strlcpy(cl.worldnamenoextension, "", sizeof(cl.worldnamenoextension));
1742                 strlcpy(cl.worldbasename, "qw", sizeof(cl.worldbasename));
1743                 Cvar_SetQuick(&cl_worldname, cl.worldname);
1744                 Cvar_SetQuick(&cl_worldnamenoextension, cl.worldnamenoextension);
1745                 Cvar_SetQuick(&cl_worldbasename, cl.worldbasename);
1746
1747                 // check memory integrity
1748                 Mem_CheckSentinelsGlobal();
1749         }
1750         else
1751         {
1752         // parse maxclients
1753                 cl.maxclients = MSG_ReadByte ();
1754                 if (cl.maxclients < 1 || cl.maxclients > MAX_SCOREBOARD)
1755                 {
1756                         Host_Error("Bad maxclients (%u) from server", cl.maxclients);
1757                         return;
1758                 }
1759                 cl.scores = (scoreboard_t *)Mem_Alloc(cls.levelmempool, cl.maxclients*sizeof(*cl.scores));
1760
1761         // parse gametype
1762                 cl.gametype = MSG_ReadByte ();
1763                 // the original id singleplayer demos are bugged and contain
1764                 // GAME_DEATHMATCH even for singleplayer
1765                 if (cl.maxclients == 1 && cls.protocol == PROTOCOL_QUAKE)
1766                         cl.gametype = GAME_COOP;
1767
1768         // parse signon message
1769                 str = MSG_ReadString ();
1770                 strlcpy (cl.worldmessage, str, sizeof(cl.worldmessage));
1771
1772         // seperate the printfs so the server message can have a color
1773                 if (cls.protocol != PROTOCOL_NEHAHRAMOVIE) // no messages when playing the Nehahra movie
1774                         Con_Printf("\n<===================================>\n\n\2%s\n", str);
1775
1776                 // check memory integrity
1777                 Mem_CheckSentinelsGlobal();
1778
1779                 // parse model precache list
1780                 for (nummodels=1 ; ; nummodels++)
1781                 {
1782                         str = MSG_ReadString();
1783                         if (!str[0])
1784                                 break;
1785                         if (nummodels==MAX_MODELS)
1786                                 Host_Error ("Server sent too many model precaches");
1787                         if (strlen(str) >= MAX_QPATH)
1788                                 Host_Error ("Server sent a precache name of %i characters (max %i)", (int)strlen(str), MAX_QPATH - 1);
1789                         strlcpy (cl.model_name[nummodels], str, sizeof (cl.model_name[nummodels]));
1790                 }
1791                 // parse sound precache list
1792                 for (numsounds=1 ; ; numsounds++)
1793                 {
1794                         str = MSG_ReadString();
1795                         if (!str[0])
1796                                 break;
1797                         if (numsounds==MAX_SOUNDS)
1798                                 Host_Error("Server sent too many sound precaches");
1799                         if (strlen(str) >= MAX_QPATH)
1800                                 Host_Error("Server sent a precache name of %i characters (max %i)", (int)strlen(str), MAX_QPATH - 1);
1801                         strlcpy (cl.sound_name[numsounds], str, sizeof (cl.sound_name[numsounds]));
1802                 }
1803
1804                 // set the base name for level-specific things...  this gets updated again by CL_SetupWorldModel later
1805                 strlcpy(cl.worldname, cl.model_name[1], sizeof(cl.worldname));
1806                 FS_StripExtension(cl.worldname, cl.worldnamenoextension, sizeof(cl.worldnamenoextension));
1807                 strlcpy(cl.worldbasename, !strncmp(cl.worldnamenoextension, "maps/", 5) ? cl.worldnamenoextension + 5 : cl.worldnamenoextension, sizeof(cl.worldbasename));
1808                 Cvar_SetQuick(&cl_worldmessage, cl.worldmessage);
1809                 Cvar_SetQuick(&cl_worldname, cl.worldname);
1810                 Cvar_SetQuick(&cl_worldnamenoextension, cl.worldnamenoextension);
1811                 Cvar_SetQuick(&cl_worldbasename, cl.worldbasename);
1812
1813                 // touch all of the precached models that are still loaded so we can free
1814                 // anything that isn't needed
1815                 if (!sv.active)
1816                         Mod_ClearUsed();
1817                 for (i = 1;i < nummodels;i++)
1818                         Mod_FindName(cl.model_name[i], cl.model_name[i][0] == '*' ? cl.model_name[1] : NULL);
1819                 // precache any models used by the client (this also marks them used)
1820                 cl.model_bolt = Mod_ForName("progs/bolt.mdl", false, false, NULL);
1821                 cl.model_bolt2 = Mod_ForName("progs/bolt2.mdl", false, false, NULL);
1822                 cl.model_bolt3 = Mod_ForName("progs/bolt3.mdl", false, false, NULL);
1823                 cl.model_beam = Mod_ForName("progs/beam.mdl", false, false, NULL);
1824
1825                 // we purge the models and sounds later in CL_SignonReply
1826                 //Mod_PurgeUnused();
1827                 //S_PurgeUnused();
1828
1829                 // clear sound usage flags for purging of unused sounds
1830                 S_ClearUsed();
1831
1832                 // precache any sounds used by the client
1833                 cl.sfx_wizhit = S_PrecacheSound(cl_sound_wizardhit.string, false, true);
1834                 cl.sfx_knighthit = S_PrecacheSound(cl_sound_hknighthit.string, false, true);
1835                 cl.sfx_tink1 = S_PrecacheSound(cl_sound_tink1.string, false, true);
1836                 cl.sfx_ric1 = S_PrecacheSound(cl_sound_ric1.string, false, true);
1837                 cl.sfx_ric2 = S_PrecacheSound(cl_sound_ric2.string, false, true);
1838                 cl.sfx_ric3 = S_PrecacheSound(cl_sound_ric3.string, false, true);
1839                 cl.sfx_r_exp3 = S_PrecacheSound(cl_sound_r_exp3.string, false, true);
1840
1841                 // sounds used by the game
1842                 for (i = 1;i < MAX_SOUNDS && cl.sound_name[i][0];i++)
1843                         cl.sound_precache[i] = S_PrecacheSound(cl.sound_name[i], true, true);
1844
1845                 // now we try to load everything that is new
1846                 cl.loadmodel_current = 1;
1847                 cl.downloadmodel_current = 1;
1848                 cl.loadmodel_total = nummodels;
1849                 cl.loadsound_current = 1;
1850                 cl.downloadsound_current = 1;
1851                 cl.loadsound_total = numsounds;
1852                 cl.downloadcsqc = true;
1853                 cl.loadbegun = false;
1854                 cl.loadfinished = false;
1855                 cl.loadcsqc = true;
1856
1857                 // check memory integrity
1858                 Mem_CheckSentinelsGlobal();
1859
1860         // if cl_autodemo is set, automatically start recording a demo if one isn't being recorded already
1861                 if (cl_autodemo.integer && cls.netcon && cls.protocol != PROTOCOL_QUAKEWORLD)
1862                 {
1863                         char demofile[MAX_OSPATH];
1864
1865                         if (cls.demorecording)
1866                         {
1867                                 // finish the previous level's demo file
1868                                 CL_Stop_f();
1869                         }
1870
1871                         // start a new demo file
1872                         dpsnprintf (demofile, sizeof(demofile), "%s_%s.dem", Sys_TimeString (cl_autodemo_nameformat.string), cl.worldbasename);
1873
1874                         Con_Printf ("Auto-recording to %s.\n", demofile);
1875
1876                         // Reset bit 0 for every new demo
1877                         Cvar_SetValueQuick(&cl_autodemo_delete,
1878                                 (cl_autodemo_delete.integer & ~0x1)
1879                                 |
1880                                 ((cl_autodemo_delete.integer & 0x2) ? 0x1 : 0)
1881                         );
1882
1883                         cls.demofile = FS_OpenRealFile(demofile, "wb", false);
1884                         if (cls.demofile)
1885                         {
1886                                 cls.forcetrack = -1;
1887                                 FS_Printf (cls.demofile, "%i\n", cls.forcetrack);
1888                                 cls.demorecording = true;
1889                                 strlcpy(cls.demoname, demofile, sizeof(cls.demoname));
1890                                 cls.demo_lastcsprogssize = -1;
1891                                 cls.demo_lastcsprogscrc = -1;
1892                         }
1893                         else
1894                                 Con_Print ("ERROR: couldn't open.\n");
1895                 }
1896         }
1897 }
1898
1899 void CL_ValidateState(entity_state_t *s)
1900 {
1901         dp_model_t *model;
1902
1903         if (!s->active)
1904                 return;
1905
1906         if (s->modelindex >= MAX_MODELS)
1907                 Host_Error("CL_ValidateState: modelindex (%i) >= MAX_MODELS (%i)\n", s->modelindex, MAX_MODELS);
1908
1909         // these warnings are only warnings, no corrections are made to the state
1910         // because states are often copied for decoding, which otherwise would
1911         // propogate some of the corrections accidentally
1912         // (this used to happen, sometimes affecting skin and frame)
1913
1914         // colormap is client index + 1
1915         if (!(s->flags & RENDER_COLORMAPPED) && s->colormap > cl.maxclients)
1916                 Con_DPrintf("CL_ValidateState: colormap (%i) > cl.maxclients (%i)\n", s->colormap, cl.maxclients);
1917
1918         if (developer_extra.integer)
1919         {
1920                 model = CL_GetModelByIndex(s->modelindex);
1921                 if (model && model->type && s->frame >= model->numframes)
1922                         Con_DPrintf("CL_ValidateState: no such frame %i in \"%s\" (which has %i frames)\n", s->frame, model->name, model->numframes);
1923                 if (model && model->type && s->skin > 0 && s->skin >= model->numskins && !(s->lightpflags & PFLAGS_FULLDYNAMIC))
1924                         Con_DPrintf("CL_ValidateState: no such skin %i in \"%s\" (which has %i skins)\n", s->skin, model->name, model->numskins);
1925         }
1926 }
1927
1928 void CL_MoveLerpEntityStates(entity_t *ent)
1929 {
1930         float odelta[3], adelta[3];
1931         VectorSubtract(ent->state_current.origin, ent->persistent.neworigin, odelta);
1932         VectorSubtract(ent->state_current.angles, ent->persistent.newangles, adelta);
1933         if (!ent->state_previous.active || ent->state_previous.modelindex != ent->state_current.modelindex)
1934         {
1935                 // reset all persistent stuff if this is a new entity
1936                 ent->persistent.lerpdeltatime = 0;
1937                 ent->persistent.lerpstarttime = cl.mtime[1];
1938                 VectorCopy(ent->state_current.origin, ent->persistent.oldorigin);
1939                 VectorCopy(ent->state_current.angles, ent->persistent.oldangles);
1940                 VectorCopy(ent->state_current.origin, ent->persistent.neworigin);
1941                 VectorCopy(ent->state_current.angles, ent->persistent.newangles);
1942                 // reset animation interpolation as well
1943                 ent->render.framegroupblend[0].frame = ent->render.framegroupblend[1].frame = ent->state_current.frame;
1944                 ent->render.framegroupblend[0].start = ent->render.framegroupblend[1].start = cl.time;
1945                 ent->render.framegroupblend[0].lerp = 1;ent->render.framegroupblend[1].lerp = 0;
1946                 ent->render.shadertime = cl.time;
1947                 // reset various persistent stuff
1948                 ent->persistent.muzzleflash = 0;
1949                 ent->persistent.trail_allowed = false;
1950         }
1951         else if ((ent->state_previous.effects & EF_TELEPORT_BIT) != (ent->state_current.effects & EF_TELEPORT_BIT))
1952         {
1953                 // don't interpolate the move
1954                 ent->persistent.lerpdeltatime = 0;
1955                 ent->persistent.lerpstarttime = cl.mtime[1];
1956                 VectorCopy(ent->state_current.origin, ent->persistent.oldorigin);
1957                 VectorCopy(ent->state_current.angles, ent->persistent.oldangles);
1958                 VectorCopy(ent->state_current.origin, ent->persistent.neworigin);
1959                 VectorCopy(ent->state_current.angles, ent->persistent.newangles);
1960                 ent->persistent.trail_allowed = false;
1961
1962                 // if(ent->state_current.frame != ent->state_previous.frame)
1963                 // do this even if we did change the frame
1964                 // teleport bit is only used if an animation restart, or a jump, is necessary
1965                 // so it should be always harmless to do this
1966                 {
1967                         ent->render.framegroupblend[0].frame = ent->render.framegroupblend[1].frame = ent->state_current.frame;
1968                         ent->render.framegroupblend[0].start = ent->render.framegroupblend[1].start = cl.time;
1969                         ent->render.framegroupblend[0].lerp = 1;ent->render.framegroupblend[1].lerp = 0;
1970                 }
1971
1972                 // note that this case must do everything the following case does too
1973         }
1974         else if ((ent->state_previous.effects & EF_RESTARTANIM_BIT) != (ent->state_current.effects & EF_RESTARTANIM_BIT))
1975         {
1976                 ent->render.framegroupblend[1] = ent->render.framegroupblend[0];
1977                 ent->render.framegroupblend[1].lerp = 1;
1978                 ent->render.framegroupblend[0].frame = ent->state_current.frame;
1979                 ent->render.framegroupblend[0].start = cl.time;
1980                 ent->render.framegroupblend[0].lerp = 0;
1981         }
1982         else if (DotProduct(odelta, odelta) > 1000*1000
1983                 || (cl.fixangle[0] && !cl.fixangle[1])
1984                 || (ent->state_previous.tagindex != ent->state_current.tagindex)
1985                 || (ent->state_previous.tagentity != ent->state_current.tagentity))
1986         {
1987                 // don't interpolate the move
1988                 // (the fixangle[] check detects teleports, but not constant fixangles
1989                 //  such as when spectating)
1990                 ent->persistent.lerpdeltatime = 0;
1991                 ent->persistent.lerpstarttime = cl.mtime[1];
1992                 VectorCopy(ent->state_current.origin, ent->persistent.oldorigin);
1993                 VectorCopy(ent->state_current.angles, ent->persistent.oldangles);
1994                 VectorCopy(ent->state_current.origin, ent->persistent.neworigin);
1995                 VectorCopy(ent->state_current.angles, ent->persistent.newangles);
1996                 ent->persistent.trail_allowed = false;
1997         }
1998         else if (ent->state_current.flags & RENDER_STEP)
1999         {
2000                 // monster interpolation
2001                 if (DotProduct(odelta, odelta) + DotProduct(adelta, adelta) > 0.01)
2002                 {
2003                         ent->persistent.lerpdeltatime = bound(0, cl.mtime[1] - ent->persistent.lerpstarttime, 0.1);
2004                         ent->persistent.lerpstarttime = cl.mtime[1];
2005                         VectorCopy(ent->persistent.neworigin, ent->persistent.oldorigin);
2006                         VectorCopy(ent->persistent.newangles, ent->persistent.oldangles);
2007                         VectorCopy(ent->state_current.origin, ent->persistent.neworigin);
2008                         VectorCopy(ent->state_current.angles, ent->persistent.newangles);
2009                 }
2010         }
2011         else
2012         {
2013                 // not a monster
2014                 ent->persistent.lerpstarttime = ent->state_previous.time;
2015                 // no lerp if it's singleplayer
2016                 if (cl.islocalgame && !sv_fixedframeratesingleplayer.integer)
2017                         ent->persistent.lerpdeltatime = 0;
2018                 else
2019                         ent->persistent.lerpdeltatime = bound(0, ent->state_current.time - ent->state_previous.time, 0.1);
2020                 VectorCopy(ent->persistent.neworigin, ent->persistent.oldorigin);
2021                 VectorCopy(ent->persistent.newangles, ent->persistent.oldangles);
2022                 VectorCopy(ent->state_current.origin, ent->persistent.neworigin);
2023                 VectorCopy(ent->state_current.angles, ent->persistent.newangles);
2024         }
2025         // trigger muzzleflash effect if necessary
2026         if (ent->state_current.effects & EF_MUZZLEFLASH)
2027                 ent->persistent.muzzleflash = 1;
2028
2029         // restart animation bit
2030         if ((ent->state_previous.effects & EF_RESTARTANIM_BIT) != (ent->state_current.effects & EF_RESTARTANIM_BIT))
2031         {
2032                 ent->render.framegroupblend[1] = ent->render.framegroupblend[0];
2033                 ent->render.framegroupblend[1].lerp = 1;
2034                 ent->render.framegroupblend[0].frame = ent->state_current.frame;
2035                 ent->render.framegroupblend[0].start = cl.time;
2036                 ent->render.framegroupblend[0].lerp = 0;
2037         }
2038 }
2039
2040 /*
2041 ==================
2042 CL_ParseBaseline
2043 ==================
2044 */
2045 void CL_ParseBaseline (entity_t *ent, int large)
2046 {
2047         int i;
2048
2049         ent->state_baseline = defaultstate;
2050         // FIXME: set ent->state_baseline.number?
2051         ent->state_baseline.active = true;
2052         if (large)
2053         {
2054                 ent->state_baseline.modelindex = (unsigned short) MSG_ReadShort ();
2055                 ent->state_baseline.frame = (unsigned short) MSG_ReadShort ();
2056         }
2057         else if (cls.protocol == PROTOCOL_NEHAHRABJP || cls.protocol == PROTOCOL_NEHAHRABJP2 || cls.protocol == PROTOCOL_NEHAHRABJP3)
2058         {
2059                 ent->state_baseline.modelindex = (unsigned short) MSG_ReadShort ();
2060                 ent->state_baseline.frame = MSG_ReadByte ();
2061         }
2062         else
2063         {
2064                 ent->state_baseline.modelindex = MSG_ReadByte ();
2065                 ent->state_baseline.frame = MSG_ReadByte ();
2066         }
2067         ent->state_baseline.colormap = MSG_ReadByte();
2068         ent->state_baseline.skin = MSG_ReadByte();
2069         for (i = 0;i < 3;i++)
2070         {
2071                 ent->state_baseline.origin[i] = MSG_ReadCoord(cls.protocol);
2072                 ent->state_baseline.angles[i] = MSG_ReadAngle(cls.protocol);
2073         }
2074         ent->state_previous = ent->state_current = ent->state_baseline;
2075 }
2076
2077
2078 /*
2079 ==================
2080 CL_ParseClientdata
2081
2082 Server information pertaining to this client only
2083 ==================
2084 */
2085 void CL_ParseClientdata (void)
2086 {
2087         int i, bits;
2088
2089         VectorCopy (cl.mpunchangle[0], cl.mpunchangle[1]);
2090         VectorCopy (cl.mpunchvector[0], cl.mpunchvector[1]);
2091         VectorCopy (cl.mvelocity[0], cl.mvelocity[1]);
2092         cl.mviewzoom[1] = cl.mviewzoom[0];
2093
2094         if (cls.protocol == PROTOCOL_QUAKE || cls.protocol == PROTOCOL_QUAKEDP || cls.protocol == PROTOCOL_NEHAHRAMOVIE || cls.protocol == PROTOCOL_NEHAHRABJP || cls.protocol == PROTOCOL_NEHAHRABJP2 || cls.protocol == PROTOCOL_NEHAHRABJP3 || cls.protocol == PROTOCOL_DARKPLACES1 || cls.protocol == PROTOCOL_DARKPLACES2 || cls.protocol == PROTOCOL_DARKPLACES3 || cls.protocol == PROTOCOL_DARKPLACES4 || cls.protocol == PROTOCOL_DARKPLACES5)
2095         {
2096                 cl.stats[STAT_VIEWHEIGHT] = DEFAULT_VIEWHEIGHT;
2097                 cl.stats[STAT_ITEMS] = 0;
2098                 cl.stats[STAT_VIEWZOOM] = 255;
2099         }
2100         cl.idealpitch = 0;
2101         cl.mpunchangle[0][0] = 0;
2102         cl.mpunchangle[0][1] = 0;
2103         cl.mpunchangle[0][2] = 0;
2104         cl.mpunchvector[0][0] = 0;
2105         cl.mpunchvector[0][1] = 0;
2106         cl.mpunchvector[0][2] = 0;
2107         cl.mvelocity[0][0] = 0;
2108         cl.mvelocity[0][1] = 0;
2109         cl.mvelocity[0][2] = 0;
2110         cl.mviewzoom[0] = 1;
2111
2112         bits = (unsigned short) MSG_ReadShort ();
2113         if (bits & SU_EXTEND1)
2114                 bits |= (MSG_ReadByte() << 16);
2115         if (bits & SU_EXTEND2)
2116                 bits |= (MSG_ReadByte() << 24);
2117
2118         if (bits & SU_VIEWHEIGHT)
2119                 cl.stats[STAT_VIEWHEIGHT] = MSG_ReadChar ();
2120
2121         if (bits & SU_IDEALPITCH)
2122                 cl.idealpitch = MSG_ReadChar ();
2123
2124         for (i = 0;i < 3;i++)
2125         {
2126                 if (bits & (SU_PUNCH1<<i) )
2127                 {
2128                         if (cls.protocol == PROTOCOL_QUAKE || cls.protocol == PROTOCOL_QUAKEDP || cls.protocol == PROTOCOL_NEHAHRAMOVIE || cls.protocol == PROTOCOL_NEHAHRABJP || cls.protocol == PROTOCOL_NEHAHRABJP2 || cls.protocol == PROTOCOL_NEHAHRABJP3)
2129                                 cl.mpunchangle[0][i] = MSG_ReadChar();
2130                         else
2131                                 cl.mpunchangle[0][i] = MSG_ReadAngle16i();
2132                 }
2133                 if (bits & (SU_PUNCHVEC1<<i))
2134                 {
2135                         if (cls.protocol == PROTOCOL_DARKPLACES1 || cls.protocol == PROTOCOL_DARKPLACES2 || cls.protocol == PROTOCOL_DARKPLACES3 || cls.protocol == PROTOCOL_DARKPLACES4)
2136                                 cl.mpunchvector[0][i] = MSG_ReadCoord16i();
2137                         else
2138                                 cl.mpunchvector[0][i] = MSG_ReadCoord32f();
2139                 }
2140                 if (bits & (SU_VELOCITY1<<i) )
2141                 {
2142                         if (cls.protocol == PROTOCOL_QUAKE || cls.protocol == PROTOCOL_QUAKEDP || cls.protocol == PROTOCOL_NEHAHRAMOVIE || cls.protocol == PROTOCOL_NEHAHRABJP || cls.protocol == PROTOCOL_NEHAHRABJP2 || cls.protocol == PROTOCOL_NEHAHRABJP3 || cls.protocol == PROTOCOL_DARKPLACES1 || cls.protocol == PROTOCOL_DARKPLACES2 || cls.protocol == PROTOCOL_DARKPLACES3 || cls.protocol == PROTOCOL_DARKPLACES4)
2143                                 cl.mvelocity[0][i] = MSG_ReadChar()*16;
2144                         else
2145                                 cl.mvelocity[0][i] = MSG_ReadCoord32f();
2146                 }
2147         }
2148
2149         // LordHavoc: hipnotic demos don't have this bit set but should
2150         if (bits & SU_ITEMS || cls.protocol == PROTOCOL_QUAKE || cls.protocol == PROTOCOL_QUAKEDP || cls.protocol == PROTOCOL_NEHAHRAMOVIE || cls.protocol == PROTOCOL_NEHAHRABJP || cls.protocol == PROTOCOL_NEHAHRABJP2 || cls.protocol == PROTOCOL_NEHAHRABJP3 || cls.protocol == PROTOCOL_DARKPLACES1 || cls.protocol == PROTOCOL_DARKPLACES2 || cls.protocol == PROTOCOL_DARKPLACES3 || cls.protocol == PROTOCOL_DARKPLACES4 || cls.protocol == PROTOCOL_DARKPLACES5)
2151                 cl.stats[STAT_ITEMS] = MSG_ReadLong ();
2152
2153         cl.onground = (bits & SU_ONGROUND) != 0;
2154         cl.inwater = (bits & SU_INWATER) != 0;
2155
2156         if (cls.protocol == PROTOCOL_DARKPLACES5)
2157         {
2158                 cl.stats[STAT_WEAPONFRAME] = (bits & SU_WEAPONFRAME) ? MSG_ReadShort() : 0;
2159                 cl.stats[STAT_ARMOR] = (bits & SU_ARMOR) ? MSG_ReadShort() : 0;
2160                 cl.stats[STAT_WEAPON] = (bits & SU_WEAPON) ? MSG_ReadShort() : 0;
2161                 cl.stats[STAT_HEALTH] = MSG_ReadShort();
2162                 cl.stats[STAT_AMMO] = MSG_ReadShort();
2163                 cl.stats[STAT_SHELLS] = MSG_ReadShort();
2164                 cl.stats[STAT_NAILS] = MSG_ReadShort();
2165                 cl.stats[STAT_ROCKETS] = MSG_ReadShort();
2166                 cl.stats[STAT_CELLS] = MSG_ReadShort();
2167                 cl.stats[STAT_ACTIVEWEAPON] = (unsigned short) MSG_ReadShort ();
2168         }
2169         else if (cls.protocol == PROTOCOL_QUAKE || cls.protocol == PROTOCOL_QUAKEDP || cls.protocol == PROTOCOL_NEHAHRAMOVIE || cls.protocol == PROTOCOL_NEHAHRABJP || cls.protocol == PROTOCOL_NEHAHRABJP2 || cls.protocol == PROTOCOL_NEHAHRABJP3 || cls.protocol == PROTOCOL_NEHAHRABJP || cls.protocol == PROTOCOL_NEHAHRABJP2 || cls.protocol == PROTOCOL_NEHAHRABJP3 || cls.protocol == PROTOCOL_DARKPLACES1 || cls.protocol == PROTOCOL_DARKPLACES2 || cls.protocol == PROTOCOL_DARKPLACES3 || cls.protocol == PROTOCOL_DARKPLACES4)
2170         {
2171                 cl.stats[STAT_WEAPONFRAME] = (bits & SU_WEAPONFRAME) ? MSG_ReadByte() : 0;
2172                 cl.stats[STAT_ARMOR] = (bits & SU_ARMOR) ? MSG_ReadByte() : 0;
2173                 if (cls.protocol == PROTOCOL_NEHAHRABJP || cls.protocol == PROTOCOL_NEHAHRABJP2 || cls.protocol == PROTOCOL_NEHAHRABJP3)
2174                         cl.stats[STAT_WEAPON] = (bits & SU_WEAPON) ? (unsigned short)MSG_ReadShort() : 0;
2175                 else
2176                         cl.stats[STAT_WEAPON] = (bits & SU_WEAPON) ? MSG_ReadByte() : 0;
2177                 cl.stats[STAT_HEALTH] = MSG_ReadShort();
2178                 cl.stats[STAT_AMMO] = MSG_ReadByte();
2179                 cl.stats[STAT_SHELLS] = MSG_ReadByte();
2180                 cl.stats[STAT_NAILS] = MSG_ReadByte();
2181                 cl.stats[STAT_ROCKETS] = MSG_ReadByte();
2182                 cl.stats[STAT_CELLS] = MSG_ReadByte();
2183                 if (gamemode == GAME_HIPNOTIC || gamemode == GAME_ROGUE || gamemode == GAME_NEXUIZ)
2184                         cl.stats[STAT_ACTIVEWEAPON] = (1<<MSG_ReadByte ());
2185                 else
2186                         cl.stats[STAT_ACTIVEWEAPON] = MSG_ReadByte ();
2187         }
2188
2189         if (bits & SU_VIEWZOOM)
2190         {
2191                 if (cls.protocol == PROTOCOL_DARKPLACES2 || cls.protocol == PROTOCOL_DARKPLACES3 || cls.protocol == PROTOCOL_DARKPLACES4)
2192                         cl.stats[STAT_VIEWZOOM] = MSG_ReadByte();
2193                 else
2194                         cl.stats[STAT_VIEWZOOM] = (unsigned short) MSG_ReadShort();
2195         }
2196
2197         // viewzoom interpolation
2198         cl.mviewzoom[0] = (float) max(cl.stats[STAT_VIEWZOOM], 2) * (1.0f / 255.0f);
2199 }
2200
2201 /*
2202 =====================
2203 CL_ParseStatic
2204 =====================
2205 */
2206 void CL_ParseStatic (int large)
2207 {
2208         entity_t *ent;
2209
2210         if (cl.num_static_entities >= cl.max_static_entities)
2211                 Host_Error ("Too many static entities");
2212         ent = &cl.static_entities[cl.num_static_entities++];
2213         CL_ParseBaseline (ent, large);
2214
2215         if (ent->state_baseline.modelindex == 0)
2216         {
2217                 Con_DPrintf("svc_parsestatic: static entity without model at %f %f %f\n", ent->state_baseline.origin[0], ent->state_baseline.origin[1], ent->state_baseline.origin[2]);
2218                 cl.num_static_entities--;
2219                 // This is definitely a cheesy way to conserve resources...
2220                 return;
2221         }
2222
2223 // copy it to the current state
2224         ent->render.model = CL_GetModelByIndex(ent->state_baseline.modelindex);
2225         ent->render.framegroupblend[0].frame = ent->state_baseline.frame;
2226         ent->render.framegroupblend[0].lerp = 1;
2227         // make torchs play out of sync
2228         ent->render.framegroupblend[0].start = lhrandom(-10, -1);
2229         ent->render.skinnum = ent->state_baseline.skin;
2230         ent->render.effects = ent->state_baseline.effects;
2231         ent->render.alpha = 1;
2232
2233         //VectorCopy (ent->state_baseline.origin, ent->render.origin);
2234         //VectorCopy (ent->state_baseline.angles, ent->render.angles);
2235
2236         Matrix4x4_CreateFromQuakeEntity(&ent->render.matrix, ent->state_baseline.origin[0], ent->state_baseline.origin[1], ent->state_baseline.origin[2], ent->state_baseline.angles[0], ent->state_baseline.angles[1], ent->state_baseline.angles[2], 1);
2237         ent->render.allowdecals = true;
2238         CL_UpdateRenderEntity(&ent->render);
2239 }
2240
2241 /*
2242 ===================
2243 CL_ParseStaticSound
2244 ===================
2245 */
2246 void CL_ParseStaticSound (int large)
2247 {
2248         vec3_t          org;
2249         int                     sound_num, vol, atten;
2250
2251         MSG_ReadVector(org, cls.protocol);
2252         if (large || cls.protocol == PROTOCOL_NEHAHRABJP2)
2253                 sound_num = (unsigned short) MSG_ReadShort ();
2254         else
2255                 sound_num = MSG_ReadByte ();
2256         vol = MSG_ReadByte ();
2257         atten = MSG_ReadByte ();
2258
2259         S_StaticSound (cl.sound_precache[sound_num], org, vol/255.0f, atten);
2260 }
2261
2262 void CL_ParseEffect (void)
2263 {
2264         vec3_t          org;
2265         int                     modelindex, startframe, framecount, framerate;
2266
2267         MSG_ReadVector(org, cls.protocol);
2268         modelindex = MSG_ReadByte ();
2269         startframe = MSG_ReadByte ();
2270         framecount = MSG_ReadByte ();
2271         framerate = MSG_ReadByte ();
2272
2273         CL_Effect(org, modelindex, startframe, framecount, framerate);
2274 }
2275
2276 void CL_ParseEffect2 (void)
2277 {
2278         vec3_t          org;
2279         int                     modelindex, startframe, framecount, framerate;
2280
2281         MSG_ReadVector(org, cls.protocol);
2282         modelindex = (unsigned short) MSG_ReadShort ();
2283         startframe = (unsigned short) MSG_ReadShort ();
2284         framecount = MSG_ReadByte ();
2285         framerate = MSG_ReadByte ();
2286
2287         CL_Effect(org, modelindex, startframe, framecount, framerate);
2288 }
2289
2290 void CL_NewBeam (int ent, vec3_t start, vec3_t end, dp_model_t *m, int lightning)
2291 {
2292         int i;
2293         beam_t *b = NULL;
2294
2295         if (ent >= MAX_EDICTS)
2296         {
2297                 Con_Printf("CL_NewBeam: invalid entity number %i\n", ent);
2298                 ent = 0;
2299         }
2300
2301         if (ent >= cl.max_entities)
2302                 CL_ExpandEntities(ent);
2303
2304         // override any beam with the same entity
2305         i = cl.max_beams;
2306         if (ent)
2307                 for (i = 0, b = cl.beams;i < cl.max_beams;i++, b++)
2308                         if (b->entity == ent)
2309                                 break;
2310         // if the entity was not found then just replace an unused beam
2311         if (i == cl.max_beams)
2312                 for (i = 0, b = cl.beams;i < cl.max_beams;i++, b++)
2313                         if (!b->model)
2314                                 break;
2315         if (i < cl.max_beams)
2316         {
2317                 cl.num_beams = max(cl.num_beams, i + 1);
2318                 b->entity = ent;
2319                 b->lightning = lightning;
2320                 b->model = m;
2321                 b->endtime = cl.mtime[0] + 0.2;
2322                 VectorCopy (start, b->start);
2323                 VectorCopy (end, b->end);
2324         }
2325         else
2326                 Con_Print("beam list overflow!\n");
2327 }
2328
2329 void CL_ParseBeam (dp_model_t *m, int lightning)
2330 {
2331         int ent;
2332         vec3_t start, end;
2333
2334         ent = (unsigned short) MSG_ReadShort ();
2335         MSG_ReadVector(start, cls.protocol);
2336         MSG_ReadVector(end, cls.protocol);
2337
2338         if (ent >= MAX_EDICTS)
2339         {
2340                 Con_Printf("CL_ParseBeam: invalid entity number %i\n", ent);
2341                 ent = 0;
2342         }
2343
2344         CL_NewBeam(ent, start, end, m, lightning);
2345 }
2346
2347 void CL_ParseTempEntity(void)
2348 {
2349         int type;
2350         vec3_t pos, pos2;
2351         vec3_t vel1, vel2;
2352         vec3_t dir;
2353         vec3_t color;
2354         int rnd;
2355         int colorStart, colorLength, count;
2356         float velspeed, radius;
2357         unsigned char *tempcolor;
2358         matrix4x4_t tempmatrix;
2359
2360         if (cls.protocol == PROTOCOL_QUAKEWORLD)
2361         {
2362                 type = MSG_ReadByte();
2363                 switch (type)
2364                 {
2365                 case QW_TE_WIZSPIKE:
2366                         // spike hitting wall
2367                         MSG_ReadVector(pos, cls.protocol);
2368                         CL_FindNonSolidLocation(pos, pos, 4);
2369                         CL_ParticleEffect(EFFECT_TE_WIZSPIKE, 1, pos, pos, vec3_origin, vec3_origin, NULL, 0);
2370                         S_StartSound(-1, 0, cl.sfx_wizhit, pos, 1, 1);
2371                         break;
2372
2373                 case QW_TE_KNIGHTSPIKE:
2374                         // spike hitting wall
2375                         MSG_ReadVector(pos, cls.protocol);
2376                         CL_FindNonSolidLocation(pos, pos, 4);
2377                         CL_ParticleEffect(EFFECT_TE_KNIGHTSPIKE, 1, pos, pos, vec3_origin, vec3_origin, NULL, 0);
2378                         S_StartSound(-1, 0, cl.sfx_knighthit, pos, 1, 1);
2379                         break;
2380
2381                 case QW_TE_SPIKE:
2382                         // spike hitting wall
2383                         MSG_ReadVector(pos, cls.protocol);
2384                         CL_FindNonSolidLocation(pos, pos, 4);
2385                         CL_ParticleEffect(EFFECT_TE_SPIKE, 1, pos, pos, vec3_origin, vec3_origin, NULL, 0);
2386                         if (rand() % 5)
2387                                 S_StartSound(-1, 0, cl.sfx_tink1, pos, 1, 1);
2388                         else
2389                         {
2390                                 rnd = rand() & 3;
2391                                 if (rnd == 1)
2392                                         S_StartSound(-1, 0, cl.sfx_ric1, pos, 1, 1);
2393                                 else if (rnd == 2)
2394                                         S_StartSound(-1, 0, cl.sfx_ric2, pos, 1, 1);
2395                                 else
2396                                         S_StartSound(-1, 0, cl.sfx_ric3, pos, 1, 1);
2397                         }
2398                         break;
2399                 case QW_TE_SUPERSPIKE:
2400                         // super spike hitting wall
2401                         MSG_ReadVector(pos, cls.protocol);
2402                         CL_FindNonSolidLocation(pos, pos, 4);
2403                         CL_ParticleEffect(EFFECT_TE_SUPERSPIKE, 1, pos, pos, vec3_origin, vec3_origin, NULL, 0);
2404                         if (rand() % 5)
2405                                 S_StartSound(-1, 0, cl.sfx_tink1, pos, 1, 1);
2406                         else
2407                         {
2408                                 rnd = rand() & 3;
2409                                 if (rnd == 1)
2410                                         S_StartSound(-1, 0, cl.sfx_ric1, pos, 1, 1);
2411                                 else if (rnd == 2)
2412                                         S_StartSound(-1, 0, cl.sfx_ric2, pos, 1, 1);
2413                                 else
2414                                         S_StartSound(-1, 0, cl.sfx_ric3, pos, 1, 1);
2415                         }
2416                         break;
2417
2418                 case QW_TE_EXPLOSION:
2419                         // rocket explosion
2420                         MSG_ReadVector(pos, cls.protocol);
2421                         CL_FindNonSolidLocation(pos, pos, 10);
2422                         CL_ParticleEffect(EFFECT_TE_EXPLOSION, 1, pos, pos, vec3_origin, vec3_origin, NULL, 0);
2423                         S_StartSound(-1, 0, cl.sfx_r_exp3, pos, 1, 1);
2424                         CL_Effect(pos, cl.qw_modelindex_s_explod, 0, 6, 10);
2425                         break;
2426
2427                 case QW_TE_TAREXPLOSION:
2428                         // tarbaby explosion
2429                         MSG_ReadVector(pos, cls.protocol);
2430                         CL_FindNonSolidLocation(pos, pos, 10);
2431                         CL_ParticleEffect(EFFECT_TE_TAREXPLOSION, 1, pos, pos, vec3_origin, vec3_origin, NULL, 0);
2432                         S_StartSound(-1, 0, cl.sfx_r_exp3, pos, 1, 1);
2433                         break;
2434
2435                 case QW_TE_LIGHTNING1:
2436                         // lightning bolts
2437                         CL_ParseBeam(cl.model_bolt, true);
2438                         break;
2439
2440                 case QW_TE_LIGHTNING2:
2441                         // lightning bolts
2442                         CL_ParseBeam(cl.model_bolt2, true);
2443                         break;
2444
2445                 case QW_TE_LIGHTNING3:
2446                         // lightning bolts
2447                         CL_ParseBeam(cl.model_bolt3, false);
2448                         break;
2449
2450                 case QW_TE_LAVASPLASH:
2451                         MSG_ReadVector(pos, cls.protocol);
2452                         CL_ParticleEffect(EFFECT_TE_LAVASPLASH, 1, pos, pos, vec3_origin, vec3_origin, NULL, 0);
2453                         break;
2454
2455                 case QW_TE_TELEPORT:
2456                         MSG_ReadVector(pos, cls.protocol);
2457                         CL_ParticleEffect(EFFECT_TE_TELEPORT, 1, pos, pos, vec3_origin, vec3_origin, NULL, 0);
2458                         break;
2459
2460                 case QW_TE_GUNSHOT:
2461                         // bullet hitting wall
2462                         radius = MSG_ReadByte();
2463                         MSG_ReadVector(pos, cls.protocol);
2464                         CL_FindNonSolidLocation(pos, pos, 4);
2465                         VectorSet(pos2, pos[0] + radius, pos[1] + radius, pos[2] + radius);
2466                         VectorSet(pos, pos[0] - radius, pos[1] - radius, pos[2] - radius);
2467                         CL_ParticleEffect(EFFECT_TE_GUNSHOT, radius, pos, pos2, vec3_origin, vec3_origin, NULL, 0);
2468                         if(cl_sound_ric_gunshot.integer & RIC_GUNSHOT)
2469                         {
2470                                 if (rand() % 5)
2471                                         S_StartSound(-1, 0, cl.sfx_tink1, pos, 1, 1);
2472                                 else
2473                                 {
2474                                         rnd = rand() & 3;
2475                                         if (rnd == 1)
2476                                                 S_StartSound(-1, 0, cl.sfx_ric1, pos, 1, 1);
2477                                         else if (rnd == 2)
2478                                                 S_StartSound(-1, 0, cl.sfx_ric2, pos, 1, 1);
2479                                         else
2480                                                 S_StartSound(-1, 0, cl.sfx_ric3, pos, 1, 1);
2481                                 }
2482                         }
2483                         break;
2484
2485                 case QW_TE_BLOOD:
2486                         count = MSG_ReadByte();
2487                         MSG_ReadVector(pos, cls.protocol);
2488                         CL_FindNonSolidLocation(pos, pos, 4);
2489                         CL_ParticleEffect(EFFECT_TE_BLOOD, count, pos, pos, vec3_origin, vec3_origin, NULL, 0);
2490                         break;
2491
2492                 case QW_TE_LIGHTNINGBLOOD:
2493                         MSG_ReadVector(pos, cls.protocol);
2494                         CL_FindNonSolidLocation(pos, pos, 4);
2495                         CL_ParticleEffect(EFFECT_TE_BLOOD, 2.5, pos, pos, vec3_origin, vec3_origin, NULL, 0);
2496                         break;
2497
2498                 default:
2499                         Host_Error("CL_ParseTempEntity: bad type %d (hex %02X)", type, type);
2500                 }
2501         }
2502         else
2503         {
2504                 type = MSG_ReadByte();
2505                 switch (type)
2506                 {
2507                 case TE_WIZSPIKE:
2508                         // spike hitting wall
2509                         MSG_ReadVector(pos, cls.protocol);
2510                         CL_FindNonSolidLocation(pos, pos, 4);
2511                         CL_ParticleEffect(EFFECT_TE_WIZSPIKE, 1, pos, pos, vec3_origin, vec3_origin, NULL, 0);
2512                         S_StartSound(-1, 0, cl.sfx_wizhit, pos, 1, 1);
2513                         break;
2514
2515                 case TE_KNIGHTSPIKE:
2516                         // spike hitting wall
2517                         MSG_ReadVector(pos, cls.protocol);
2518                         CL_FindNonSolidLocation(pos, pos, 4);
2519                         CL_ParticleEffect(EFFECT_TE_KNIGHTSPIKE, 1, pos, pos, vec3_origin, vec3_origin, NULL, 0);
2520                         S_StartSound(-1, 0, cl.sfx_knighthit, pos, 1, 1);
2521                         break;
2522
2523                 case TE_SPIKE:
2524                         // spike hitting wall
2525                         MSG_ReadVector(pos, cls.protocol);
2526                         CL_FindNonSolidLocation(pos, pos, 4);
2527                         CL_ParticleEffect(EFFECT_TE_SPIKE, 1, pos, pos, vec3_origin, vec3_origin, NULL, 0);
2528                         if (rand() % 5)
2529                                 S_StartSound(-1, 0, cl.sfx_tink1, pos, 1, 1);
2530                         else
2531                         {
2532                                 rnd = rand() & 3;
2533                                 if (rnd == 1)
2534                                         S_StartSound(-1, 0, cl.sfx_ric1, pos, 1, 1);
2535                                 else if (rnd == 2)
2536                                         S_StartSound(-1, 0, cl.sfx_ric2, pos, 1, 1);
2537                                 else
2538                                         S_StartSound(-1, 0, cl.sfx_ric3, pos, 1, 1);
2539                         }
2540                         break;
2541                 case TE_SPIKEQUAD:
2542                         // quad spike hitting wall
2543                         MSG_ReadVector(pos, cls.protocol);
2544                         CL_FindNonSolidLocation(pos, pos, 4);
2545                         CL_ParticleEffect(EFFECT_TE_SPIKEQUAD, 1, pos, pos, vec3_origin, vec3_origin, NULL, 0);
2546                         if (rand() % 5)
2547                                 S_StartSound(-1, 0, cl.sfx_tink1, pos, 1, 1);
2548                         else
2549                         {
2550                                 rnd = rand() & 3;
2551                                 if (rnd == 1)
2552                                         S_StartSound(-1, 0, cl.sfx_ric1, pos, 1, 1);
2553                                 else if (rnd == 2)
2554                                         S_StartSound(-1, 0, cl.sfx_ric2, pos, 1, 1);
2555                                 else
2556                                         S_StartSound(-1, 0, cl.sfx_ric3, pos, 1, 1);
2557                         }
2558                         break;
2559                 case TE_SUPERSPIKE:
2560                         // super spike hitting wall
2561                         MSG_ReadVector(pos, cls.protocol);
2562                         CL_FindNonSolidLocation(pos, pos, 4);
2563                         CL_ParticleEffect(EFFECT_TE_SUPERSPIKE, 1, pos, pos, vec3_origin, vec3_origin, NULL, 0);
2564                         if (rand() % 5)
2565                                 S_StartSound(-1, 0, cl.sfx_tink1, pos, 1, 1);
2566                         else
2567                         {
2568                                 rnd = rand() & 3;
2569                                 if (rnd == 1)
2570                                         S_StartSound(-1, 0, cl.sfx_ric1, pos, 1, 1);
2571                                 else if (rnd == 2)
2572                                         S_StartSound(-1, 0, cl.sfx_ric2, pos, 1, 1);
2573                                 else
2574                                         S_StartSound(-1, 0, cl.sfx_ric3, pos, 1, 1);
2575                         }
2576                         break;
2577                 case TE_SUPERSPIKEQUAD:
2578                         // quad super spike hitting wall
2579                         MSG_ReadVector(pos, cls.protocol);
2580                         CL_FindNonSolidLocation(pos, pos, 4);
2581                         CL_ParticleEffect(EFFECT_TE_SUPERSPIKEQUAD, 1, pos, pos, vec3_origin, vec3_origin, NULL, 0);
2582                         if (rand() % 5)
2583                                 S_StartSound(-1, 0, cl.sfx_tink1, pos, 1, 1);
2584                         else
2585                         {
2586                                 rnd = rand() & 3;
2587                                 if (rnd == 1)
2588                                         S_StartSound(-1, 0, cl.sfx_ric1, pos, 1, 1);
2589                                 else if (rnd == 2)
2590                                         S_StartSound(-1, 0, cl.sfx_ric2, pos, 1, 1);
2591                                 else
2592                                         S_StartSound(-1, 0, cl.sfx_ric3, pos, 1, 1);
2593                         }
2594                         break;
2595                         // LordHavoc: added for improved blood splatters
2596                 case TE_BLOOD:
2597                         // blood puff
2598                         MSG_ReadVector(pos, cls.protocol);
2599                         CL_FindNonSolidLocation(pos, pos, 4);
2600                         dir[0] = MSG_ReadChar();
2601                         dir[1] = MSG_ReadChar();
2602                         dir[2] = MSG_ReadChar();
2603                         count = MSG_ReadByte();
2604                         CL_ParticleEffect(EFFECT_TE_BLOOD, count, pos, pos, dir, dir, NULL, 0);
2605                         break;
2606                 case TE_SPARK:
2607                         // spark shower
2608                         MSG_ReadVector(pos, cls.protocol);
2609                         CL_FindNonSolidLocation(pos, pos, 4);
2610                         dir[0] = MSG_ReadChar();
2611                         dir[1] = MSG_ReadChar();
2612                         dir[2] = MSG_ReadChar();
2613                         count = MSG_ReadByte();
2614                         CL_ParticleEffect(EFFECT_TE_SPARK, count, pos, pos, dir, dir, NULL, 0);
2615                         break;
2616                 case TE_PLASMABURN:
2617                         MSG_ReadVector(pos, cls.protocol);
2618                         CL_FindNonSolidLocation(pos, pos, 4);
2619                         CL_ParticleEffect(EFFECT_TE_PLASMABURN, 1, pos, pos, vec3_origin, vec3_origin, NULL, 0);
2620                         break;
2621                         // LordHavoc: added for improved gore
2622                 case TE_BLOODSHOWER:
2623                         // vaporized body
2624                         MSG_ReadVector(pos, cls.protocol); // mins
2625                         MSG_ReadVector(pos2, cls.protocol); // maxs
2626                         velspeed = MSG_ReadCoord(cls.protocol); // speed
2627                         count = (unsigned short) MSG_ReadShort(); // number of particles
2628                         vel1[0] = -velspeed;
2629                         vel1[1] = -velspeed;
2630                         vel1[2] = -velspeed;
2631                         vel2[0] = velspeed;
2632                         vel2[1] = velspeed;
2633                         vel2[2] = velspeed;
2634                         CL_ParticleEffect(EFFECT_TE_BLOOD, count, pos, pos2, vel1, vel2, NULL, 0);
2635                         break;
2636
2637                 case TE_PARTICLECUBE:
2638                         // general purpose particle effect
2639                         MSG_ReadVector(pos, cls.protocol); // mins
2640                         MSG_ReadVector(pos2, cls.protocol); // maxs
2641                         MSG_ReadVector(dir, cls.protocol); // dir
2642                         count = (unsigned short) MSG_ReadShort(); // number of particles
2643                         colorStart = MSG_ReadByte(); // color
2644                         colorLength = MSG_ReadByte(); // gravity (1 or 0)
2645                         velspeed = MSG_ReadCoord(cls.protocol); // randomvel
2646                         CL_ParticleCube(pos, pos2, dir, count, colorStart, colorLength != 0, velspeed);
2647                         break;
2648
2649                 case TE_PARTICLERAIN:
2650                         // general purpose particle effect
2651                         MSG_ReadVector(pos, cls.protocol); // mins
2652                         MSG_ReadVector(pos2, cls.protocol); // maxs
2653                         MSG_ReadVector(dir, cls.protocol); // dir
2654                         count = (unsigned short) MSG_ReadShort(); // number of particles
2655                         colorStart = MSG_ReadByte(); // color
2656                         CL_ParticleRain(pos, pos2, dir, count, colorStart, 0);
2657                         break;
2658
2659                 case TE_PARTICLESNOW:
2660                         // general purpose particle effect
2661                         MSG_ReadVector(pos, cls.protocol); // mins
2662                         MSG_ReadVector(pos2, cls.protocol); // maxs
2663                         MSG_ReadVector(dir, cls.protocol); // dir
2664                         count = (unsigned short) MSG_ReadShort(); // number of particles
2665                         colorStart = MSG_ReadByte(); // color
2666                         CL_ParticleRain(pos, pos2, dir, count, colorStart, 1);
2667                         break;
2668
2669                 case TE_GUNSHOT:
2670                         // bullet hitting wall
2671                         MSG_ReadVector(pos, cls.protocol);
2672                         CL_FindNonSolidLocation(pos, pos, 4);
2673                         CL_ParticleEffect(EFFECT_TE_GUNSHOT, 1, pos, pos, vec3_origin, vec3_origin, NULL, 0);
2674                         if(cl_sound_ric_gunshot.integer & RIC_GUNSHOT)
2675                         {
2676                                 if (rand() % 5)
2677                                         S_StartSound(-1, 0, cl.sfx_tink1, pos, 1, 1);
2678                                 else
2679                                 {
2680                                         rnd = rand() & 3;
2681                                         if (rnd == 1)
2682                                                 S_StartSound(-1, 0, cl.sfx_ric1, pos, 1, 1);
2683                                         else if (rnd == 2)
2684                                                 S_StartSound(-1, 0, cl.sfx_ric2, pos, 1, 1);
2685                                         else
2686                                                 S_StartSound(-1, 0, cl.sfx_ric3, pos, 1, 1);
2687                                 }
2688                         }
2689                         break;
2690
2691                 case TE_GUNSHOTQUAD:
2692                         // quad bullet hitting wall
2693                         MSG_ReadVector(pos, cls.protocol);
2694                         CL_FindNonSolidLocation(pos, pos, 4);
2695                         CL_ParticleEffect(EFFECT_TE_GUNSHOTQUAD, 1, pos, pos, vec3_origin, vec3_origin, NULL, 0);
2696                         if(cl_sound_ric_gunshot.integer & RIC_GUNSHOTQUAD)
2697                         {
2698                                 if (rand() % 5)
2699                                         S_StartSound(-1, 0, cl.sfx_tink1, pos, 1, 1);
2700                                 else
2701                                 {
2702                                         rnd = rand() & 3;
2703                                         if (rnd == 1)
2704                                                 S_StartSound(-1, 0, cl.sfx_ric1, pos, 1, 1);
2705                                         else if (rnd == 2)
2706                                                 S_StartSound(-1, 0, cl.sfx_ric2, pos, 1, 1);
2707                                         else
2708                                                 S_StartSound(-1, 0, cl.sfx_ric3, pos, 1, 1);
2709                                 }
2710                         }
2711                         break;
2712
2713                 case TE_EXPLOSION:
2714                         // rocket explosion
2715                         MSG_ReadVector(pos, cls.protocol);
2716                         CL_FindNonSolidLocation(pos, pos, 10);
2717                         CL_ParticleEffect(EFFECT_TE_EXPLOSION, 1, pos, pos, vec3_origin, vec3_origin, NULL, 0);
2718                         S_StartSound(-1, 0, cl.sfx_r_exp3, pos, 1, 1);
2719                         break;
2720
2721                 case TE_EXPLOSIONQUAD:
2722                         // quad rocket explosion
2723                         MSG_ReadVector(pos, cls.protocol);
2724                         CL_FindNonSolidLocation(pos, pos, 10);
2725                         CL_ParticleEffect(EFFECT_TE_EXPLOSIONQUAD, 1, pos, pos, vec3_origin, vec3_origin, NULL, 0);
2726                         S_StartSound(-1, 0, cl.sfx_r_exp3, pos, 1, 1);
2727                         break;
2728
2729                 case TE_EXPLOSION3:
2730                         // Nehahra movie colored lighting explosion
2731                         MSG_ReadVector(pos, cls.protocol);
2732                         CL_FindNonSolidLocation(pos, pos, 10);
2733                         color[0] = MSG_ReadCoord(cls.protocol) * (2.0f / 1.0f);
2734                         color[1] = MSG_ReadCoord(cls.protocol) * (2.0f / 1.0f);
2735                         color[2] = MSG_ReadCoord(cls.protocol) * (2.0f / 1.0f);
2736                         CL_ParticleExplosion(pos);
2737                         Matrix4x4_CreateTranslate(&tempmatrix, pos[0], pos[1], pos[2]);
2738                         CL_AllocLightFlash(NULL, &tempmatrix, 350, color[0], color[1], color[2], 700, 0.5, 0, -1, true, 1, 0.25, 0.25, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
2739                         S_StartSound(-1, 0, cl.sfx_r_exp3, pos, 1, 1);
2740                         break;
2741
2742                 case TE_EXPLOSIONRGB:
2743                         // colored lighting explosion
2744                         MSG_ReadVector(pos, cls.protocol);
2745                         CL_FindNonSolidLocation(pos, pos, 10);
2746                         CL_ParticleExplosion(pos);
2747                         color[0] = MSG_ReadByte() * (2.0f / 255.0f);
2748                         color[1] = MSG_ReadByte() * (2.0f / 255.0f);
2749                         color[2] = MSG_ReadByte() * (2.0f / 255.0f);
2750                         Matrix4x4_CreateTranslate(&tempmatrix, pos[0], pos[1], pos[2]);
2751                         CL_AllocLightFlash(NULL, &tempmatrix, 350, color[0], color[1], color[2], 700, 0.5, 0, -1, true, 1, 0.25, 0.25, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
2752                         S_StartSound(-1, 0, cl.sfx_r_exp3, pos, 1, 1);
2753                         break;
2754
2755                 case TE_TAREXPLOSION:
2756                         // tarbaby explosion
2757                         MSG_ReadVector(pos, cls.protocol);
2758                         CL_FindNonSolidLocation(pos, pos, 10);
2759                         CL_ParticleEffect(EFFECT_TE_TAREXPLOSION, 1, pos, pos, vec3_origin, vec3_origin, NULL, 0);
2760                         S_StartSound(-1, 0, cl.sfx_r_exp3, pos, 1, 1);
2761                         break;
2762
2763                 case TE_SMALLFLASH:
2764                         MSG_ReadVector(pos, cls.protocol);
2765                         CL_FindNonSolidLocation(pos, pos, 10);
2766                         CL_ParticleEffect(EFFECT_TE_SMALLFLASH, 1, pos, pos, vec3_origin, vec3_origin, NULL, 0);
2767                         break;
2768
2769                 case TE_CUSTOMFLASH:
2770                         MSG_ReadVector(pos, cls.protocol);
2771                         CL_FindNonSolidLocation(pos, pos, 4);
2772                         radius = (MSG_ReadByte() + 1) * 8;
2773                         velspeed = (MSG_ReadByte() + 1) * (1.0 / 256.0);
2774                         color[0] = MSG_ReadByte() * (2.0f / 255.0f);
2775                         color[1] = MSG_ReadByte() * (2.0f / 255.0f);
2776                         color[2] = MSG_ReadByte() * (2.0f / 255.0f);
2777                         Matrix4x4_CreateTranslate(&tempmatrix, pos[0], pos[1], pos[2]);
2778                         CL_AllocLightFlash(NULL, &tempmatrix, radius, color[0], color[1], color[2], radius / velspeed, velspeed, 0, -1, true, 1, 0.25, 1, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
2779                         break;
2780
2781                 case TE_FLAMEJET:
2782                         MSG_ReadVector(pos, cls.protocol);
2783                         MSG_ReadVector(dir, cls.protocol);
2784                         count = MSG_ReadByte();
2785                         CL_ParticleEffect(EFFECT_TE_FLAMEJET, count, pos, pos, dir, dir, NULL, 0);
2786                         break;
2787
2788                 case TE_LIGHTNING1:
2789                         // lightning bolts
2790                         CL_ParseBeam(cl.model_bolt, true);
2791                         break;
2792
2793                 case TE_LIGHTNING2:
2794                         // lightning bolts
2795                         CL_ParseBeam(cl.model_bolt2, true);
2796                         break;
2797
2798                 case TE_LIGHTNING3:
2799                         // lightning bolts
2800                         CL_ParseBeam(cl.model_bolt3, false);
2801                         break;
2802
2803         // PGM 01/21/97
2804                 case TE_BEAM:
2805                         // grappling hook beam
2806                         CL_ParseBeam(cl.model_beam, false);
2807                         break;
2808         // PGM 01/21/97
2809
2810         // LordHavoc: for compatibility with the Nehahra movie...
2811                 case TE_LIGHTNING4NEH:
2812                         CL_ParseBeam(Mod_ForName(MSG_ReadString(), true, false, NULL), false);
2813                         break;
2814
2815                 case TE_LAVASPLASH:
2816                         MSG_ReadVector(pos, cls.protocol);
2817                         CL_ParticleEffect(EFFECT_TE_LAVASPLASH, 1, pos, pos, vec3_origin, vec3_origin, NULL, 0);
2818                         break;
2819
2820                 case TE_TELEPORT:
2821                         MSG_ReadVector(pos, cls.protocol);
2822                         CL_ParticleEffect(EFFECT_TE_TELEPORT, 1, pos, pos, vec3_origin, vec3_origin, NULL, 0);
2823                         break;
2824
2825                 case TE_EXPLOSION2:
2826                         // color mapped explosion
2827                         MSG_ReadVector(pos, cls.protocol);
2828                         CL_FindNonSolidLocation(pos, pos, 10);
2829                         colorStart = MSG_ReadByte();
2830                         colorLength = MSG_ReadByte();
2831                         CL_ParticleExplosion2(pos, colorStart, colorLength);
2832                         tempcolor = palette_rgb[(rand()%colorLength) + colorStart];
2833                         color[0] = tempcolor[0] * (2.0f / 255.0f);
2834                         color[1] = tempcolor[1] * (2.0f / 255.0f);
2835                         color[2] = tempcolor[2] * (2.0f / 255.0f);
2836                         Matrix4x4_CreateTranslate(&tempmatrix, pos[0], pos[1], pos[2]);
2837                         CL_AllocLightFlash(NULL, &tempmatrix, 350, color[0], color[1], color[2], 700, 0.5, 0, -1, true, 1, 0.25, 0.25, 1, 1, LIGHTFLAG_NORMALMODE | LIGHTFLAG_REALTIMEMODE);
2838                         S_StartSound(-1, 0, cl.sfx_r_exp3, pos, 1, 1);
2839                         break;
2840
2841                 case TE_TEI_G3:
2842                         MSG_ReadVector(pos, cls.protocol);
2843                         MSG_ReadVector(pos2, cls.protocol);
2844                         MSG_ReadVector(dir, cls.protocol);
2845                         CL_ParticleEffect(EFFECT_TE_TEI_G3, 1, pos, pos2, dir, dir, NULL, 0);
2846                         break;
2847
2848                 case TE_TEI_SMOKE:
2849                         MSG_ReadVector(pos, cls.protocol);
2850                         MSG_ReadVector(dir, cls.protocol);
2851                         count = MSG_ReadByte();
2852                         CL_FindNonSolidLocation(pos, pos, 4);
2853                         CL_ParticleEffect(EFFECT_TE_TEI_SMOKE, count, pos, pos, vec3_origin, vec3_origin, NULL, 0);
2854                         break;
2855
2856                 case TE_TEI_BIGEXPLOSION:
2857                         MSG_ReadVector(pos, cls.protocol);
2858                         CL_FindNonSolidLocation(pos, pos, 10);
2859                         CL_ParticleEffect(EFFECT_TE_TEI_BIGEXPLOSION, 1, pos, pos, vec3_origin, vec3_origin, NULL, 0);
2860                         S_StartSound(-1, 0, cl.sfx_r_exp3, pos, 1, 1);
2861                         break;
2862
2863                 case TE_TEI_PLASMAHIT:
2864                         MSG_ReadVector(pos, cls.protocol);
2865                         MSG_ReadVector(dir, cls.protocol);
2866                         count = MSG_ReadByte();
2867                         CL_FindNonSolidLocation(pos, pos, 5);
2868                         CL_ParticleEffect(EFFECT_TE_TEI_PLASMAHIT, count, pos, pos, vec3_origin, vec3_origin, NULL, 0);
2869                         break;
2870
2871                 default:
2872                         Host_Error("CL_ParseTempEntity: bad type %d (hex %02X)", type, type);
2873                 }
2874         }
2875 }
2876
2877 void CL_ParseTrailParticles(void)
2878 {
2879         int entityindex;
2880         int effectindex;
2881         vec3_t start, end;
2882         entityindex = (unsigned short)MSG_ReadShort();
2883         if (entityindex >= MAX_EDICTS)
2884                 entityindex = 0;
2885         if (entityindex >= cl.max_entities)
2886                 CL_ExpandEntities(entityindex);
2887         effectindex = (unsigned short)MSG_ReadShort();
2888         MSG_ReadVector(start, cls.protocol);
2889         MSG_ReadVector(end, cls.protocol);
2890         CL_ParticleEffect(effectindex, 1, start, end, vec3_origin, vec3_origin, entityindex > 0 ? cl.entities + entityindex : NULL, 0);
2891 }
2892
2893 void CL_ParsePointParticles(void)
2894 {
2895         int effectindex, count;
2896         vec3_t origin, velocity;
2897         effectindex = (unsigned short)MSG_ReadShort();
2898         MSG_ReadVector(origin, cls.protocol);
2899         MSG_ReadVector(velocity, cls.protocol);
2900         count = (unsigned short)MSG_ReadShort();
2901         CL_ParticleEffect(effectindex, count, origin, origin, velocity, velocity, NULL, 0);
2902 }
2903
2904 void CL_ParsePointParticles1(void)
2905 {
2906         int effectindex;
2907         vec3_t origin;
2908         effectindex = (unsigned short)MSG_ReadShort();
2909         MSG_ReadVector(origin, cls.protocol);
2910         CL_ParticleEffect(effectindex, 1, origin, origin, vec3_origin, vec3_origin, NULL, 0);
2911 }
2912
2913 typedef struct cl_iplog_item_s
2914 {
2915         char *address;
2916         char *name;
2917 }
2918 cl_iplog_item_t;
2919
2920 static qboolean cl_iplog_loaded = false;
2921 static int cl_iplog_numitems = 0;
2922 static int cl_iplog_maxitems = 0;
2923 static cl_iplog_item_t *cl_iplog_items;
2924
2925 static void CL_IPLog_Load(void);
2926 static void CL_IPLog_Add(const char *address, const char *name, qboolean checkexisting, qboolean addtofile)
2927 {
2928         int i;
2929         if (!address || !address[0] || !name || !name[0])
2930                 return;
2931         if (!cl_iplog_loaded)
2932                 CL_IPLog_Load();
2933         if (developer_extra.integer)
2934                 Con_DPrintf("CL_IPLog_Add(\"%s\", \"%s\", %i, %i);\n", address, name, checkexisting, addtofile);
2935         // see if it already exists
2936         if (checkexisting)
2937         {
2938                 for (i = 0;i < cl_iplog_numitems;i++)
2939                 {
2940                         if (!strcmp(cl_iplog_items[i].address, address) && !strcmp(cl_iplog_items[i].name, name))
2941                         {
2942                                 if (developer_extra.integer)
2943                                         Con_DPrintf("... found existing \"%s\" \"%s\"\n", cl_iplog_items[i].address, cl_iplog_items[i].name);
2944                                 return;
2945                         }
2946                 }
2947         }
2948         // it does not already exist in the iplog, so add it
2949         if (cl_iplog_maxitems <= cl_iplog_numitems || !cl_iplog_items)
2950         {
2951                 cl_iplog_item_t *olditems = cl_iplog_items;
2952                 cl_iplog_maxitems = max(1024, cl_iplog_maxitems + 256);
2953                 cl_iplog_items = (cl_iplog_item_t *) Mem_Alloc(cls.permanentmempool, cl_iplog_maxitems * sizeof(cl_iplog_item_t));
2954                 if (olditems)
2955                 {
2956                         if (cl_iplog_numitems)
2957                                 memcpy(cl_iplog_items, olditems, cl_iplog_numitems * sizeof(cl_iplog_item_t));
2958                         Mem_Free(olditems);
2959                 }
2960         }
2961         cl_iplog_items[cl_iplog_numitems].address = (char *) Mem_Alloc(cls.permanentmempool, strlen(address) + 1);
2962         cl_iplog_items[cl_iplog_numitems].name = (char *) Mem_Alloc(cls.permanentmempool, strlen(name) + 1);
2963         strlcpy(cl_iplog_items[cl_iplog_numitems].address, address, strlen(address) + 1);
2964         // TODO: maybe it would be better to strip weird characters from name when
2965         // copying it here rather than using a straight strcpy?
2966         strlcpy(cl_iplog_items[cl_iplog_numitems].name, name, strlen(name) + 1);
2967         cl_iplog_numitems++;
2968         if (addtofile)
2969         {
2970                 // add it to the iplog.txt file
2971                 // TODO: this ought to open the one in the userpath version of the base
2972                 // gamedir, not the current gamedir
2973                 Log_Printf(cl_iplog_name.string, "%s %s\n", address, name);
2974                 if (developer_extra.integer)
2975                         Con_DPrintf("CL_IPLog_Add: appending this line to %s: %s %s\n", cl_iplog_name.string, address, name);
2976         }
2977 }
2978
2979 static void CL_IPLog_Load(void)
2980 {
2981         int i, len, linenumber;
2982         char *text, *textend;
2983         unsigned char *filedata;
2984         fs_offset_t filesize;
2985         char line[MAX_INPUTLINE];
2986         char address[MAX_INPUTLINE];
2987         cl_iplog_loaded = true;
2988         // TODO: this ought to open the one in the userpath version of the base
2989         // gamedir, not the current gamedir
2990         filedata = FS_LoadFile(cl_iplog_name.string, tempmempool, true, &filesize);
2991         if (!filedata)
2992                 return;
2993         text = (char *)filedata;
2994         textend = text + filesize;
2995         for (linenumber = 1;text < textend;linenumber++)
2996         {
2997                 for (len = 0;text < textend && *text != '\r' && *text != '\n';text++)
2998                         if (len < (int)sizeof(line) - 1)
2999                                 line[len++] = *text;
3000                 line[len] = 0;
3001                 if (text < textend && *text == '\r' && text[1] == '\n')
3002                         text++;
3003                 if (text < textend && *text == '\n')
3004                         text++;
3005                 if (line[0] == '/' && line[1] == '/')
3006                         continue; // skip comments if anyone happens to add them
3007                 for (i = 0;i < len && !ISWHITESPACE(line[i]);i++)
3008                         address[i] = line[i];
3009                 address[i] = 0;
3010                 // skip exactly one space character
3011                 i++;
3012                 // address contains the address with termination,
3013                 // line + i contains the name with termination
3014                 if (address[0] && line[i])
3015                         CL_IPLog_Add(address, line + i, false, false);
3016                 else
3017                         Con_Printf("%s:%i: could not parse address and name:\n%s\n", cl_iplog_name.string, linenumber, line);
3018         }
3019 }
3020
3021 static void CL_IPLog_List_f(void)
3022 {
3023         int i, j;
3024         const char *addressprefix;
3025         if (Cmd_Argc() > 2)
3026         {
3027                 Con_Printf("usage: %s 123.456.789.\n", Cmd_Argv(0));
3028                 return;
3029         }
3030         addressprefix = "";
3031         if (Cmd_Argc() >= 2)
3032                 addressprefix = Cmd_Argv(1);
3033         if (!cl_iplog_loaded)
3034                 CL_IPLog_Load();
3035         if (addressprefix && addressprefix[0])
3036                 Con_Printf("Listing iplog addresses beginning with %s\n", addressprefix);
3037         else
3038                 Con_Printf("Listing all iplog entries\n");
3039         Con_Printf("address         name\n");
3040         for (i = 0;i < cl_iplog_numitems;i++)
3041         {
3042                 if (addressprefix && addressprefix[0])
3043                 {
3044                         for (j = 0;addressprefix[j];j++)
3045                                 if (addressprefix[j] != cl_iplog_items[i].address[j])
3046                                         break;
3047                         // if this address