PageRenderTime 77ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 1ms

/cl_parse.c

https://gitlab.com/xonotic/darkplaces
C | 4332 lines | 3417 code | 468 blank | 447 comment | 928 complexity | f088ff2932dabf23136a6a26e62e3c94 MD5 | raw file
Possible License(s): GPL-2.0
  1. /*
  2. Copyright (C) 1996-1997 Id Software, Inc.
  3. This program is free software; you can redistribute it and/or
  4. modify it under the terms of the GNU General Public License
  5. as published by the Free Software Foundation; either version 2
  6. of the License, or (at your option) any later version.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. See the GNU General Public License for more details.
  11. You should have received a copy of the GNU General Public License
  12. along with this program; if not, write to the Free Software
  13. Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  14. */
  15. // cl_parse.c -- parse a message received from the server
  16. #include "quakedef.h"
  17. #ifdef CONFIG_CD
  18. #include "cdaudio.h"
  19. #endif
  20. #include "cl_collision.h"
  21. #include "csprogs.h"
  22. #include "libcurl.h"
  23. #include "utf8lib.h"
  24. #ifdef CONFIG_MENU
  25. #include "menu.h"
  26. #endif
  27. #include "cl_video.h"
  28. const char *svc_strings[128] =
  29. {
  30. "svc_bad",
  31. "svc_nop",
  32. "svc_disconnect",
  33. "svc_updatestat",
  34. "svc_version", // [int] server version
  35. "svc_setview", // [short] entity number
  36. "svc_sound", // <see code>
  37. "svc_time", // [float] server time
  38. "svc_print", // [string] null terminated string
  39. "svc_stufftext", // [string] stuffed into client's console buffer
  40. // the string should be \n terminated
  41. "svc_setangle", // [vec3] set the view angle to this absolute value
  42. "svc_serverinfo", // [int] version
  43. // [string] signon string
  44. // [string]..[0]model cache [string]...[0]sounds cache
  45. // [string]..[0]item cache
  46. "svc_lightstyle", // [byte] [string]
  47. "svc_updatename", // [byte] [string]
  48. "svc_updatefrags", // [byte] [short]
  49. "svc_clientdata", // <shortbits + data>
  50. "svc_stopsound", // <see code>
  51. "svc_updatecolors", // [byte] [byte]
  52. "svc_particle", // [vec3] <variable>
  53. "svc_damage", // [byte] impact [byte] blood [vec3] from
  54. "svc_spawnstatic",
  55. "OBSOLETE svc_spawnbinary",
  56. "svc_spawnbaseline",
  57. "svc_temp_entity", // <variable>
  58. "svc_setpause",
  59. "svc_signonnum",
  60. "svc_centerprint",
  61. "svc_killedmonster",
  62. "svc_foundsecret",
  63. "svc_spawnstaticsound",
  64. "svc_intermission",
  65. "svc_finale", // [string] music [string] text
  66. "svc_cdtrack", // [byte] track [byte] looptrack
  67. "svc_sellscreen",
  68. "svc_cutscene",
  69. "svc_showlmp", // [string] iconlabel [string] lmpfile [short] x [short] y
  70. "svc_hidelmp", // [string] iconlabel
  71. "svc_skybox", // [string] skyname
  72. "", // 38
  73. "", // 39
  74. "", // 40
  75. "", // 41
  76. "", // 42
  77. "", // 43
  78. "", // 44
  79. "", // 45
  80. "", // 46
  81. "", // 47
  82. "", // 48
  83. "", // 49
  84. "svc_downloaddata", // 50 // [int] start [short] size [variable length] data
  85. "svc_updatestatubyte", // 51 // [byte] stat [byte] value
  86. "svc_effect", // 52 // [vector] org [byte] modelindex [byte] startframe [byte] framecount [byte] framerate
  87. "svc_effect2", // 53 // [vector] org [short] modelindex [short] startframe [byte] framecount [byte] framerate
  88. "svc_sound2", // 54 // short soundindex instead of byte
  89. "svc_spawnbaseline2", // 55 // short modelindex instead of byte
  90. "svc_spawnstatic2", // 56 // short modelindex instead of byte
  91. "svc_entities", // 57 // [int] deltaframe [int] thisframe [float vector] eye [variable length] entitydata
  92. "svc_csqcentities", // 58 // [short] entnum [variable length] entitydata ... [short] 0x0000
  93. "svc_spawnstaticsound2", // 59 // [coord3] [short] samp [byte] vol [byte] aten
  94. "svc_trailparticles", // 60 // [short] entnum [short] effectnum [vector] start [vector] end
  95. "svc_pointparticles", // 61 // [short] effectnum [vector] start [vector] velocity [short] count
  96. "svc_pointparticles1", // 62 // [short] effectnum [vector] start, same as svc_pointparticles except velocity is zero and count is 1
  97. };
  98. const char *qw_svc_strings[128] =
  99. {
  100. "qw_svc_bad", // 0
  101. "qw_svc_nop", // 1
  102. "qw_svc_disconnect", // 2
  103. "qw_svc_updatestat", // 3 // [byte] [byte]
  104. "", // 4
  105. "qw_svc_setview", // 5 // [short] entity number
  106. "qw_svc_sound", // 6 // <see code>
  107. "", // 7
  108. "qw_svc_print", // 8 // [byte] id [string] null terminated string
  109. "qw_svc_stufftext", // 9 // [string] stuffed into client's console buffer
  110. "qw_svc_setangle", // 10 // [angle3] set the view angle to this absolute value
  111. "qw_svc_serverdata", // 11 // [long] protocol ...
  112. "qw_svc_lightstyle", // 12 // [byte] [string]
  113. "", // 13
  114. "qw_svc_updatefrags", // 14 // [byte] [short]
  115. "", // 15
  116. "qw_svc_stopsound", // 16 // <see code>
  117. "", // 17
  118. "", // 18
  119. "qw_svc_damage", // 19
  120. "qw_svc_spawnstatic", // 20
  121. "", // 21
  122. "qw_svc_spawnbaseline", // 22
  123. "qw_svc_temp_entity", // 23 // variable
  124. "qw_svc_setpause", // 24 // [byte] on / off
  125. "", // 25
  126. "qw_svc_centerprint", // 26 // [string] to put in center of the screen
  127. "qw_svc_killedmonster", // 27
  128. "qw_svc_foundsecret", // 28
  129. "qw_svc_spawnstaticsound", // 29 // [coord3] [byte] samp [byte] vol [byte] aten
  130. "qw_svc_intermission", // 30 // [vec3_t] origin [vec3_t] angle
  131. "qw_svc_finale", // 31 // [string] text
  132. "qw_svc_cdtrack", // 32 // [byte] track
  133. "qw_svc_sellscreen", // 33
  134. "qw_svc_smallkick", // 34 // set client punchangle to 2
  135. "qw_svc_bigkick", // 35 // set client punchangle to 4
  136. "qw_svc_updateping", // 36 // [byte] [short]
  137. "qw_svc_updateentertime", // 37 // [byte] [float]
  138. "qw_svc_updatestatlong", // 38 // [byte] [long]
  139. "qw_svc_muzzleflash", // 39 // [short] entity
  140. "qw_svc_updateuserinfo", // 40 // [byte] slot [long] uid
  141. "qw_svc_download", // 41 // [short] size [size bytes]
  142. "qw_svc_playerinfo", // 42 // variable
  143. "qw_svc_nails", // 43 // [byte] num [48 bits] xyzpy 12 12 12 4 8
  144. "qw_svc_chokecount", // 44 // [byte] packets choked
  145. "qw_svc_modellist", // 45 // [strings]
  146. "qw_svc_soundlist", // 46 // [strings]
  147. "qw_svc_packetentities", // 47 // [...]
  148. "qw_svc_deltapacketentities", // 48 // [...]
  149. "qw_svc_maxspeed", // 49 // maxspeed change, for prediction
  150. "qw_svc_entgravity", // 50 // gravity change, for prediction
  151. "qw_svc_setinfo", // 51 // setinfo on a client
  152. "qw_svc_serverinfo", // 52 // serverinfo
  153. "qw_svc_updatepl", // 53 // [byte] [byte]
  154. };
  155. //=============================================================================
  156. cvar_t cl_worldmessage = {CVAR_READONLY, "cl_worldmessage", "", "title of current level"};
  157. cvar_t cl_worldname = {CVAR_READONLY, "cl_worldname", "", "name of current worldmodel"};
  158. cvar_t cl_worldnamenoextension = {CVAR_READONLY, "cl_worldnamenoextension", "", "name of current worldmodel without extension"};
  159. cvar_t cl_worldbasename = {CVAR_READONLY, "cl_worldbasename", "", "name of current worldmodel without maps/ prefix or extension"};
  160. cvar_t developer_networkentities = {0, "developer_networkentities", "0", "prints received entities, value is 0-10 (higher for more info, 10 being the most verbose)"};
  161. 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"};
  162. cvar_t cl_sound_wizardhit = {0, "cl_sound_wizardhit", "wizard/hit.wav", "sound to play during TE_WIZSPIKE (empty cvar disables sound)"};
  163. cvar_t cl_sound_hknighthit = {0, "cl_sound_hknighthit", "hknight/hit.wav", "sound to play during TE_KNIGHTSPIKE (empty cvar disables sound)"};
  164. 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)"};
  165. 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)"};
  166. 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)"};
  167. 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)"};
  168. 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"};
  169. #define RIC_GUNSHOT 1
  170. #define RIC_GUNSHOTQUAD 2
  171. 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"};
  172. 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)"};
  173. cvar_t cl_serverextension_download = {0, "cl_serverextension_download", "0", "indicates whether the server supports the download command"};
  174. 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"};
  175. 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)"};
  176. 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"};
  177. 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)"};
  178. 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"};
  179. static qboolean QW_CL_CheckOrDownloadFile(const char *filename);
  180. static void QW_CL_RequestNextDownload(void);
  181. static void QW_CL_NextUpload(void);
  182. //static qboolean QW_CL_IsUploading(void);
  183. static void QW_CL_StopUpload(void);
  184. /*
  185. ==================
  186. CL_ParseStartSoundPacket
  187. ==================
  188. */
  189. static void CL_ParseStartSoundPacket(int largesoundindex)
  190. {
  191. vec3_t pos;
  192. int channel, ent;
  193. int sound_num;
  194. int nvolume;
  195. int field_mask;
  196. float attenuation;
  197. float speed;
  198. int fflags = CHANNELFLAG_NONE;
  199. if (cls.protocol == PROTOCOL_QUAKEWORLD)
  200. {
  201. channel = MSG_ReadShort(&cl_message);
  202. if (channel & (1<<15))
  203. nvolume = MSG_ReadByte(&cl_message);
  204. else
  205. nvolume = DEFAULT_SOUND_PACKET_VOLUME;
  206. if (channel & (1<<14))
  207. attenuation = MSG_ReadByte(&cl_message) / 64.0;
  208. else
  209. attenuation = DEFAULT_SOUND_PACKET_ATTENUATION;
  210. speed = 1.0f;
  211. ent = (channel>>3)&1023;
  212. channel &= 7;
  213. sound_num = MSG_ReadByte(&cl_message);
  214. }
  215. else
  216. {
  217. field_mask = MSG_ReadByte(&cl_message);
  218. if (field_mask & SND_VOLUME)
  219. nvolume = MSG_ReadByte(&cl_message);
  220. else
  221. nvolume = DEFAULT_SOUND_PACKET_VOLUME;
  222. if (field_mask & SND_ATTENUATION)
  223. attenuation = MSG_ReadByte(&cl_message) / 64.0;
  224. else
  225. attenuation = DEFAULT_SOUND_PACKET_ATTENUATION;
  226. if (field_mask & SND_SPEEDUSHORT4000)
  227. speed = ((unsigned short)MSG_ReadShort(&cl_message)) / 4000.0f;
  228. else
  229. speed = 1.0f;
  230. if (field_mask & SND_LARGEENTITY)
  231. {
  232. ent = (unsigned short) MSG_ReadShort(&cl_message);
  233. channel = MSG_ReadChar(&cl_message);
  234. }
  235. else
  236. {
  237. channel = (unsigned short) MSG_ReadShort(&cl_message);
  238. ent = channel >> 3;
  239. channel &= 7;
  240. }
  241. if (largesoundindex || (field_mask & SND_LARGESOUND) || cls.protocol == PROTOCOL_NEHAHRABJP2 || cls.protocol == PROTOCOL_NEHAHRABJP3)
  242. sound_num = (unsigned short) MSG_ReadShort(&cl_message);
  243. else
  244. sound_num = MSG_ReadByte(&cl_message);
  245. }
  246. channel = CHAN_NET2ENGINE(channel);
  247. MSG_ReadVector(&cl_message, pos, cls.protocol);
  248. if (sound_num < 0 || sound_num >= MAX_SOUNDS)
  249. {
  250. Con_Printf("CL_ParseStartSoundPacket: sound_num (%i) >= MAX_SOUNDS (%i)\n", sound_num, MAX_SOUNDS);
  251. return;
  252. }
  253. if (ent >= MAX_EDICTS)
  254. {
  255. Con_Printf("CL_ParseStartSoundPacket: ent = %i", ent);
  256. return;
  257. }
  258. if (ent >= cl.max_entities)
  259. CL_ExpandEntities(ent);
  260. if( !CL_VM_Event_Sound(sound_num, nvolume / 255.0f, channel, attenuation, ent, pos, fflags, speed) )
  261. S_StartSound_StartPosition_Flags (ent, channel, cl.sound_precache[sound_num], pos, nvolume/255.0f, attenuation, 0, fflags, speed);
  262. }
  263. /*
  264. ==================
  265. CL_KeepaliveMessage
  266. When the client is taking a long time to load stuff, send keepalive messages
  267. so the server doesn't disconnect.
  268. ==================
  269. */
  270. static unsigned char olddata[NET_MAXMESSAGE];
  271. void CL_KeepaliveMessage (qboolean readmessages)
  272. {
  273. static double lastdirtytime = 0;
  274. static qboolean recursive = false;
  275. double dirtytime;
  276. double deltatime;
  277. static double countdownmsg = 0;
  278. static double countdownupdate = 0;
  279. sizebuf_t old;
  280. qboolean thisrecursive;
  281. thisrecursive = recursive;
  282. recursive = true;
  283. dirtytime = Sys_DirtyTime();
  284. deltatime = dirtytime - lastdirtytime;
  285. lastdirtytime = dirtytime;
  286. if (deltatime <= 0 || deltatime >= 1800.0)
  287. return;
  288. countdownmsg -= deltatime;
  289. countdownupdate -= deltatime;
  290. if(!thisrecursive)
  291. {
  292. if(cls.state != ca_dedicated)
  293. {
  294. if(countdownupdate <= 0) // check if time stepped backwards
  295. {
  296. SCR_UpdateLoadingScreenIfShown();
  297. countdownupdate = 2;
  298. }
  299. }
  300. }
  301. // no need if server is local and definitely not if this is a demo
  302. if (sv.active || !cls.netcon || cls.protocol == PROTOCOL_QUAKEWORLD || cls.signon >= SIGNONS)
  303. {
  304. recursive = thisrecursive;
  305. return;
  306. }
  307. if (readmessages)
  308. {
  309. // read messages from server, should just be nops
  310. old = cl_message;
  311. memcpy(olddata, cl_message.data, cl_message.cursize);
  312. NetConn_ClientFrame();
  313. cl_message = old;
  314. memcpy(cl_message.data, olddata, cl_message.cursize);
  315. }
  316. if (cls.netcon && countdownmsg <= 0) // check if time stepped backwards
  317. {
  318. sizebuf_t msg;
  319. unsigned char buf[4];
  320. countdownmsg = 5;
  321. // write out a nop
  322. // LordHavoc: must use unreliable because reliable could kill the sigon message!
  323. Con_Print("--> client to server keepalive\n");
  324. memset(&msg, 0, sizeof(msg));
  325. msg.data = buf;
  326. msg.maxsize = sizeof(buf);
  327. MSG_WriteChar(&msg, clc_nop);
  328. NetConn_SendUnreliableMessage(cls.netcon, &msg, cls.protocol, 10000, 0, false);
  329. }
  330. recursive = thisrecursive;
  331. }
  332. void CL_ParseEntityLump(char *entdata)
  333. {
  334. qboolean loadedsky = false;
  335. const char *data;
  336. char key[128], value[MAX_INPUTLINE];
  337. FOG_clear(); // LordHavoc: no fog until set
  338. // LordHavoc: default to the map's sky (q3 shader parsing sets this)
  339. R_SetSkyBox(cl.worldmodel->brush.skybox);
  340. data = entdata;
  341. if (!data)
  342. return;
  343. if (!COM_ParseToken_Simple(&data, false, false, true))
  344. return; // error
  345. if (com_token[0] != '{')
  346. return; // error
  347. while (1)
  348. {
  349. if (!COM_ParseToken_Simple(&data, false, false, true))
  350. return; // error
  351. if (com_token[0] == '}')
  352. break; // end of worldspawn
  353. if (com_token[0] == '_')
  354. strlcpy (key, com_token + 1, sizeof (key));
  355. else
  356. strlcpy (key, com_token, sizeof (key));
  357. while (key[strlen(key)-1] == ' ') // remove trailing spaces
  358. key[strlen(key)-1] = 0;
  359. if (!COM_ParseToken_Simple(&data, false, false, true))
  360. return; // error
  361. strlcpy (value, com_token, sizeof (value));
  362. if (!strcmp("sky", key))
  363. {
  364. loadedsky = true;
  365. R_SetSkyBox(value);
  366. }
  367. else if (!strcmp("skyname", key)) // non-standard, introduced by QuakeForge... sigh.
  368. {
  369. loadedsky = true;
  370. R_SetSkyBox(value);
  371. }
  372. else if (!strcmp("qlsky", key)) // non-standard, introduced by QuakeLives (EEK)
  373. {
  374. loadedsky = true;
  375. R_SetSkyBox(value);
  376. }
  377. else if (!strcmp("fog", key))
  378. {
  379. FOG_clear(); // so missing values get good defaults
  380. r_refdef.fog_start = 0;
  381. r_refdef.fog_alpha = 1;
  382. r_refdef.fog_end = 16384;
  383. r_refdef.fog_height = 1<<30;
  384. r_refdef.fog_fadedepth = 128;
  385. #if _MSC_VER >= 1400
  386. #define sscanf sscanf_s
  387. #endif
  388. 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);
  389. }
  390. else if (!strcmp("fog_density", key))
  391. r_refdef.fog_density = atof(value);
  392. else if (!strcmp("fog_red", key))
  393. r_refdef.fog_red = atof(value);
  394. else if (!strcmp("fog_green", key))
  395. r_refdef.fog_green = atof(value);
  396. else if (!strcmp("fog_blue", key))
  397. r_refdef.fog_blue = atof(value);
  398. else if (!strcmp("fog_alpha", key))
  399. r_refdef.fog_alpha = atof(value);
  400. else if (!strcmp("fog_start", key))
  401. r_refdef.fog_start = atof(value);
  402. else if (!strcmp("fog_end", key))
  403. r_refdef.fog_end = atof(value);
  404. else if (!strcmp("fog_height", key))
  405. r_refdef.fog_height = atof(value);
  406. else if (!strcmp("fog_fadedepth", key))
  407. r_refdef.fog_fadedepth = atof(value);
  408. else if (!strcmp("fog_heighttexture", key))
  409. {
  410. FOG_clear(); // so missing values get good defaults
  411. #if _MSC_VER >= 1400
  412. 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));
  413. #else
  414. 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);
  415. #endif
  416. r_refdef.fog_height_texturename[63] = 0;
  417. }
  418. }
  419. if (!loadedsky && cl.worldmodel->brush.isq2bsp)
  420. R_SetSkyBox("unit1_");
  421. }
  422. static const vec3_t defaultmins = {-4096, -4096, -4096};
  423. static const vec3_t defaultmaxs = {4096, 4096, 4096};
  424. static void CL_SetupWorldModel(void)
  425. {
  426. prvm_prog_t *prog = CLVM_prog;
  427. // update the world model
  428. cl.entities[0].render.model = cl.worldmodel = CL_GetModelByIndex(1);
  429. CL_UpdateRenderEntity(&cl.entities[0].render);
  430. // make sure the cl.worldname and related cvars are set up now that we know the world model name
  431. // set up csqc world for collision culling
  432. if (cl.worldmodel)
  433. {
  434. strlcpy(cl.worldname, cl.worldmodel->name, sizeof(cl.worldname));
  435. FS_StripExtension(cl.worldname, cl.worldnamenoextension, sizeof(cl.worldnamenoextension));
  436. strlcpy(cl.worldbasename, !strncmp(cl.worldnamenoextension, "maps/", 5) ? cl.worldnamenoextension + 5 : cl.worldnamenoextension, sizeof(cl.worldbasename));
  437. Cvar_SetQuick(&cl_worldmessage, cl.worldmessage);
  438. Cvar_SetQuick(&cl_worldname, cl.worldname);
  439. Cvar_SetQuick(&cl_worldnamenoextension, cl.worldnamenoextension);
  440. Cvar_SetQuick(&cl_worldbasename, cl.worldbasename);
  441. World_SetSize(&cl.world, cl.worldname, cl.worldmodel->normalmins, cl.worldmodel->normalmaxs, prog);
  442. }
  443. else
  444. {
  445. Cvar_SetQuick(&cl_worldmessage, cl.worldmessage);
  446. Cvar_SetQuick(&cl_worldnamenoextension, "");
  447. Cvar_SetQuick(&cl_worldbasename, "");
  448. World_SetSize(&cl.world, "", defaultmins, defaultmaxs, prog);
  449. }
  450. World_Start(&cl.world);
  451. // load or reload .loc file for team chat messages
  452. CL_Locs_Reload_f();
  453. // make sure we send enough keepalives
  454. CL_KeepaliveMessage(false);
  455. // reset particles and other per-level things
  456. R_Modules_NewMap();
  457. // make sure we send enough keepalives
  458. CL_KeepaliveMessage(false);
  459. // load the team chat beep if possible
  460. cl.foundtalk2wav = FS_FileExists("sound/misc/talk2.wav");
  461. // check memory integrity
  462. Mem_CheckSentinelsGlobal();
  463. #ifdef CONFIG_MENU
  464. // make menu know
  465. MR_NewMap();
  466. #endif
  467. // load the csqc now
  468. if (cl.loadcsqc)
  469. {
  470. cl.loadcsqc = false;
  471. CL_VM_Init();
  472. }
  473. }
  474. static qboolean QW_CL_CheckOrDownloadFile(const char *filename)
  475. {
  476. qfile_t *file;
  477. char vabuf[1024];
  478. // see if the file already exists
  479. file = FS_OpenVirtualFile(filename, true);
  480. if (file)
  481. {
  482. FS_Close(file);
  483. return true;
  484. }
  485. // download messages in a demo would be bad
  486. if (cls.demorecording)
  487. {
  488. Con_Printf("Unable to download \"%s\" when recording.\n", filename);
  489. return true;
  490. }
  491. // don't try to download when playing a demo
  492. if (!cls.netcon)
  493. return true;
  494. strlcpy(cls.qw_downloadname, filename, sizeof(cls.qw_downloadname));
  495. Con_Printf("Downloading %s\n", filename);
  496. if (!cls.qw_downloadmemory)
  497. {
  498. cls.qw_downloadmemory = NULL;
  499. cls.qw_downloadmemorycursize = 0;
  500. cls.qw_downloadmemorymaxsize = 1024*1024; // start out with a 1MB buffer
  501. }
  502. MSG_WriteByte(&cls.netcon->message, qw_clc_stringcmd);
  503. MSG_WriteString(&cls.netcon->message, va(vabuf, sizeof(vabuf), "download %s", filename));
  504. cls.qw_downloadnumber++;
  505. cls.qw_downloadpercent = 0;
  506. cls.qw_downloadmemorycursize = 0;
  507. return false;
  508. }
  509. static void QW_CL_ProcessUserInfo(int slot);
  510. static void QW_CL_RequestNextDownload(void)
  511. {
  512. int i;
  513. char vabuf[1024];
  514. // clear name of file that just finished
  515. cls.qw_downloadname[0] = 0;
  516. // skip the download fragment if playing a demo
  517. if (!cls.netcon)
  518. {
  519. return;
  520. }
  521. switch (cls.qw_downloadtype)
  522. {
  523. case dl_single:
  524. break;
  525. case dl_skin:
  526. if (cls.qw_downloadnumber == 0)
  527. Con_Printf("Checking skins...\n");
  528. for (;cls.qw_downloadnumber < cl.maxclients;cls.qw_downloadnumber++)
  529. {
  530. if (!cl.scores[cls.qw_downloadnumber].name[0])
  531. continue;
  532. // check if we need to download the file, and return if so
  533. if (!QW_CL_CheckOrDownloadFile(va(vabuf, sizeof(vabuf), "skins/%s.pcx", cl.scores[cls.qw_downloadnumber].qw_skin)))
  534. return;
  535. }
  536. cls.qw_downloadtype = dl_none;
  537. // load any newly downloaded skins
  538. for (i = 0;i < cl.maxclients;i++)
  539. QW_CL_ProcessUserInfo(i);
  540. // if we're still in signon stages, request the next one
  541. if (cls.signon != SIGNONS)
  542. {
  543. cls.signon = SIGNONS-1;
  544. // we'll go to SIGNONS when the first entity update is received
  545. MSG_WriteByte(&cls.netcon->message, qw_clc_stringcmd);
  546. MSG_WriteString(&cls.netcon->message, va(vabuf, sizeof(vabuf), "begin %i", cl.qw_servercount));
  547. }
  548. break;
  549. case dl_model:
  550. if (cls.qw_downloadnumber == 0)
  551. {
  552. Con_Printf("Checking models...\n");
  553. cls.qw_downloadnumber = 1;
  554. }
  555. for (;cls.qw_downloadnumber < MAX_MODELS && cl.model_name[cls.qw_downloadnumber][0];cls.qw_downloadnumber++)
  556. {
  557. // skip submodels
  558. if (cl.model_name[cls.qw_downloadnumber][0] == '*')
  559. continue;
  560. if (!strcmp(cl.model_name[cls.qw_downloadnumber], "progs/spike.mdl"))
  561. cl.qw_modelindex_spike = cls.qw_downloadnumber;
  562. if (!strcmp(cl.model_name[cls.qw_downloadnumber], "progs/player.mdl"))
  563. cl.qw_modelindex_player = cls.qw_downloadnumber;
  564. if (!strcmp(cl.model_name[cls.qw_downloadnumber], "progs/flag.mdl"))
  565. cl.qw_modelindex_flag = cls.qw_downloadnumber;
  566. if (!strcmp(cl.model_name[cls.qw_downloadnumber], "progs/s_explod.spr"))
  567. cl.qw_modelindex_s_explod = cls.qw_downloadnumber;
  568. // check if we need to download the file, and return if so
  569. if (!QW_CL_CheckOrDownloadFile(cl.model_name[cls.qw_downloadnumber]))
  570. return;
  571. }
  572. cls.qw_downloadtype = dl_none;
  573. // touch all of the precached models that are still loaded so we can free
  574. // anything that isn't needed
  575. if (!sv.active)
  576. Mod_ClearUsed();
  577. for (i = 1;i < MAX_MODELS && cl.model_name[i][0];i++)
  578. Mod_FindName(cl.model_name[i], cl.model_name[i][0] == '*' ? cl.model_name[1] : NULL);
  579. // precache any models used by the client (this also marks them used)
  580. cl.model_bolt = Mod_ForName("progs/bolt.mdl", false, false, NULL);
  581. cl.model_bolt2 = Mod_ForName("progs/bolt2.mdl", false, false, NULL);
  582. cl.model_bolt3 = Mod_ForName("progs/bolt3.mdl", false, false, NULL);
  583. cl.model_beam = Mod_ForName("progs/beam.mdl", false, false, NULL);
  584. // we purge the models and sounds later in CL_SignonReply
  585. //Mod_PurgeUnused();
  586. // now we try to load everything that is new
  587. // world model
  588. cl.model_precache[1] = Mod_ForName(cl.model_name[1], false, false, NULL);
  589. if (cl.model_precache[1]->Draw == NULL)
  590. Con_Printf("Map %s could not be found or downloaded\n", cl.model_name[1]);
  591. // normal models
  592. for (i = 2;i < MAX_MODELS && cl.model_name[i][0];i++)
  593. 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)
  594. Con_Printf("Model %s could not be found or downloaded\n", cl.model_name[i]);
  595. // check memory integrity
  596. Mem_CheckSentinelsGlobal();
  597. // now that we have a world model, set up the world entity, renderer
  598. // modules and csqc
  599. CL_SetupWorldModel();
  600. // add pmodel/emodel CRCs to userinfo
  601. CL_SetInfo("pmodel", va(vabuf, sizeof(vabuf), "%i", FS_CRCFile("progs/player.mdl", NULL)), true, true, true, true);
  602. CL_SetInfo("emodel", va(vabuf, sizeof(vabuf), "%i", FS_CRCFile("progs/eyes.mdl", NULL)), true, true, true, true);
  603. // done checking sounds and models, send a prespawn command now
  604. MSG_WriteByte(&cls.netcon->message, qw_clc_stringcmd);
  605. MSG_WriteString(&cls.netcon->message, va(vabuf, sizeof(vabuf), "prespawn %i 0 %i", cl.qw_servercount, cl.model_precache[1]->brush.qw_md4sum2));
  606. if (cls.qw_downloadmemory)
  607. {
  608. Mem_Free(cls.qw_downloadmemory);
  609. cls.qw_downloadmemory = NULL;
  610. }
  611. // done loading
  612. cl.loadfinished = true;
  613. break;
  614. case dl_sound:
  615. if (cls.qw_downloadnumber == 0)
  616. {
  617. Con_Printf("Checking sounds...\n");
  618. cls.qw_downloadnumber = 1;
  619. }
  620. for (;cl.sound_name[cls.qw_downloadnumber][0];cls.qw_downloadnumber++)
  621. {
  622. // check if we need to download the file, and return if so
  623. if (!QW_CL_CheckOrDownloadFile(va(vabuf, sizeof(vabuf), "sound/%s", cl.sound_name[cls.qw_downloadnumber])))
  624. return;
  625. }
  626. cls.qw_downloadtype = dl_none;
  627. // clear sound usage flags for purging of unused sounds
  628. S_ClearUsed();
  629. // precache any sounds used by the client
  630. cl.sfx_wizhit = S_PrecacheSound(cl_sound_wizardhit.string, false, true);
  631. cl.sfx_knighthit = S_PrecacheSound(cl_sound_hknighthit.string, false, true);
  632. cl.sfx_tink1 = S_PrecacheSound(cl_sound_tink1.string, false, true);
  633. cl.sfx_ric1 = S_PrecacheSound(cl_sound_ric1.string, false, true);
  634. cl.sfx_ric2 = S_PrecacheSound(cl_sound_ric2.string, false, true);
  635. cl.sfx_ric3 = S_PrecacheSound(cl_sound_ric3.string, false, true);
  636. cl.sfx_r_exp3 = S_PrecacheSound(cl_sound_r_exp3.string, false, true);
  637. // sounds used by the game
  638. for (i = 1;i < MAX_SOUNDS && cl.sound_name[i][0];i++)
  639. cl.sound_precache[i] = S_PrecacheSound(cl.sound_name[i], true, true);
  640. // we purge the models and sounds later in CL_SignonReply
  641. //S_PurgeUnused();
  642. // check memory integrity
  643. Mem_CheckSentinelsGlobal();
  644. // done with sound downloads, next we check models
  645. MSG_WriteByte(&cls.netcon->message, qw_clc_stringcmd);
  646. MSG_WriteString(&cls.netcon->message, va(vabuf, sizeof(vabuf), "modellist %i %i", cl.qw_servercount, 0));
  647. break;
  648. case dl_none:
  649. default:
  650. Con_Printf("Unknown download type.\n");
  651. }
  652. }
  653. static void QW_CL_ParseDownload(void)
  654. {
  655. int size = (signed short)MSG_ReadShort(&cl_message);
  656. int percent = MSG_ReadByte(&cl_message);
  657. //Con_Printf("download %i %i%% (%i/%i)\n", size, percent, cls.qw_downloadmemorycursize, cls.qw_downloadmemorymaxsize);
  658. // skip the download fragment if playing a demo
  659. if (!cls.netcon)
  660. {
  661. if (size > 0)
  662. cl_message.readcount += size;
  663. return;
  664. }
  665. if (size == -1)
  666. {
  667. Con_Printf("File not found.\n");
  668. QW_CL_RequestNextDownload();
  669. return;
  670. }
  671. if (cl_message.readcount + (unsigned short)size > cl_message.cursize)
  672. Host_Error("corrupt download message\n");
  673. // make sure the buffer is big enough to include this new fragment
  674. if (!cls.qw_downloadmemory || cls.qw_downloadmemorymaxsize < cls.qw_downloadmemorycursize + size)
  675. {
  676. unsigned char *old;
  677. while (cls.qw_downloadmemorymaxsize < cls.qw_downloadmemorycursize + size)
  678. cls.qw_downloadmemorymaxsize *= 2;
  679. old = cls.qw_downloadmemory;
  680. cls.qw_downloadmemory = (unsigned char *)Mem_Alloc(cls.permanentmempool, cls.qw_downloadmemorymaxsize);
  681. if (old)
  682. {
  683. memcpy(cls.qw_downloadmemory, old, cls.qw_downloadmemorycursize);
  684. Mem_Free(old);
  685. }
  686. }
  687. // read the fragment out of the packet
  688. MSG_ReadBytes(&cl_message, size, cls.qw_downloadmemory + cls.qw_downloadmemorycursize);
  689. cls.qw_downloadmemorycursize += size;
  690. cls.qw_downloadspeedcount += size;
  691. cls.qw_downloadpercent = percent;
  692. if (percent != 100)
  693. {
  694. // request next fragment
  695. MSG_WriteByte(&cls.netcon->message, qw_clc_stringcmd);
  696. MSG_WriteString(&cls.netcon->message, "nextdl");
  697. }
  698. else
  699. {
  700. // finished file
  701. Con_Printf("Downloaded \"%s\"\n", cls.qw_downloadname);
  702. FS_WriteFile(cls.qw_downloadname, cls.qw_downloadmemory, cls.qw_downloadmemorycursize);
  703. cls.qw_downloadpercent = 0;
  704. // start downloading the next file (or join the game)
  705. QW_CL_RequestNextDownload();
  706. }
  707. }
  708. static void QW_CL_ParseModelList(void)
  709. {
  710. int n;
  711. int nummodels = MSG_ReadByte(&cl_message);
  712. char *str;
  713. char vabuf[1024];
  714. // parse model precache list
  715. for (;;)
  716. {
  717. str = MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring));
  718. if (!str[0])
  719. break;
  720. nummodels++;
  721. if (nummodels==MAX_MODELS)
  722. Host_Error("Server sent too many model precaches");
  723. if (strlen(str) >= MAX_QPATH)
  724. Host_Error("Server sent a precache name of %i characters (max %i)", (int)strlen(str), MAX_QPATH - 1);
  725. strlcpy(cl.model_name[nummodels], str, sizeof (cl.model_name[nummodels]));
  726. }
  727. n = MSG_ReadByte(&cl_message);
  728. if (n)
  729. {
  730. MSG_WriteByte(&cls.netcon->message, qw_clc_stringcmd);
  731. MSG_WriteString(&cls.netcon->message, va(vabuf, sizeof(vabuf), "modellist %i %i", cl.qw_servercount, n));
  732. return;
  733. }
  734. cls.signon = 2;
  735. cls.qw_downloadnumber = 0;
  736. cls.qw_downloadtype = dl_model;
  737. QW_CL_RequestNextDownload();
  738. }
  739. static void QW_CL_ParseSoundList(void)
  740. {
  741. int n;
  742. int numsounds = MSG_ReadByte(&cl_message);
  743. char *str;
  744. char vabuf[1024];
  745. // parse sound precache list
  746. for (;;)
  747. {
  748. str = MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring));
  749. if (!str[0])
  750. break;
  751. numsounds++;
  752. if (numsounds==MAX_SOUNDS)
  753. Host_Error("Server sent too many sound precaches");
  754. if (strlen(str) >= MAX_QPATH)
  755. Host_Error("Server sent a precache name of %i characters (max %i)", (int)strlen(str), MAX_QPATH - 1);
  756. strlcpy(cl.sound_name[numsounds], str, sizeof (cl.sound_name[numsounds]));
  757. }
  758. n = MSG_ReadByte(&cl_message);
  759. if (n)
  760. {
  761. MSG_WriteByte(&cls.netcon->message, qw_clc_stringcmd);
  762. MSG_WriteString(&cls.netcon->message, va(vabuf, sizeof(vabuf), "soundlist %i %i", cl.qw_servercount, n));
  763. return;
  764. }
  765. cls.signon = 2;
  766. cls.qw_downloadnumber = 0;
  767. cls.qw_downloadtype = dl_sound;
  768. QW_CL_RequestNextDownload();
  769. }
  770. static void QW_CL_Skins_f(void)
  771. {
  772. cls.qw_downloadnumber = 0;
  773. cls.qw_downloadtype = dl_skin;
  774. QW_CL_RequestNextDownload();
  775. }
  776. static void QW_CL_Changing_f(void)
  777. {
  778. if (cls.qw_downloadmemory) // don't change when downloading
  779. return;
  780. S_StopAllSounds();
  781. cl.intermission = 0;
  782. cls.signon = 1; // not active anymore, but not disconnected
  783. Con_Printf("\nChanging map...\n");
  784. }
  785. void QW_CL_NextUpload(void)
  786. {
  787. int r, percent, size;
  788. if (!cls.qw_uploaddata)
  789. return;
  790. r = cls.qw_uploadsize - cls.qw_uploadpos;
  791. if (r > 768)
  792. r = 768;
  793. size = min(1, cls.qw_uploadsize);
  794. percent = (cls.qw_uploadpos+r)*100/size;
  795. MSG_WriteByte(&cls.netcon->message, qw_clc_upload);
  796. MSG_WriteShort(&cls.netcon->message, r);
  797. MSG_WriteByte(&cls.netcon->message, percent);
  798. SZ_Write(&cls.netcon->message, cls.qw_uploaddata + cls.qw_uploadpos, r);
  799. Con_DPrintf("UPLOAD: %6d: %d written\n", cls.qw_uploadpos, r);
  800. cls.qw_uploadpos += r;
  801. if (cls.qw_uploadpos < cls.qw_uploadsize)
  802. return;
  803. Con_Printf("Upload completed\n");
  804. QW_CL_StopUpload();
  805. }
  806. void QW_CL_StartUpload(unsigned char *data, int size)
  807. {
  808. // do nothing in demos or if not connected
  809. if (!cls.netcon)
  810. return;
  811. // abort existing upload if in progress
  812. QW_CL_StopUpload();
  813. Con_DPrintf("Starting upload of %d bytes...\n", size);
  814. cls.qw_uploaddata = (unsigned char *)Mem_Alloc(cls.permanentmempool, size);
  815. memcpy(cls.qw_uploaddata, data, size);
  816. cls.qw_uploadsize = size;
  817. cls.qw_uploadpos = 0;
  818. QW_CL_NextUpload();
  819. }
  820. #if 0
  821. qboolean QW_CL_IsUploading(void)
  822. {
  823. return cls.qw_uploaddata != NULL;
  824. }
  825. #endif
  826. void QW_CL_StopUpload(void)
  827. {
  828. if (cls.qw_uploaddata)
  829. Mem_Free(cls.qw_uploaddata);
  830. cls.qw_uploaddata = NULL;
  831. cls.qw_uploadsize = 0;
  832. cls.qw_uploadpos = 0;
  833. }
  834. static void QW_CL_ProcessUserInfo(int slot)
  835. {
  836. int topcolor, bottomcolor;
  837. char temp[2048];
  838. InfoString_GetValue(cl.scores[slot].qw_userinfo, "name", cl.scores[slot].name, sizeof(cl.scores[slot].name));
  839. InfoString_GetValue(cl.scores[slot].qw_userinfo, "topcolor", temp, sizeof(temp));topcolor = atoi(temp);
  840. InfoString_GetValue(cl.scores[slot].qw_userinfo, "bottomcolor", temp, sizeof(temp));bottomcolor = atoi(temp);
  841. cl.scores[slot].colors = topcolor * 16 + bottomcolor;
  842. InfoString_GetValue(cl.scores[slot].qw_userinfo, "*spectator", temp, sizeof(temp));
  843. cl.scores[slot].qw_spectator = temp[0] != 0;
  844. InfoString_GetValue(cl.scores[slot].qw_userinfo, "team", cl.scores[slot].qw_team, sizeof(cl.scores[slot].qw_team));
  845. InfoString_GetValue(cl.scores[slot].qw_userinfo, "skin", cl.scores[slot].qw_skin, sizeof(cl.scores[slot].qw_skin));
  846. if (!cl.scores[slot].qw_skin[0])
  847. strlcpy(cl.scores[slot].qw_skin, "base", sizeof(cl.scores[slot].qw_skin));
  848. // TODO: skin cache
  849. }
  850. static void QW_CL_UpdateUserInfo(void)
  851. {
  852. int slot;
  853. slot = MSG_ReadByte(&cl_message);
  854. if (slot >= cl.maxclients)
  855. {
  856. Con_Printf("svc_updateuserinfo >= cl.maxclients\n");
  857. MSG_ReadLong(&cl_message);
  858. MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring));
  859. return;
  860. }
  861. cl.scores[slot].qw_userid = MSG_ReadLong(&cl_message);
  862. strlcpy(cl.scores[slot].qw_userinfo, MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)), sizeof(cl.scores[slot].qw_userinfo));
  863. QW_CL_ProcessUserInfo(slot);
  864. }
  865. static void QW_CL_SetInfo(void)
  866. {
  867. int slot;
  868. char key[2048];
  869. char value[2048];
  870. slot = MSG_ReadByte(&cl_message);
  871. strlcpy(key, MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)), sizeof(key));
  872. strlcpy(value, MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)), sizeof(value));
  873. if (slot >= cl.maxclients)
  874. {
  875. Con_Printf("svc_setinfo >= cl.maxclients\n");
  876. return;
  877. }
  878. InfoString_SetValue(cl.scores[slot].qw_userinfo, sizeof(cl.scores[slot].qw_userinfo), key, value);
  879. QW_CL_ProcessUserInfo(slot);
  880. }
  881. static void QW_CL_ServerInfo(void)
  882. {
  883. char key[2048];
  884. char value[2048];
  885. char temp[32];
  886. strlcpy(key, MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)), sizeof(key));
  887. strlcpy(value, MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)), sizeof(value));
  888. Con_DPrintf("SERVERINFO: %s=%s\n", key, value);
  889. InfoString_SetValue(cl.qw_serverinfo, sizeof(cl.qw_serverinfo), key, value);
  890. InfoString_GetValue(cl.qw_serverinfo, "teamplay", temp, sizeof(temp));
  891. cl.qw_teamplay = atoi(temp);
  892. }
  893. static void QW_CL_ParseNails(void)
  894. {
  895. int i, j;
  896. int numnails = MSG_ReadByte(&cl_message);
  897. vec_t *v;
  898. unsigned char bits[6];
  899. for (i = 0;i < numnails;i++)
  900. {
  901. for (j = 0;j < 6;j++)
  902. bits[j] = MSG_ReadByte(&cl_message);
  903. if (cl.qw_num_nails >= 255)
  904. continue;
  905. v = cl.qw_nails[cl.qw_num_nails++];
  906. v[0] = ( ( bits[0] + ((bits[1]&15)<<8) ) <<1) - 4096;
  907. v[1] = ( ( (bits[1]>>4) + (bits[2]<<4) ) <<1) - 4096;
  908. v[2] = ( ( bits[3] + ((bits[4]&15)<<8) ) <<1) - 4096;
  909. v[3] = -360*(bits[4]>>4)/16;
  910. v[4] = 360*bits[5]/256;
  911. v[5] = 0;
  912. }
  913. }
  914. static void CL_UpdateItemsAndWeapon(void)
  915. {
  916. int j;
  917. // check for important changes
  918. // set flash times
  919. if (cl.olditems != cl.stats[STAT_ITEMS])
  920. for (j = 0;j < 32;j++)
  921. if ((cl.stats[STAT_ITEMS] & (1<<j)) && !(cl.olditems & (1<<j)))
  922. cl.item_gettime[j] = cl.time;
  923. cl.olditems = cl.stats[STAT_ITEMS];
  924. // GAME_NEXUIZ hud needs weapon change time
  925. if (cl.activeweapon != cl.stats[STAT_ACTIVEWEAPON])
  926. cl.weapontime = cl.time;
  927. cl.activeweapon = cl.stats[STAT_ACTIVEWEAPON];
  928. }
  929. #define LOADPROGRESSWEIGHT_SOUND 1.0
  930. #define LOADPROGRESSWEIGHT_MODEL 4.0
  931. #define LOADPROGRESSWEIGHT_WORLDMODEL 30.0
  932. #define LOADPROGRESSWEIGHT_WORLDMODEL_INIT 2.0
  933. static void CL_BeginDownloads(qboolean aborteddownload)
  934. {
  935. char vabuf[1024];
  936. // quakeworld works differently
  937. if (cls.protocol == PROTOCOL_QUAKEWORLD)
  938. return;
  939. // this would be a good place to do curl downloads
  940. if(Curl_Have_forthismap())
  941. {
  942. Curl_Register_predownload(); // come back later
  943. return;
  944. }
  945. // if we got here...
  946. // curl is done, so let's start with the business
  947. if(!cl.loadbegun)
  948. SCR_PushLoadingScreen(false, "Loading precaches", 1);
  949. cl.loadbegun = true;
  950. // if already downloading something from the previous level, don't stop it
  951. if (cls.qw_downloadname[0])
  952. return;
  953. if (cl.downloadcsqc)
  954. {
  955. size_t progsize;
  956. cl.downloadcsqc = false;
  957. if (cls.netcon
  958. && !sv.active
  959. && csqc_progname.string
  960. && csqc_progname.string[0]
  961. && csqc_progcrc.integer >= 0
  962. && cl_serverextension_download.integer
  963. && (FS_CRCFile(csqc_progname.string, &progsize) != csqc_progcrc.integer || ((int)progsize != csqc_progsize.integer && csqc_progsize.integer != -1))
  964. && !FS_FileExists(va(vabuf, sizeof(vabuf), "dlcache/%s.%i.%i", csqc_progname.string, csqc_progsize.integer, csqc_progcrc.integer)))
  965. {
  966. Con_Printf("Downloading new CSQC code to dlcache/%s.%i.%i\n", csqc_progname.string, csqc_progsize.integer, csqc_progcrc.integer);
  967. if(cl_serverextension_download.integer == 2 && FS_HasZlib())
  968. Cmd_ForwardStringToServer(va(vabuf, sizeof(vabuf), "download %s deflate", csqc_progname.string));
  969. else
  970. Cmd_ForwardStringToServer(va(vabuf, sizeof(vabuf), "download %s", csqc_progname.string));
  971. return;
  972. }
  973. }
  974. if (cl.loadmodel_current < cl.loadmodel_total)
  975. {
  976. // loading models
  977. if(cl.loadmodel_current == 1)
  978. {
  979. // worldmodel counts as 16 models (15 + world model setup), for better progress bar
  980. SCR_PushLoadingScreen(false, "Loading precached models",
  981. (
  982. (cl.loadmodel_total - 1) * LOADPROGRESSWEIGHT_MODEL
  983. + LOADPROGRESSWEIGHT_WORLDMODEL
  984. + LOADPROGRESSWEIGHT_WORLDMODEL_INIT
  985. ) / (
  986. (cl.loadmodel_total - 1) * LOADPROGRESSWEIGHT_MODEL
  987. + LOADPROGRESSWEIGHT_WORLDMODEL
  988. + LOADPROGRESSWEIGHT_WORLDMODEL_INIT
  989. + cl.loadsound_total * LOADPROGRESSWEIGHT_SOUND
  990. )
  991. );
  992. SCR_BeginLoadingPlaque(false);
  993. }
  994. for (;cl.loadmodel_current < cl.loadmodel_total;cl.loadmodel_current++)
  995. {
  996. SCR_PushLoadingScreen(false, cl.model_name[cl.loadmodel_current],
  997. (
  998. (cl.loadmodel_current == 1) ? LOADPROGRESSWEIGHT_WORLDMODEL : LOADPROGRESSWEIGHT_MODEL
  999. ) / (
  1000. (cl.loadmodel_total - 1) * LOADPROGRESSWEIGHT_MODEL
  1001. + LOADPROGRESSWEIGHT_WORLDMODEL
  1002. + LOADPROGRESSWEIGHT_WORLDMODEL_INIT
  1003. )
  1004. );
  1005. if (cl.model_precache[cl.loadmodel_current] && cl.model_precache[cl.loadmodel_current]->Draw)
  1006. {
  1007. SCR_PopLoadingScreen(false);
  1008. if(cl.loadmodel_current == 1)
  1009. {
  1010. SCR_PushLoadingScreen(false, cl.model_name[cl.loadmodel_current], 1.0 / cl.loadmodel_total);
  1011. SCR_PopLoadingScreen(false);
  1012. }
  1013. continue;
  1014. }
  1015. CL_KeepaliveMessage(true);
  1016. // if running a local game, calling Mod_ForName is a completely wasted effort...
  1017. if (sv.active)
  1018. cl.model_precache[cl.loadmodel_current] = sv.models[cl.loadmodel_current];
  1019. else
  1020. {
  1021. if(cl.loadmodel_current == 1)
  1022. {
  1023. // they'll be soon loaded, but make sure we apply freshly downloaded shaders from a curled pk3
  1024. Mod_FreeQ3Shaders();
  1025. }
  1026. 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);
  1027. }
  1028. SCR_PopLoadingScreen(false);
  1029. if (cl.model_precache[cl.loadmodel_current] && cl.model_precache[cl.loadmodel_current]->Draw && cl.loadmodel_current == 1)
  1030. {
  1031. // we now have the worldmodel so we can set up the game world
  1032. SCR_PushLoadingScreen(true, "world model setup",
  1033. (
  1034. LOADPROGRESSWEIGHT_WORLDMODEL_INIT
  1035. ) / (
  1036. (cl.loadmodel_total - 1) * LOADPROGRESSWEIGHT_MODEL
  1037. + LOADPROGRESSWEIGHT_WORLDMODEL
  1038. + LOADPROGRESSWEIGHT_WORLDMODEL_INIT
  1039. )
  1040. );
  1041. CL_SetupWorldModel();
  1042. SCR_PopLoadingScreen(true);
  1043. if (!cl.loadfinished && cl_joinbeforedownloadsfinish.integer)
  1044. {
  1045. cl.loadfinished = true;
  1046. // now issue the spawn to move on to signon 2 like normal
  1047. if (cls.netcon)
  1048. Cmd_ForwardStringToServer("prespawn");
  1049. }
  1050. }
  1051. }
  1052. SCR_PopLoadingScreen(false);
  1053. // finished loading models
  1054. }
  1055. if (cl.loadsound_current < cl.loadsound_total)
  1056. {
  1057. // loading sounds
  1058. if(cl.loadsound_current == 1)
  1059. SCR_PushLoadingScreen(false, "Loading precached sounds",
  1060. (
  1061. cl.loadsound_total * LOADPROGRESSWEIGHT_SOUND
  1062. ) / (
  1063. (cl.loadmodel_total - 1) * LOADPROGRESSWEIGHT_MODEL
  1064. + LOADPROGRESSWEIGHT_WORLDMODEL
  1065. + LOADPROGRESSWEIGHT_WORLDMODEL_INIT
  1066. + cl.loadsound_total * LOADPROGRESSWEIGHT_SOUND
  1067. )
  1068. );
  1069. for (;cl.loadsound_current < cl.loadsound_total;cl.loadsound_current++)
  1070. {
  1071. SCR_PushLoadingScreen(false, cl.sound_name[cl.loadsound_current], 1.0 / cl.loadsound_total);
  1072. if (cl.sound_precache[cl.loadsound_current] && S_IsSoundPrecached(cl.sound_precache[cl.loadsound_current]))
  1073. {
  1074. SCR_PopLoadingScreen(false);
  1075. continue;
  1076. }
  1077. CL_KeepaliveMessage(true);
  1078. cl.sound_precache[cl.loadsound_current] = S_PrecacheSound(cl.sound_name[cl.loadsound_current], false, true);
  1079. SCR_PopLoadingScreen(false);
  1080. }
  1081. SCR_PopLoadingScreen(false);
  1082. // finished loading sounds
  1083. }
  1084. if(IS_NEXUIZ_DERIVED(gamemode))
  1085. Cvar_SetValueQuick(&cl_serverextension_download, false);
  1086. // in Nexuiz/Xonotic, the built in download protocol is kinda broken (misses lots
  1087. // of dependencies) anyway, and can mess around with the game directory;
  1088. // until this is fixed, only support pk3 downloads via curl, and turn off
  1089. // individual file downloads other than for CSQC
  1090. // on the other end of the download protocol, GAME_NEXUIZ/GAME_XONOTIC enforces writing
  1091. // to dlcache only
  1092. // idea: support download of pk3 files using this protocol later
  1093. // note: the reason these loops skip already-loaded things is that it
  1094. // enables this command to be issued during the game if desired
  1095. if (cl.downloadmodel_current < cl.loadmodel_total)
  1096. {
  1097. // loading models
  1098. for (;cl.downloadmodel_current < cl.loadmodel_total;cl.downloadmodel_current++)
  1099. {
  1100. if (aborteddownload)
  1101. {
  1102. if (cl.downloadmodel_current == 1)
  1103. {
  1104. // the worldmodel failed, but we need to set up anyway
  1105. Mod_FreeQ3Shaders();
  1106. CL_SetupWorldModel();
  1107. if (!cl.loadfinished && cl_joinbeforedownloadsfinish.integer)
  1108. {
  1109. cl.loadfinished = true;
  1110. // now issue the spawn to move on to signon 2 like normal
  1111. if (cls.netcon)
  1112. Cmd_ForwardStringToServer("prespawn");
  1113. }
  1114. }
  1115. aborteddownload = false;
  1116. continue;
  1117. }
  1118. if (cl.model_precache[cl.downloadmodel_current] && cl.model_precache[cl.downloadmodel_current]->Draw)
  1119. continue;
  1120. CL_KeepaliveMessage(true);
  1121. if (cl.model_name[cl.downloadmodel_current][0] != '*' && strcmp(cl.model_name[cl.downloadmodel_current], "null") && !FS_FileExists(cl.model_name[cl.downloadmodel_current]))
  1122. {
  1123. if (cl.downloadmodel_current == 1)
  1124. Con_Printf("Map %s not found\n", cl.model_name[cl.downloadmodel_current]);
  1125. else
  1126. Con_Printf("Model %s not found\n", cl.model_name[cl.downloadmodel_current]);
  1127. // regarding the * check: don't try to download submodels
  1128. if (cl_serverextension_download.integer && cls.netcon && cl.model_name[cl.downloadmodel_current][0] != '*' && !sv.active)
  1129. {
  1130. Cmd_ForwardStringToServer(va(vabuf, sizeof(vabuf), "download %s", cl.model_name[cl.downloadmodel_current]));
  1131. // we'll try loading again when the download finishes
  1132. return;
  1133. }
  1134. }
  1135. if(cl.downloadmodel_current == 1)
  1136. {
  1137. // they'll be soon loaded, but make sure we apply freshly downloaded shaders from a curled pk3
  1138. Mod_FreeQ3Shaders();
  1139. }
  1140. cl.model_precache[cl.downloadmodel_current] = Mod_ForName(cl.model_name[cl.downloadmodel_current], false, true, cl.model_name[cl.downloadmodel_current][0] == '*' ? cl.model_name[1] : NULL);
  1141. if (cl.downloadmodel_current == 1)
  1142. {
  1143. // we now have the worldmodel so we can set up the game world
  1144. // or maybe we do not have it (cl_serverextension_download 0)
  1145. // then we need to continue loading ANYWAY!
  1146. CL_SetupWorldModel();
  1147. if (!cl.loadfinished && cl_joinbeforedownloadsfinish.integer)
  1148. {
  1149. cl.loadfinished = true;
  1150. // now issue the spawn to move on to signon 2 like normal
  1151. if (cls.netcon)
  1152. Cmd_ForwardStringToServer("prespawn");
  1153. }
  1154. }
  1155. }
  1156. // finished loading models
  1157. }
  1158. if (cl.downloadsound_current < cl.loadsound_total)
  1159. {
  1160. // loading sounds
  1161. for (;cl.downloadsound_current < cl.loadsound_total;cl.downloadsound_current++)
  1162. {
  1163. char soundname[MAX_QPATH];
  1164. if (aborteddownload)
  1165. {
  1166. aborteddownload = false;
  1167. continue;
  1168. }
  1169. if (cl.sound_precache[cl.downloadsound_current] && S_IsSoundPrecached(cl.sound_precache[cl.downloadsound_current]))
  1170. continue;
  1171. CL_KeepaliveMessage(true);
  1172. dpsnprintf(soundname, sizeof(soundname), "sound/%s", cl.sound_name[cl.downloadsound_current]);
  1173. if (!FS_FileExists(soundname) && !FS_FileExists(cl.sound_name[cl.downloadsound_current]))
  1174. {
  1175. Con_Printf("Sound %s not found\n", soundname);
  1176. if (cl_serverextension_download.integer && cls.netcon && !sv.active)
  1177. {
  1178. Cmd_ForwardStringToServer(va(vabuf, sizeof(vabuf), "download %s", soundname));
  1179. // we'll try loading again when the download finishes
  1180. return;
  1181. }
  1182. }
  1183. cl.sound_precache[cl.downloadsound_current] = S_PrecacheSound(cl.sound_name[cl.downloadsound_current], false, true);
  1184. }
  1185. // finished loading sounds
  1186. }
  1187. SCR_PopLoadingScreen(false);
  1188. if (!cl.loadfinished)
  1189. {
  1190. cl.loadfinished = true;
  1191. // check memory integrity
  1192. Mem_CheckSentinelsGlobal();
  1193. // now issue the spawn to move on to signon 2 like normal
  1194. if (cls.netcon)
  1195. Cmd_ForwardStringToServer("prespawn");
  1196. }
  1197. }
  1198. static void CL_BeginDownloads_f(void)
  1199. {
  1200. // prevent cl_begindownloads from being issued multiple times in one match
  1201. // to prevent accidentally cancelled downloads
  1202. if(cl.loadbegun)
  1203. Con_Printf("cl_begindownloads is only valid once per match\n");
  1204. else
  1205. CL_BeginDownloads(false);
  1206. }
  1207. static void CL_StopDownload(int size, int crc)
  1208. {
  1209. if (cls.qw_downloadmemory && cls.qw_downloadmemorycursize == size && CRC_Block(cls.qw_downloadmemory, cls.qw_downloadmemorycursize) == crc)
  1210. {
  1211. int existingcrc;
  1212. size_t existingsize;
  1213. const char *extension;
  1214. if(cls.qw_download_deflate)
  1215. {
  1216. unsigned char *out;
  1217. size_t inflated_size;
  1218. out = FS_Inflate(cls.qw_downloadmemory, cls.qw_downloadmemorycursize, &inflated_size, tempmempool);
  1219. Mem_Free(cls.qw_downloadmemory);
  1220. if(out)
  1221. {
  1222. Con_Printf("Inflated download: new size: %u (%g%%)\n", (unsigned)inflated_size, 100.0 - 100.0*(cls.qw_downloadmemorycursize / (float)inflated_size));
  1223. cls.qw_downloadmemory = out;
  1224. cls.qw_downloadmemorycursize = (int)inflated_size;
  1225. }
  1226. else
  1227. {
  1228. cls.qw_downloadmemory = NULL;
  1229. cls.qw_downloadmemorycursize = 0;
  1230. Con_Printf("Cannot inflate download, possibly corrupt or zlib not present\n");
  1231. }
  1232. }
  1233. if(!cls.qw_downloadmemory)
  1234. {
  1235. Con_Printf("Download \"%s\" is corrupt (see above!)\n", cls.qw_downloadname);
  1236. }
  1237. else
  1238. {
  1239. crc = CRC_Block(cls.qw_downloadmemory, cls.qw_downloadmemorycursize);
  1240. size = cls.qw_downloadmemorycursize;
  1241. // finished file
  1242. // save to disk only if we don't already have it
  1243. // (this is mainly for playing back demos)
  1244. existingcrc = FS_CRCFile(cls.qw_downloadname, &existingsize);
  1245. if (existingsize || IS_NEXUIZ_DERIVED(gamemode) || !strcmp(cls.qw_downloadname, csqc_progname.string))
  1246. // let csprogs ALWAYS go to dlcache, to prevent "viral csprogs"; also, never put files outside dlcache for Nexuiz/Xonotic
  1247. {
  1248. if ((int)existingsize != size || existingcrc != crc)
  1249. {
  1250. // we have a mismatching file, pick another name for it
  1251. char name[MAX_QPATH*2];
  1252. dpsnprintf(name, sizeof(name), "dlcache/%s.%i.%i", cls.qw_downloadname, size, crc);
  1253. if (!FS_FileExists(name))
  1254. {
  1255. Con_Printf("Downloaded \"%s\" (%i bytes, %i CRC)\n", name, size, crc);
  1256. FS_WriteFile(name, cls.qw_downloadmemory, cls.qw_downloadmemorycursize);
  1257. if(!strcmp(cls.qw_downloadname, csqc_progname.string))
  1258. {
  1259. if(cls.caughtcsprogsdata)
  1260. Mem_Free(cls.caughtcsprogsdata);
  1261. cls.caughtcsprogsdata = (unsigned char *) Mem_Alloc(cls.permanentmempool, cls.qw_downloadmemorycursize);
  1262. memcpy(cls.caughtcsprogsdata, cls.qw_downloadmemory, cls.qw_downloadmemorycursize);
  1263. cls.caughtcsprogsdatasize = cls.qw_downloadmemorycursize;
  1264. Con_DPrintf("Buffered \"%s\"\n", name);
  1265. }
  1266. }
  1267. }
  1268. }
  1269. else
  1270. {
  1271. // we either don't have it or have a mismatching file...
  1272. // so it's time to accept the file
  1273. // but if we already have a mismatching file we need to rename
  1274. // this new one, and if we already have this file in renamed form,
  1275. // we do nothing
  1276. Con_Printf("Downloaded \"%s\" (%i bytes, %i CRC)\n", cls.qw_downloadname, size, crc);
  1277. FS_WriteFile(cls.qw_downloadname, cls.qw_downloadmemory, cls.qw_downloadmemorycursize);
  1278. extension = FS_FileExtension(cls.qw_downloadname);
  1279. if (!strcasecmp(extension, "pak") || !strcasecmp(extension, "pk3"))
  1280. FS_Rescan();
  1281. }
  1282. }
  1283. }
  1284. else if (cls.qw_downloadmemory && size)
  1285. {
  1286. 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));
  1287. CL_BeginDownloads(true);
  1288. }
  1289. if (cls.qw_downloadmemory)
  1290. Mem_Free(cls.qw_downloadmemory);
  1291. cls.qw_downloadmemory = NULL;
  1292. cls.qw_downloadname[0] = 0;
  1293. cls.qw_downloadmemorymaxsize = 0;
  1294. cls.qw_downloadmemorycursize = 0;
  1295. cls.qw_downloadpercent = 0;
  1296. }
  1297. static void CL_ParseDownload(void)
  1298. {
  1299. int i, start, size;
  1300. static unsigned char data[NET_MAXMESSAGE];
  1301. start = MSG_ReadLong(&cl_message);
  1302. size = (unsigned short)MSG_ReadShort(&cl_message);
  1303. // record the start/size information to ack in the next input packet
  1304. for (i = 0;i < CL_MAX_DOWNLOADACKS;i++)
  1305. {
  1306. if (!cls.dp_downloadack[i].start && !cls.dp_downloadack[i].size)
  1307. {
  1308. cls.dp_downloadack[i].start = start;
  1309. cls.dp_downloadack[i].size = size;
  1310. break;
  1311. }
  1312. }
  1313. MSG_ReadBytes(&cl_message, size, data);
  1314. if (!cls.qw_downloadname[0])
  1315. {
  1316. if (size > 0)
  1317. Con_Printf("CL_ParseDownload: received %i bytes with no download active\n", size);
  1318. return;
  1319. }
  1320. if (start + size > cls.qw_downloadmemorymaxsize)
  1321. Host_Error("corrupt download message\n");
  1322. // only advance cursize if the data is at the expected position
  1323. // (gaps are unacceptable)
  1324. memcpy(cls.qw_downloadmemory + start, data, size);
  1325. cls.qw_downloadmemorycursize = start + size;
  1326. cls.qw_downloadpercent = (int)floor((start+size) * 100.0 / cls.qw_downloadmemorymaxsize);
  1327. cls.qw_downloadpercent = bound(0, cls.qw_downloadpercent, 100);
  1328. cls.qw_downloadspeedcount += size;
  1329. }
  1330. static void CL_DownloadBegin_f(void)
  1331. {
  1332. int size = atoi(Cmd_Argv(1));
  1333. if (size < 0 || size > 1<<30 || FS_CheckNastyPath(Cmd_Argv(2), false))
  1334. {
  1335. Con_Printf("cl_downloadbegin: received bogus information\n");
  1336. CL_StopDownload(0, 0);
  1337. return;
  1338. }
  1339. if (cls.qw_downloadname[0])
  1340. Con_Printf("Download of %s aborted\n", cls.qw_downloadname);
  1341. CL_StopDownload(0, 0);
  1342. // we're really beginning a download now, so initialize stuff
  1343. strlcpy(cls.qw_downloadname, Cmd_Argv(2), sizeof(cls.qw_downloadname));
  1344. cls.qw_downloadmemorymaxsize = size;
  1345. cls.qw_downloadmemory = (unsigned char *) Mem_Alloc(cls.permanentmempool, cls.qw_downloadmemorymaxsize);
  1346. cls.qw_downloadnumber++;
  1347. cls.qw_download_deflate = false;
  1348. if(Cmd_Argc() >= 4)
  1349. {
  1350. if(!strcmp(Cmd_Argv(3), "deflate"))
  1351. cls.qw_download_deflate = true;
  1352. // check further encodings here
  1353. }
  1354. Cmd_ForwardStringToServer("sv_startdownload");
  1355. }
  1356. static void CL_StopDownload_f(void)
  1357. {
  1358. Curl_CancelAll();
  1359. if (cls.qw_downloadname[0])
  1360. {
  1361. Con_Printf("Download of %s aborted\n", cls.qw_downloadname);
  1362. CL_StopDownload(0, 0);
  1363. }
  1364. CL_BeginDownloads(true);
  1365. }
  1366. static void CL_DownloadFinished_f(void)
  1367. {
  1368. if (Cmd_Argc() < 3)
  1369. {
  1370. Con_Printf("Malformed cl_downloadfinished command\n");
  1371. return;
  1372. }
  1373. CL_StopDownload(atoi(Cmd_Argv(1)), atoi(Cmd_Argv(2)));
  1374. CL_BeginDownloads(false);
  1375. }
  1376. static void CL_SendPlayerInfo(void)
  1377. {
  1378. char vabuf[1024];
  1379. MSG_WriteByte (&cls.netcon->message, clc_stringcmd);
  1380. MSG_WriteString (&cls.netcon->message, va(vabuf, sizeof(vabuf), "name \"%s\"", cl_name.string));
  1381. MSG_WriteByte (&cls.netcon->message, clc_stringcmd);
  1382. MSG_WriteString (&cls.netcon->message, va(vabuf, sizeof(vabuf), "color %i %i", cl_color.integer >> 4, cl_color.integer & 15));
  1383. MSG_WriteByte (&cls.netcon->message, clc_stringcmd);
  1384. MSG_WriteString (&cls.netcon->message, va(vabuf, sizeof(vabuf), "rate %i", cl_rate.integer));
  1385. MSG_WriteByte (&cls.netcon->message, clc_stringcmd);
  1386. MSG_WriteString (&cls.netcon->message, va(vabuf, sizeof(vabuf), "rate_burstsize %i", cl_rate_burstsize.integer));
  1387. if (cl_pmodel.integer)
  1388. {
  1389. MSG_WriteByte (&cls.netcon->message, clc_stringcmd);
  1390. MSG_WriteString (&cls.netcon->message, va(vabuf, sizeof(vabuf), "pmodel %i", cl_pmodel.integer));
  1391. }
  1392. if (*cl_playermodel.string)
  1393. {
  1394. MSG_WriteByte (&cls.netcon->message, clc_stringcmd);
  1395. MSG_WriteString (&cls.netcon->message, va(vabuf, sizeof(vabuf), "playermodel %s", cl_playermodel.string));
  1396. }
  1397. if (*cl_playerskin.string)
  1398. {
  1399. MSG_WriteByte (&cls.netcon->message, clc_stringcmd);
  1400. MSG_WriteString (&cls.netcon->message, va(vabuf, sizeof(vabuf), "playerskin %s", cl_playerskin.string));
  1401. }
  1402. }
  1403. /*
  1404. =====================
  1405. CL_SignonReply
  1406. An svc_signonnum has been received, perform a client side setup
  1407. =====================
  1408. */
  1409. static void CL_SignonReply (void)
  1410. {
  1411. Con_DPrintf("CL_SignonReply: %i\n", cls.signon);
  1412. switch (cls.signon)
  1413. {
  1414. case 1:
  1415. if (cls.netcon)
  1416. {
  1417. // send player info before we begin downloads
  1418. // (so that the server can see the player name while downloading)
  1419. CL_SendPlayerInfo();
  1420. // execute cl_begindownloads next frame
  1421. // (after any commands added by svc_stufftext have been executed)
  1422. // when done with downloads the "prespawn" will be sent
  1423. Cbuf_AddText("\ncl_begindownloads\n");
  1424. //MSG_WriteByte (&cls.netcon->message, clc_stringcmd);
  1425. //MSG_WriteString (&cls.netcon->message, "prespawn");
  1426. }
  1427. else // playing a demo... make sure loading occurs as soon as possible
  1428. CL_BeginDownloads(false);
  1429. break;
  1430. case 2:
  1431. if (cls.netcon)
  1432. {
  1433. // LordHavoc: quake sent the player info here but due to downloads
  1434. // it is sent earlier instead
  1435. // CL_SendPlayerInfo();
  1436. // LordHavoc: changed to begin a loading stage and issue this when done
  1437. MSG_WriteByte (&cls.netcon->message, clc_stringcmd);
  1438. MSG_WriteString (&cls.netcon->message, "spawn");
  1439. }
  1440. break;
  1441. case 3:
  1442. if (cls.netcon)
  1443. {
  1444. MSG_WriteByte (&cls.netcon->message, clc_stringcmd);
  1445. MSG_WriteString (&cls.netcon->message, "begin");
  1446. }
  1447. break;
  1448. case 4:
  1449. // after the level has been loaded, we shouldn't need the shaders, and
  1450. // if they are needed again they will be automatically loaded...
  1451. // we also don't need the unused models or sounds from the last level
  1452. Mod_FreeQ3Shaders();
  1453. Mod_PurgeUnused();
  1454. S_PurgeUnused();
  1455. Con_ClearNotify();
  1456. if (COM_CheckParm("-profilegameonly"))
  1457. Sys_AllowProfiling(true);
  1458. break;
  1459. }
  1460. }
  1461. /*
  1462. ==================
  1463. CL_ParseServerInfo
  1464. ==================
  1465. */
  1466. static void CL_ParseServerInfo (void)
  1467. {
  1468. char *str;
  1469. int i;
  1470. protocolversion_t protocol;
  1471. int nummodels, numsounds;
  1472. char vabuf[1024];
  1473. // if we start loading a level and a video is still playing, stop it
  1474. CL_VideoStop();
  1475. Con_DPrint("Serverinfo packet received.\n");
  1476. Collision_Cache_Reset(true);
  1477. // if server is active, we already began a loading plaque
  1478. if (!sv.active)
  1479. {
  1480. SCR_BeginLoadingPlaque(false);
  1481. S_StopAllSounds();
  1482. // free q3 shaders so that any newly downloaded shaders will be active
  1483. Mod_FreeQ3Shaders();
  1484. }
  1485. // check memory integrity
  1486. Mem_CheckSentinelsGlobal();
  1487. // clear cl_serverextension cvars
  1488. Cvar_SetValueQuick(&cl_serverextension_download, 0);
  1489. //
  1490. // wipe the client_state_t struct
  1491. //
  1492. CL_ClearState ();
  1493. // parse protocol version number
  1494. i = MSG_ReadLong(&cl_message);
  1495. protocol = Protocol_EnumForNumber(i);
  1496. if (protocol == PROTOCOL_UNKNOWN)
  1497. {
  1498. Host_Error("CL_ParseServerInfo: Server is unrecognized protocol number (%i)", i);
  1499. return;
  1500. }
  1501. // hack for unmarked Nehahra movie demos which had a custom protocol
  1502. if (protocol == PROTOCOL_QUAKEDP && cls.demoplayback && gamemode == GAME_NEHAHRA)
  1503. protocol = PROTOCOL_NEHAHRAMOVIE;
  1504. cls.protocol = protocol;
  1505. Con_DPrintf("Server protocol is %s\n", Protocol_NameForEnum(cls.protocol));
  1506. cl.num_entities = 1;
  1507. if (protocol == PROTOCOL_QUAKEWORLD)
  1508. {
  1509. char gamedir[1][MAX_QPATH];
  1510. cl.qw_servercount = MSG_ReadLong(&cl_message);
  1511. str = MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring));
  1512. Con_Printf("server gamedir is %s\n", str);
  1513. strlcpy(gamedir[0], str, sizeof(gamedir[0]));
  1514. // change gamedir if needed
  1515. if (!FS_ChangeGameDirs(1, gamedir, true, false))
  1516. Host_Error("CL_ParseServerInfo: unable to switch to server specified gamedir");
  1517. cl.gametype = GAME_DEATHMATCH;
  1518. cl.maxclients = 32;
  1519. // parse player number
  1520. i = MSG_ReadByte(&cl_message);
  1521. // 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)
  1522. //cl.qw_spectator = (i & 128) != 0;
  1523. cl.realplayerentity = cl.playerentity = cl.viewentity = (i & 127) + 1;
  1524. cl.scores = (scoreboard_t *)Mem_Alloc(cls.levelmempool, cl.maxclients*sizeof(*cl.scores));
  1525. // get the full level name
  1526. str = MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring));
  1527. strlcpy (cl.worldmessage, str, sizeof(cl.worldmessage));
  1528. // get the movevars that are defined in the qw protocol
  1529. cl.movevars_gravity = MSG_ReadFloat(&cl_message);
  1530. cl.movevars_stopspeed = MSG_ReadFloat(&cl_message);
  1531. cl.movevars_maxspeed = MSG_ReadFloat(&cl_message);
  1532. cl.movevars_spectatormaxspeed = MSG_ReadFloat(&cl_message);
  1533. cl.movevars_accelerate = MSG_ReadFloat(&cl_message);
  1534. cl.movevars_airaccelerate = MSG_ReadFloat(&cl_message);
  1535. cl.movevars_wateraccelerate = MSG_ReadFloat(&cl_message);
  1536. cl.movevars_friction = MSG_ReadFloat(&cl_message);
  1537. cl.movevars_waterfriction = MSG_ReadFloat(&cl_message);
  1538. cl.movevars_entgravity = MSG_ReadFloat(&cl_message);
  1539. // other movevars not in the protocol...
  1540. cl.movevars_wallfriction = 0;
  1541. cl.movevars_timescale = 1;
  1542. cl.movevars_jumpvelocity = 270;
  1543. cl.movevars_edgefriction = 1;
  1544. cl.movevars_maxairspeed = 30;
  1545. cl.movevars_stepheight = 18;
  1546. cl.movevars_airaccel_qw = 1;
  1547. cl.movevars_airaccel_sideways_friction = 0;
  1548. // seperate the printfs so the server message can have a color
  1549. Con_Printf("\n\n<===================================>\n\n\2%s\n", str);
  1550. // check memory integrity
  1551. Mem_CheckSentinelsGlobal();
  1552. if (cls.netcon)
  1553. {
  1554. MSG_WriteByte(&cls.netcon->message, qw_clc_stringcmd);
  1555. MSG_WriteString(&cls.netcon->message, va(vabuf, sizeof(vabuf), "soundlist %i %i", cl.qw_servercount, 0));
  1556. }
  1557. cl.loadbegun = false;
  1558. cl.loadfinished = false;
  1559. cls.state = ca_connected;
  1560. cls.signon = 1;
  1561. // note: on QW protocol we can't set up the gameworld until after
  1562. // downloads finish...
  1563. // (we don't even know the name of the map yet)
  1564. // this also means cl_autodemo does not work on QW protocol...
  1565. strlcpy(cl.worldname, "", sizeof(cl.worldname));
  1566. strlcpy(cl.worldnamenoextension, "", sizeof(cl.worldnamenoextension));
  1567. strlcpy(cl.worldbasename, "qw", sizeof(cl.worldbasename));
  1568. Cvar_SetQuick(&cl_worldname, cl.worldname);
  1569. Cvar_SetQuick(&cl_worldnamenoextension, cl.worldnamenoextension);
  1570. Cvar_SetQuick(&cl_worldbasename, cl.worldbasename);
  1571. // check memory integrity
  1572. Mem_CheckSentinelsGlobal();
  1573. }
  1574. else
  1575. {
  1576. // parse maxclients
  1577. cl.maxclients = MSG_ReadByte(&cl_message);
  1578. if (cl.maxclients < 1 || cl.maxclients > MAX_SCOREBOARD)
  1579. {
  1580. Host_Error("Bad maxclients (%u) from server", cl.maxclients);
  1581. return;
  1582. }
  1583. cl.scores = (scoreboard_t *)Mem_Alloc(cls.levelmempool, cl.maxclients*sizeof(*cl.scores));
  1584. // parse gametype
  1585. cl.gametype = MSG_ReadByte(&cl_message);
  1586. // the original id singleplayer demos are bugged and contain
  1587. // GAME_DEATHMATCH even for singleplayer
  1588. if (cl.maxclients == 1 && cls.protocol == PROTOCOL_QUAKE)
  1589. cl.gametype = GAME_COOP;
  1590. // parse signon message
  1591. str = MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring));
  1592. strlcpy (cl.worldmessage, str, sizeof(cl.worldmessage));
  1593. // seperate the printfs so the server message can have a color
  1594. if (cls.protocol != PROTOCOL_NEHAHRAMOVIE) // no messages when playing the Nehahra movie
  1595. Con_Printf("\n<===================================>\n\n\2%s\n", str);
  1596. // check memory integrity
  1597. Mem_CheckSentinelsGlobal();
  1598. // parse model precache list
  1599. for (nummodels=1 ; ; nummodels++)
  1600. {
  1601. str = MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring));
  1602. if (!str[0])
  1603. break;
  1604. if (nummodels==MAX_MODELS)
  1605. Host_Error ("Server sent too many model precaches");
  1606. if (strlen(str) >= MAX_QPATH)
  1607. Host_Error ("Server sent a precache name of %i characters (max %i)", (int)strlen(str), MAX_QPATH - 1);
  1608. strlcpy (cl.model_name[nummodels], str, sizeof (cl.model_name[nummodels]));
  1609. }
  1610. // parse sound precache list
  1611. for (numsounds=1 ; ; numsounds++)
  1612. {
  1613. str = MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring));
  1614. if (!str[0])
  1615. break;
  1616. if (numsounds==MAX_SOUNDS)
  1617. Host_Error("Server sent too many sound precaches");
  1618. if (strlen(str) >= MAX_QPATH)
  1619. Host_Error("Server sent a precache name of %i characters (max %i)", (int)strlen(str), MAX_QPATH - 1);
  1620. strlcpy (cl.sound_name[numsounds], str, sizeof (cl.sound_name[numsounds]));
  1621. }
  1622. // set the base name for level-specific things... this gets updated again by CL_SetupWorldModel later
  1623. strlcpy(cl.worldname, cl.model_name[1], sizeof(cl.worldname));
  1624. FS_StripExtension(cl.worldname, cl.worldnamenoextension, sizeof(cl.worldnamenoextension));
  1625. strlcpy(cl.worldbasename, !strncmp(cl.worldnamenoextension, "maps/", 5) ? cl.worldnamenoextension + 5 : cl.worldnamenoextension, sizeof(cl.worldbasename));
  1626. Cvar_SetQuick(&cl_worldmessage, cl.worldmessage);
  1627. Cvar_SetQuick(&cl_worldname, cl.worldname);
  1628. Cvar_SetQuick(&cl_worldnamenoextension, cl.worldnamenoextension);
  1629. Cvar_SetQuick(&cl_worldbasename, cl.worldbasename);
  1630. // touch all of the precached models that are still loaded so we can free
  1631. // anything that isn't needed
  1632. if (!sv.active)
  1633. Mod_ClearUsed();
  1634. for (i = 1;i < nummodels;i++)
  1635. Mod_FindName(cl.model_name[i], cl.model_name[i][0] == '*' ? cl.model_name[1] : NULL);
  1636. // precache any models used by the client (this also marks them used)
  1637. cl.model_bolt = Mod_ForName("progs/bolt.mdl", false, false, NULL);
  1638. cl.model_bolt2 = Mod_ForName("progs/bolt2.mdl", false, false, NULL);
  1639. cl.model_bolt3 = Mod_ForName("progs/bolt3.mdl", false, false, NULL);
  1640. cl.model_beam = Mod_ForName("progs/beam.mdl", false, false, NULL);
  1641. // we purge the models and sounds later in CL_SignonReply
  1642. //Mod_PurgeUnused();
  1643. //S_PurgeUnused();
  1644. // clear sound usage flags for purging of unused sounds
  1645. S_ClearUsed();
  1646. // precache any sounds used by the client
  1647. cl.sfx_wizhit = S_PrecacheSound(cl_sound_wizardhit.string, false, true);
  1648. cl.sfx_knighthit = S_PrecacheSound(cl_sound_hknighthit.string, false, true);
  1649. cl.sfx_tink1 = S_PrecacheSound(cl_sound_tink1.string, false, true);
  1650. cl.sfx_ric1 = S_PrecacheSound(cl_sound_ric1.string, false, true);
  1651. cl.sfx_ric2 = S_PrecacheSound(cl_sound_ric2.string, false, true);
  1652. cl.sfx_ric3 = S_PrecacheSound(cl_sound_ric3.string, false, true);
  1653. cl.sfx_r_exp3 = S_PrecacheSound(cl_sound_r_exp3.string, false, true);
  1654. // sounds used by the game
  1655. for (i = 1;i < MAX_SOUNDS && cl.sound_name[i][0];i++)
  1656. cl.sound_precache[i] = S_PrecacheSound(cl.sound_name[i], true, true);
  1657. // now we try to load everything that is new
  1658. cl.loadmodel_current = 1;
  1659. cl.downloadmodel_current = 1;
  1660. cl.loadmodel_total = nummodels;
  1661. cl.loadsound_current = 1;
  1662. cl.downloadsound_current = 1;
  1663. cl.loadsound_total = numsounds;
  1664. cl.downloadcsqc = true;
  1665. cl.loadbegun = false;
  1666. cl.loadfinished = false;
  1667. cl.loadcsqc = true;
  1668. // check memory integrity
  1669. Mem_CheckSentinelsGlobal();
  1670. // if cl_autodemo is set, automatically start recording a demo if one isn't being recorded already
  1671. if (cl_autodemo.integer && cls.netcon && cls.protocol != PROTOCOL_QUAKEWORLD)
  1672. {
  1673. char demofile[MAX_OSPATH];
  1674. if (cls.demorecording)
  1675. {
  1676. // finish the previous level's demo file
  1677. CL_Stop_f();
  1678. }
  1679. // start a new demo file
  1680. dpsnprintf (demofile, sizeof(demofile), "%s_%s.dem", Sys_TimeString (cl_autodemo_nameformat.string), cl.worldbasename);
  1681. Con_Printf ("Auto-recording to %s.\n", demofile);
  1682. // Reset bit 0 for every new demo
  1683. Cvar_SetValueQuick(&cl_autodemo_delete,
  1684. (cl_autodemo_delete.integer & ~0x1)
  1685. |
  1686. ((cl_autodemo_delete.integer & 0x2) ? 0x1 : 0)
  1687. );
  1688. cls.demofile = FS_OpenRealFile(demofile, "wb", false);
  1689. if (cls.demofile)
  1690. {
  1691. cls.forcetrack = -1;
  1692. FS_Printf (cls.demofile, "%i\n", cls.forcetrack);
  1693. cls.demorecording = true;
  1694. strlcpy(cls.demoname, demofile, sizeof(cls.demoname));
  1695. cls.demo_lastcsprogssize = -1;
  1696. cls.demo_lastcsprogscrc = -1;
  1697. }
  1698. else
  1699. Con_Print ("ERROR: couldn't open.\n");
  1700. }
  1701. }
  1702. cl.islocalgame = NetConn_IsLocalGame();
  1703. }
  1704. void CL_ValidateState(entity_state_t *s)
  1705. {
  1706. dp_model_t *model;
  1707. if (!s->active)
  1708. return;
  1709. if (s->modelindex >= MAX_MODELS)
  1710. Host_Error("CL_ValidateState: modelindex (%i) >= MAX_MODELS (%i)\n", s->modelindex, MAX_MODELS);
  1711. // these warnings are only warnings, no corrections are made to the state
  1712. // because states are often copied for decoding, which otherwise would
  1713. // propogate some of the corrections accidentally
  1714. // (this used to happen, sometimes affecting skin and frame)
  1715. // colormap is client index + 1
  1716. if (!(s->flags & RENDER_COLORMAPPED) && s->colormap > cl.maxclients)
  1717. Con_DPrintf("CL_ValidateState: colormap (%i) > cl.maxclients (%i)\n", s->colormap, cl.maxclients);
  1718. if (developer_extra.integer)
  1719. {
  1720. model = CL_GetModelByIndex(s->modelindex);
  1721. if (model && model->type && s->frame >= model->numframes)
  1722. Con_DPrintf("CL_ValidateState: no such frame %i in \"%s\" (which has %i frames)\n", s->frame, model->name, model->numframes);
  1723. if (model && model->type && s->skin > 0 && s->skin >= model->numskins && !(s->lightpflags & PFLAGS_FULLDYNAMIC))
  1724. Con_DPrintf("CL_ValidateState: no such skin %i in \"%s\" (which has %i skins)\n", s->skin, model->name, model->numskins);
  1725. }
  1726. }
  1727. void CL_MoveLerpEntityStates(entity_t *ent)
  1728. {
  1729. float odelta[3], adelta[3];
  1730. VectorSubtract(ent->state_current.origin, ent->persistent.neworigin, odelta);
  1731. VectorSubtract(ent->state_current.angles, ent->persistent.newangles, adelta);
  1732. if (!ent->state_previous.active || ent->state_previous.modelindex != ent->state_current.modelindex)
  1733. {
  1734. // reset all persistent stuff if this is a new entity
  1735. ent->persistent.lerpdeltatime = 0;
  1736. ent->persistent.lerpstarttime = cl.mtime[1];
  1737. VectorCopy(ent->state_current.origin, ent->persistent.oldorigin);
  1738. VectorCopy(ent->state_current.angles, ent->persistent.oldangles);
  1739. VectorCopy(ent->state_current.origin, ent->persistent.neworigin);
  1740. VectorCopy(ent->state_current.angles, ent->persistent.newangles);
  1741. // reset animation interpolation as well
  1742. ent->render.framegroupblend[0].frame = ent->render.framegroupblend[1].frame = ent->state_current.frame;
  1743. ent->render.framegroupblend[0].start = ent->render.framegroupblend[1].start = cl.time;
  1744. ent->render.framegroupblend[0].lerp = 1;ent->render.framegroupblend[1].lerp = 0;
  1745. ent->render.shadertime = cl.time;
  1746. // reset various persistent stuff
  1747. ent->persistent.muzzleflash = 0;
  1748. ent->persistent.trail_allowed = false;
  1749. }
  1750. else if ((ent->state_previous.effects & EF_TELEPORT_BIT) != (ent->state_current.effects & EF_TELEPORT_BIT))
  1751. {
  1752. // don't interpolate the move
  1753. ent->persistent.lerpdeltatime = 0;
  1754. ent->persistent.lerpstarttime = cl.mtime[1];
  1755. VectorCopy(ent->state_current.origin, ent->persistent.oldorigin);
  1756. VectorCopy(ent->state_current.angles, ent->persistent.oldangles);
  1757. VectorCopy(ent->state_current.origin, ent->persistent.neworigin);
  1758. VectorCopy(ent->state_current.angles, ent->persistent.newangles);
  1759. ent->persistent.trail_allowed = false;
  1760. // if(ent->state_current.frame != ent->state_previous.frame)
  1761. // do this even if we did change the frame
  1762. // teleport bit is only used if an animation restart, or a jump, is necessary
  1763. // so it should be always harmless to do this
  1764. {
  1765. ent->render.framegroupblend[0].frame = ent->render.framegroupblend[1].frame = ent->state_current.frame;
  1766. ent->render.framegroupblend[0].start = ent->render.framegroupblend[1].start = cl.time;
  1767. ent->render.framegroupblend[0].lerp = 1;ent->render.framegroupblend[1].lerp = 0;
  1768. }
  1769. // note that this case must do everything the following case does too
  1770. }
  1771. else if ((ent->state_previous.effects & EF_RESTARTANIM_BIT) != (ent->state_current.effects & EF_RESTARTANIM_BIT))
  1772. {
  1773. ent->render.framegroupblend[1] = ent->render.framegroupblend[0];
  1774. ent->render.framegroupblend[1].lerp = 1;
  1775. ent->render.framegroupblend[0].frame = ent->state_current.frame;
  1776. ent->render.framegroupblend[0].start = cl.time;
  1777. ent->render.framegroupblend[0].lerp = 0;
  1778. }
  1779. else if (DotProduct(odelta, odelta) > 1000*1000
  1780. || (cl.fixangle[0] && !cl.fixangle[1])
  1781. || (ent->state_previous.tagindex != ent->state_current.tagindex)
  1782. || (ent->state_previous.tagentity != ent->state_current.tagentity))
  1783. {
  1784. // don't interpolate the move
  1785. // (the fixangle[] check detects teleports, but not constant fixangles
  1786. // such as when spectating)
  1787. ent->persistent.lerpdeltatime = 0;
  1788. ent->persistent.lerpstarttime = cl.mtime[1];
  1789. VectorCopy(ent->state_current.origin, ent->persistent.oldorigin);
  1790. VectorCopy(ent->state_current.angles, ent->persistent.oldangles);
  1791. VectorCopy(ent->state_current.origin, ent->persistent.neworigin);
  1792. VectorCopy(ent->state_current.angles, ent->persistent.newangles);
  1793. ent->persistent.trail_allowed = false;
  1794. }
  1795. else if (ent->state_current.flags & RENDER_STEP)
  1796. {
  1797. // monster interpolation
  1798. if (DotProduct(odelta, odelta) + DotProduct(adelta, adelta) > 0.01)
  1799. {
  1800. ent->persistent.lerpdeltatime = bound(0, cl.mtime[1] - ent->persistent.lerpstarttime, 0.1);
  1801. ent->persistent.lerpstarttime = cl.mtime[1];
  1802. VectorCopy(ent->persistent.neworigin, ent->persistent.oldorigin);
  1803. VectorCopy(ent->persistent.newangles, ent->persistent.oldangles);
  1804. VectorCopy(ent->state_current.origin, ent->persistent.neworigin);
  1805. VectorCopy(ent->state_current.angles, ent->persistent.newangles);
  1806. }
  1807. }
  1808. else
  1809. {
  1810. // not a monster
  1811. ent->persistent.lerpstarttime = ent->state_previous.time;
  1812. // no lerp if it's singleplayer
  1813. if (cl.islocalgame && !sv_fixedframeratesingleplayer.integer)
  1814. ent->persistent.lerpdeltatime = 0;
  1815. else
  1816. ent->persistent.lerpdeltatime = bound(0, ent->state_current.time - ent->state_previous.time, 0.1);
  1817. VectorCopy(ent->persistent.neworigin, ent->persistent.oldorigin);
  1818. VectorCopy(ent->persistent.newangles, ent->persistent.oldangles);
  1819. VectorCopy(ent->state_current.origin, ent->persistent.neworigin);
  1820. VectorCopy(ent->state_current.angles, ent->persistent.newangles);
  1821. }
  1822. // trigger muzzleflash effect if necessary
  1823. if (ent->state_current.effects & EF_MUZZLEFLASH)
  1824. ent->persistent.muzzleflash = 1;
  1825. // restart animation bit
  1826. if ((ent->state_previous.effects & EF_RESTARTANIM_BIT) != (ent->state_current.effects & EF_RESTARTANIM_BIT))
  1827. {
  1828. ent->render.framegroupblend[1] = ent->render.framegroupblend[0];
  1829. ent->render.framegroupblend[1].lerp = 1;
  1830. ent->render.framegroupblend[0].frame = ent->state_current.frame;
  1831. ent->render.framegroupblend[0].start = cl.time;
  1832. ent->render.framegroupblend[0].lerp = 0;
  1833. }
  1834. }
  1835. /*
  1836. ==================
  1837. CL_ParseBaseline
  1838. ==================
  1839. */
  1840. static void CL_ParseBaseline (entity_t *ent, int large)
  1841. {
  1842. int i;
  1843. ent->state_baseline = defaultstate;
  1844. // FIXME: set ent->state_baseline.number?
  1845. ent->state_baseline.active = true;
  1846. if (large)
  1847. {
  1848. ent->state_baseline.modelindex = (unsigned short) MSG_ReadShort(&cl_message);
  1849. ent->state_baseline.frame = (unsigned short) MSG_ReadShort(&cl_message);
  1850. }
  1851. else if (cls.protocol == PROTOCOL_NEHAHRABJP || cls.protocol == PROTOCOL_NEHAHRABJP2 || cls.protocol == PROTOCOL_NEHAHRABJP3)
  1852. {
  1853. ent->state_baseline.modelindex = (unsigned short) MSG_ReadShort(&cl_message);
  1854. ent->state_baseline.frame = MSG_ReadByte(&cl_message);
  1855. }
  1856. else
  1857. {
  1858. ent->state_baseline.modelindex = MSG_ReadByte(&cl_message);
  1859. ent->state_baseline.frame = MSG_ReadByte(&cl_message);
  1860. }
  1861. ent->state_baseline.colormap = MSG_ReadByte(&cl_message);
  1862. ent->state_baseline.skin = MSG_ReadByte(&cl_message);
  1863. for (i = 0;i < 3;i++)
  1864. {
  1865. ent->state_baseline.origin[i] = MSG_ReadCoord(&cl_message, cls.protocol);
  1866. ent->state_baseline.angles[i] = MSG_ReadAngle(&cl_message, cls.protocol);
  1867. }
  1868. ent->state_previous = ent->state_current = ent->state_baseline;
  1869. }
  1870. /*
  1871. ==================
  1872. CL_ParseClientdata
  1873. Server information pertaining to this client only
  1874. ==================
  1875. */
  1876. static void CL_ParseClientdata (void)
  1877. {
  1878. int i, bits;
  1879. VectorCopy (cl.mpunchangle[0], cl.mpunchangle[1]);
  1880. VectorCopy (cl.mpunchvector[0], cl.mpunchvector[1]);
  1881. VectorCopy (cl.mvelocity[0], cl.mvelocity[1]);
  1882. cl.mviewzoom[1] = cl.mviewzoom[0];
  1883. 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)
  1884. {
  1885. cl.stats[STAT_VIEWHEIGHT] = DEFAULT_VIEWHEIGHT;
  1886. cl.stats[STAT_ITEMS] = 0;
  1887. cl.stats[STAT_VIEWZOOM] = 255;
  1888. }
  1889. cl.idealpitch = 0;
  1890. cl.mpunchangle[0][0] = 0;
  1891. cl.mpunchangle[0][1] = 0;
  1892. cl.mpunchangle[0][2] = 0;
  1893. cl.mpunchvector[0][0] = 0;
  1894. cl.mpunchvector[0][1] = 0;
  1895. cl.mpunchvector[0][2] = 0;
  1896. cl.mvelocity[0][0] = 0;
  1897. cl.mvelocity[0][1] = 0;
  1898. cl.mvelocity[0][2] = 0;
  1899. cl.mviewzoom[0] = 1;
  1900. bits = (unsigned short) MSG_ReadShort(&cl_message);
  1901. if (bits & SU_EXTEND1)
  1902. bits |= (MSG_ReadByte(&cl_message) << 16);
  1903. if (bits & SU_EXTEND2)
  1904. bits |= (MSG_ReadByte(&cl_message) << 24);
  1905. if (bits & SU_VIEWHEIGHT)
  1906. cl.stats[STAT_VIEWHEIGHT] = MSG_ReadChar(&cl_message);
  1907. if (bits & SU_IDEALPITCH)
  1908. cl.idealpitch = MSG_ReadChar(&cl_message);
  1909. for (i = 0;i < 3;i++)
  1910. {
  1911. if (bits & (SU_PUNCH1<<i) )
  1912. {
  1913. 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)
  1914. cl.mpunchangle[0][i] = MSG_ReadChar(&cl_message);
  1915. else
  1916. cl.mpunchangle[0][i] = MSG_ReadAngle16i(&cl_message);
  1917. }
  1918. if (bits & (SU_PUNCHVEC1<<i))
  1919. {
  1920. if (cls.protocol == PROTOCOL_DARKPLACES1 || cls.protocol == PROTOCOL_DARKPLACES2 || cls.protocol == PROTOCOL_DARKPLACES3 || cls.protocol == PROTOCOL_DARKPLACES4)
  1921. cl.mpunchvector[0][i] = MSG_ReadCoord16i(&cl_message);
  1922. else
  1923. cl.mpunchvector[0][i] = MSG_ReadCoord32f(&cl_message);
  1924. }
  1925. if (bits & (SU_VELOCITY1<<i) )
  1926. {
  1927. 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)
  1928. cl.mvelocity[0][i] = MSG_ReadChar(&cl_message)*16;
  1929. else
  1930. cl.mvelocity[0][i] = MSG_ReadCoord32f(&cl_message);
  1931. }
  1932. }
  1933. // LordHavoc: hipnotic demos don't have this bit set but should
  1934. 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)
  1935. cl.stats[STAT_ITEMS] = MSG_ReadLong(&cl_message);
  1936. cl.onground = (bits & SU_ONGROUND) != 0;
  1937. cl.inwater = (bits & SU_INWATER) != 0;
  1938. if (cls.protocol == PROTOCOL_DARKPLACES5)
  1939. {
  1940. cl.stats[STAT_WEAPONFRAME] = (bits & SU_WEAPONFRAME) ? MSG_ReadShort(&cl_message) : 0;
  1941. cl.stats[STAT_ARMOR] = (bits & SU_ARMOR) ? MSG_ReadShort(&cl_message) : 0;
  1942. cl.stats[STAT_WEAPON] = (bits & SU_WEAPON) ? MSG_ReadShort(&cl_message) : 0;
  1943. cl.stats[STAT_HEALTH] = MSG_ReadShort(&cl_message);
  1944. cl.stats[STAT_AMMO] = MSG_ReadShort(&cl_message);
  1945. cl.stats[STAT_SHELLS] = MSG_ReadShort(&cl_message);
  1946. cl.stats[STAT_NAILS] = MSG_ReadShort(&cl_message);
  1947. cl.stats[STAT_ROCKETS] = MSG_ReadShort(&cl_message);
  1948. cl.stats[STAT_CELLS] = MSG_ReadShort(&cl_message);
  1949. cl.stats[STAT_ACTIVEWEAPON] = (unsigned short) MSG_ReadShort(&cl_message);
  1950. }
  1951. 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_DARKPLACES1 || cls.protocol == PROTOCOL_DARKPLACES2 || cls.protocol == PROTOCOL_DARKPLACES3 || cls.protocol == PROTOCOL_DARKPLACES4)
  1952. {
  1953. cl.stats[STAT_WEAPONFRAME] = (bits & SU_WEAPONFRAME) ? MSG_ReadByte(&cl_message) : 0;
  1954. cl.stats[STAT_ARMOR] = (bits & SU_ARMOR) ? MSG_ReadByte(&cl_message) : 0;
  1955. if (cls.protocol == PROTOCOL_NEHAHRABJP || cls.protocol == PROTOCOL_NEHAHRABJP2 || cls.protocol == PROTOCOL_NEHAHRABJP3)
  1956. cl.stats[STAT_WEAPON] = (bits & SU_WEAPON) ? (unsigned short)MSG_ReadShort(&cl_message) : 0;
  1957. else
  1958. cl.stats[STAT_WEAPON] = (bits & SU_WEAPON) ? MSG_ReadByte(&cl_message) : 0;
  1959. cl.stats[STAT_HEALTH] = MSG_ReadShort(&cl_message);
  1960. cl.stats[STAT_AMMO] = MSG_ReadByte(&cl_message);
  1961. cl.stats[STAT_SHELLS] = MSG_ReadByte(&cl_message);
  1962. cl.stats[STAT_NAILS] = MSG_ReadByte(&cl_message);
  1963. cl.stats[STAT_ROCKETS] = MSG_ReadByte(&cl_message);
  1964. cl.stats[STAT_CELLS] = MSG_ReadByte(&cl_message);
  1965. if (gamemode == GAME_HIPNOTIC || gamemode == GAME_ROGUE || gamemode == GAME_QUOTH || IS_OLDNEXUIZ_DERIVED(gamemode))
  1966. cl.stats[STAT_ACTIVEWEAPON] = (1<<MSG_ReadByte(&cl_message));
  1967. else
  1968. cl.stats[STAT_ACTIVEWEAPON] = MSG_ReadByte(&cl_message);
  1969. }
  1970. if (bits & SU_VIEWZOOM)
  1971. {
  1972. if (cls.protocol == PROTOCOL_DARKPLACES2 || cls.protocol == PROTOCOL_DARKPLACES3 || cls.protocol == PROTOCOL_DARKPLACES4)
  1973. cl.stats[STAT_VIEWZOOM] = MSG_ReadByte(&cl_message);
  1974. else
  1975. cl.stats[STAT_VIEWZOOM] = (unsigned short) MSG_ReadShort(&cl_message);
  1976. }
  1977. // viewzoom interpolation
  1978. cl.mviewzoom[0] = (float) max(cl.stats[STAT_VIEWZOOM], 2) * (1.0f / 255.0f);
  1979. }
  1980. /*
  1981. =====================
  1982. CL_ParseStatic
  1983. =====================
  1984. */
  1985. static void CL_ParseStatic (int large)
  1986. {
  1987. entity_t *ent;
  1988. if (cl.num_static_entities >= cl.max_static_entities)
  1989. Host_Error ("Too many static entities");
  1990. ent = &cl.static_entities[cl.num_static_entities++];
  1991. CL_ParseBaseline (ent, large);
  1992. if (ent->state_baseline.modelindex == 0)
  1993. {
  1994. 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]);
  1995. cl.num_static_entities--;
  1996. // This is definitely a cheesy way to conserve resources...
  1997. return;
  1998. }
  1999. // copy it to the current state
  2000. ent->render.model = CL_GetModelByIndex(ent->state_baseline.modelindex);
  2001. ent->render.framegroupblend[0].frame = ent->state_baseline.frame;
  2002. ent->render.framegroupblend[0].lerp = 1;
  2003. // make torchs play out of sync
  2004. ent->render.framegroupblend[0].start = lhrandom(-10, -1);
  2005. ent->render.skinnum = ent->state_baseline.skin;
  2006. ent->render.effects = ent->state_baseline.effects;
  2007. ent->render.alpha = 1;
  2008. //VectorCopy (ent->state_baseline.origin, ent->render.origin);
  2009. //VectorCopy (ent->state_baseline.angles, ent->render.angles);
  2010. 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);
  2011. ent->render.allowdecals = true;
  2012. CL_UpdateRenderEntity(&ent->render);
  2013. }
  2014. /*
  2015. ===================
  2016. CL_ParseStaticSound
  2017. ===================
  2018. */
  2019. static void CL_ParseStaticSound (int large)
  2020. {
  2021. vec3_t org;
  2022. int sound_num, vol, atten;
  2023. MSG_ReadVector(&cl_message, org, cls.protocol);
  2024. if (large || cls.protocol == PROTOCOL_NEHAHRABJP2)
  2025. sound_num = (unsigned short) MSG_ReadShort(&cl_message);
  2026. else
  2027. sound_num = MSG_ReadByte(&cl_message);
  2028. if (sound_num < 0 || sound_num >= MAX_SOUNDS)
  2029. {
  2030. Con_Printf("CL_ParseStaticSound: sound_num(%i) >= MAX_SOUNDS (%i)\n", sound_num, MAX_SOUNDS);
  2031. return;
  2032. }
  2033. vol = MSG_ReadByte(&cl_message);
  2034. atten = MSG_ReadByte(&cl_message);
  2035. S_StaticSound (cl.sound_precache[sound_num], org, vol/255.0f, atten);
  2036. }
  2037. static void CL_ParseEffect (void)
  2038. {
  2039. vec3_t org;
  2040. int modelindex, startframe, framecount, framerate;
  2041. MSG_ReadVector(&cl_message, org, cls.protocol);
  2042. modelindex = MSG_ReadByte(&cl_message);
  2043. startframe = MSG_ReadByte(&cl_message);
  2044. framecount = MSG_ReadByte(&cl_message);
  2045. framerate = MSG_ReadByte(&cl_message);
  2046. CL_Effect(org, modelindex, startframe, framecount, framerate);
  2047. }
  2048. static void CL_ParseEffect2 (void)
  2049. {
  2050. vec3_t org;
  2051. int modelindex, startframe, framecount, framerate;
  2052. MSG_ReadVector(&cl_message, org, cls.protocol);
  2053. modelindex = (unsigned short) MSG_ReadShort(&cl_message);
  2054. startframe = (unsigned short) MSG_ReadShort(&cl_message);
  2055. framecount = MSG_ReadByte(&cl_message);
  2056. framerate = MSG_ReadByte(&cl_message);
  2057. CL_Effect(org, modelindex, startframe, framecount, framerate);
  2058. }
  2059. void CL_NewBeam (int ent, vec3_t start, vec3_t end, dp_model_t *m, int lightning)
  2060. {
  2061. int i;
  2062. beam_t *b = NULL;
  2063. if (ent >= MAX_EDICTS)
  2064. {
  2065. Con_Printf("CL_NewBeam: invalid entity number %i\n", ent);
  2066. ent = 0;
  2067. }
  2068. if (ent >= cl.max_entities)
  2069. CL_ExpandEntities(ent);
  2070. // override any beam with the same entity
  2071. i = cl.max_beams;
  2072. if (ent)
  2073. for (i = 0, b = cl.beams;i < cl.max_beams;i++, b++)
  2074. if (b->entity == ent)
  2075. break;
  2076. // if the entity was not found then just replace an unused beam
  2077. if (i == cl.max_beams)
  2078. for (i = 0, b = cl.beams;i < cl.max_beams;i++, b++)
  2079. if (!b->model)
  2080. break;
  2081. if (i < cl.max_beams)
  2082. {
  2083. cl.num_beams = max(cl.num_beams, i + 1);
  2084. b->entity = ent;
  2085. b->lightning = lightning;
  2086. b->model = m;
  2087. b->endtime = cl.mtime[0] + 0.2;
  2088. VectorCopy (start, b->start);
  2089. VectorCopy (end, b->end);
  2090. }
  2091. else
  2092. Con_Print("beam list overflow!\n");
  2093. }
  2094. static void CL_ParseBeam (dp_model_t *m, int lightning)
  2095. {
  2096. int ent;
  2097. vec3_t start, end;
  2098. ent = (unsigned short) MSG_ReadShort(&cl_message);
  2099. MSG_ReadVector(&cl_message, start, cls.protocol);
  2100. MSG_ReadVector(&cl_message, end, cls.protocol);
  2101. if (ent >= MAX_EDICTS)
  2102. {
  2103. Con_Printf("CL_ParseBeam: invalid entity number %i\n", ent);
  2104. ent = 0;
  2105. }
  2106. CL_NewBeam(ent, start, end, m, lightning);
  2107. }
  2108. static void CL_ParseTempEntity(void)
  2109. {
  2110. int type;
  2111. vec3_t pos, pos2;
  2112. vec3_t vel1, vel2;
  2113. vec3_t dir;
  2114. vec3_t color;
  2115. int rnd;
  2116. int colorStart, colorLength, count;
  2117. float velspeed, radius;
  2118. unsigned char *tempcolor;
  2119. matrix4x4_t tempmatrix;
  2120. if (cls.protocol == PROTOCOL_QUAKEWORLD)
  2121. {
  2122. type = MSG_ReadByte(&cl_message);
  2123. switch (type)
  2124. {
  2125. case QW_TE_WIZSPIKE:
  2126. // spike hitting wall
  2127. MSG_ReadVector(&cl_message, pos, cls.protocol);
  2128. CL_FindNonSolidLocation(pos, pos, 4);
  2129. CL_ParticleEffect(EFFECT_TE_WIZSPIKE, 1, pos, pos, vec3_origin, vec3_origin, NULL, 0);
  2130. S_StartSound(-1, 0, cl.sfx_wizhit, pos, 1, 1);
  2131. break;
  2132. case QW_TE_KNIGHTSPIKE:
  2133. // spike hitting wall
  2134. MSG_ReadVector(&cl_message, pos, cls.protocol);
  2135. CL_FindNonSolidLocation(pos, pos, 4);
  2136. CL_ParticleEffect(EFFECT_TE_KNIGHTSPIKE, 1, pos, pos, vec3_origin, vec3_origin, NULL, 0);
  2137. S_StartSound(-1, 0, cl.sfx_knighthit, pos, 1, 1);
  2138. break;
  2139. case QW_TE_SPIKE:
  2140. // spike hitting wall
  2141. MSG_ReadVector(&cl_message, pos, cls.protocol);
  2142. CL_FindNonSolidLocation(pos, pos, 4);
  2143. CL_ParticleEffect(EFFECT_TE_SPIKE, 1, pos, pos, vec3_origin, vec3_origin, NULL, 0);
  2144. if (rand() % 5)
  2145. S_StartSound(-1, 0, cl.sfx_tink1, pos, 1, 1);
  2146. else
  2147. {
  2148. rnd = rand() & 3;
  2149. if (rnd == 1)
  2150. S_StartSound(-1, 0, cl.sfx_ric1, pos, 1, 1);
  2151. else if (rnd == 2)
  2152. S_StartSound(-1, 0, cl.sfx_ric2, pos, 1, 1);
  2153. else
  2154. S_StartSound(-1, 0, cl.sfx_ric3, pos, 1, 1);
  2155. }
  2156. break;
  2157. case QW_TE_SUPERSPIKE:
  2158. // super spike hitting wall
  2159. MSG_ReadVector(&cl_message, pos, cls.protocol);
  2160. CL_FindNonSolidLocation(pos, pos, 4);
  2161. CL_ParticleEffect(EFFECT_TE_SUPERSPIKE, 1, pos, pos, vec3_origin, vec3_origin, NULL, 0);
  2162. if (rand() % 5)
  2163. S_StartSound(-1, 0, cl.sfx_tink1, pos, 1, 1);
  2164. else
  2165. {
  2166. rnd = rand() & 3;
  2167. if (rnd == 1)
  2168. S_StartSound(-1, 0, cl.sfx_ric1, pos, 1, 1);
  2169. else if (rnd == 2)
  2170. S_StartSound(-1, 0, cl.sfx_ric2, pos, 1, 1);
  2171. else
  2172. S_StartSound(-1, 0, cl.sfx_ric3, pos, 1, 1);
  2173. }
  2174. break;
  2175. case QW_TE_EXPLOSION:
  2176. // rocket explosion
  2177. MSG_ReadVector(&cl_message, pos, cls.protocol);
  2178. CL_FindNonSolidLocation(pos, pos, 10);
  2179. CL_ParticleEffect(EFFECT_TE_EXPLOSION, 1, pos, pos, vec3_origin, vec3_origin, NULL, 0);
  2180. S_StartSound(-1, 0, cl.sfx_r_exp3, pos, 1, 1);
  2181. CL_Effect(pos, cl.qw_modelindex_s_explod, 0, 6, 10);
  2182. break;
  2183. case QW_TE_TAREXPLOSION:
  2184. // tarbaby explosion
  2185. MSG_ReadVector(&cl_message, pos, cls.protocol);
  2186. CL_FindNonSolidLocation(pos, pos, 10);
  2187. CL_ParticleEffect(EFFECT_TE_TAREXPLOSION, 1, pos, pos, vec3_origin, vec3_origin, NULL, 0);
  2188. S_StartSound(-1, 0, cl.sfx_r_exp3, pos, 1, 1);
  2189. break;
  2190. case QW_TE_LIGHTNING1:
  2191. // lightning bolts
  2192. CL_ParseBeam(cl.model_bolt, true);
  2193. break;
  2194. case QW_TE_LIGHTNING2:
  2195. // lightning bolts
  2196. CL_ParseBeam(cl.model_bolt2, true);
  2197. break;
  2198. case QW_TE_LIGHTNING3:
  2199. // lightning bolts
  2200. CL_ParseBeam(cl.model_bolt3, false);
  2201. break;
  2202. case QW_TE_LAVASPLASH:
  2203. MSG_ReadVector(&cl_message, pos, cls.protocol);
  2204. CL_ParticleEffect(EFFECT_TE_LAVASPLASH, 1, pos, pos, vec3_origin, vec3_origin, NULL, 0);
  2205. break;
  2206. case QW_TE_TELEPORT:
  2207. MSG_ReadVector(&cl_message, pos, cls.protocol);
  2208. CL_ParticleEffect(EFFECT_TE_TELEPORT, 1, pos, pos, vec3_origin, vec3_origin, NULL, 0);
  2209. break;
  2210. case QW_TE_GUNSHOT:
  2211. // bullet hitting wall
  2212. radius = MSG_ReadByte(&cl_message);
  2213. MSG_ReadVector(&cl_message, pos, cls.protocol);
  2214. CL_FindNonSolidLocation(pos, pos, 4);
  2215. VectorSet(pos2, pos[0] + radius, pos[1] + radius, pos[2] + radius);
  2216. VectorSet(pos, pos[0] - radius, pos[1] - radius, pos[2] - radius);
  2217. CL_ParticleEffect(EFFECT_TE_GUNSHOT, radius, pos, pos2, vec3_origin, vec3_origin, NULL, 0);
  2218. if(cl_sound_ric_gunshot.integer & RIC_GUNSHOT)
  2219. {
  2220. if (rand() % 5)
  2221. S_StartSound(-1, 0, cl.sfx_tink1, pos, 1, 1);
  2222. else
  2223. {
  2224. rnd = rand() & 3;
  2225. if (rnd == 1)
  2226. S_StartSound(-1, 0, cl.sfx_ric1, pos, 1, 1);
  2227. else if (rnd == 2)
  2228. S_StartSound(-1, 0, cl.sfx_ric2, pos, 1, 1);
  2229. else
  2230. S_StartSound(-1, 0, cl.sfx_ric3, pos, 1, 1);
  2231. }
  2232. }
  2233. break;
  2234. case QW_TE_BLOOD:
  2235. count = MSG_ReadByte(&cl_message);
  2236. MSG_ReadVector(&cl_message, pos, cls.protocol);
  2237. CL_FindNonSolidLocation(pos, pos, 4);
  2238. CL_ParticleEffect(EFFECT_TE_BLOOD, count, pos, pos, vec3_origin, vec3_origin, NULL, 0);
  2239. break;
  2240. case QW_TE_LIGHTNINGBLOOD:
  2241. MSG_ReadVector(&cl_message, pos, cls.protocol);
  2242. CL_FindNonSolidLocation(pos, pos, 4);
  2243. CL_ParticleEffect(EFFECT_TE_BLOOD, 2.5, pos, pos, vec3_origin, vec3_origin, NULL, 0);
  2244. break;
  2245. default:
  2246. Host_Error("CL_ParseTempEntity: bad type %d (hex %02X)", type, type);
  2247. }
  2248. }
  2249. else
  2250. {
  2251. type = MSG_ReadByte(&cl_message);
  2252. switch (type)
  2253. {
  2254. case TE_WIZSPIKE:
  2255. // spike hitting wall
  2256. MSG_ReadVector(&cl_message, pos, cls.protocol);
  2257. CL_FindNonSolidLocation(pos, pos, 4);
  2258. CL_ParticleEffect(EFFECT_TE_WIZSPIKE, 1, pos, pos, vec3_origin, vec3_origin, NULL, 0);
  2259. S_StartSound(-1, 0, cl.sfx_wizhit, pos, 1, 1);
  2260. break;
  2261. case TE_KNIGHTSPIKE:
  2262. // spike hitting wall
  2263. MSG_ReadVector(&cl_message, pos, cls.protocol);
  2264. CL_FindNonSolidLocation(pos, pos, 4);
  2265. CL_ParticleEffect(EFFECT_TE_KNIGHTSPIKE, 1, pos, pos, vec3_origin, vec3_origin, NULL, 0);
  2266. S_StartSound(-1, 0, cl.sfx_knighthit, pos, 1, 1);
  2267. break;
  2268. case TE_SPIKE:
  2269. // spike hitting wall
  2270. MSG_ReadVector(&cl_message, pos, cls.protocol);
  2271. CL_FindNonSolidLocation(pos, pos, 4);
  2272. CL_ParticleEffect(EFFECT_TE_SPIKE, 1, pos, pos, vec3_origin, vec3_origin, NULL, 0);
  2273. if (rand() % 5)
  2274. S_StartSound(-1, 0, cl.sfx_tink1, pos, 1, 1);
  2275. else
  2276. {
  2277. rnd = rand() & 3;
  2278. if (rnd == 1)
  2279. S_StartSound(-1, 0, cl.sfx_ric1, pos, 1, 1);
  2280. else if (rnd == 2)
  2281. S_StartSound(-1, 0, cl.sfx_ric2, pos, 1, 1);
  2282. else
  2283. S_StartSound(-1, 0, cl.sfx_ric3, pos, 1, 1);
  2284. }
  2285. break;
  2286. case TE_SPIKEQUAD:
  2287. // quad spike hitting wall
  2288. MSG_ReadVector(&cl_message, pos, cls.protocol);
  2289. CL_FindNonSolidLocation(pos, pos, 4);
  2290. CL_ParticleEffect(EFFECT_TE_SPIKEQUAD, 1, pos, pos, vec3_origin, vec3_origin, NULL, 0);
  2291. if (rand() % 5)
  2292. S_StartSound(-1, 0, cl.sfx_tink1, pos, 1, 1);
  2293. else
  2294. {
  2295. rnd = rand() & 3;
  2296. if (rnd == 1)
  2297. S_StartSound(-1, 0, cl.sfx_ric1, pos, 1, 1);
  2298. else if (rnd == 2)
  2299. S_StartSound(-1, 0, cl.sfx_ric2, pos, 1, 1);
  2300. else
  2301. S_StartSound(-1, 0, cl.sfx_ric3, pos, 1, 1);
  2302. }
  2303. break;
  2304. case TE_SUPERSPIKE:
  2305. // super spike hitting wall
  2306. MSG_ReadVector(&cl_message, pos, cls.protocol);
  2307. CL_FindNonSolidLocation(pos, pos, 4);
  2308. CL_ParticleEffect(EFFECT_TE_SUPERSPIKE, 1, pos, pos, vec3_origin, vec3_origin, NULL, 0);
  2309. if (rand() % 5)
  2310. S_StartSound(-1, 0, cl.sfx_tink1, pos, 1, 1);
  2311. else
  2312. {
  2313. rnd = rand() & 3;
  2314. if (rnd == 1)
  2315. S_StartSound(-1, 0, cl.sfx_ric1, pos, 1, 1);
  2316. else if (rnd == 2)
  2317. S_StartSound(-1, 0, cl.sfx_ric2, pos, 1, 1);
  2318. else
  2319. S_StartSound(-1, 0, cl.sfx_ric3, pos, 1, 1);
  2320. }
  2321. break;
  2322. case TE_SUPERSPIKEQUAD:
  2323. // quad super spike hitting wall
  2324. MSG_ReadVector(&cl_message, pos, cls.protocol);
  2325. CL_FindNonSolidLocation(pos, pos, 4);
  2326. CL_ParticleEffect(EFFECT_TE_SUPERSPIKEQUAD, 1, pos, pos, vec3_origin, vec3_origin, NULL, 0);
  2327. if (rand() % 5)
  2328. S_StartSound(-1, 0, cl.sfx_tink1, pos, 1, 1);
  2329. else
  2330. {
  2331. rnd = rand() & 3;
  2332. if (rnd == 1)
  2333. S_StartSound(-1, 0, cl.sfx_ric1, pos, 1, 1);
  2334. else if (rnd == 2)
  2335. S_StartSound(-1, 0, cl.sfx_ric2, pos, 1, 1);
  2336. else
  2337. S_StartSound(-1, 0, cl.sfx_ric3, pos, 1, 1);
  2338. }
  2339. break;
  2340. // LordHavoc: added for improved blood splatters
  2341. case TE_BLOOD:
  2342. // blood puff
  2343. MSG_ReadVector(&cl_message, pos, cls.protocol);
  2344. CL_FindNonSolidLocation(pos, pos, 4);
  2345. dir[0] = MSG_ReadChar(&cl_message);
  2346. dir[1] = MSG_ReadChar(&cl_message);
  2347. dir[2] = MSG_ReadChar(&cl_message);
  2348. count = MSG_ReadByte(&cl_message);
  2349. CL_ParticleEffect(EFFECT_TE_BLOOD, count, pos, pos, dir, dir, NULL, 0);
  2350. break;
  2351. case TE_SPARK:
  2352. // spark shower
  2353. MSG_ReadVector(&cl_message, pos, cls.protocol);
  2354. CL_FindNonSolidLocation(pos, pos, 4);
  2355. dir[0] = MSG_ReadChar(&cl_message);
  2356. dir[1] = MSG_ReadChar(&cl_message);
  2357. dir[2] = MSG_ReadChar(&cl_message);
  2358. count = MSG_ReadByte(&cl_message);
  2359. CL_ParticleEffect(EFFECT_TE_SPARK, count, pos, pos, dir, dir, NULL, 0);
  2360. break;
  2361. case TE_PLASMABURN:
  2362. MSG_ReadVector(&cl_message, pos, cls.protocol);
  2363. CL_FindNonSolidLocation(pos, pos, 4);
  2364. CL_ParticleEffect(EFFECT_TE_PLASMABURN, 1, pos, pos, vec3_origin, vec3_origin, NULL, 0);
  2365. break;
  2366. // LordHavoc: added for improved gore
  2367. case TE_BLOODSHOWER:
  2368. // vaporized body
  2369. MSG_ReadVector(&cl_message, pos, cls.protocol); // mins
  2370. MSG_ReadVector(&cl_message, pos2, cls.protocol); // maxs
  2371. velspeed = MSG_ReadCoord(&cl_message, cls.protocol); // speed
  2372. count = (unsigned short) MSG_ReadShort(&cl_message); // number of particles
  2373. vel1[0] = -velspeed;
  2374. vel1[1] = -velspeed;
  2375. vel1[2] = -velspeed;
  2376. vel2[0] = velspeed;
  2377. vel2[1] = velspeed;
  2378. vel2[2] = velspeed;
  2379. CL_ParticleEffect(EFFECT_TE_BLOOD, count, pos, pos2, vel1, vel2, NULL, 0);
  2380. break;
  2381. case TE_PARTICLECUBE:
  2382. // general purpose particle effect
  2383. MSG_ReadVector(&cl_message, pos, cls.protocol); // mins
  2384. MSG_ReadVector(&cl_message, pos2, cls.protocol); // maxs
  2385. MSG_ReadVector(&cl_message, dir, cls.protocol); // dir
  2386. count = (unsigned short) MSG_ReadShort(&cl_message); // number of particles
  2387. colorStart = MSG_ReadByte(&cl_message); // color
  2388. colorLength = MSG_ReadByte(&cl_message); // gravity (1 or 0)
  2389. velspeed = MSG_ReadCoord(&cl_message, cls.protocol); // randomvel
  2390. CL_ParticleCube(pos, pos2, dir, count, colorStart, colorLength != 0, velspeed);
  2391. break;
  2392. case TE_PARTICLERAIN:
  2393. // general purpose particle effect
  2394. MSG_ReadVector(&cl_message, pos, cls.protocol); // mins
  2395. MSG_ReadVector(&cl_message, pos2, cls.protocol); // maxs
  2396. MSG_ReadVector(&cl_message, dir, cls.protocol); // dir
  2397. count = (unsigned short) MSG_ReadShort(&cl_message); // number of particles
  2398. colorStart = MSG_ReadByte(&cl_message); // color
  2399. CL_ParticleRain(pos, pos2, dir, count, colorStart, 0);
  2400. break;
  2401. case TE_PARTICLESNOW:
  2402. // general purpose particle effect
  2403. MSG_ReadVector(&cl_message, pos, cls.protocol); // mins
  2404. MSG_ReadVector(&cl_message, pos2, cls.protocol); // maxs
  2405. MSG_ReadVector(&cl_message, dir, cls.protocol); // dir
  2406. count = (unsigned short) MSG_ReadShort(&cl_message); // number of particles
  2407. colorStart = MSG_ReadByte(&cl_message); // color
  2408. CL_ParticleRain(pos, pos2, dir, count, colorStart, 1);
  2409. break;
  2410. case TE_GUNSHOT:
  2411. // bullet hitting wall
  2412. MSG_ReadVector(&cl_message, pos, cls.protocol);
  2413. CL_FindNonSolidLocation(pos, pos, 4);
  2414. CL_ParticleEffect(EFFECT_TE_GUNSHOT, 1, pos, pos, vec3_origin, vec3_origin, NULL, 0);
  2415. if(cl_sound_ric_gunshot.integer & RIC_GUNSHOT)
  2416. {
  2417. if (rand() % 5)
  2418. S_StartSound(-1, 0, cl.sfx_tink1, pos, 1, 1);
  2419. else
  2420. {
  2421. rnd = rand() & 3;
  2422. if (rnd == 1)
  2423. S_StartSound(-1, 0, cl.sfx_ric1, pos, 1, 1);
  2424. else if (rnd == 2)
  2425. S_StartSound(-1, 0, cl.sfx_ric2, pos, 1, 1);
  2426. else
  2427. S_StartSound(-1, 0, cl.sfx_ric3, pos, 1, 1);
  2428. }
  2429. }
  2430. break;
  2431. case TE_GUNSHOTQUAD:
  2432. // quad bullet hitting wall
  2433. MSG_ReadVector(&cl_message, pos, cls.protocol);
  2434. CL_FindNonSolidLocation(pos, pos, 4);
  2435. CL_ParticleEffect(EFFECT_TE_GUNSHOTQUAD, 1, pos, pos, vec3_origin, vec3_origin, NULL, 0);
  2436. if(cl_sound_ric_gunshot.integer & RIC_GUNSHOTQUAD)
  2437. {
  2438. if (rand() % 5)
  2439. S_StartSound(-1, 0, cl.sfx_tink1, pos, 1, 1);
  2440. else
  2441. {
  2442. rnd = rand() & 3;
  2443. if (rnd == 1)
  2444. S_StartSound(-1, 0, cl.sfx_ric1, pos, 1, 1);
  2445. else if (rnd == 2)
  2446. S_StartSound(-1, 0, cl.sfx_ric2, pos, 1, 1);
  2447. else
  2448. S_StartSound(-1, 0, cl.sfx_ric3, pos, 1, 1);
  2449. }
  2450. }
  2451. break;
  2452. case TE_EXPLOSION:
  2453. // rocket explosion
  2454. MSG_ReadVector(&cl_message, pos, cls.protocol);
  2455. CL_FindNonSolidLocation(pos, pos, 10);
  2456. CL_ParticleEffect(EFFECT_TE_EXPLOSION, 1, pos, pos, vec3_origin, vec3_origin, NULL, 0);
  2457. S_StartSound(-1, 0, cl.sfx_r_exp3, pos, 1, 1);
  2458. break;
  2459. case TE_EXPLOSIONQUAD:
  2460. // quad rocket explosion
  2461. MSG_ReadVector(&cl_message, pos, cls.protocol);
  2462. CL_FindNonSolidLocation(pos, pos, 10);
  2463. CL_ParticleEffect(EFFECT_TE_EXPLOSIONQUAD, 1, pos, pos, vec3_origin, vec3_origin, NULL, 0);
  2464. S_StartSound(-1, 0, cl.sfx_r_exp3, pos, 1, 1);
  2465. break;
  2466. case TE_EXPLOSION3:
  2467. // Nehahra movie colored lighting explosion
  2468. MSG_ReadVector(&cl_message, pos, cls.protocol);
  2469. CL_FindNonSolidLocation(pos, pos, 10);
  2470. color[0] = MSG_ReadCoord(&cl_message, cls.protocol) * (2.0f / 1.0f);
  2471. color[1] = MSG_ReadCoord(&cl_message, cls.protocol) * (2.0f / 1.0f);
  2472. color[2] = MSG_ReadCoord(&cl_message, cls.protocol) * (2.0f / 1.0f);
  2473. CL_ParticleExplosion(pos);
  2474. Matrix4x4_CreateTranslate(&tempmatrix, pos[0], pos[1], pos[2]);
  2475. 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);
  2476. S_StartSound(-1, 0, cl.sfx_r_exp3, pos, 1, 1);
  2477. break;
  2478. case TE_EXPLOSIONRGB:
  2479. // colored lighting explosion
  2480. MSG_ReadVector(&cl_message, pos, cls.protocol);
  2481. CL_FindNonSolidLocation(pos, pos, 10);
  2482. CL_ParticleExplosion(pos);
  2483. color[0] = MSG_ReadByte(&cl_message) * (2.0f / 255.0f);
  2484. color[1] = MSG_ReadByte(&cl_message) * (2.0f / 255.0f);
  2485. color[2] = MSG_ReadByte(&cl_message) * (2.0f / 255.0f);
  2486. Matrix4x4_CreateTranslate(&tempmatrix, pos[0], pos[1], pos[2]);
  2487. 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);
  2488. S_StartSound(-1, 0, cl.sfx_r_exp3, pos, 1, 1);
  2489. break;
  2490. case TE_TAREXPLOSION:
  2491. // tarbaby explosion
  2492. MSG_ReadVector(&cl_message, pos, cls.protocol);
  2493. CL_FindNonSolidLocation(pos, pos, 10);
  2494. CL_ParticleEffect(EFFECT_TE_TAREXPLOSION, 1, pos, pos, vec3_origin, vec3_origin, NULL, 0);
  2495. S_StartSound(-1, 0, cl.sfx_r_exp3, pos, 1, 1);
  2496. break;
  2497. case TE_SMALLFLASH:
  2498. MSG_ReadVector(&cl_message, pos, cls.protocol);
  2499. CL_FindNonSolidLocation(pos, pos, 10);
  2500. CL_ParticleEffect(EFFECT_TE_SMALLFLASH, 1, pos, pos, vec3_origin, vec3_origin, NULL, 0);
  2501. break;
  2502. case TE_CUSTOMFLASH:
  2503. MSG_ReadVector(&cl_message, pos, cls.protocol);
  2504. CL_FindNonSolidLocation(pos, pos, 4);
  2505. radius = (MSG_ReadByte(&cl_message) + 1) * 8;
  2506. velspeed = (MSG_ReadByte(&cl_message) + 1) * (1.0 / 256.0);
  2507. color[0] = MSG_ReadByte(&cl_message) * (2.0f / 255.0f);
  2508. color[1] = MSG_ReadByte(&cl_message) * (2.0f / 255.0f);
  2509. color[2] = MSG_ReadByte(&cl_message) * (2.0f / 255.0f);
  2510. Matrix4x4_CreateTranslate(&tempmatrix, pos[0], pos[1], pos[2]);
  2511. 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);
  2512. break;
  2513. case TE_FLAMEJET:
  2514. MSG_ReadVector(&cl_message, pos, cls.protocol);
  2515. MSG_ReadVector(&cl_message, dir, cls.protocol);
  2516. count = MSG_ReadByte(&cl_message);
  2517. CL_ParticleEffect(EFFECT_TE_FLAMEJET, count, pos, pos, dir, dir, NULL, 0);
  2518. break;
  2519. case TE_LIGHTNING1:
  2520. // lightning bolts
  2521. CL_ParseBeam(cl.model_bolt, true);
  2522. break;
  2523. case TE_LIGHTNING2:
  2524. // lightning bolts
  2525. CL_ParseBeam(cl.model_bolt2, true);
  2526. break;
  2527. case TE_LIGHTNING3:
  2528. // lightning bolts
  2529. CL_ParseBeam(cl.model_bolt3, false);
  2530. break;
  2531. // PGM 01/21/97
  2532. case TE_BEAM:
  2533. // grappling hook beam
  2534. CL_ParseBeam(cl.model_beam, false);
  2535. break;
  2536. // PGM 01/21/97
  2537. // LordHavoc: for compatibility with the Nehahra movie...
  2538. case TE_LIGHTNING4NEH:
  2539. CL_ParseBeam(Mod_ForName(MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)), true, false, NULL), false);
  2540. break;
  2541. case TE_LAVASPLASH:
  2542. MSG_ReadVector(&cl_message, pos, cls.protocol);
  2543. CL_ParticleEffect(EFFECT_TE_LAVASPLASH, 1, pos, pos, vec3_origin, vec3_origin, NULL, 0);
  2544. break;
  2545. case TE_TELEPORT:
  2546. MSG_ReadVector(&cl_message, pos, cls.protocol);
  2547. CL_ParticleEffect(EFFECT_TE_TELEPORT, 1, pos, pos, vec3_origin, vec3_origin, NULL, 0);
  2548. break;
  2549. case TE_EXPLOSION2:
  2550. // color mapped explosion
  2551. MSG_ReadVector(&cl_message, pos, cls.protocol);
  2552. CL_FindNonSolidLocation(pos, pos, 10);
  2553. colorStart = MSG_ReadByte(&cl_message);
  2554. colorLength = MSG_ReadByte(&cl_message);
  2555. if (colorLength == 0)
  2556. colorLength = 1;
  2557. CL_ParticleExplosion2(pos, colorStart, colorLength);
  2558. tempcolor = palette_rgb[(rand()%colorLength) + colorStart];
  2559. color[0] = tempcolor[0] * (2.0f / 255.0f);
  2560. color[1] = tempcolor[1] * (2.0f / 255.0f);
  2561. color[2] = tempcolor[2] * (2.0f / 255.0f);
  2562. Matrix4x4_CreateTranslate(&tempmatrix, pos[0], pos[1], pos[2]);
  2563. 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);
  2564. S_StartSound(-1, 0, cl.sfx_r_exp3, pos, 1, 1);
  2565. break;
  2566. case TE_TEI_G3:
  2567. MSG_ReadVector(&cl_message, pos, cls.protocol);
  2568. MSG_ReadVector(&cl_message, pos2, cls.protocol);
  2569. MSG_ReadVector(&cl_message, dir, cls.protocol);
  2570. CL_ParticleTrail(EFFECT_TE_TEI_G3, 1, pos, pos2, dir, dir, NULL, 0, true, true, NULL, NULL, 1);
  2571. break;
  2572. case TE_TEI_SMOKE:
  2573. MSG_ReadVector(&cl_message, pos, cls.protocol);
  2574. MSG_ReadVector(&cl_message, dir, cls.protocol);
  2575. count = MSG_ReadByte(&cl_message);
  2576. CL_FindNonSolidLocation(pos, pos, 4);
  2577. CL_ParticleEffect(EFFECT_TE_TEI_SMOKE, count, pos, pos, vec3_origin, vec3_origin, NULL, 0);
  2578. break;
  2579. case TE_TEI_BIGEXPLOSION:
  2580. MSG_ReadVector(&cl_message, pos, cls.protocol);
  2581. CL_FindNonSolidLocation(pos, pos, 10);
  2582. CL_ParticleEffect(EFFECT_TE_TEI_BIGEXPLOSION, 1, pos, pos, vec3_origin, vec3_origin, NULL, 0);
  2583. S_StartSound(-1, 0, cl.sfx_r_exp3, pos, 1, 1);
  2584. break;
  2585. case TE_TEI_PLASMAHIT:
  2586. MSG_ReadVector(&cl_message, pos, cls.protocol);
  2587. MSG_ReadVector(&cl_message, dir, cls.protocol);
  2588. count = MSG_ReadByte(&cl_message);
  2589. CL_FindNonSolidLocation(pos, pos, 5);
  2590. CL_ParticleEffect(EFFECT_TE_TEI_PLASMAHIT, count, pos, pos, vec3_origin, vec3_origin, NULL, 0);
  2591. break;
  2592. default:
  2593. Host_Error("CL_ParseTempEntity: bad type %d (hex %02X)", type, type);
  2594. }
  2595. }
  2596. }
  2597. static void CL_ParseTrailParticles(void)
  2598. {
  2599. int entityindex;
  2600. int effectindex;
  2601. vec3_t start, end;
  2602. entityindex = (unsigned short)MSG_ReadShort(&cl_message);
  2603. if (entityindex >= MAX_EDICTS)
  2604. entityindex = 0;
  2605. if (entityindex >= cl.max_entities)
  2606. CL_ExpandEntities(entityindex);
  2607. effectindex = (unsigned short)MSG_ReadShort(&cl_message);
  2608. MSG_ReadVector(&cl_message, start, cls.protocol);
  2609. MSG_ReadVector(&cl_message, end, cls.protocol);
  2610. CL_ParticleTrail(effectindex, 1, start, end, vec3_origin, vec3_origin, entityindex > 0 ? cl.entities + entityindex : NULL, 0, true, true, NULL, NULL, 1);
  2611. }
  2612. static void CL_ParsePointParticles(void)
  2613. {
  2614. int effectindex, count;
  2615. vec3_t origin, velocity;
  2616. effectindex = (unsigned short)MSG_ReadShort(&cl_message);
  2617. MSG_ReadVector(&cl_message, origin, cls.protocol);
  2618. MSG_ReadVector(&cl_message, velocity, cls.protocol);
  2619. count = (unsigned short)MSG_ReadShort(&cl_message);
  2620. CL_ParticleEffect(effectindex, count, origin, origin, velocity, velocity, NULL, 0);
  2621. }
  2622. static void CL_ParsePointParticles1(void)
  2623. {
  2624. int effectindex;
  2625. vec3_t origin;
  2626. effectindex = (unsigned short)MSG_ReadShort(&cl_message);
  2627. MSG_ReadVector(&cl_message, origin, cls.protocol);
  2628. CL_ParticleEffect(effectindex, 1, origin, origin, vec3_origin, vec3_origin, NULL, 0);
  2629. }
  2630. typedef struct cl_iplog_item_s
  2631. {
  2632. char *address;
  2633. char *name;
  2634. }
  2635. cl_iplog_item_t;
  2636. static qboolean cl_iplog_loaded = false;
  2637. static int cl_iplog_numitems = 0;
  2638. static int cl_iplog_maxitems = 0;
  2639. static cl_iplog_item_t *cl_iplog_items;
  2640. static void CL_IPLog_Load(void);
  2641. static void CL_IPLog_Add(const char *address, const char *name, qboolean checkexisting, qboolean addtofile)
  2642. {
  2643. int i;
  2644. size_t sz_name, sz_address;
  2645. if (!address || !address[0] || !name || !name[0])
  2646. return;
  2647. if (!cl_iplog_loaded)
  2648. CL_IPLog_Load();
  2649. if (developer_extra.integer)
  2650. Con_DPrintf("CL_IPLog_Add(\"%s\", \"%s\", %i, %i);\n", address, name, checkexisting, addtofile);
  2651. // see if it already exists
  2652. if (checkexisting)
  2653. {
  2654. for (i = 0;i < cl_iplog_numitems;i++)
  2655. {
  2656. if (!strcmp(cl_iplog_items[i].address, address) && !strcmp(cl_iplog_items[i].name, name))
  2657. {
  2658. if (developer_extra.integer)
  2659. Con_DPrintf("... found existing \"%s\" \"%s\"\n", cl_iplog_items[i].address, cl_iplog_items[i].name);
  2660. return;
  2661. }
  2662. }
  2663. }
  2664. // it does not already exist in the iplog, so add it
  2665. if (cl_iplog_maxitems <= cl_iplog_numitems || !cl_iplog_items)
  2666. {
  2667. cl_iplog_item_t *olditems = cl_iplog_items;
  2668. cl_iplog_maxitems = max(1024, cl_iplog_maxitems + 256);
  2669. cl_iplog_items = (cl_iplog_item_t *) Mem_Alloc(cls.permanentmempool, cl_iplog_maxitems * sizeof(cl_iplog_item_t));
  2670. if (olditems)
  2671. {
  2672. if (cl_iplog_numitems)
  2673. memcpy(cl_iplog_items, olditems, cl_iplog_numitems * sizeof(cl_iplog_item_t));
  2674. Mem_Free(olditems);
  2675. }
  2676. }
  2677. sz_address = strlen(address) + 1;
  2678. sz_name = strlen(name) + 1;
  2679. cl_iplog_items[cl_iplog_numitems].address = (char *) Mem_Alloc(cls.permanentmempool, sz_address);
  2680. cl_iplog_items[cl_iplog_numitems].name = (char *) Mem_Alloc(cls.permanentmempool, sz_name);
  2681. strlcpy(cl_iplog_items[cl_iplog_numitems].address, address, sz_address);
  2682. // TODO: maybe it would be better to strip weird characters from name when
  2683. // copying it here rather than using a straight strcpy?
  2684. strlcpy(cl_iplog_items[cl_iplog_numitems].name, name, sz_name);
  2685. cl_iplog_numitems++;
  2686. if (addtofile)
  2687. {
  2688. // add it to the iplog.txt file
  2689. // TODO: this ought to open the one in the userpath version of the base
  2690. // gamedir, not the current gamedir
  2691. // not necessary for mobile
  2692. #ifndef DP_MOBILETOUCH
  2693. Log_Printf(cl_iplog_name.string, "%s %s\n", address, name);
  2694. if (developer_extra.integer)
  2695. Con_DPrintf("CL_IPLog_Add: appending this line to %s: %s %s\n", cl_iplog_name.string, address, name);
  2696. #endif
  2697. }
  2698. }
  2699. static void CL_IPLog_Load(void)
  2700. {
  2701. int i, len, linenumber;
  2702. char *text, *textend;
  2703. unsigned char *filedata;
  2704. fs_offset_t filesize;
  2705. char line[MAX_INPUTLINE];
  2706. char address[MAX_INPUTLINE];
  2707. cl_iplog_loaded = true;
  2708. // TODO: this ought to open the one in the userpath version of the base
  2709. // gamedir, not the current gamedir
  2710. // not necessary for mobile
  2711. #ifndef DP_MOBILETOUCH
  2712. filedata = FS_LoadFile(cl_iplog_name.string, tempmempool, true, &filesize);
  2713. #else
  2714. filedata = NULL;
  2715. #endif
  2716. if (!filedata)
  2717. return;
  2718. text = (char *)filedata;
  2719. textend = text + filesize;
  2720. for (linenumber = 1;text < textend;linenumber++)
  2721. {
  2722. for (len = 0;text < textend && *text != '\r' && *text != '\n';text++)
  2723. if (len < (int)sizeof(line) - 1)
  2724. line[len++] = *text;
  2725. line[len] = 0;
  2726. if (text < textend && *text == '\r' && text[1] == '\n')
  2727. text++;
  2728. if (text < textend && *text == '\n')
  2729. text++;
  2730. if (line[0] == '/' && line[1] == '/')
  2731. continue; // skip comments if anyone happens to add them
  2732. for (i = 0;i < len && !ISWHITESPACE(line[i]);i++)
  2733. address[i] = line[i];
  2734. address[i] = 0;
  2735. // skip exactly one space character
  2736. i++;
  2737. // address contains the address with termination,
  2738. // line + i contains the name with termination
  2739. if (address[0] && line[i])
  2740. CL_IPLog_Add(address, line + i, false, false);
  2741. else
  2742. Con_Printf("%s:%i: could not parse address and name:\n%s\n", cl_iplog_name.string, linenumber, line);
  2743. }
  2744. }
  2745. static void CL_IPLog_List_f(void)
  2746. {
  2747. int i, j;
  2748. const char *addressprefix;
  2749. if (Cmd_Argc() > 2)
  2750. {
  2751. Con_Printf("usage: %s 123.456.789.\n", Cmd_Argv(0));
  2752. return;
  2753. }
  2754. addressprefix = "";
  2755. if (Cmd_Argc() >= 2)
  2756. addressprefix = Cmd_Argv(1);
  2757. if (!cl_iplog_loaded)
  2758. CL_IPLog_Load();
  2759. if (addressprefix && addressprefix[0])
  2760. Con_Printf("Listing iplog addresses beginning with %s\n", addressprefix);
  2761. else
  2762. Con_Printf("Listing all iplog entries\n");
  2763. Con_Printf("address name\n");
  2764. for (i = 0;i < cl_iplog_numitems;i++)
  2765. {
  2766. if (addressprefix && addressprefix[0])
  2767. {
  2768. for (j = 0;addressprefix[j];j++)
  2769. if (addressprefix[j] != cl_iplog_items[i].address[j])
  2770. break;
  2771. // if this address does not begin with the addressprefix string
  2772. // simply omit it from the output
  2773. if (addressprefix[j])
  2774. continue;
  2775. }
  2776. // if name is less than 15 characters, left justify it and pad
  2777. // if name is more than 15 characters, print all of it, not worrying
  2778. // about the fact it will misalign the columns
  2779. if (strlen(cl_iplog_items[i].address) < 15)
  2780. Con_Printf("%-15s %s\n", cl_iplog_items[i].address, cl_iplog_items[i].name);
  2781. else
  2782. Con_Printf("%5s %s\n", cl_iplog_items[i].address, cl_iplog_items[i].name);
  2783. }
  2784. }
  2785. // look for anything interesting like player IP addresses or ping reports
  2786. static qboolean CL_ExaminePrintString(const char *text)
  2787. {
  2788. int len;
  2789. const char *t;
  2790. char temp[MAX_INPUTLINE];
  2791. if (!strcmp(text, "Client ping times:\n"))
  2792. {
  2793. cl.parsingtextmode = CL_PARSETEXTMODE_PING;
  2794. // hide ping reports in demos
  2795. if (cls.demoplayback)
  2796. cl.parsingtextexpectingpingforscores = 1;
  2797. for(cl.parsingtextplayerindex = 0; cl.parsingtextplayerindex < cl.maxclients && !cl.scores[cl.parsingtextplayerindex].name[0]; cl.parsingtextplayerindex++)
  2798. ;
  2799. if (cl.parsingtextplayerindex >= cl.maxclients) // should never happen, since the client itself should be in cl.scores
  2800. {
  2801. Con_Printf("ping reply but empty scoreboard?!?\n");
  2802. cl.parsingtextmode = CL_PARSETEXTMODE_NONE;
  2803. cl.parsingtextexpectingpingforscores = 0;
  2804. }
  2805. cl.parsingtextexpectingpingforscores = cl.parsingtextexpectingpingforscores ? 2 : 0;
  2806. return !cl.parsingtextexpectingpingforscores;
  2807. }
  2808. if (!strncmp(text, "host: ", 9))
  2809. {
  2810. // cl.parsingtextexpectingpingforscores = false; // really?
  2811. cl.parsingtextmode = CL_PARSETEXTMODE_STATUS;
  2812. cl.parsingtextplayerindex = 0;
  2813. return true;
  2814. }
  2815. if (cl.parsingtextmode == CL_PARSETEXTMODE_PING)
  2816. {
  2817. // if anything goes wrong, we'll assume this is not a ping report
  2818. qboolean expected = cl.parsingtextexpectingpingforscores != 0;
  2819. cl.parsingtextexpectingpingforscores = 0;
  2820. cl.parsingtextmode = CL_PARSETEXTMODE_NONE;
  2821. t = text;
  2822. while (*t == ' ')
  2823. t++;
  2824. if ((*t >= '0' && *t <= '9') || *t == '-')
  2825. {
  2826. int ping = atoi(t);
  2827. while ((*t >= '0' && *t <= '9') || *t == '-')
  2828. t++;
  2829. if (*t == ' ')
  2830. {
  2831. int charindex = 0;
  2832. t++;
  2833. if(cl.parsingtextplayerindex < cl.maxclients)
  2834. {
  2835. for (charindex = 0;cl.scores[cl.parsingtextplayerindex].name[charindex] == t[charindex];charindex++)
  2836. ;
  2837. // note: the matching algorithm stops at the end of the player name because some servers append text such as " READY" after the player name in the scoreboard but not in the ping report
  2838. //if (cl.scores[cl.parsingtextplayerindex].name[charindex] == 0 && t[charindex] == '\n')
  2839. if (t[charindex] == '\n')
  2840. {
  2841. cl.scores[cl.parsingtextplayerindex].qw_ping = bound(0, ping, 9999);
  2842. for (cl.parsingtextplayerindex++;cl.parsingtextplayerindex < cl.maxclients && !cl.scores[cl.parsingtextplayerindex].name[0];cl.parsingtextplayerindex++)
  2843. ;
  2844. //if (cl.parsingtextplayerindex < cl.maxclients) // we could still get unconnecteds!
  2845. {
  2846. // we parsed a valid ping entry, so expect another to follow
  2847. cl.parsingtextmode = CL_PARSETEXTMODE_PING;
  2848. cl.parsingtextexpectingpingforscores = expected;
  2849. }
  2850. return !expected;
  2851. }
  2852. }
  2853. if (!strncmp(t, "unconnected\n", 12))
  2854. {
  2855. // just ignore
  2856. cl.parsingtextmode = CL_PARSETEXTMODE_PING;
  2857. cl.parsingtextexpectingpingforscores = expected;
  2858. return !expected;
  2859. }
  2860. else
  2861. Con_DPrintf("player names '%s' and '%s' didn't match\n", cl.scores[cl.parsingtextplayerindex].name, t);
  2862. }
  2863. }
  2864. }
  2865. if (cl.parsingtextmode == CL_PARSETEXTMODE_STATUS)
  2866. {
  2867. if (!strncmp(text, "players: ", 9))
  2868. {
  2869. cl.parsingtextmode = CL_PARSETEXTMODE_STATUS_PLAYERID;
  2870. cl.parsingtextplayerindex = 0;
  2871. return true;
  2872. }
  2873. else if (!strstr(text, ": "))
  2874. {
  2875. cl.parsingtextmode = CL_PARSETEXTMODE_NONE; // status report ended
  2876. return true;
  2877. }
  2878. }
  2879. if (cl.parsingtextmode == CL_PARSETEXTMODE_STATUS_PLAYERID)
  2880. {
  2881. // if anything goes wrong, we'll assume this is not a status report
  2882. cl.parsingtextmode = CL_PARSETEXTMODE_NONE;
  2883. if (text[0] == '#' && text[1] >= '0' && text[1] <= '9')
  2884. {
  2885. t = text + 1;
  2886. cl.parsingtextplayerindex = atoi(t) - 1;
  2887. while (*t >= '0' && *t <= '9')
  2888. t++;
  2889. if (*t == ' ')
  2890. {
  2891. cl.parsingtextmode = CL_PARSETEXTMODE_STATUS_PLAYERIP;
  2892. return true;
  2893. }
  2894. // the player name follows here, along with frags and time
  2895. }
  2896. }
  2897. if (cl.parsingtextmode == CL_PARSETEXTMODE_STATUS_PLAYERIP)
  2898. {
  2899. // if anything goes wrong, we'll assume this is not a status report
  2900. cl.parsingtextmode = CL_PARSETEXTMODE_NONE;
  2901. if (text[0] == ' ')
  2902. {
  2903. t = text;
  2904. while (*t == ' ')
  2905. t++;
  2906. for (len = 0;*t && *t != '\n';t++)
  2907. if (len < (int)sizeof(temp) - 1)
  2908. temp[len++] = *t;
  2909. temp[len] = 0;
  2910. // botclient is perfectly valid, but we don't care about bots
  2911. // also don't try to look up the name of an invalid player index
  2912. if (strcmp(temp, "botclient")
  2913. && cl.parsingtextplayerindex >= 0
  2914. && cl.parsingtextplayerindex < cl.maxclients
  2915. && cl.scores[cl.parsingtextplayerindex].name[0])
  2916. {
  2917. // log the player name and IP address string
  2918. // (this operates entirely on strings to avoid issues with the
  2919. // nature of a network address)
  2920. CL_IPLog_Add(temp, cl.scores[cl.parsingtextplayerindex].name, true, true);
  2921. }
  2922. cl.parsingtextmode = CL_PARSETEXTMODE_STATUS_PLAYERID;
  2923. return true;
  2924. }
  2925. }
  2926. return true;
  2927. }
  2928. extern cvar_t slowmo;
  2929. extern cvar_t cl_lerpexcess;
  2930. static void CL_NetworkTimeReceived(double newtime)
  2931. {
  2932. double timehigh;
  2933. cl.mtime[1] = cl.mtime[0];
  2934. cl.mtime[0] = newtime;
  2935. if (cl_nolerp.integer || cls.timedemo || (cl.islocalgame && !sv_fixedframeratesingleplayer.integer) || cl.mtime[1] == cl.mtime[0] || cls.signon < SIGNONS)
  2936. cl.time = cl.mtime[1] = newtime;
  2937. else if (cls.demoplayback)
  2938. {
  2939. // when time falls behind during demo playback it means the cl.mtime[1] was altered
  2940. // due to a large time gap, so treat it as an instant change in time
  2941. // (this can also happen during heavy packet loss in the demo)
  2942. if (cl.time < newtime - 0.1)
  2943. cl.mtime[1] = cl.time = newtime;
  2944. }
  2945. else if (cls.protocol != PROTOCOL_QUAKEWORLD)
  2946. {
  2947. cl.mtime[1] = max(cl.mtime[1], cl.mtime[0] - 0.1);
  2948. if (developer_extra.integer && vid_activewindow)
  2949. {
  2950. if (cl.time < cl.mtime[1] - (cl.mtime[0] - cl.mtime[1]))
  2951. Con_DPrintf("--- cl.time < cl.mtime[1] (%f < %f ... %f)\n", cl.time, cl.mtime[1], cl.mtime[0]);
  2952. else if (cl.time > cl.mtime[0] + (cl.mtime[0] - cl.mtime[1]))
  2953. Con_DPrintf("--- cl.time > cl.mtime[0] (%f > %f ... %f)\n", cl.time, cl.mtime[1], cl.mtime[0]);
  2954. }
  2955. cl.time += (cl.mtime[1] - cl.time) * bound(0, cl_nettimesyncfactor.value, 1);
  2956. timehigh = cl.mtime[1] + (cl.mtime[0] - cl.mtime[1]) * cl_nettimesyncboundtolerance.value;
  2957. if (cl_nettimesyncboundmode.integer == 1)
  2958. cl.time = bound(cl.mtime[1], cl.time, cl.mtime[0]);
  2959. else if (cl_nettimesyncboundmode.integer == 2)
  2960. {
  2961. if (cl.time < cl.mtime[1] || cl.time > timehigh)
  2962. cl.time = cl.mtime[1];
  2963. }
  2964. else if (cl_nettimesyncboundmode.integer == 3)
  2965. {
  2966. if ((cl.time < cl.mtime[1] && cl.oldtime < cl.mtime[1]) || (cl.time > timehigh && cl.oldtime > timehigh))
  2967. cl.time = cl.mtime[1];
  2968. }
  2969. else if (cl_nettimesyncboundmode.integer == 4)
  2970. {
  2971. if (fabs(cl.time - cl.mtime[1]) > 0.5)
  2972. cl.time = cl.mtime[1]; // reset
  2973. else if (fabs(cl.time - cl.mtime[1]) > 0.1)
  2974. cl.time += 0.5 * (cl.mtime[1] - cl.time); // fast
  2975. else if (cl.time > cl.mtime[1])
  2976. cl.time -= 0.002 * cl.movevars_timescale; // fall into the past by 2ms
  2977. else
  2978. cl.time += 0.001 * cl.movevars_timescale; // creep forward 1ms
  2979. }
  2980. else if (cl_nettimesyncboundmode.integer == 5)
  2981. {
  2982. if (fabs(cl.time - cl.mtime[1]) > 0.5)
  2983. cl.time = cl.mtime[1]; // reset
  2984. else if (fabs(cl.time - cl.mtime[1]) > 0.1)
  2985. cl.time += 0.5 * (cl.mtime[1] - cl.time); // fast
  2986. else
  2987. cl.time = bound(cl.time - 0.002 * cl.movevars_timescale, cl.mtime[1], cl.time + 0.001 * cl.movevars_timescale);
  2988. }
  2989. else if (cl_nettimesyncboundmode.integer == 6)
  2990. {
  2991. cl.time = bound(cl.mtime[1], cl.time, cl.mtime[0]);
  2992. cl.time = bound(cl.time - 0.002 * cl.movevars_timescale, cl.mtime[1], cl.time + 0.001 * cl.movevars_timescale);
  2993. }
  2994. }
  2995. // this packet probably contains a player entity update, so we will need
  2996. // to update the prediction
  2997. cl.movement_replay = true;
  2998. // this may get updated later in parsing by svc_clientdata
  2999. cl.onground = false;
  3000. // if true the cl.viewangles are interpolated from cl.mviewangles[]
  3001. // during this frame
  3002. // (makes spectating players much smoother and prevents mouse movement from turning)
  3003. cl.fixangle[1] = cl.fixangle[0];
  3004. cl.fixangle[0] = false;
  3005. if (!cls.demoplayback)
  3006. VectorCopy(cl.mviewangles[0], cl.mviewangles[1]);
  3007. // update the csqc's server timestamps, critical for proper sync
  3008. CSQC_UpdateNetworkTimes(cl.mtime[0], cl.mtime[1]);
  3009. if (cl.mtime[0] > cl.mtime[1])
  3010. World_Physics_Frame(&cl.world, cl.mtime[0] - cl.mtime[1], cl.movevars_gravity);
  3011. // only lerp entities that also get an update in this frame, when lerp excess is used
  3012. if(cl_lerpexcess.value > 0)
  3013. {
  3014. int i;
  3015. for (i = 1;i < cl.num_entities;i++)
  3016. {
  3017. if (cl.entities_active[i])
  3018. {
  3019. entity_t *ent = cl.entities + i;
  3020. ent->persistent.lerpdeltatime = 0;
  3021. }
  3022. }
  3023. }
  3024. }
  3025. #define SHOWNET(x) if(cl_shownet.integer==2)Con_Printf("%3i:%s(%i)\n", cl_message.readcount-1, x, cmd);
  3026. /*
  3027. =====================
  3028. CL_ParseServerMessage
  3029. =====================
  3030. */
  3031. int parsingerror = false;
  3032. void CL_ParseServerMessage(void)
  3033. {
  3034. int cmd;
  3035. int i;
  3036. protocolversion_t protocol;
  3037. unsigned char cmdlog[32];
  3038. const char *cmdlogname[32], *temp;
  3039. int cmdindex, cmdcount = 0;
  3040. qboolean qwplayerupdatereceived;
  3041. qboolean strip_pqc;
  3042. char vabuf[1024];
  3043. // LordHavoc: moved demo message writing from before the packet parse to
  3044. // after the packet parse so that CL_Stop_f can be called by cl_autodemo
  3045. // code in CL_ParseServerinfo
  3046. //if (cls.demorecording)
  3047. // CL_WriteDemoMessage (&cl_message);
  3048. cl.last_received_message = realtime;
  3049. CL_KeepaliveMessage(false);
  3050. //
  3051. // if recording demos, copy the message out
  3052. //
  3053. if (cl_shownet.integer == 1)
  3054. Con_Printf("%f %i\n", realtime, cl_message.cursize);
  3055. else if (cl_shownet.integer == 2)
  3056. Con_Print("------------------\n");
  3057. //
  3058. // parse the message
  3059. //
  3060. //MSG_BeginReading ();
  3061. parsingerror = true;
  3062. if (cls.protocol == PROTOCOL_QUAKEWORLD)
  3063. {
  3064. CL_NetworkTimeReceived(realtime); // qw has no clock
  3065. // kill all qw nails
  3066. cl.qw_num_nails = 0;
  3067. // fade weapon view kick
  3068. cl.qw_weaponkick = min(cl.qw_weaponkick + 10 * bound(0, cl.time - cl.oldtime, 0.1), 0);
  3069. cls.servermovesequence = cls.netcon->qw.incoming_sequence;
  3070. qwplayerupdatereceived = false;
  3071. while (1)
  3072. {
  3073. if (cl_message.badread)
  3074. Host_Error ("CL_ParseServerMessage: Bad QW server message");
  3075. cmd = MSG_ReadByte(&cl_message);
  3076. if (cmd == -1)
  3077. {
  3078. SHOWNET("END OF MESSAGE");
  3079. break; // end of message
  3080. }
  3081. cmdindex = cmdcount & 31;
  3082. cmdcount++;
  3083. cmdlog[cmdindex] = cmd;
  3084. SHOWNET(qw_svc_strings[cmd]);
  3085. cmdlogname[cmdindex] = qw_svc_strings[cmd];
  3086. if (!cmdlogname[cmdindex])
  3087. {
  3088. // LordHavoc: fix for bizarre problem in MSVC that I do not understand (if I assign the string pointer directly it ends up storing a NULL pointer)
  3089. const char *d = "<unknown>";
  3090. cmdlogname[cmdindex] = d;
  3091. }
  3092. // other commands
  3093. switch (cmd)
  3094. {
  3095. default:
  3096. {
  3097. char description[32*64], logtemp[64];
  3098. int count;
  3099. strlcpy(description, "packet dump: ", sizeof(description));
  3100. i = cmdcount - 32;
  3101. if (i < 0)
  3102. i = 0;
  3103. count = cmdcount - i;
  3104. i &= 31;
  3105. while(count > 0)
  3106. {
  3107. dpsnprintf(logtemp, sizeof(logtemp), "%3i:%s ", cmdlog[i], cmdlogname[i]);
  3108. strlcat(description, logtemp, sizeof(description));
  3109. count--;
  3110. i++;
  3111. i &= 31;
  3112. }
  3113. description[strlen(description)-1] = '\n'; // replace the last space with a newline
  3114. Con_Print(description);
  3115. Host_Error("CL_ParseServerMessage: Illegible server message");
  3116. }
  3117. break;
  3118. case qw_svc_nop:
  3119. //Con_Printf("qw_svc_nop\n");
  3120. break;
  3121. case qw_svc_disconnect:
  3122. Con_Printf("Server disconnected\n");
  3123. if (cls.demonum != -1)
  3124. CL_NextDemo();
  3125. else
  3126. CL_Disconnect();
  3127. return;
  3128. case qw_svc_print:
  3129. i = MSG_ReadByte(&cl_message);
  3130. temp = MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring));
  3131. if (CL_ExaminePrintString(temp)) // look for anything interesting like player IP addresses or ping reports
  3132. {
  3133. if (i == 3) // chat
  3134. CSQC_AddPrintText(va(vabuf, sizeof(vabuf), "\1%s", temp)); //[515]: csqc
  3135. else
  3136. CSQC_AddPrintText(temp);
  3137. }
  3138. break;
  3139. case qw_svc_centerprint:
  3140. CL_VM_Parse_CenterPrint(MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring))); //[515]: csqc
  3141. break;
  3142. case qw_svc_stufftext:
  3143. CL_VM_Parse_StuffCmd(MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring))); //[515]: csqc
  3144. break;
  3145. case qw_svc_damage:
  3146. // svc_damage protocol is identical to nq
  3147. V_ParseDamage ();
  3148. break;
  3149. case qw_svc_serverdata:
  3150. //Cbuf_Execute(); // make sure any stuffed commands are done
  3151. CL_ParseServerInfo();
  3152. break;
  3153. case qw_svc_setangle:
  3154. for (i=0 ; i<3 ; i++)
  3155. cl.viewangles[i] = MSG_ReadAngle(&cl_message, cls.protocol);
  3156. if (!cls.demoplayback)
  3157. {
  3158. cl.fixangle[0] = true;
  3159. VectorCopy(cl.viewangles, cl.mviewangles[0]);
  3160. // disable interpolation if this is new
  3161. if (!cl.fixangle[1])
  3162. VectorCopy(cl.viewangles, cl.mviewangles[1]);
  3163. }
  3164. break;
  3165. case qw_svc_lightstyle:
  3166. i = MSG_ReadByte(&cl_message);
  3167. if (i >= cl.max_lightstyle)
  3168. {
  3169. Con_Printf ("svc_lightstyle >= MAX_LIGHTSTYLES");
  3170. break;
  3171. }
  3172. strlcpy (cl.lightstyle[i].map, MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)), sizeof (cl.lightstyle[i].map));
  3173. cl.lightstyle[i].map[MAX_STYLESTRING - 1] = 0;
  3174. cl.lightstyle[i].length = (int)strlen(cl.lightstyle[i].map);
  3175. break;
  3176. case qw_svc_sound:
  3177. CL_ParseStartSoundPacket(false);
  3178. break;
  3179. case qw_svc_stopsound:
  3180. i = (unsigned short) MSG_ReadShort(&cl_message);
  3181. S_StopSound(i>>3, i&7);
  3182. break;
  3183. case qw_svc_updatefrags:
  3184. i = MSG_ReadByte(&cl_message);
  3185. if (i >= cl.maxclients)
  3186. Host_Error("CL_ParseServerMessage: svc_updatefrags >= cl.maxclients");
  3187. cl.scores[i].frags = (signed short) MSG_ReadShort(&cl_message);
  3188. break;
  3189. case qw_svc_updateping:
  3190. i = MSG_ReadByte(&cl_message);
  3191. if (i >= cl.maxclients)
  3192. Host_Error("CL_ParseServerMessage: svc_updateping >= cl.maxclients");
  3193. cl.scores[i].qw_ping = MSG_ReadShort(&cl_message);
  3194. break;
  3195. case qw_svc_updatepl:
  3196. i = MSG_ReadByte(&cl_message);
  3197. if (i >= cl.maxclients)
  3198. Host_Error("CL_ParseServerMessage: svc_updatepl >= cl.maxclients");
  3199. cl.scores[i].qw_packetloss = MSG_ReadByte(&cl_message);
  3200. break;
  3201. case qw_svc_updateentertime:
  3202. i = MSG_ReadByte(&cl_message);
  3203. if (i >= cl.maxclients)
  3204. Host_Error("CL_ParseServerMessage: svc_updateentertime >= cl.maxclients");
  3205. // seconds ago
  3206. cl.scores[i].qw_entertime = cl.time - MSG_ReadFloat(&cl_message);
  3207. break;
  3208. case qw_svc_spawnbaseline:
  3209. i = (unsigned short) MSG_ReadShort(&cl_message);
  3210. if (i < 0 || i >= MAX_EDICTS)
  3211. Host_Error ("CL_ParseServerMessage: svc_spawnbaseline: invalid entity number %i", i);
  3212. if (i >= cl.max_entities)
  3213. CL_ExpandEntities(i);
  3214. CL_ParseBaseline(cl.entities + i, false);
  3215. break;
  3216. case qw_svc_spawnstatic:
  3217. CL_ParseStatic(false);
  3218. break;
  3219. case qw_svc_temp_entity:
  3220. if(!CL_VM_Parse_TempEntity())
  3221. CL_ParseTempEntity ();
  3222. break;
  3223. case qw_svc_killedmonster:
  3224. cl.stats[STAT_MONSTERS]++;
  3225. break;
  3226. case qw_svc_foundsecret:
  3227. cl.stats[STAT_SECRETS]++;
  3228. break;
  3229. case qw_svc_updatestat:
  3230. i = MSG_ReadByte(&cl_message);
  3231. if (i < 0 || i >= MAX_CL_STATS)
  3232. Host_Error ("svc_updatestat: %i is invalid", i);
  3233. cl.stats[i] = MSG_ReadByte(&cl_message);
  3234. break;
  3235. case qw_svc_updatestatlong:
  3236. i = MSG_ReadByte(&cl_message);
  3237. if (i < 0 || i >= MAX_CL_STATS)
  3238. Host_Error ("svc_updatestatlong: %i is invalid", i);
  3239. cl.stats[i] = MSG_ReadLong(&cl_message);
  3240. break;
  3241. case qw_svc_spawnstaticsound:
  3242. CL_ParseStaticSound (false);
  3243. break;
  3244. case qw_svc_cdtrack:
  3245. cl.cdtrack = cl.looptrack = MSG_ReadByte(&cl_message);
  3246. #ifdef CONFIG_CD
  3247. if ( (cls.demoplayback || cls.demorecording) && (cls.forcetrack != -1) )
  3248. CDAudio_Play ((unsigned char)cls.forcetrack, true);
  3249. else
  3250. CDAudio_Play ((unsigned char)cl.cdtrack, true);
  3251. #endif
  3252. break;
  3253. case qw_svc_intermission:
  3254. if(!cl.intermission)
  3255. cl.completed_time = cl.time;
  3256. cl.intermission = 1;
  3257. MSG_ReadVector(&cl_message, cl.qw_intermission_origin, cls.protocol);
  3258. for (i = 0;i < 3;i++)
  3259. cl.qw_intermission_angles[i] = MSG_ReadAngle(&cl_message, cls.protocol);
  3260. break;
  3261. case qw_svc_finale:
  3262. if(!cl.intermission)
  3263. cl.completed_time = cl.time;
  3264. cl.intermission = 2;
  3265. SCR_CenterPrint(MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)));
  3266. break;
  3267. case qw_svc_sellscreen:
  3268. Cmd_ExecuteString ("help", src_command, true);
  3269. break;
  3270. case qw_svc_smallkick:
  3271. cl.qw_weaponkick = -2;
  3272. break;
  3273. case qw_svc_bigkick:
  3274. cl.qw_weaponkick = -4;
  3275. break;
  3276. case qw_svc_muzzleflash:
  3277. i = (unsigned short) MSG_ReadShort(&cl_message);
  3278. // NOTE: in QW this only worked on clients
  3279. if (i < 0 || i >= MAX_EDICTS)
  3280. Host_Error("CL_ParseServerMessage: svc_spawnbaseline: invalid entity number %i", i);
  3281. if (i >= cl.max_entities)
  3282. CL_ExpandEntities(i);
  3283. cl.entities[i].persistent.muzzleflash = 1.0f;
  3284. break;
  3285. case qw_svc_updateuserinfo:
  3286. QW_CL_UpdateUserInfo();
  3287. break;
  3288. case qw_svc_setinfo:
  3289. QW_CL_SetInfo();
  3290. break;
  3291. case qw_svc_serverinfo:
  3292. QW_CL_ServerInfo();
  3293. break;
  3294. case qw_svc_download:
  3295. QW_CL_ParseDownload();
  3296. break;
  3297. case qw_svc_playerinfo:
  3298. // slightly kill qw player entities now that we know there is
  3299. // an update of player entities this frame...
  3300. if (!qwplayerupdatereceived)
  3301. {
  3302. qwplayerupdatereceived = true;
  3303. for (i = 1;i < cl.maxclients;i++)
  3304. cl.entities_active[i] = false;
  3305. }
  3306. EntityStateQW_ReadPlayerUpdate();
  3307. break;
  3308. case qw_svc_nails:
  3309. QW_CL_ParseNails();
  3310. break;
  3311. case qw_svc_chokecount:
  3312. (void) MSG_ReadByte(&cl_message);
  3313. // FIXME: apply to netgraph
  3314. //for (j = 0;j < i;j++)
  3315. // cl.frames[(cls.netcon->qw.incoming_acknowledged-1-j)&QW_UPDATE_MASK].receivedtime = -2;
  3316. break;
  3317. case qw_svc_modellist:
  3318. QW_CL_ParseModelList();
  3319. break;
  3320. case qw_svc_soundlist:
  3321. QW_CL_ParseSoundList();
  3322. break;
  3323. case qw_svc_packetentities:
  3324. EntityFrameQW_CL_ReadFrame(false);
  3325. // first update is the final signon stage
  3326. if (cls.signon == SIGNONS - 1)
  3327. {
  3328. cls.signon = SIGNONS;
  3329. CL_SignonReply ();
  3330. }
  3331. break;
  3332. case qw_svc_deltapacketentities:
  3333. EntityFrameQW_CL_ReadFrame(true);
  3334. // first update is the final signon stage
  3335. if (cls.signon == SIGNONS - 1)
  3336. {
  3337. cls.signon = SIGNONS;
  3338. CL_SignonReply ();
  3339. }
  3340. break;
  3341. case qw_svc_maxspeed:
  3342. cl.movevars_maxspeed = MSG_ReadFloat(&cl_message);
  3343. break;
  3344. case qw_svc_entgravity:
  3345. cl.movevars_entgravity = MSG_ReadFloat(&cl_message);
  3346. if (!cl.movevars_entgravity)
  3347. cl.movevars_entgravity = 1.0f;
  3348. break;
  3349. case qw_svc_setpause:
  3350. cl.paused = MSG_ReadByte(&cl_message) != 0;
  3351. #ifdef CONFIG_CD
  3352. if (cl.paused)
  3353. CDAudio_Pause ();
  3354. else
  3355. CDAudio_Resume ();
  3356. #endif
  3357. S_PauseGameSounds (cl.paused);
  3358. break;
  3359. }
  3360. }
  3361. if (qwplayerupdatereceived)
  3362. {
  3363. // fully kill any player entities that were not updated this frame
  3364. for (i = 1;i <= cl.maxclients;i++)
  3365. if (!cl.entities_active[i])
  3366. cl.entities[i].state_current.active = false;
  3367. }
  3368. }
  3369. else
  3370. {
  3371. while (1)
  3372. {
  3373. if (cl_message.badread)
  3374. Host_Error ("CL_ParseServerMessage: Bad server message");
  3375. cmd = MSG_ReadByte(&cl_message);
  3376. if (cmd == -1)
  3377. {
  3378. // R_TimeReport("END OF MESSAGE");
  3379. SHOWNET("END OF MESSAGE");
  3380. break; // end of message
  3381. }
  3382. cmdindex = cmdcount & 31;
  3383. cmdcount++;
  3384. cmdlog[cmdindex] = cmd;
  3385. // if the high bit of the command byte is set, it is a fast update
  3386. if (cmd & 128)
  3387. {
  3388. // LordHavoc: fix for bizarre problem in MSVC that I do not understand (if I assign the string pointer directly it ends up storing a NULL pointer)
  3389. temp = "entity";
  3390. cmdlogname[cmdindex] = temp;
  3391. SHOWNET("fast update");
  3392. if (cls.signon == SIGNONS - 1)
  3393. {
  3394. // first update is the final signon stage
  3395. cls.signon = SIGNONS;
  3396. CL_SignonReply ();
  3397. }
  3398. EntityFrameQuake_ReadEntity (cmd&127);
  3399. continue;
  3400. }
  3401. SHOWNET(svc_strings[cmd]);
  3402. cmdlogname[cmdindex] = svc_strings[cmd];
  3403. if (!cmdlogname[cmdindex])
  3404. {
  3405. // LordHavoc: fix for bizarre problem in MSVC that I do not understand (if I assign the string pointer directly it ends up storing a NULL pointer)
  3406. const char *d = "<unknown>";
  3407. cmdlogname[cmdindex] = d;
  3408. }
  3409. // other commands
  3410. switch (cmd)
  3411. {
  3412. default:
  3413. {
  3414. char description[32*64], tempdesc[64];
  3415. int count;
  3416. strlcpy (description, "packet dump: ", sizeof(description));
  3417. i = cmdcount - 32;
  3418. if (i < 0)
  3419. i = 0;
  3420. count = cmdcount - i;
  3421. i &= 31;
  3422. while(count > 0)
  3423. {
  3424. dpsnprintf (tempdesc, sizeof (tempdesc), "%3i:%s ", cmdlog[i], cmdlogname[i]);
  3425. strlcat (description, tempdesc, sizeof (description));
  3426. count--;
  3427. i++;
  3428. i &= 31;
  3429. }
  3430. description[strlen(description)-1] = '\n'; // replace the last space with a newline
  3431. Con_Print(description);
  3432. Host_Error ("CL_ParseServerMessage: Illegible server message");
  3433. }
  3434. break;
  3435. case svc_nop:
  3436. if (cls.signon < SIGNONS)
  3437. Con_Print("<-- server to client keepalive\n");
  3438. break;
  3439. case svc_time:
  3440. CL_NetworkTimeReceived(MSG_ReadFloat(&cl_message));
  3441. break;
  3442. case svc_clientdata:
  3443. CL_ParseClientdata();
  3444. break;
  3445. case svc_version:
  3446. i = MSG_ReadLong(&cl_message);
  3447. protocol = Protocol_EnumForNumber(i);
  3448. if (protocol == PROTOCOL_UNKNOWN)
  3449. Host_Error("CL_ParseServerMessage: Server is unrecognized protocol number (%i)", i);
  3450. // hack for unmarked Nehahra movie demos which had a custom protocol
  3451. if (protocol == PROTOCOL_QUAKEDP && cls.demoplayback && gamemode == GAME_NEHAHRA)
  3452. protocol = PROTOCOL_NEHAHRAMOVIE;
  3453. cls.protocol = protocol;
  3454. break;
  3455. case svc_disconnect:
  3456. Con_Printf ("Server disconnected\n");
  3457. if (cls.demonum != -1)
  3458. CL_NextDemo ();
  3459. else
  3460. CL_Disconnect ();
  3461. break;
  3462. case svc_print:
  3463. temp = MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring));
  3464. if (CL_ExaminePrintString(temp)) // look for anything interesting like player IP addresses or ping reports
  3465. CSQC_AddPrintText(temp); //[515]: csqc
  3466. break;
  3467. case svc_centerprint:
  3468. CL_VM_Parse_CenterPrint(MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring))); //[515]: csqc
  3469. break;
  3470. case svc_stufftext:
  3471. temp = MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring));
  3472. /* if(utf8_enable.integer)
  3473. {
  3474. strip_pqc = true;
  3475. // we can safely strip and even
  3476. // interpret these in utf8 mode
  3477. }
  3478. else */ switch(cls.protocol)
  3479. {
  3480. case PROTOCOL_QUAKE:
  3481. case PROTOCOL_QUAKEDP:
  3482. // maybe add other protocols if
  3483. // so desired, but not DP7
  3484. strip_pqc = true;
  3485. break;
  3486. case PROTOCOL_DARKPLACES7:
  3487. default:
  3488. // ProQuake does not support
  3489. // these protocols
  3490. strip_pqc = false;
  3491. break;
  3492. }
  3493. if(strip_pqc)
  3494. {
  3495. // skip over ProQuake messages,
  3496. // TODO actually interpret them
  3497. // (they are sbar team score
  3498. // updates), see proquake cl_parse.c
  3499. if(*temp == 0x01)
  3500. {
  3501. ++temp;
  3502. while(*temp >= 0x01 && *temp <= 0x1F)
  3503. ++temp;
  3504. }
  3505. }
  3506. CL_VM_Parse_StuffCmd(temp); //[515]: csqc
  3507. break;
  3508. case svc_damage:
  3509. V_ParseDamage ();
  3510. break;
  3511. case svc_serverinfo:
  3512. CL_ParseServerInfo ();
  3513. break;
  3514. case svc_setangle:
  3515. for (i=0 ; i<3 ; i++)
  3516. cl.viewangles[i] = MSG_ReadAngle(&cl_message, cls.protocol);
  3517. if (!cls.demoplayback)
  3518. {
  3519. cl.fixangle[0] = true;
  3520. VectorCopy(cl.viewangles, cl.mviewangles[0]);
  3521. // disable interpolation if this is new
  3522. if (!cl.fixangle[1])
  3523. VectorCopy(cl.viewangles, cl.mviewangles[1]);
  3524. }
  3525. break;
  3526. case svc_setview:
  3527. cl.viewentity = (unsigned short)MSG_ReadShort(&cl_message);
  3528. if (cl.viewentity >= MAX_EDICTS)
  3529. Host_Error("svc_setview >= MAX_EDICTS");
  3530. if (cl.viewentity >= cl.max_entities)
  3531. CL_ExpandEntities(cl.viewentity);
  3532. // LordHavoc: assume first setview recieved is the real player entity
  3533. if (!cl.realplayerentity)
  3534. cl.realplayerentity = cl.viewentity;
  3535. // update cl.playerentity to this one if it is a valid player
  3536. if (cl.viewentity >= 1 && cl.viewentity <= cl.maxclients)
  3537. cl.playerentity = cl.viewentity;
  3538. break;
  3539. case svc_lightstyle:
  3540. i = MSG_ReadByte(&cl_message);
  3541. if (i >= cl.max_lightstyle)
  3542. {
  3543. Con_Printf ("svc_lightstyle >= MAX_LIGHTSTYLES");
  3544. break;
  3545. }
  3546. strlcpy (cl.lightstyle[i].map, MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)), sizeof (cl.lightstyle[i].map));
  3547. cl.lightstyle[i].map[MAX_STYLESTRING - 1] = 0;
  3548. cl.lightstyle[i].length = (int)strlen(cl.lightstyle[i].map);
  3549. break;
  3550. case svc_sound:
  3551. CL_ParseStartSoundPacket(false);
  3552. break;
  3553. case svc_precache:
  3554. if (cls.protocol == PROTOCOL_DARKPLACES1 || cls.protocol == PROTOCOL_DARKPLACES2 || cls.protocol == PROTOCOL_DARKPLACES3)
  3555. {
  3556. // was svc_sound2 in protocols 1, 2, 3, removed in 4, 5, changed to svc_precache in 6
  3557. CL_ParseStartSoundPacket(true);
  3558. }
  3559. else
  3560. {
  3561. char *s;
  3562. i = (unsigned short)MSG_ReadShort(&cl_message);
  3563. s = MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring));
  3564. if (i < 32768)
  3565. {
  3566. if (i >= 1 && i < MAX_MODELS)
  3567. {
  3568. dp_model_t *model = Mod_ForName(s, false, false, s[0] == '*' ? cl.model_name[1] : NULL);
  3569. if (!model)
  3570. Con_DPrintf("svc_precache: Mod_ForName(\"%s\") failed\n", s);
  3571. cl.model_precache[i] = model;
  3572. }
  3573. else
  3574. Con_Printf("svc_precache: index %i outside range %i...%i\n", i, 1, MAX_MODELS);
  3575. }
  3576. else
  3577. {
  3578. i -= 32768;
  3579. if (i >= 1 && i < MAX_SOUNDS)
  3580. {
  3581. sfx_t *sfx = S_PrecacheSound (s, true, true);
  3582. if (!sfx && snd_initialized.integer)
  3583. Con_DPrintf("svc_precache: S_PrecacheSound(\"%s\") failed\n", s);
  3584. cl.sound_precache[i] = sfx;
  3585. }
  3586. else
  3587. Con_Printf("svc_precache: index %i outside range %i...%i\n", i, 1, MAX_SOUNDS);
  3588. }
  3589. }
  3590. break;
  3591. case svc_stopsound:
  3592. i = (unsigned short) MSG_ReadShort(&cl_message);
  3593. S_StopSound(i>>3, i&7);
  3594. break;
  3595. case svc_updatename:
  3596. i = MSG_ReadByte(&cl_message);
  3597. if (i >= cl.maxclients)
  3598. Host_Error ("CL_ParseServerMessage: svc_updatename >= cl.maxclients");
  3599. strlcpy (cl.scores[i].name, MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)), sizeof (cl.scores[i].name));
  3600. break;
  3601. case svc_updatefrags:
  3602. i = MSG_ReadByte(&cl_message);
  3603. if (i >= cl.maxclients)
  3604. Host_Error ("CL_ParseServerMessage: svc_updatefrags >= cl.maxclients");
  3605. cl.scores[i].frags = (signed short) MSG_ReadShort(&cl_message);
  3606. break;
  3607. case svc_updatecolors:
  3608. i = MSG_ReadByte(&cl_message);
  3609. if (i >= cl.maxclients)
  3610. Host_Error ("CL_ParseServerMessage: svc_updatecolors >= cl.maxclients");
  3611. cl.scores[i].colors = MSG_ReadByte(&cl_message);
  3612. break;
  3613. case svc_particle:
  3614. CL_ParseParticleEffect ();
  3615. break;
  3616. case svc_effect:
  3617. CL_ParseEffect ();
  3618. break;
  3619. case svc_effect2:
  3620. CL_ParseEffect2 ();
  3621. break;
  3622. case svc_spawnbaseline:
  3623. i = (unsigned short) MSG_ReadShort(&cl_message);
  3624. if (i < 0 || i >= MAX_EDICTS)
  3625. Host_Error ("CL_ParseServerMessage: svc_spawnbaseline: invalid entity number %i", i);
  3626. if (i >= cl.max_entities)
  3627. CL_ExpandEntities(i);
  3628. CL_ParseBaseline (cl.entities + i, false);
  3629. break;
  3630. case svc_spawnbaseline2:
  3631. i = (unsigned short) MSG_ReadShort(&cl_message);
  3632. if (i < 0 || i >= MAX_EDICTS)
  3633. Host_Error ("CL_ParseServerMessage: svc_spawnbaseline2: invalid entity number %i", i);
  3634. if (i >= cl.max_entities)
  3635. CL_ExpandEntities(i);
  3636. CL_ParseBaseline (cl.entities + i, true);
  3637. break;
  3638. case svc_spawnstatic:
  3639. CL_ParseStatic (false);
  3640. break;
  3641. case svc_spawnstatic2:
  3642. CL_ParseStatic (true);
  3643. break;
  3644. case svc_temp_entity:
  3645. if(!CL_VM_Parse_TempEntity())
  3646. CL_ParseTempEntity ();
  3647. break;
  3648. case svc_setpause:
  3649. cl.paused = MSG_ReadByte(&cl_message) != 0;
  3650. #ifdef CONFIG_CD
  3651. if (cl.paused)
  3652. CDAudio_Pause ();
  3653. else
  3654. CDAudio_Resume ();
  3655. #endif
  3656. S_PauseGameSounds (cl.paused);
  3657. break;
  3658. case svc_signonnum:
  3659. i = MSG_ReadByte(&cl_message);
  3660. // LordHavoc: it's rude to kick off the client if they missed the
  3661. // reconnect somehow, so allow signon 1 even if at signon 1
  3662. if (i <= cls.signon && i != 1)
  3663. Host_Error ("Received signon %i when at %i", i, cls.signon);
  3664. cls.signon = i;
  3665. CL_SignonReply ();
  3666. break;
  3667. case svc_killedmonster:
  3668. cl.stats[STAT_MONSTERS]++;
  3669. break;
  3670. case svc_foundsecret:
  3671. cl.stats[STAT_SECRETS]++;
  3672. break;
  3673. case svc_updatestat:
  3674. i = MSG_ReadByte(&cl_message);
  3675. if (i < 0 || i >= MAX_CL_STATS)
  3676. Host_Error ("svc_updatestat: %i is invalid", i);
  3677. cl.stats[i] = MSG_ReadLong(&cl_message);
  3678. break;
  3679. case svc_updatestatubyte:
  3680. i = MSG_ReadByte(&cl_message);
  3681. if (i < 0 || i >= MAX_CL_STATS)
  3682. Host_Error ("svc_updatestat: %i is invalid", i);
  3683. cl.stats[i] = MSG_ReadByte(&cl_message);
  3684. break;
  3685. case svc_spawnstaticsound:
  3686. CL_ParseStaticSound (false);
  3687. break;
  3688. case svc_spawnstaticsound2:
  3689. CL_ParseStaticSound (true);
  3690. break;
  3691. case svc_cdtrack:
  3692. cl.cdtrack = MSG_ReadByte(&cl_message);
  3693. cl.looptrack = MSG_ReadByte(&cl_message);
  3694. #ifdef CONFIG_CD
  3695. if ( (cls.demoplayback || cls.demorecording) && (cls.forcetrack != -1) )
  3696. CDAudio_Play ((unsigned char)cls.forcetrack, true);
  3697. else
  3698. CDAudio_Play ((unsigned char)cl.cdtrack, true);
  3699. #endif
  3700. break;
  3701. case svc_intermission:
  3702. if(!cl.intermission)
  3703. cl.completed_time = cl.time;
  3704. cl.intermission = 1;
  3705. CL_VM_UpdateIntermissionState(cl.intermission);
  3706. break;
  3707. case svc_finale:
  3708. if(!cl.intermission)
  3709. cl.completed_time = cl.time;
  3710. cl.intermission = 2;
  3711. CL_VM_UpdateIntermissionState(cl.intermission);
  3712. SCR_CenterPrint(MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)));
  3713. break;
  3714. case svc_cutscene:
  3715. if(!cl.intermission)
  3716. cl.completed_time = cl.time;
  3717. cl.intermission = 3;
  3718. CL_VM_UpdateIntermissionState(cl.intermission);
  3719. SCR_CenterPrint(MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)));
  3720. break;
  3721. case svc_sellscreen:
  3722. Cmd_ExecuteString ("help", src_command, true);
  3723. break;
  3724. case svc_hidelmp:
  3725. if (gamemode == GAME_TENEBRAE)
  3726. {
  3727. // repeating particle effect
  3728. MSG_ReadCoord(&cl_message, cls.protocol);
  3729. MSG_ReadCoord(&cl_message, cls.protocol);
  3730. MSG_ReadCoord(&cl_message, cls.protocol);
  3731. MSG_ReadCoord(&cl_message, cls.protocol);
  3732. MSG_ReadCoord(&cl_message, cls.protocol);
  3733. MSG_ReadCoord(&cl_message, cls.protocol);
  3734. (void) MSG_ReadByte(&cl_message);
  3735. MSG_ReadLong(&cl_message);
  3736. MSG_ReadLong(&cl_message);
  3737. MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring));
  3738. }
  3739. else
  3740. SHOWLMP_decodehide();
  3741. break;
  3742. case svc_showlmp:
  3743. if (gamemode == GAME_TENEBRAE)
  3744. {
  3745. // particle effect
  3746. MSG_ReadCoord(&cl_message, cls.protocol);
  3747. MSG_ReadCoord(&cl_message, cls.protocol);
  3748. MSG_ReadCoord(&cl_message, cls.protocol);
  3749. (void) MSG_ReadByte(&cl_message);
  3750. MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring));
  3751. }
  3752. else
  3753. SHOWLMP_decodeshow();
  3754. break;
  3755. case svc_skybox:
  3756. R_SetSkyBox(MSG_ReadString(&cl_message, cl_readstring, sizeof(cl_readstring)));
  3757. break;
  3758. case svc_entities:
  3759. if (cls.signon == SIGNONS - 1)
  3760. {
  3761. // first update is the final signon stage
  3762. cls.signon = SIGNONS;
  3763. CL_SignonReply ();
  3764. }
  3765. if (cls.protocol == PROTOCOL_DARKPLACES1 || cls.protocol == PROTOCOL_DARKPLACES2 || cls.protocol == PROTOCOL_DARKPLACES3)
  3766. EntityFrame_CL_ReadFrame();
  3767. else if (cls.protocol == PROTOCOL_DARKPLACES4)
  3768. EntityFrame4_CL_ReadFrame();
  3769. else
  3770. EntityFrame5_CL_ReadFrame();
  3771. break;
  3772. case svc_csqcentities:
  3773. CSQC_ReadEntities();
  3774. break;
  3775. case svc_downloaddata:
  3776. CL_ParseDownload();
  3777. break;
  3778. case svc_trailparticles:
  3779. CL_ParseTrailParticles();
  3780. break;
  3781. case svc_pointparticles:
  3782. CL_ParsePointParticles();
  3783. break;
  3784. case svc_pointparticles1:
  3785. CL_ParsePointParticles1();
  3786. break;
  3787. }
  3788. // R_TimeReport(svc_strings[cmd]);
  3789. }
  3790. }
  3791. if (cls.signon == SIGNONS)
  3792. CL_UpdateItemsAndWeapon();
  3793. // R_TimeReport("UpdateItems");
  3794. EntityFrameQuake_ISeeDeadEntities();
  3795. // R_TimeReport("ISeeDeadEntities");
  3796. CL_UpdateMoveVars();
  3797. // R_TimeReport("UpdateMoveVars");
  3798. parsingerror = false;
  3799. // LordHavoc: this was at the start of the function before cl_autodemo was
  3800. // implemented
  3801. if (cls.demorecording)
  3802. {
  3803. CL_WriteDemoMessage (&cl_message);
  3804. // R_TimeReport("WriteDemo");
  3805. }
  3806. }
  3807. void CL_Parse_DumpPacket(void)
  3808. {
  3809. if (!parsingerror)
  3810. return;
  3811. Con_Print("Packet dump:\n");
  3812. SZ_HexDumpToConsole(&cl_message);
  3813. parsingerror = false;
  3814. }
  3815. void CL_Parse_ErrorCleanUp(void)
  3816. {
  3817. CL_StopDownload(0, 0);
  3818. QW_CL_StopUpload();
  3819. }
  3820. void CL_Parse_Init(void)
  3821. {
  3822. Cvar_RegisterVariable(&cl_worldmessage);
  3823. Cvar_RegisterVariable(&cl_worldname);
  3824. Cvar_RegisterVariable(&cl_worldnamenoextension);
  3825. Cvar_RegisterVariable(&cl_worldbasename);
  3826. Cvar_RegisterVariable(&developer_networkentities);
  3827. Cvar_RegisterVariable(&cl_gameplayfix_soundsmovewithentities);
  3828. Cvar_RegisterVariable(&cl_sound_wizardhit);
  3829. Cvar_RegisterVariable(&cl_sound_hknighthit);
  3830. Cvar_RegisterVariable(&cl_sound_tink1);
  3831. Cvar_RegisterVariable(&cl_sound_ric1);
  3832. Cvar_RegisterVariable(&cl_sound_ric2);
  3833. Cvar_RegisterVariable(&cl_sound_ric3);
  3834. Cvar_RegisterVariable(&cl_sound_ric_gunshot);
  3835. Cvar_RegisterVariable(&cl_sound_r_exp3);
  3836. Cvar_RegisterVariable(&cl_joinbeforedownloadsfinish);
  3837. // server extension cvars set by commands issued from the server during connect
  3838. Cvar_RegisterVariable(&cl_serverextension_download);
  3839. Cvar_RegisterVariable(&cl_nettimesyncfactor);
  3840. Cvar_RegisterVariable(&cl_nettimesyncboundmode);
  3841. Cvar_RegisterVariable(&cl_nettimesyncboundtolerance);
  3842. Cvar_RegisterVariable(&cl_iplog_name);
  3843. Cvar_RegisterVariable(&cl_readpicture_force);
  3844. Cmd_AddCommand("nextul", QW_CL_NextUpload, "sends next fragment of current upload buffer (screenshot for example)");
  3845. Cmd_AddCommand("stopul", QW_CL_StopUpload, "aborts current upload (screenshot for example)");
  3846. Cmd_AddCommand("skins", QW_CL_Skins_f, "downloads missing qw skins from server");
  3847. Cmd_AddCommand("changing", QW_CL_Changing_f, "sent by qw servers to tell client to wait for level change");
  3848. Cmd_AddCommand("cl_begindownloads", CL_BeginDownloads_f, "used internally by darkplaces client while connecting (causes loading of models and sounds or triggers downloads for missing ones)");
  3849. Cmd_AddCommand("cl_downloadbegin", CL_DownloadBegin_f, "(networking) informs client of download file information, client replies with sv_startsoundload to begin the transfer");
  3850. Cmd_AddCommand("stopdownload", CL_StopDownload_f, "terminates a download");
  3851. Cmd_AddCommand("cl_downloadfinished", CL_DownloadFinished_f, "signals that a download has finished and provides the client with file size and crc to check its integrity");
  3852. Cmd_AddCommand("iplog_list", CL_IPLog_List_f, "lists names of players whose IP address begins with the supplied text (example: iplog_list 123.456.789)");
  3853. }
  3854. void CL_Parse_Shutdown(void)
  3855. {
  3856. }