PageRenderTime 57ms CodeModel.GetById 19ms 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

Large files files are truncated, but you can click here to view the full file

  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, c

Large files files are truncated, but you can click here to view the full file