#include "server.h" #ifdef SERVERSIDE_DEMOS /* ============== SV_ServerRecord_f Begins server demo recording. Every entity and every message will be recorded, but no playerinfo will be stored. Primarily for demo merging. ============== */ void SV_ServerRecord_f (void) { char name[MAX_OSPATH]; qbyte buf_data[MAX_DEMO_MSGLEN]; msg_t buf; int len; int i; if (Cmd_Argc() != 2) { Com_Printf ("serverrecord \n"); return; } if (svs.demofile) { Com_Printf ("Already recording.\n"); return; } if (sv.state != ss_game) { Com_Printf ("You must be in a level to record.\n"); return; } // // open the demo file // Q_snprintfz( name, sizeof(name), "%s/demos/%s", FS_Gamedir(), Cmd_Argv(1) ); COM_DefaultExtension( name, va(".wmd%d",PROTOCOL_VERSION) , sizeof(name) ); Com_Printf ("recording to %s.\n", name); FS_CreatePath (name); svs.demofile = fopen (name, "wb"); if (!svs.demofile) { Com_Printf ("ERROR: couldn't open.\n"); return; } // setup a buffer to catch all multicasts MSG_Init (&svs.demo_multicast, svs.demo_multicast_buf, sizeof(svs.demo_multicast_buf)); // // write a single giant fake message with all the startup info // MSG_Init (&buf, buf_data, sizeof(buf_data)); // // serverdata needs to go over for all types of servers // to make sure the protocol is right, and to set the gamedir // // send the serverdata MSG_WriteByte (&buf, svc_serverdata); MSG_WriteLong (&buf, PROTOCOL_VERSION); MSG_WriteLong (&buf, svs.spawncount); // 2 means server demo MSG_WriteByte (&buf, 2); // demos are always attract loops MSG_WriteString (&buf, Cvar_VariableString ("fs_gamedir")); MSG_WriteShort (&buf, -1); // send full levelname MSG_WriteString (&buf, sv.name); for (i=0 ; i (unsigned int) buf.maxsize) { // write it out len = LittleLong (buf.cursize); fwrite (&len, 4, 1, svs.demofile); fwrite (buf.data, buf.cursize, 1, svs.demofile); buf.cursize = 0; } MSG_WriteByte (&buf, svc_servercmd); MSG_WriteString (&buf, va("cs %i \"%s\"", i, sv.configstrings[i])); } // write it to the demo file Com_DPrintf ("signon message length: %i\n", buf.cursize); len = LittleLong (buf.cursize); fwrite (&len, 4, 1, svs.demofile); fwrite (buf.data, buf.cursize, 1, svs.demofile); // the rest of the demo file will be individual frames } /* ============== SV_ServerStop_f Ends server demo recording ============== */ void SV_ServerStop_f (void) { if (!svs.demofile) { Com_Printf ("Not doing a serverrecord.\n"); return; } fclose (svs.demofile); svs.demofile = NULL; Com_Printf ("Recording completed.\n"); } /* ================== SV_RecordDemoMessage Save everything in the world out without deltas. Used for recording footage for merged or assembled demos ================== */ void SV_RecordDemoMessage (void) { int e; edict_t *ent; entity_state_t nostate; msg_t buf; qbyte buf_data[MAX_DEMO_MSGLEN]; int len; if (!svs.demofile) return; memset (&nostate, 0, sizeof(nostate)); MSG_Init (&buf, buf_data, sizeof(buf_data)); // write a frame message that doesn't contain a player_state_t MSG_WriteByte (&buf, svc_frame); MSG_WriteLong (&buf, sv.framenum); MSG_WriteByte (&buf, svc_packetentities); e = 1; ent = EDICT_NUM(e); while (e < sv.num_edicts) { // ignore ents without visible models unless they have an effect if (ent->r.inuse && ent->s.number && (ent->s.modelindex || ent->s.effects || ent->s.sound || ent->s.events[0] || ent->s.light) && !(ent->r.svflags & SVF_NOCLIENT)) MSG_WriteDeltaEntity (&nostate, &ent->s, &buf, qfalse, qtrue); e++; ent = EDICT_NUM(e); } MSG_WriteShort (&buf, 0); // end of packetentities // now add the accumulated multicast information MSG_Write (&buf, svs.demo_multicast.data, svs.demo_multicast.cursize); MSG_Clear (&svs.demo_multicast); // now write the entire message to the file, prefixed by the length len = LittleLong (buf.cursize); fwrite (&len, 4, 1, svs.demofile); fwrite (buf.data, buf.cursize, 1, svs.demofile); } #endif