cl_demo.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806
  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 included (GNU.txt) 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. #include "quakedef.h"
  16. void CL_FinishTimeDemo (void);
  17. /*
  18. ==============================================================================
  19. DEMO CODE
  20. When a demo is playing back, all NET_SendMessages are skipped, and
  21. NET_GetMessages are read from the demo file.
  22. Whenever cl.time gets past the last received message, another message is
  23. read from the demo file.
  24. ==============================================================================
  25. */
  26. /*
  27. ==============
  28. CL_StopPlayback
  29. Called when a demo file runs out, or the user starts a game
  30. ==============
  31. */
  32. void CL_StopPlayback (void)
  33. {
  34. if (!cls.demoplayback)
  35. return;
  36. fclose (cls.demofile);
  37. cls.demofile = NULL;
  38. cls.state = ca_disconnected;
  39. cls.demoplayback = 0;
  40. if (cls.timedemo)
  41. CL_FinishTimeDemo ();
  42. }
  43. #define dem_cmd 0
  44. #define dem_read 1
  45. #define dem_set 2
  46. /*
  47. ====================
  48. CL_WriteDemoCmd
  49. Writes the current user cmd
  50. ====================
  51. */
  52. void CL_WriteDemoCmd (usercmd_t *pcmd)
  53. {
  54. int i;
  55. float fl;
  56. byte c;
  57. usercmd_t cmd;
  58. //Con_Printf("write: %ld bytes, %4.4f\n", msg->cursize, realtime);
  59. fl = LittleFloat((float)realtime);
  60. fwrite (&fl, sizeof(fl), 1, cls.demofile);
  61. c = dem_cmd;
  62. fwrite (&c, sizeof(c), 1, cls.demofile);
  63. // correct for byte order, bytes don't matter
  64. cmd = *pcmd;
  65. for (i = 0; i < 3; i++)
  66. cmd.angles[i] = LittleFloat(cmd.angles[i]);
  67. cmd.forwardmove = LittleShort(cmd.forwardmove);
  68. cmd.sidemove = LittleShort(cmd.sidemove);
  69. cmd.upmove = LittleShort(cmd.upmove);
  70. fwrite(&cmd, sizeof(cmd), 1, cls.demofile);
  71. for (i=0 ; i<3 ; i++)
  72. {
  73. fl = LittleFloat (cl.viewangles[i]);
  74. fwrite (&fl, 4, 1, cls.demofile);
  75. }
  76. fflush (cls.demofile);
  77. }
  78. /*
  79. ====================
  80. CL_WriteDemoMessage
  81. Dumps the current net message, prefixed by the length and view angles
  82. ====================
  83. */
  84. void CL_WriteDemoMessage (sizebuf_t *msg)
  85. {
  86. int len;
  87. float fl;
  88. byte c;
  89. //Con_Printf("write: %ld bytes, %4.4f\n", msg->cursize, realtime);
  90. if (!cls.demorecording)
  91. return;
  92. fl = LittleFloat((float)realtime);
  93. fwrite (&fl, sizeof(fl), 1, cls.demofile);
  94. c = dem_read;
  95. fwrite (&c, sizeof(c), 1, cls.demofile);
  96. len = LittleLong (msg->cursize);
  97. fwrite (&len, 4, 1, cls.demofile);
  98. fwrite (msg->data, msg->cursize, 1, cls.demofile);
  99. fflush (cls.demofile);
  100. }
  101. /*
  102. ====================
  103. CL_GetDemoMessage
  104. FIXME...
  105. ====================
  106. */
  107. qboolean CL_GetDemoMessage (void)
  108. {
  109. int r, i, j;
  110. float f;
  111. float demotime;
  112. byte c;
  113. usercmd_t *pcmd;
  114. // read the time from the packet
  115. fread(&demotime, sizeof(demotime), 1, cls.demofile);
  116. demotime = LittleFloat(demotime);
  117. // decide if it is time to grab the next message
  118. if (cls.timedemo) {
  119. if (cls.td_lastframe < 0)
  120. cls.td_lastframe = demotime;
  121. else if (demotime > cls.td_lastframe) {
  122. cls.td_lastframe = demotime;
  123. // rewind back to time
  124. fseek(cls.demofile, ftell(cls.demofile) - sizeof(demotime),
  125. SEEK_SET);
  126. return 0; // allready read this frame's message
  127. }
  128. if (!cls.td_starttime && cls.state == ca_active) {
  129. cls.td_starttime = Sys_DoubleTime();
  130. cls.td_startframe = host_framecount;
  131. }
  132. realtime = demotime; // warp
  133. } else if (!cl.paused && cls.state >= ca_onserver) { // allways grab until fully connected
  134. if (realtime + 1.0 < demotime) {
  135. // too far back
  136. realtime = demotime - 1.0;
  137. // rewind back to time
  138. fseek(cls.demofile, ftell(cls.demofile) - sizeof(demotime),
  139. SEEK_SET);
  140. return 0;
  141. } else if (realtime < demotime) {
  142. // rewind back to time
  143. fseek(cls.demofile, ftell(cls.demofile) - sizeof(demotime),
  144. SEEK_SET);
  145. return 0; // don't need another message yet
  146. }
  147. } else
  148. realtime = demotime; // we're warping
  149. if (cls.state < ca_demostart)
  150. Host_Error ("CL_GetDemoMessage: cls.state != ca_active");
  151. // get the msg type
  152. fread (&c, sizeof(c), 1, cls.demofile);
  153. switch (c) {
  154. case dem_cmd :
  155. // user sent input
  156. i = cls.netchan.outgoing_sequence & UPDATE_MASK;
  157. pcmd = &cl.frames[i].cmd;
  158. r = fread (pcmd, sizeof(*pcmd), 1, cls.demofile);
  159. if (r != 1)
  160. {
  161. CL_StopPlayback ();
  162. return 0;
  163. }
  164. // byte order stuff
  165. for (j = 0; j < 3; j++)
  166. pcmd->angles[j] = LittleFloat(pcmd->angles[j]);
  167. pcmd->forwardmove = LittleShort(pcmd->forwardmove);
  168. pcmd->sidemove = LittleShort(pcmd->sidemove);
  169. pcmd->upmove = LittleShort(pcmd->upmove);
  170. cl.frames[i].senttime = demotime;
  171. cl.frames[i].receivedtime = -1; // we haven't gotten a reply yet
  172. cls.netchan.outgoing_sequence++;
  173. for (i=0 ; i<3 ; i++)
  174. {
  175. r = fread (&f, 4, 1, cls.demofile);
  176. cl.viewangles[i] = LittleFloat (f);
  177. }
  178. break;
  179. case dem_read:
  180. // get the next message
  181. fread (&net_message.cursize, 4, 1, cls.demofile);
  182. net_message.cursize = LittleLong (net_message.cursize);
  183. //Con_Printf("read: %ld bytes\n", net_message.cursize);
  184. if (net_message.cursize > MAX_MSGLEN)
  185. Sys_Error ("Demo message > MAX_MSGLEN");
  186. r = fread (net_message.data, net_message.cursize, 1, cls.demofile);
  187. if (r != 1)
  188. {
  189. CL_StopPlayback ();
  190. return 0;
  191. }
  192. break;
  193. case dem_set :
  194. fread (&i, 4, 1, cls.demofile);
  195. cls.netchan.outgoing_sequence = LittleLong(i);
  196. fread (&i, 4, 1, cls.demofile);
  197. cls.netchan.incoming_sequence = LittleLong(i);
  198. break;
  199. default :
  200. Con_Printf("Corrupted demo.\n");
  201. CL_StopPlayback ();
  202. return 0;
  203. }
  204. return 1;
  205. }
  206. /*
  207. ====================
  208. CL_GetMessage
  209. Handles recording and playback of demos, on top of NET_ code
  210. ====================
  211. */
  212. qboolean CL_GetMessage (void)
  213. {
  214. if (cls.demoplayback)
  215. return CL_GetDemoMessage ();
  216. if (!NET_GetPacket ())
  217. return false;
  218. CL_WriteDemoMessage (&net_message);
  219. return true;
  220. }
  221. /*
  222. ====================
  223. CL_Stop_f
  224. stop recording a demo
  225. ====================
  226. */
  227. void CL_Stop_f (void)
  228. {
  229. if (!cls.demorecording)
  230. {
  231. Con_Printf ("Not recording a demo.\n");
  232. return;
  233. }
  234. // write a disconnect message to the demo file
  235. SZ_Clear (&net_message);
  236. MSG_WriteLong (&net_message, -1); // -1 sequence means out of band
  237. MSG_WriteByte (&net_message, svc_disconnect);
  238. MSG_WriteString (&net_message, "EndOfDemo");
  239. CL_WriteDemoMessage (&net_message);
  240. // finish up
  241. fclose (cls.demofile);
  242. cls.demofile = NULL;
  243. cls.demorecording = false;
  244. Con_Printf ("Completed demo\n");
  245. }
  246. /*
  247. ====================
  248. CL_WriteDemoMessage
  249. Dumps the current net message, prefixed by the length and view angles
  250. ====================
  251. */
  252. void CL_WriteRecordDemoMessage (sizebuf_t *msg, int seq)
  253. {
  254. int len;
  255. int i;
  256. float fl;
  257. byte c;
  258. //Con_Printf("write: %ld bytes, %4.4f\n", msg->cursize, realtime);
  259. if (!cls.demorecording)
  260. return;
  261. fl = LittleFloat((float)realtime);
  262. fwrite (&fl, sizeof(fl), 1, cls.demofile);
  263. c = dem_read;
  264. fwrite (&c, sizeof(c), 1, cls.demofile);
  265. len = LittleLong (msg->cursize + 8);
  266. fwrite (&len, 4, 1, cls.demofile);
  267. i = LittleLong(seq);
  268. fwrite (&i, 4, 1, cls.demofile);
  269. fwrite (&i, 4, 1, cls.demofile);
  270. fwrite (msg->data, msg->cursize, 1, cls.demofile);
  271. fflush (cls.demofile);
  272. }
  273. void CL_WriteSetDemoMessage (void)
  274. {
  275. int len;
  276. float fl;
  277. byte c;
  278. //Con_Printf("write: %ld bytes, %4.4f\n", msg->cursize, realtime);
  279. if (!cls.demorecording)
  280. return;
  281. fl = LittleFloat((float)realtime);
  282. fwrite (&fl, sizeof(fl), 1, cls.demofile);
  283. c = dem_set;
  284. fwrite (&c, sizeof(c), 1, cls.demofile);
  285. len = LittleLong(cls.netchan.outgoing_sequence);
  286. fwrite (&len, 4, 1, cls.demofile);
  287. len = LittleLong(cls.netchan.incoming_sequence);
  288. fwrite (&len, 4, 1, cls.demofile);
  289. fflush (cls.demofile);
  290. }
  291. /*
  292. ====================
  293. CL_Record_f
  294. record <demoname> <server>
  295. ====================
  296. */
  297. void CL_Record_f (void)
  298. {
  299. int c;
  300. char name[MAX_OSPATH];
  301. sizebuf_t buf;
  302. char buf_data[MAX_MSGLEN];
  303. int n, i, j;
  304. char *s;
  305. entity_t *ent;
  306. entity_state_t *es, blankes;
  307. player_info_t *player;
  308. extern char gamedirfile[];
  309. int seq = 1;
  310. c = Cmd_Argc();
  311. if (c != 2)
  312. {
  313. Con_Printf ("record <demoname>\n");
  314. return;
  315. }
  316. if (cls.state != ca_active) {
  317. Con_Printf ("You must be connected to record.\n");
  318. return;
  319. }
  320. if (cls.demorecording)
  321. CL_Stop_f();
  322. sprintf (name, "%s/%s", com_gamedir, Cmd_Argv(1));
  323. //
  324. // open the demo file
  325. //
  326. COM_DefaultExtension (name, ".qwd");
  327. cls.demofile = fopen (name, "wb");
  328. if (!cls.demofile)
  329. {
  330. Con_Printf ("ERROR: couldn't open.\n");
  331. return;
  332. }
  333. Con_Printf ("recording to %s.\n", name);
  334. cls.demorecording = true;
  335. /*-------------------------------------------------*/
  336. // serverdata
  337. // send the info about the new client to all connected clients
  338. memset(&buf, 0, sizeof(buf));
  339. buf.data = buf_data;
  340. buf.maxsize = sizeof(buf_data);
  341. // send the serverdata
  342. MSG_WriteByte (&buf, svc_serverdata);
  343. MSG_WriteLong (&buf, PROTOCOL_VERSION);
  344. MSG_WriteLong (&buf, cl.servercount);
  345. MSG_WriteString (&buf, gamedirfile);
  346. if (cl.spectator)
  347. MSG_WriteByte (&buf, cl.playernum | 128);
  348. else
  349. MSG_WriteByte (&buf, cl.playernum);
  350. // send full levelname
  351. MSG_WriteString (&buf, cl.levelname);
  352. // send the movevars
  353. MSG_WriteFloat(&buf, movevars.gravity);
  354. MSG_WriteFloat(&buf, movevars.stopspeed);
  355. MSG_WriteFloat(&buf, movevars.maxspeed);
  356. MSG_WriteFloat(&buf, movevars.spectatormaxspeed);
  357. MSG_WriteFloat(&buf, movevars.accelerate);
  358. MSG_WriteFloat(&buf, movevars.airaccelerate);
  359. MSG_WriteFloat(&buf, movevars.wateraccelerate);
  360. MSG_WriteFloat(&buf, movevars.friction);
  361. MSG_WriteFloat(&buf, movevars.waterfriction);
  362. MSG_WriteFloat(&buf, movevars.entgravity);
  363. // send music
  364. MSG_WriteByte (&buf, svc_cdtrack);
  365. MSG_WriteByte (&buf, 0); // none in demos
  366. // send server info string
  367. MSG_WriteByte (&buf, svc_stufftext);
  368. MSG_WriteString (&buf, va("fullserverinfo \"%s\"\n", cl.serverinfo) );
  369. // flush packet
  370. CL_WriteRecordDemoMessage (&buf, seq++);
  371. SZ_Clear (&buf);
  372. // soundlist
  373. MSG_WriteByte (&buf, svc_soundlist);
  374. MSG_WriteByte (&buf, 0);
  375. n = 0;
  376. s = cl.sound_name[n+1];
  377. while (*s) {
  378. MSG_WriteString (&buf, s);
  379. if (buf.cursize > MAX_MSGLEN/2) {
  380. MSG_WriteByte (&buf, 0);
  381. MSG_WriteByte (&buf, n);
  382. CL_WriteRecordDemoMessage (&buf, seq++);
  383. SZ_Clear (&buf);
  384. MSG_WriteByte (&buf, svc_soundlist);
  385. MSG_WriteByte (&buf, n + 1);
  386. }
  387. n++;
  388. s = cl.sound_name[n+1];
  389. }
  390. if (buf.cursize) {
  391. MSG_WriteByte (&buf, 0);
  392. MSG_WriteByte (&buf, 0);
  393. CL_WriteRecordDemoMessage (&buf, seq++);
  394. SZ_Clear (&buf);
  395. }
  396. // modellist
  397. MSG_WriteByte (&buf, svc_modellist);
  398. MSG_WriteByte (&buf, 0);
  399. n = 0;
  400. s = cl.model_name[n+1];
  401. while (*s) {
  402. MSG_WriteString (&buf, s);
  403. if (buf.cursize > MAX_MSGLEN/2) {
  404. MSG_WriteByte (&buf, 0);
  405. MSG_WriteByte (&buf, n);
  406. CL_WriteRecordDemoMessage (&buf, seq++);
  407. SZ_Clear (&buf);
  408. MSG_WriteByte (&buf, svc_modellist);
  409. MSG_WriteByte (&buf, n + 1);
  410. }
  411. n++;
  412. s = cl.model_name[n+1];
  413. }
  414. if (buf.cursize) {
  415. MSG_WriteByte (&buf, 0);
  416. MSG_WriteByte (&buf, 0);
  417. CL_WriteRecordDemoMessage (&buf, seq++);
  418. SZ_Clear (&buf);
  419. }
  420. // spawnstatic
  421. for (i = 0; i < cl.num_statics; i++) {
  422. ent = cl_static_entities + i;
  423. MSG_WriteByte (&buf, svc_spawnstatic);
  424. for (j = 1; j < MAX_MODELS; j++)
  425. if (ent->model == cl.model_precache[j])
  426. break;
  427. if (j == MAX_MODELS)
  428. MSG_WriteByte (&buf, 0);
  429. else
  430. MSG_WriteByte (&buf, j);
  431. MSG_WriteByte (&buf, ent->frame);
  432. MSG_WriteByte (&buf, 0);
  433. MSG_WriteByte (&buf, ent->skinnum);
  434. for (j=0 ; j<3 ; j++)
  435. {
  436. MSG_WriteCoord (&buf, ent->origin[j]);
  437. MSG_WriteAngle (&buf, ent->angles[j]);
  438. }
  439. if (buf.cursize > MAX_MSGLEN/2) {
  440. CL_WriteRecordDemoMessage (&buf, seq++);
  441. SZ_Clear (&buf);
  442. }
  443. }
  444. // spawnstaticsound
  445. // static sounds are skipped in demos, life is hard
  446. // baselines
  447. memset(&blankes, 0, sizeof(blankes));
  448. for (i = 0; i < MAX_EDICTS; i++) {
  449. es = cl_baselines + i;
  450. if (memcmp(es, &blankes, sizeof(blankes))) {
  451. MSG_WriteByte (&buf,svc_spawnbaseline);
  452. MSG_WriteShort (&buf, i);
  453. MSG_WriteByte (&buf, es->modelindex);
  454. MSG_WriteByte (&buf, es->frame);
  455. MSG_WriteByte (&buf, es->colormap);
  456. MSG_WriteByte (&buf, es->skinnum);
  457. for (j=0 ; j<3 ; j++)
  458. {
  459. MSG_WriteCoord(&buf, es->origin[j]);
  460. MSG_WriteAngle(&buf, es->angles[j]);
  461. }
  462. if (buf.cursize > MAX_MSGLEN/2) {
  463. CL_WriteRecordDemoMessage (&buf, seq++);
  464. SZ_Clear (&buf);
  465. }
  466. }
  467. }
  468. MSG_WriteByte (&buf, svc_stufftext);
  469. MSG_WriteString (&buf, va("cmd spawn %i 0\n", cl.servercount) );
  470. if (buf.cursize) {
  471. CL_WriteRecordDemoMessage (&buf, seq++);
  472. SZ_Clear (&buf);
  473. }
  474. // send current status of all other players
  475. for (i = 0; i < MAX_CLIENTS; i++) {
  476. player = cl.players + i;
  477. MSG_WriteByte (&buf, svc_updatefrags);
  478. MSG_WriteByte (&buf, i);
  479. MSG_WriteShort (&buf, player->frags);
  480. MSG_WriteByte (&buf, svc_updateping);
  481. MSG_WriteByte (&buf, i);
  482. MSG_WriteShort (&buf, player->ping);
  483. MSG_WriteByte (&buf, svc_updatepl);
  484. MSG_WriteByte (&buf, i);
  485. MSG_WriteByte (&buf, player->pl);
  486. MSG_WriteByte (&buf, svc_updateentertime);
  487. MSG_WriteByte (&buf, i);
  488. MSG_WriteFloat (&buf, player->entertime);
  489. MSG_WriteByte (&buf, svc_updateuserinfo);
  490. MSG_WriteByte (&buf, i);
  491. MSG_WriteLong (&buf, player->userid);
  492. MSG_WriteString (&buf, player->userinfo);
  493. if (buf.cursize > MAX_MSGLEN/2) {
  494. CL_WriteRecordDemoMessage (&buf, seq++);
  495. SZ_Clear (&buf);
  496. }
  497. }
  498. // send all current light styles
  499. for (i=0 ; i<MAX_LIGHTSTYLES ; i++)
  500. {
  501. MSG_WriteByte (&buf, svc_lightstyle);
  502. MSG_WriteByte (&buf, (char)i);
  503. MSG_WriteString (&buf, cl_lightstyle[i].map);
  504. }
  505. for (i = 0; i < MAX_CL_STATS; i++) {
  506. MSG_WriteByte (&buf, svc_updatestatlong);
  507. MSG_WriteByte (&buf, i);
  508. MSG_WriteLong (&buf, cl.stats[i]);
  509. if (buf.cursize > MAX_MSGLEN/2) {
  510. CL_WriteRecordDemoMessage (&buf, seq++);
  511. SZ_Clear (&buf);
  512. }
  513. }
  514. #if 0
  515. MSG_WriteByte (&buf, svc_updatestatlong);
  516. MSG_WriteByte (&buf, STAT_TOTALMONSTERS);
  517. MSG_WriteLong (&buf, cl.stats[STAT_TOTALMONSTERS]);
  518. MSG_WriteByte (&buf, svc_updatestatlong);
  519. MSG_WriteByte (&buf, STAT_SECRETS);
  520. MSG_WriteLong (&buf, cl.stats[STAT_SECRETS]);
  521. MSG_WriteByte (&buf, svc_updatestatlong);
  522. MSG_WriteByte (&buf, STAT_MONSTERS);
  523. MSG_WriteLong (&buf, cl.stats[STAT_MONSTERS]);
  524. #endif
  525. // get the client to check and download skins
  526. // when that is completed, a begin command will be issued
  527. MSG_WriteByte (&buf, svc_stufftext);
  528. MSG_WriteString (&buf, va("skins\n") );
  529. CL_WriteRecordDemoMessage (&buf, seq++);
  530. CL_WriteSetDemoMessage();
  531. // done
  532. }
  533. /*
  534. ====================
  535. CL_ReRecord_f
  536. record <demoname>
  537. ====================
  538. */
  539. void CL_ReRecord_f (void)
  540. {
  541. int c;
  542. char name[MAX_OSPATH];
  543. c = Cmd_Argc();
  544. if (c != 2)
  545. {
  546. Con_Printf ("rerecord <demoname>\n");
  547. return;
  548. }
  549. if (!*cls.servername) {
  550. Con_Printf("No server to reconnect to...\n");
  551. return;
  552. }
  553. if (cls.demorecording)
  554. CL_Stop_f();
  555. sprintf (name, "%s/%s", com_gamedir, Cmd_Argv(1));
  556. //
  557. // open the demo file
  558. //
  559. COM_DefaultExtension (name, ".qwd");
  560. cls.demofile = fopen (name, "wb");
  561. if (!cls.demofile)
  562. {
  563. Con_Printf ("ERROR: couldn't open.\n");
  564. return;
  565. }
  566. Con_Printf ("recording to %s.\n", name);
  567. cls.demorecording = true;
  568. CL_Disconnect();
  569. CL_BeginServerConnect();
  570. }
  571. /*
  572. ====================
  573. CL_PlayDemo_f
  574. play [demoname]
  575. ====================
  576. */
  577. void CL_PlayDemo_f (void)
  578. {
  579. char name[256];
  580. if (Cmd_Argc() != 2)
  581. {
  582. Con_Printf ("play <demoname> : plays a demo\n");
  583. return;
  584. }
  585. //
  586. // disconnect from server
  587. //
  588. CL_Disconnect ();
  589. //
  590. // open the demo file
  591. //
  592. strcpy (name, Cmd_Argv(1));
  593. COM_DefaultExtension (name, ".qwd");
  594. Con_Printf ("Playing demo from %s.\n", name);
  595. COM_FOpenFile (name, &cls.demofile);
  596. if (!cls.demofile)
  597. {
  598. Con_Printf ("ERROR: couldn't open.\n");
  599. cls.demonum = -1; // stop demo loop
  600. return;
  601. }
  602. cls.demoplayback = true;
  603. cls.state = ca_demostart;
  604. Netchan_Setup (&cls.netchan, net_from, 0);
  605. realtime = 0;
  606. }
  607. /*
  608. ====================
  609. CL_FinishTimeDemo
  610. ====================
  611. */
  612. void CL_FinishTimeDemo (void)
  613. {
  614. int frames;
  615. float time;
  616. cls.timedemo = false;
  617. // the first frame didn't count
  618. frames = (host_framecount - cls.td_startframe) - 1;
  619. time = Sys_DoubleTime() - cls.td_starttime;
  620. if (!time)
  621. time = 1;
  622. Con_Printf ("%i frames %5.1f seconds %5.1f fps\n", frames, time, frames/time);
  623. }
  624. /*
  625. ====================
  626. CL_TimeDemo_f
  627. timedemo [demoname]
  628. ====================
  629. */
  630. void CL_TimeDemo_f (void)
  631. {
  632. if (Cmd_Argc() != 2)
  633. {
  634. Con_Printf ("timedemo <demoname> : gets demo speeds\n");
  635. return;
  636. }
  637. CL_PlayDemo_f ();
  638. if (cls.state != ca_demostart)
  639. return;
  640. // cls.td_starttime will be grabbed at the second frame of the demo, so
  641. // all the loading time doesn't get counted
  642. cls.timedemo = true;
  643. cls.td_starttime = 0;
  644. cls.td_startframe = host_framecount;
  645. cls.td_lastframe = -1; // get a new message this frame
  646. }