123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806 |
- /*
- Copyright (C) 1996-1997 Id Software, Inc.
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License
- as published by the Free Software Foundation; either version 2
- of the License, or (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the included (GNU.txt) GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- */
- #include "quakedef.h"
- void CL_FinishTimeDemo (void);
- /*
- ==============================================================================
- DEMO CODE
- When a demo is playing back, all NET_SendMessages are skipped, and
- NET_GetMessages are read from the demo file.
- Whenever cl.time gets past the last received message, another message is
- read from the demo file.
- ==============================================================================
- */
- /*
- ==============
- CL_StopPlayback
- Called when a demo file runs out, or the user starts a game
- ==============
- */
- void CL_StopPlayback (void)
- {
- if (!cls.demoplayback)
- return;
- fclose (cls.demofile);
- cls.demofile = NULL;
- cls.state = ca_disconnected;
- cls.demoplayback = 0;
- if (cls.timedemo)
- CL_FinishTimeDemo ();
- }
- #define dem_cmd 0
- #define dem_read 1
- #define dem_set 2
- /*
- ====================
- CL_WriteDemoCmd
- Writes the current user cmd
- ====================
- */
- void CL_WriteDemoCmd (usercmd_t *pcmd)
- {
- int i;
- float fl;
- byte c;
- usercmd_t cmd;
- //Con_Printf("write: %ld bytes, %4.4f\n", msg->cursize, realtime);
- fl = LittleFloat((float)realtime);
- fwrite (&fl, sizeof(fl), 1, cls.demofile);
- c = dem_cmd;
- fwrite (&c, sizeof(c), 1, cls.demofile);
- // correct for byte order, bytes don't matter
- cmd = *pcmd;
- for (i = 0; i < 3; i++)
- cmd.angles[i] = LittleFloat(cmd.angles[i]);
- cmd.forwardmove = LittleShort(cmd.forwardmove);
- cmd.sidemove = LittleShort(cmd.sidemove);
- cmd.upmove = LittleShort(cmd.upmove);
- fwrite(&cmd, sizeof(cmd), 1, cls.demofile);
- for (i=0 ; i<3 ; i++)
- {
- fl = LittleFloat (cl.viewangles[i]);
- fwrite (&fl, 4, 1, cls.demofile);
- }
- fflush (cls.demofile);
- }
- /*
- ====================
- CL_WriteDemoMessage
- Dumps the current net message, prefixed by the length and view angles
- ====================
- */
- void CL_WriteDemoMessage (sizebuf_t *msg)
- {
- int len;
- float fl;
- byte c;
- //Con_Printf("write: %ld bytes, %4.4f\n", msg->cursize, realtime);
- if (!cls.demorecording)
- return;
- fl = LittleFloat((float)realtime);
- fwrite (&fl, sizeof(fl), 1, cls.demofile);
- c = dem_read;
- fwrite (&c, sizeof(c), 1, cls.demofile);
- len = LittleLong (msg->cursize);
- fwrite (&len, 4, 1, cls.demofile);
- fwrite (msg->data, msg->cursize, 1, cls.demofile);
- fflush (cls.demofile);
- }
- /*
- ====================
- CL_GetDemoMessage
- FIXME...
- ====================
- */
- qboolean CL_GetDemoMessage (void)
- {
- int r, i, j;
- float f;
- float demotime;
- byte c;
- usercmd_t *pcmd;
- // read the time from the packet
- fread(&demotime, sizeof(demotime), 1, cls.demofile);
- demotime = LittleFloat(demotime);
- // decide if it is time to grab the next message
- if (cls.timedemo) {
- if (cls.td_lastframe < 0)
- cls.td_lastframe = demotime;
- else if (demotime > cls.td_lastframe) {
- cls.td_lastframe = demotime;
- // rewind back to time
- fseek(cls.demofile, ftell(cls.demofile) - sizeof(demotime),
- SEEK_SET);
- return 0; // allready read this frame's message
- }
- if (!cls.td_starttime && cls.state == ca_active) {
- cls.td_starttime = Sys_DoubleTime();
- cls.td_startframe = host_framecount;
- }
- realtime = demotime; // warp
- } else if (!cl.paused && cls.state >= ca_onserver) { // allways grab until fully connected
- if (realtime + 1.0 < demotime) {
- // too far back
- realtime = demotime - 1.0;
- // rewind back to time
- fseek(cls.demofile, ftell(cls.demofile) - sizeof(demotime),
- SEEK_SET);
- return 0;
- } else if (realtime < demotime) {
- // rewind back to time
- fseek(cls.demofile, ftell(cls.demofile) - sizeof(demotime),
- SEEK_SET);
- return 0; // don't need another message yet
- }
- } else
- realtime = demotime; // we're warping
- if (cls.state < ca_demostart)
- Host_Error ("CL_GetDemoMessage: cls.state != ca_active");
-
- // get the msg type
- fread (&c, sizeof(c), 1, cls.demofile);
-
- switch (c) {
- case dem_cmd :
- // user sent input
- i = cls.netchan.outgoing_sequence & UPDATE_MASK;
- pcmd = &cl.frames[i].cmd;
- r = fread (pcmd, sizeof(*pcmd), 1, cls.demofile);
- if (r != 1)
- {
- CL_StopPlayback ();
- return 0;
- }
- // byte order stuff
- for (j = 0; j < 3; j++)
- pcmd->angles[j] = LittleFloat(pcmd->angles[j]);
- pcmd->forwardmove = LittleShort(pcmd->forwardmove);
- pcmd->sidemove = LittleShort(pcmd->sidemove);
- pcmd->upmove = LittleShort(pcmd->upmove);
- cl.frames[i].senttime = demotime;
- cl.frames[i].receivedtime = -1; // we haven't gotten a reply yet
- cls.netchan.outgoing_sequence++;
- for (i=0 ; i<3 ; i++)
- {
- r = fread (&f, 4, 1, cls.demofile);
- cl.viewangles[i] = LittleFloat (f);
- }
- break;
- case dem_read:
- // get the next message
- fread (&net_message.cursize, 4, 1, cls.demofile);
- net_message.cursize = LittleLong (net_message.cursize);
- //Con_Printf("read: %ld bytes\n", net_message.cursize);
- if (net_message.cursize > MAX_MSGLEN)
- Sys_Error ("Demo message > MAX_MSGLEN");
- r = fread (net_message.data, net_message.cursize, 1, cls.demofile);
- if (r != 1)
- {
- CL_StopPlayback ();
- return 0;
- }
- break;
- case dem_set :
- fread (&i, 4, 1, cls.demofile);
- cls.netchan.outgoing_sequence = LittleLong(i);
- fread (&i, 4, 1, cls.demofile);
- cls.netchan.incoming_sequence = LittleLong(i);
- break;
- default :
- Con_Printf("Corrupted demo.\n");
- CL_StopPlayback ();
- return 0;
- }
- return 1;
- }
- /*
- ====================
- CL_GetMessage
- Handles recording and playback of demos, on top of NET_ code
- ====================
- */
- qboolean CL_GetMessage (void)
- {
- if (cls.demoplayback)
- return CL_GetDemoMessage ();
- if (!NET_GetPacket ())
- return false;
- CL_WriteDemoMessage (&net_message);
-
- return true;
- }
- /*
- ====================
- CL_Stop_f
- stop recording a demo
- ====================
- */
- void CL_Stop_f (void)
- {
- if (!cls.demorecording)
- {
- Con_Printf ("Not recording a demo.\n");
- return;
- }
- // write a disconnect message to the demo file
- SZ_Clear (&net_message);
- MSG_WriteLong (&net_message, -1); // -1 sequence means out of band
- MSG_WriteByte (&net_message, svc_disconnect);
- MSG_WriteString (&net_message, "EndOfDemo");
- CL_WriteDemoMessage (&net_message);
- // finish up
- fclose (cls.demofile);
- cls.demofile = NULL;
- cls.demorecording = false;
- Con_Printf ("Completed demo\n");
- }
- /*
- ====================
- CL_WriteDemoMessage
- Dumps the current net message, prefixed by the length and view angles
- ====================
- */
- void CL_WriteRecordDemoMessage (sizebuf_t *msg, int seq)
- {
- int len;
- int i;
- float fl;
- byte c;
- //Con_Printf("write: %ld bytes, %4.4f\n", msg->cursize, realtime);
- if (!cls.demorecording)
- return;
- fl = LittleFloat((float)realtime);
- fwrite (&fl, sizeof(fl), 1, cls.demofile);
- c = dem_read;
- fwrite (&c, sizeof(c), 1, cls.demofile);
- len = LittleLong (msg->cursize + 8);
- fwrite (&len, 4, 1, cls.demofile);
- i = LittleLong(seq);
- fwrite (&i, 4, 1, cls.demofile);
- fwrite (&i, 4, 1, cls.demofile);
- fwrite (msg->data, msg->cursize, 1, cls.demofile);
- fflush (cls.demofile);
- }
- void CL_WriteSetDemoMessage (void)
- {
- int len;
- float fl;
- byte c;
- //Con_Printf("write: %ld bytes, %4.4f\n", msg->cursize, realtime);
- if (!cls.demorecording)
- return;
- fl = LittleFloat((float)realtime);
- fwrite (&fl, sizeof(fl), 1, cls.demofile);
- c = dem_set;
- fwrite (&c, sizeof(c), 1, cls.demofile);
- len = LittleLong(cls.netchan.outgoing_sequence);
- fwrite (&len, 4, 1, cls.demofile);
- len = LittleLong(cls.netchan.incoming_sequence);
- fwrite (&len, 4, 1, cls.demofile);
- fflush (cls.demofile);
- }
- /*
- ====================
- CL_Record_f
- record <demoname> <server>
- ====================
- */
- void CL_Record_f (void)
- {
- int c;
- char name[MAX_OSPATH];
- sizebuf_t buf;
- char buf_data[MAX_MSGLEN];
- int n, i, j;
- char *s;
- entity_t *ent;
- entity_state_t *es, blankes;
- player_info_t *player;
- extern char gamedirfile[];
- int seq = 1;
- c = Cmd_Argc();
- if (c != 2)
- {
- Con_Printf ("record <demoname>\n");
- return;
- }
- if (cls.state != ca_active) {
- Con_Printf ("You must be connected to record.\n");
- return;
- }
- if (cls.demorecording)
- CL_Stop_f();
-
- sprintf (name, "%s/%s", com_gamedir, Cmd_Argv(1));
- //
- // open the demo file
- //
- COM_DefaultExtension (name, ".qwd");
- cls.demofile = fopen (name, "wb");
- if (!cls.demofile)
- {
- Con_Printf ("ERROR: couldn't open.\n");
- return;
- }
- Con_Printf ("recording to %s.\n", name);
- cls.demorecording = true;
- /*-------------------------------------------------*/
- // serverdata
- // send the info about the new client to all connected clients
- memset(&buf, 0, sizeof(buf));
- buf.data = buf_data;
- buf.maxsize = sizeof(buf_data);
- // send the serverdata
- MSG_WriteByte (&buf, svc_serverdata);
- MSG_WriteLong (&buf, PROTOCOL_VERSION);
- MSG_WriteLong (&buf, cl.servercount);
- MSG_WriteString (&buf, gamedirfile);
- if (cl.spectator)
- MSG_WriteByte (&buf, cl.playernum | 128);
- else
- MSG_WriteByte (&buf, cl.playernum);
- // send full levelname
- MSG_WriteString (&buf, cl.levelname);
- // send the movevars
- MSG_WriteFloat(&buf, movevars.gravity);
- MSG_WriteFloat(&buf, movevars.stopspeed);
- MSG_WriteFloat(&buf, movevars.maxspeed);
- MSG_WriteFloat(&buf, movevars.spectatormaxspeed);
- MSG_WriteFloat(&buf, movevars.accelerate);
- MSG_WriteFloat(&buf, movevars.airaccelerate);
- MSG_WriteFloat(&buf, movevars.wateraccelerate);
- MSG_WriteFloat(&buf, movevars.friction);
- MSG_WriteFloat(&buf, movevars.waterfriction);
- MSG_WriteFloat(&buf, movevars.entgravity);
- // send music
- MSG_WriteByte (&buf, svc_cdtrack);
- MSG_WriteByte (&buf, 0); // none in demos
- // send server info string
- MSG_WriteByte (&buf, svc_stufftext);
- MSG_WriteString (&buf, va("fullserverinfo \"%s\"\n", cl.serverinfo) );
- // flush packet
- CL_WriteRecordDemoMessage (&buf, seq++);
- SZ_Clear (&buf);
- // soundlist
- MSG_WriteByte (&buf, svc_soundlist);
- MSG_WriteByte (&buf, 0);
- n = 0;
- s = cl.sound_name[n+1];
- while (*s) {
- MSG_WriteString (&buf, s);
- if (buf.cursize > MAX_MSGLEN/2) {
- MSG_WriteByte (&buf, 0);
- MSG_WriteByte (&buf, n);
- CL_WriteRecordDemoMessage (&buf, seq++);
- SZ_Clear (&buf);
- MSG_WriteByte (&buf, svc_soundlist);
- MSG_WriteByte (&buf, n + 1);
- }
- n++;
- s = cl.sound_name[n+1];
- }
- if (buf.cursize) {
- MSG_WriteByte (&buf, 0);
- MSG_WriteByte (&buf, 0);
- CL_WriteRecordDemoMessage (&buf, seq++);
- SZ_Clear (&buf);
- }
- // modellist
- MSG_WriteByte (&buf, svc_modellist);
- MSG_WriteByte (&buf, 0);
- n = 0;
- s = cl.model_name[n+1];
- while (*s) {
- MSG_WriteString (&buf, s);
- if (buf.cursize > MAX_MSGLEN/2) {
- MSG_WriteByte (&buf, 0);
- MSG_WriteByte (&buf, n);
- CL_WriteRecordDemoMessage (&buf, seq++);
- SZ_Clear (&buf);
- MSG_WriteByte (&buf, svc_modellist);
- MSG_WriteByte (&buf, n + 1);
- }
- n++;
- s = cl.model_name[n+1];
- }
- if (buf.cursize) {
- MSG_WriteByte (&buf, 0);
- MSG_WriteByte (&buf, 0);
- CL_WriteRecordDemoMessage (&buf, seq++);
- SZ_Clear (&buf);
- }
- // spawnstatic
- for (i = 0; i < cl.num_statics; i++) {
- ent = cl_static_entities + i;
- MSG_WriteByte (&buf, svc_spawnstatic);
- for (j = 1; j < MAX_MODELS; j++)
- if (ent->model == cl.model_precache[j])
- break;
- if (j == MAX_MODELS)
- MSG_WriteByte (&buf, 0);
- else
- MSG_WriteByte (&buf, j);
- MSG_WriteByte (&buf, ent->frame);
- MSG_WriteByte (&buf, 0);
- MSG_WriteByte (&buf, ent->skinnum);
- for (j=0 ; j<3 ; j++)
- {
- MSG_WriteCoord (&buf, ent->origin[j]);
- MSG_WriteAngle (&buf, ent->angles[j]);
- }
- if (buf.cursize > MAX_MSGLEN/2) {
- CL_WriteRecordDemoMessage (&buf, seq++);
- SZ_Clear (&buf);
- }
- }
- // spawnstaticsound
- // static sounds are skipped in demos, life is hard
- // baselines
- memset(&blankes, 0, sizeof(blankes));
- for (i = 0; i < MAX_EDICTS; i++) {
- es = cl_baselines + i;
- if (memcmp(es, &blankes, sizeof(blankes))) {
- MSG_WriteByte (&buf,svc_spawnbaseline);
- MSG_WriteShort (&buf, i);
- MSG_WriteByte (&buf, es->modelindex);
- MSG_WriteByte (&buf, es->frame);
- MSG_WriteByte (&buf, es->colormap);
- MSG_WriteByte (&buf, es->skinnum);
- for (j=0 ; j<3 ; j++)
- {
- MSG_WriteCoord(&buf, es->origin[j]);
- MSG_WriteAngle(&buf, es->angles[j]);
- }
- if (buf.cursize > MAX_MSGLEN/2) {
- CL_WriteRecordDemoMessage (&buf, seq++);
- SZ_Clear (&buf);
- }
- }
- }
- MSG_WriteByte (&buf, svc_stufftext);
- MSG_WriteString (&buf, va("cmd spawn %i 0\n", cl.servercount) );
- if (buf.cursize) {
- CL_WriteRecordDemoMessage (&buf, seq++);
- SZ_Clear (&buf);
- }
- // send current status of all other players
- for (i = 0; i < MAX_CLIENTS; i++) {
- player = cl.players + i;
- MSG_WriteByte (&buf, svc_updatefrags);
- MSG_WriteByte (&buf, i);
- MSG_WriteShort (&buf, player->frags);
-
- MSG_WriteByte (&buf, svc_updateping);
- MSG_WriteByte (&buf, i);
- MSG_WriteShort (&buf, player->ping);
-
- MSG_WriteByte (&buf, svc_updatepl);
- MSG_WriteByte (&buf, i);
- MSG_WriteByte (&buf, player->pl);
-
- MSG_WriteByte (&buf, svc_updateentertime);
- MSG_WriteByte (&buf, i);
- MSG_WriteFloat (&buf, player->entertime);
- MSG_WriteByte (&buf, svc_updateuserinfo);
- MSG_WriteByte (&buf, i);
- MSG_WriteLong (&buf, player->userid);
- MSG_WriteString (&buf, player->userinfo);
- if (buf.cursize > MAX_MSGLEN/2) {
- CL_WriteRecordDemoMessage (&buf, seq++);
- SZ_Clear (&buf);
- }
- }
-
- // send all current light styles
- for (i=0 ; i<MAX_LIGHTSTYLES ; i++)
- {
- MSG_WriteByte (&buf, svc_lightstyle);
- MSG_WriteByte (&buf, (char)i);
- MSG_WriteString (&buf, cl_lightstyle[i].map);
- }
- for (i = 0; i < MAX_CL_STATS; i++) {
- MSG_WriteByte (&buf, svc_updatestatlong);
- MSG_WriteByte (&buf, i);
- MSG_WriteLong (&buf, cl.stats[i]);
- if (buf.cursize > MAX_MSGLEN/2) {
- CL_WriteRecordDemoMessage (&buf, seq++);
- SZ_Clear (&buf);
- }
- }
- #if 0
- MSG_WriteByte (&buf, svc_updatestatlong);
- MSG_WriteByte (&buf, STAT_TOTALMONSTERS);
- MSG_WriteLong (&buf, cl.stats[STAT_TOTALMONSTERS]);
- MSG_WriteByte (&buf, svc_updatestatlong);
- MSG_WriteByte (&buf, STAT_SECRETS);
- MSG_WriteLong (&buf, cl.stats[STAT_SECRETS]);
- MSG_WriteByte (&buf, svc_updatestatlong);
- MSG_WriteByte (&buf, STAT_MONSTERS);
- MSG_WriteLong (&buf, cl.stats[STAT_MONSTERS]);
- #endif
- // get the client to check and download skins
- // when that is completed, a begin command will be issued
- MSG_WriteByte (&buf, svc_stufftext);
- MSG_WriteString (&buf, va("skins\n") );
- CL_WriteRecordDemoMessage (&buf, seq++);
- CL_WriteSetDemoMessage();
- // done
- }
- /*
- ====================
- CL_ReRecord_f
- record <demoname>
- ====================
- */
- void CL_ReRecord_f (void)
- {
- int c;
- char name[MAX_OSPATH];
- c = Cmd_Argc();
- if (c != 2)
- {
- Con_Printf ("rerecord <demoname>\n");
- return;
- }
- if (!*cls.servername) {
- Con_Printf("No server to reconnect to...\n");
- return;
- }
- if (cls.demorecording)
- CL_Stop_f();
-
- sprintf (name, "%s/%s", com_gamedir, Cmd_Argv(1));
- //
- // open the demo file
- //
- COM_DefaultExtension (name, ".qwd");
- cls.demofile = fopen (name, "wb");
- if (!cls.demofile)
- {
- Con_Printf ("ERROR: couldn't open.\n");
- return;
- }
- Con_Printf ("recording to %s.\n", name);
- cls.demorecording = true;
- CL_Disconnect();
- CL_BeginServerConnect();
- }
- /*
- ====================
- CL_PlayDemo_f
- play [demoname]
- ====================
- */
- void CL_PlayDemo_f (void)
- {
- char name[256];
- if (Cmd_Argc() != 2)
- {
- Con_Printf ("play <demoname> : plays a demo\n");
- return;
- }
- //
- // disconnect from server
- //
- CL_Disconnect ();
-
- //
- // open the demo file
- //
- strcpy (name, Cmd_Argv(1));
- COM_DefaultExtension (name, ".qwd");
- Con_Printf ("Playing demo from %s.\n", name);
- COM_FOpenFile (name, &cls.demofile);
- if (!cls.demofile)
- {
- Con_Printf ("ERROR: couldn't open.\n");
- cls.demonum = -1; // stop demo loop
- return;
- }
- cls.demoplayback = true;
- cls.state = ca_demostart;
- Netchan_Setup (&cls.netchan, net_from, 0);
- realtime = 0;
- }
- /*
- ====================
- CL_FinishTimeDemo
- ====================
- */
- void CL_FinishTimeDemo (void)
- {
- int frames;
- float time;
-
- cls.timedemo = false;
-
- // the first frame didn't count
- frames = (host_framecount - cls.td_startframe) - 1;
- time = Sys_DoubleTime() - cls.td_starttime;
- if (!time)
- time = 1;
- Con_Printf ("%i frames %5.1f seconds %5.1f fps\n", frames, time, frames/time);
- }
- /*
- ====================
- CL_TimeDemo_f
- timedemo [demoname]
- ====================
- */
- void CL_TimeDemo_f (void)
- {
- if (Cmd_Argc() != 2)
- {
- Con_Printf ("timedemo <demoname> : gets demo speeds\n");
- return;
- }
- CL_PlayDemo_f ();
-
- if (cls.state != ca_demostart)
- return;
- // cls.td_starttime will be grabbed at the second frame of the demo, so
- // all the loading time doesn't get counted
-
- cls.timedemo = true;
- cls.td_starttime = 0;
- cls.td_startframe = host_framecount;
- cls.td_lastframe = -1; // get a new message this frame
- }
|