mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2024-11-30 16:10:53 +00:00
20adccc9e4
networking support. Second, I moves keys.c from qw and nq to libs/video/targets when I did the next thing. Existing user configs which do binds, sledge hammer. Sledge hammer, existing user configs which do binds. *WHACK* *WHACK* *WHACK* See, much nicer now. Someone should document it, and fix all targets which don't use SDL for input. (I honestly don't expect svgalib and the like to ever be fixed.)
369 lines
7.3 KiB
C
369 lines
7.3 KiB
C
|
|
/*
|
|
cl_demo.c
|
|
|
|
@description@
|
|
|
|
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 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:
|
|
|
|
Free Software Foundation, Inc.
|
|
59 Temple Place - Suite 330
|
|
Boston, MA 02111-1307, USA
|
|
|
|
$Id$
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
# include "config.h"
|
|
#endif
|
|
#ifdef HAVE_STRING_H
|
|
# include <string.h>
|
|
#endif
|
|
#ifdef HAVE_STRINGS_H
|
|
# include <strings.h>
|
|
#endif
|
|
|
|
#include "QF/qendian.h"
|
|
#include "QF/va.h"
|
|
#include "host.h"
|
|
#include "QF/msg.h"
|
|
#include "client.h"
|
|
#include "compat.h"
|
|
#include "QF/sys.h"
|
|
#include "QF/console.h"
|
|
#include "QF/cmd.h"
|
|
#include "QF/keys.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;
|
|
|
|
Qclose (cls.demofile);
|
|
cls.demoplayback = false;
|
|
cls.demofile = NULL;
|
|
cls.state = ca_disconnected;
|
|
|
|
if (cls.timedemo)
|
|
CL_FinishTimeDemo ();
|
|
}
|
|
|
|
|
|
/*
|
|
CL_WriteDemoMessage
|
|
|
|
Dumps the current net message, prefixed by the length and view angles
|
|
*/
|
|
void
|
|
CL_WriteDemoMessage (void)
|
|
{
|
|
int len;
|
|
int i;
|
|
float f;
|
|
|
|
len = LittleLong (net_message->message->cursize);
|
|
Qwrite (cls.demofile, &len, 4);
|
|
for (i = 0; i < 3; i++) {
|
|
f = LittleFloat (cl.viewangles[i]);
|
|
Qwrite (cls.demofile, &f, 4);
|
|
}
|
|
Qwrite (cls.demofile, net_message->message->data,
|
|
net_message->message->cursize);
|
|
Qflush (cls.demofile);
|
|
}
|
|
|
|
|
|
/*
|
|
CL_GetMessage
|
|
|
|
Handles recording and playback of demos, on top of NET_ code
|
|
*/
|
|
int
|
|
CL_GetMessage (void)
|
|
{
|
|
int r, i;
|
|
float f;
|
|
|
|
if (cls.demoplayback) {
|
|
// decide if it is time to grab the next message
|
|
if (cls.signon == SIGNONS) // always grab until fully connected
|
|
{
|
|
if (cls.timedemo) {
|
|
if (host_framecount == cls.td_lastframe)
|
|
return 0; // already read this frame's message
|
|
cls.td_lastframe = host_framecount;
|
|
// if this is the second frame, grab the real td_starttime
|
|
// so the bogus time on the first frame doesn't count
|
|
if (host_framecount == cls.td_startframe + 1)
|
|
cls.td_starttime = realtime;
|
|
} else if ( /* cl.time > 0 && */ cl.time <= cl.mtime[0]) {
|
|
return 0; // don't need another message yet
|
|
}
|
|
}
|
|
// get the next message
|
|
Qread (cls.demofile, &net_message->message->cursize, 4);
|
|
VectorCopy (cl.mviewangles[0], cl.mviewangles[1]);
|
|
for (i = 0; i < 3; i++) {
|
|
r = Qread (cls.demofile, &f, 4);
|
|
cl.mviewangles[0][i] = LittleFloat (f);
|
|
}
|
|
|
|
net_message->message->cursize =
|
|
LittleLong (net_message->message->cursize);
|
|
if (net_message->message->cursize > MAX_MSGLEN)
|
|
Sys_Error ("Demo message > MAX_MSGLEN");
|
|
r =
|
|
Qread (cls.demofile, net_message->message->data,
|
|
net_message->message->cursize);
|
|
if (r != net_message->message->cursize) {
|
|
CL_StopPlayback ();
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
while (1) {
|
|
r = NET_GetMessage (cls.netcon);
|
|
|
|
if (r != 1 && r != 2)
|
|
return r;
|
|
|
|
// discard nop keepalive message
|
|
if (net_message->message->cursize == 1
|
|
&& net_message->message->data[0] == svc_nop)
|
|
Con_Printf ("<-- server to client keepalive\n");
|
|
else
|
|
break;
|
|
}
|
|
|
|
if (cls.demorecording)
|
|
CL_WriteDemoMessage ();
|
|
|
|
return r;
|
|
}
|
|
|
|
|
|
/*
|
|
CL_Stop_f
|
|
|
|
stop recording a demo
|
|
*/
|
|
void
|
|
CL_Stop_f (void)
|
|
{
|
|
if (cmd_source != src_command)
|
|
return;
|
|
|
|
if (!cls.demorecording) {
|
|
Con_Printf ("Not recording a demo.\n");
|
|
return;
|
|
}
|
|
// write a disconnect message to the demo file
|
|
SZ_Clear (net_message->message);
|
|
MSG_WriteByte (net_message->message, svc_disconnect);
|
|
CL_WriteDemoMessage ();
|
|
|
|
// finish up
|
|
Qclose (cls.demofile);
|
|
cls.demofile = NULL;
|
|
cls.demorecording = false;
|
|
Con_Printf ("Completed demo\n");
|
|
}
|
|
|
|
|
|
/*
|
|
CL_Record_f
|
|
|
|
record <demoname> <map> [cd track]
|
|
*/
|
|
void
|
|
CL_Record_f (void)
|
|
{
|
|
int c;
|
|
char name[MAX_OSPATH];
|
|
int track;
|
|
|
|
if (cmd_source != src_command)
|
|
return;
|
|
|
|
c = Cmd_Argc ();
|
|
if (c != 2 && c != 3 && c != 4) {
|
|
Con_Printf ("record <demoname> [<map> [cd track]]\n");
|
|
return;
|
|
}
|
|
|
|
if (strstr (Cmd_Argv (1), "..")) {
|
|
Con_Printf ("Relative pathnames are not allowed.\n");
|
|
return;
|
|
}
|
|
|
|
if (c == 2 && cls.state == ca_connected) {
|
|
Con_Printf
|
|
("Can not record - already connected to server\nClient demo recording must be started before connecting\n");
|
|
return;
|
|
}
|
|
// write the forced cd track number, or -1
|
|
if (c == 4) {
|
|
track = atoi (Cmd_Argv (3));
|
|
Con_Printf ("Forcing CD track to %i\n", cls.forcetrack);
|
|
} else
|
|
track = -1;
|
|
|
|
snprintf (name, sizeof (name), "%s/%s", com_gamedir, Cmd_Argv (1));
|
|
|
|
// start the map up
|
|
//
|
|
if (c > 2)
|
|
Cmd_ExecuteString (va ("map %s", Cmd_Argv (2)), src_command);
|
|
|
|
// open the demo file
|
|
//
|
|
COM_DefaultExtension (name, ".dem");
|
|
|
|
Con_Printf ("recording to %s.\n", name);
|
|
cls.demofile = Qopen (name, "wb");
|
|
if (!cls.demofile) {
|
|
Con_Printf ("ERROR: couldn't open.\n");
|
|
return;
|
|
}
|
|
|
|
cls.forcetrack = track;
|
|
Qprintf (cls.demofile, "%i\n", cls.forcetrack);
|
|
|
|
cls.demorecording = true;
|
|
}
|
|
|
|
|
|
/*
|
|
CL_PlayDemo_f
|
|
|
|
play [demoname]
|
|
*/
|
|
void
|
|
CL_PlayDemo_f (void)
|
|
{
|
|
char name[256];
|
|
int c;
|
|
qboolean neg = false;
|
|
|
|
if (cmd_source != src_command)
|
|
return;
|
|
|
|
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, ".dem");
|
|
|
|
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_connected;
|
|
cls.forcetrack = 0;
|
|
key_dest = key_game;
|
|
game_target = KGT_DEFAULT;
|
|
|
|
while ((c = Qgetc (cls.demofile)) != '\n')
|
|
if (c == '-')
|
|
neg = true;
|
|
else
|
|
cls.forcetrack = cls.forcetrack * 10 + (c - '0');
|
|
|
|
if (neg)
|
|
cls.forcetrack = -cls.forcetrack;
|
|
// ZOID, fscanf is evil
|
|
// fscanf (cls.demofile, "%i\n", &cls.forcetrack);
|
|
}
|
|
|
|
|
|
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 = realtime - 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_source != src_command)
|
|
return;
|
|
|
|
if (Cmd_Argc () != 2) {
|
|
Con_Printf ("timedemo <demoname> : gets demo speeds\n");
|
|
return;
|
|
}
|
|
|
|
CL_PlayDemo_f ();
|
|
|
|
// 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_startframe = host_framecount;
|
|
cls.td_lastframe = -1; // get a new message this frame
|
|
}
|