mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2025-03-23 02:41:30 +00:00
mvd playback support. seems to work (get some weird entities hanging around
but I suspect that's the mod used in the demo I was testing with), but probably needs some cleanup.
This commit is contained in:
parent
533a74aa75
commit
2383340031
11 changed files with 355 additions and 33 deletions
|
@ -41,8 +41,11 @@
|
|||
|
||||
extern int autocam;
|
||||
extern int spec_track; // player# of who we are tracking
|
||||
extern int ideal_track;
|
||||
extern struct cvar_s *chase_active;
|
||||
|
||||
void Cam_Lock (int playernum);
|
||||
int Cam_TrackNum (void);
|
||||
qboolean Cam_DrawViewModel(void);
|
||||
qboolean Cam_DrawPlayer(int playernum);
|
||||
void Cam_Track(usercmd_t *cmd);
|
||||
|
|
|
@ -32,10 +32,11 @@
|
|||
#include "QF/qtypes.h"
|
||||
|
||||
void CL_SetSolidPlayers (int playernum);
|
||||
void CL_ClearPredict (void);
|
||||
void CL_SetUpPlayerPrediction(qboolean dopred);
|
||||
void CL_EmitEntities (void);
|
||||
void CL_ClearProjectiles (void);
|
||||
void CL_ParseProjectiles (void);
|
||||
void CL_ParseProjectiles (qboolean nail2);
|
||||
void CL_ParsePacketEntities (qboolean delta);
|
||||
void CL_SetSolidEntities (void);
|
||||
void CL_ParsePlayerinfo (void);
|
||||
|
|
|
@ -50,6 +50,7 @@ extern int cl_gib3index;
|
|||
|
||||
int CL_CalcNet (void);
|
||||
void CL_ParseServerMessage (void);
|
||||
void CL_ParseClientdata (void);
|
||||
void CL_NewTranslation (int slot, struct skin_s *skin);
|
||||
qboolean CL_CheckOrDownloadFile (const char *filename);
|
||||
qboolean CL_IsUploading(void);
|
||||
|
|
|
@ -102,6 +102,8 @@ typedef struct player_info_s
|
|||
byte translations[4*VID_GRADES*256]; // space for colormap32
|
||||
int translationcolor[256];
|
||||
struct skin_s *skin;
|
||||
int stats[MAX_CL_STATS]; // health, etc
|
||||
int prevcount;
|
||||
} player_info_t;
|
||||
|
||||
|
||||
|
@ -178,6 +180,12 @@ typedef struct
|
|||
// entering a map (and clearing client_state_t)
|
||||
qboolean demorecording;
|
||||
qboolean demoplayback;
|
||||
qboolean demoplayback2;
|
||||
qboolean findtrack;
|
||||
int lastto;
|
||||
int lasttype;
|
||||
int prevtime;
|
||||
double basetime;
|
||||
qboolean timedemo;
|
||||
QFile *demofile;
|
||||
float td_lastframe; // to meter out one message a frame
|
||||
|
|
|
@ -118,6 +118,7 @@
|
|||
#define svc_setinfo 51 // setinfo on a client
|
||||
#define svc_serverinfo 52 // serverinfo
|
||||
#define svc_updatepl 53 // [byte] [byte]
|
||||
#define svc_nails2 54 // FIXME: from qwex. for interpolation, stores edict num
|
||||
|
||||
// client to server ===========================================================
|
||||
|
||||
|
@ -130,6 +131,17 @@
|
|||
#define clc_tmove 6 // teleport request, spectator only
|
||||
#define clc_upload 7 // teleport request, spectator only
|
||||
|
||||
// demo recording
|
||||
|
||||
#define dem_cmd 0
|
||||
#define dem_read 1
|
||||
#define dem_set 2
|
||||
#define dem_multiple 3
|
||||
#define dem_single 4
|
||||
#define dem_stats 5
|
||||
#define dem_all 6
|
||||
|
||||
|
||||
// ==============================================
|
||||
|
||||
// playerinfo flags from server
|
||||
|
|
|
@ -93,6 +93,8 @@ qboolean cam_forceview;
|
|||
vec3_t cam_viewangles;
|
||||
|
||||
int spec_track = 0; // player# of who we are tracking
|
||||
int ideal_track = 0;
|
||||
float last_lock = 0;
|
||||
int autocam = CAM_NONE;
|
||||
|
||||
|
||||
|
@ -158,6 +160,14 @@ Cam_DrawPlayer (int playernum)
|
|||
return false;
|
||||
}
|
||||
|
||||
int
|
||||
Cam_TrackNum (void)
|
||||
{
|
||||
if (!autocam)
|
||||
return -1;
|
||||
return spec_track;
|
||||
}
|
||||
|
||||
void
|
||||
Cam_Unlock (void)
|
||||
{
|
||||
|
@ -174,11 +184,16 @@ void
|
|||
Cam_Lock (int playernum)
|
||||
{
|
||||
char st[40];
|
||||
|
||||
printf ("Cam_Lock: %d\n", playernum);
|
||||
snprintf (st, sizeof (st), "ptrack %i", playernum);
|
||||
if (cls.demoplayback2) {
|
||||
memcpy(cl.stats, cl.players[playernum].stats, sizeof (cl.stats));
|
||||
}
|
||||
|
||||
MSG_WriteByte (&cls.netchan.message, clc_stringcmd);
|
||||
MSG_WriteString (&cls.netchan.message, st);
|
||||
spec_track = playernum;
|
||||
last_lock = realtime;
|
||||
cam_forceview = true;
|
||||
locked = false;
|
||||
Sbar_Changed ();
|
||||
|
@ -355,8 +370,10 @@ Cam_CheckHighTarget (void)
|
|||
}
|
||||
}
|
||||
if (j >= 0) {
|
||||
if (!locked || cl.players[j].frags > cl.players[spec_track].frags)
|
||||
if (!locked || cl.players[j].frags > cl.players[spec_track].frags) {
|
||||
Cam_Lock (j);
|
||||
ideal_track = spec_track;
|
||||
}
|
||||
} else
|
||||
Cam_Unlock ();
|
||||
}
|
||||
|
@ -394,6 +411,23 @@ Cam_Track (usercmd_t *cmd)
|
|||
}
|
||||
|
||||
frame = &cl.frames[cls.netchan.incoming_sequence & UPDATE_MASK];
|
||||
if (autocam && cls.demoplayback2 && 0) {
|
||||
if (ideal_track != spec_track && realtime - last_lock > 1
|
||||
&& frame->playerstate[ideal_track].messagenum == cl.parsecount)
|
||||
Cam_Lock (ideal_track);
|
||||
|
||||
if (frame->playerstate[spec_track].messagenum != cl.parsecount) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MAX_CLIENTS; i++) {
|
||||
if (frame->playerstate[i].messagenum == cl.parsecount)
|
||||
break;
|
||||
}
|
||||
if (i < MAX_CLIENTS)
|
||||
Cam_Lock (i);
|
||||
}
|
||||
}
|
||||
|
||||
player = frame->playerstate + spec_track;
|
||||
self = frame->playerstate + cl.playernum;
|
||||
|
||||
|
@ -594,6 +628,8 @@ Cam_FinishMove (usercmd_t *cmd)
|
|||
s = &cl.players[i];
|
||||
if (s->name[0] && !s->spectator) {
|
||||
Cam_Lock (i);
|
||||
Con_Printf("tracking %s\n", s->name);
|
||||
ideal_track = i;
|
||||
return;
|
||||
}
|
||||
i = (i + 1) % MAX_CLIENTS;
|
||||
|
@ -603,6 +639,7 @@ Cam_FinishMove (usercmd_t *cmd)
|
|||
s = &cl.players[i];
|
||||
if (s->name[0] && !s->spectator) {
|
||||
Cam_Lock (i);
|
||||
ideal_track = i;
|
||||
return;
|
||||
}
|
||||
Con_Printf ("No target found ...\n");
|
||||
|
@ -614,6 +651,7 @@ Cam_Reset (void)
|
|||
{
|
||||
autocam = CAM_NONE;
|
||||
spec_track = 0;
|
||||
ideal_track = 0;
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -51,6 +51,8 @@ static const char rcsid[] =
|
|||
#include "QF/sys.h"
|
||||
#include "QF/va.h"
|
||||
|
||||
#include "cl_cam.h"
|
||||
#include "cl_ents.h"
|
||||
#include "cl_main.h"
|
||||
#include "client.h"
|
||||
#include "compat.h"
|
||||
|
@ -66,6 +68,7 @@ typedef struct {
|
|||
int cl_timeframes_isactive;
|
||||
int cl_timeframes_index;
|
||||
int demotime_cached;
|
||||
float nextdemotime;
|
||||
char demoname[1024];
|
||||
double *cl_timeframes_array;
|
||||
#define CL_TIMEFRAMES_ARRAYBLOCK 4096
|
||||
|
@ -108,16 +111,13 @@ CL_StopPlayback (void)
|
|||
cls.demofile = NULL;
|
||||
CL_SetState (ca_disconnected);
|
||||
cls.demoplayback = 0;
|
||||
cls.demoplayback2 = 0;
|
||||
demotime_cached = 0;
|
||||
|
||||
if (cls.timedemo)
|
||||
CL_FinishTimeDemo ();
|
||||
}
|
||||
|
||||
#define dem_cmd 0
|
||||
#define dem_read 1
|
||||
#define dem_set 2
|
||||
|
||||
/*
|
||||
CL_WriteDemoCmd
|
||||
|
||||
|
@ -184,22 +184,51 @@ CL_WriteDemoMessage (sizebuf_t *msg)
|
|||
Qflush (cls.demofile);
|
||||
}
|
||||
|
||||
#if 0
|
||||
static const char *dem_names[] = {
|
||||
"dem_cmd",
|
||||
"dem_read",
|
||||
"dem_set",
|
||||
"dem_multiple",
|
||||
"dem_single",
|
||||
"dem_stats",
|
||||
"dem_all",
|
||||
"dem_invalid",
|
||||
};
|
||||
#endif
|
||||
|
||||
qboolean
|
||||
CL_GetDemoMessage (void)
|
||||
{
|
||||
byte c;
|
||||
byte c, newtime;
|
||||
float demotime, f;
|
||||
static float cached_demotime;
|
||||
int r, i, j;
|
||||
static byte cached_newtime;
|
||||
int r, i, j, tracknum;
|
||||
usercmd_t *pcmd;
|
||||
|
||||
if (!cls.demoplayback2)
|
||||
nextdemotime = realtime;
|
||||
if (realtime + 1.0 < nextdemotime)
|
||||
realtime = nextdemotime - 1.0;
|
||||
|
||||
nextdemomessage:
|
||||
// read the time from the packet
|
||||
newtime = 0;
|
||||
if (demotime_cached) {
|
||||
demotime = cached_demotime;
|
||||
newtime = cached_newtime;
|
||||
demotime_cached = 0;
|
||||
} else {
|
||||
Qread (cls.demofile, &demotime, sizeof (demotime));
|
||||
demotime = LittleFloat (demotime);
|
||||
if (cls.demoplayback2) {
|
||||
Qread (cls.demofile, &newtime, sizeof (newtime));
|
||||
demotime = cls.basetime + (cls.prevtime + newtime) * 0.001;
|
||||
} else {
|
||||
Qread (cls.demofile, &demotime, sizeof (demotime));
|
||||
demotime = LittleFloat (demotime);
|
||||
if (!nextdemotime)
|
||||
realtime = nextdemotime = demotime;
|
||||
}
|
||||
}
|
||||
|
||||
// decide if it is time to grab the next message
|
||||
|
@ -211,6 +240,7 @@ CL_GetDemoMessage (void)
|
|||
// rewind back to time
|
||||
demotime_cached = 1;
|
||||
cached_demotime = demotime;
|
||||
cached_newtime = newtime;
|
||||
return 0; // already read this frame's message
|
||||
}
|
||||
if (!cls.td_starttime && cls.state == ca_active) {
|
||||
|
@ -220,29 +250,44 @@ CL_GetDemoMessage (void)
|
|||
realtime = demotime; // warp
|
||||
} else if (!cl.paused && cls.state >= ca_onserver) {
|
||||
// always grab until fully connected
|
||||
if (realtime + 1.0 < demotime) {
|
||||
if (!cls.demoplayback2 && realtime + 1.0 < demotime) {
|
||||
// too far back
|
||||
realtime = demotime - 1.0;
|
||||
// rewind back to time
|
||||
demotime_cached = 1;
|
||||
cached_demotime = demotime;
|
||||
cached_newtime = newtime;
|
||||
return 0;
|
||||
} else if (realtime < demotime) {
|
||||
// rewind back to time
|
||||
demotime_cached = 1;
|
||||
cached_demotime = demotime;
|
||||
cached_newtime = newtime;
|
||||
return 0; // don't need another message yet
|
||||
}
|
||||
} else
|
||||
realtime = demotime; // we're warping
|
||||
|
||||
if (realtime - nextdemotime > 0.0001) {
|
||||
if (nextdemotime != demotime) {
|
||||
if (cls.demoplayback2) {
|
||||
cls.netchan.incoming_sequence++;
|
||||
cls.netchan.incoming_acknowledged++;
|
||||
cls.netchan.frame_latency = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
nextdemotime = demotime;
|
||||
|
||||
cls.prevtime += newtime;
|
||||
|
||||
if (cls.state < ca_demostart)
|
||||
Host_Error ("CL_GetDemoMessage: cls.state != ca_active");
|
||||
|
||||
// get the msg type
|
||||
Qread (cls.demofile, &c, sizeof (c));
|
||||
|
||||
switch (c) {
|
||||
switch (c & 7) {
|
||||
case dem_cmd:
|
||||
// user sent input
|
||||
net_message->message->cursize = -1;
|
||||
|
@ -269,6 +314,7 @@ CL_GetDemoMessage (void)
|
|||
break;
|
||||
|
||||
case dem_read:
|
||||
readit:
|
||||
// get the next message
|
||||
Qread (cls.demofile, &net_message->message->cursize, 4);
|
||||
net_message->message->cursize = LittleLong
|
||||
|
@ -282,6 +328,20 @@ CL_GetDemoMessage (void)
|
|||
CL_StopPlayback ();
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (cls.demoplayback2) {
|
||||
tracknum = Cam_TrackNum ();
|
||||
|
||||
if (cls.lasttype == dem_multiple) {
|
||||
if (tracknum == -1)
|
||||
goto nextdemomessage;
|
||||
if (!(cls.lastto & (1 << tracknum)))
|
||||
goto nextdemomessage;
|
||||
} else if (cls.lasttype == dem_single) {
|
||||
if (tracknum == -1 || cls.lastto != spec_track)
|
||||
goto nextdemomessage;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case dem_set:
|
||||
|
@ -289,8 +349,38 @@ CL_GetDemoMessage (void)
|
|||
cls.netchan.outgoing_sequence = LittleLong (i);
|
||||
Qread (cls.demofile, &i, 4);
|
||||
cls.netchan.incoming_sequence = LittleLong (i);
|
||||
if (cls.demoplayback2) {
|
||||
cls.netchan.incoming_acknowledged =
|
||||
cls.netchan.incoming_sequence;
|
||||
goto nextdemomessage;
|
||||
}
|
||||
break;
|
||||
|
||||
case dem_multiple:
|
||||
r = Qread (cls.demofile, &i, 4);
|
||||
if (r != 4) {
|
||||
CL_StopPlayback ();
|
||||
return 0;
|
||||
}
|
||||
cls.lastto = LittleLong (i);
|
||||
cls.lasttype = dem_multiple;
|
||||
goto readit;
|
||||
|
||||
case dem_single:
|
||||
cls.lastto = c >> 3;
|
||||
cls.lasttype = dem_single;
|
||||
goto readit;
|
||||
|
||||
case dem_stats:
|
||||
cls.lastto = c >> 3;
|
||||
cls.lasttype = dem_stats;
|
||||
goto readit;
|
||||
|
||||
case dem_all:
|
||||
cls.lastto = 0;
|
||||
cls.lasttype = dem_all;
|
||||
goto readit;
|
||||
|
||||
default:
|
||||
Con_Printf ("Corrupted demo.\n");
|
||||
CL_StopPlayback ();
|
||||
|
@ -789,10 +879,23 @@ CL_StartDemo (void)
|
|||
}
|
||||
|
||||
cls.demoplayback = true;
|
||||
if (strequal (COM_FileExtension (name), ".mvd")) {
|
||||
cls.demoplayback2 = true;
|
||||
Con_Printf ("mvd\n");
|
||||
} else {
|
||||
Con_Printf ("qwd\n");
|
||||
}
|
||||
CL_SetState (ca_demostart);
|
||||
Netchan_Setup (&cls.netchan, net_from, 0);
|
||||
realtime = 0;
|
||||
cls.findtrack = true;
|
||||
cls.lasttype = 0;
|
||||
cls.lastto = 0;
|
||||
cls.prevtime = 0;
|
||||
cls.basetime = 0;
|
||||
demotime_cached = 0;
|
||||
nextdemotime = 0;
|
||||
CL_ClearPredict ();
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -301,7 +301,8 @@ CL_ParsePacketEntities (qboolean delta)
|
|||
from = MSG_ReadByte (net_message);
|
||||
|
||||
oldpacket = cl.frames[newpacket].delta_sequence;
|
||||
|
||||
if (cls.demoplayback2)
|
||||
from = oldpacket = (cls.netchan.incoming_sequence - 1);
|
||||
if ((from & UPDATE_MASK) != (oldpacket & UPDATE_MASK))
|
||||
Con_DPrintf ("WARNING: from mismatch\n");
|
||||
} else
|
||||
|
@ -564,10 +565,10 @@ CL_ClearProjectiles (void)
|
|||
Nails are passed as efficient temporary entities
|
||||
*/
|
||||
void
|
||||
CL_ParseProjectiles (void)
|
||||
CL_ParseProjectiles (qboolean nail2)
|
||||
{
|
||||
byte bits[6];
|
||||
int i, c, d, j;
|
||||
int i, c, d, j, num;
|
||||
entity_t *pr;
|
||||
|
||||
c = MSG_ReadByte (net_message);
|
||||
|
@ -578,6 +579,11 @@ CL_ParseProjectiles (void)
|
|||
d = c;
|
||||
|
||||
for (i = 0; i < d; i++) {
|
||||
if (nail2)
|
||||
num = MSG_ReadByte (net_message);
|
||||
else
|
||||
num = 0;
|
||||
|
||||
for (j = 0; j < 6; j++)
|
||||
bits[j] = MSG_ReadByte (net_message);
|
||||
|
||||
|
@ -595,8 +601,11 @@ CL_ParseProjectiles (void)
|
|||
|
||||
if (d < c) {
|
||||
c = (c - d) * 6;
|
||||
for (i = 0; i < c; i++)
|
||||
for (i = 0; i < c; i++) {
|
||||
if (nail2)
|
||||
MSG_ReadByte (net_message);
|
||||
MSG_ReadByte (net_message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -618,19 +627,98 @@ CL_LinkProjectiles (void)
|
|||
}
|
||||
}
|
||||
|
||||
#define DF_ORIGIN 1
|
||||
#define DF_ANGLES (1<<3)
|
||||
#define DF_EFFECTS (1<<6)
|
||||
#define DF_SKINNUM (1<<7)
|
||||
#define DF_DEAD (1<<8)
|
||||
#define DF_GIB (1<<9)
|
||||
#define DF_WEAPONFRAME (1<<10)
|
||||
#define DF_MODEL (1<<11)
|
||||
|
||||
int
|
||||
TranslateFlags (int src)
|
||||
{
|
||||
int dst = 0;
|
||||
|
||||
if (src & DF_EFFECTS)
|
||||
dst |= PF_EFFECTS;
|
||||
if (src & DF_SKINNUM)
|
||||
dst |= PF_SKINNUM;
|
||||
if (src & DF_DEAD)
|
||||
dst |= PF_DEAD;
|
||||
if (src & DF_GIB)
|
||||
dst |= PF_GIB;
|
||||
if (src & DF_WEAPONFRAME)
|
||||
dst |= PF_WEAPONFRAME;
|
||||
if (src & DF_MODEL)
|
||||
dst |= PF_MODEL;
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
||||
void
|
||||
CL_ParsePlayerinfo (void)
|
||||
{
|
||||
int flags, msec, num, i;
|
||||
player_state_t *state;
|
||||
int flags, msec, num, i;
|
||||
player_info_t *info;
|
||||
player_state_t *state, *prevstate;
|
||||
static player_state_t dummy;
|
||||
|
||||
num = MSG_ReadByte (net_message);
|
||||
if (num > MAX_CLIENTS)
|
||||
Host_Error ("CL_ParsePlayerinfo: bad num");
|
||||
|
||||
info = &cl.players[num];
|
||||
state = &cl.frames[parsecountmod].playerstate[num];
|
||||
|
||||
state->number = num;
|
||||
|
||||
if (cls.demoplayback2) {
|
||||
if (info->prevcount > cl.parsecount || !cl.parsecount) {
|
||||
prevstate = &dummy;
|
||||
} else {
|
||||
if (cl.parsecount - info->prevcount >= UPDATE_BACKUP-1)
|
||||
prevstate = &dummy;
|
||||
else
|
||||
prevstate = &cl.frames[info->prevcount
|
||||
& UPDATE_MASK].playerstate[num];
|
||||
}
|
||||
info->prevcount = cl.parsecount;
|
||||
|
||||
if (cls.findtrack && info->stats[STAT_HEALTH] != 0) {
|
||||
autocam = CAM_TRACK;
|
||||
Cam_Lock (num);
|
||||
ideal_track = num;
|
||||
cls.findtrack = false;
|
||||
}
|
||||
|
||||
memcpy(state, prevstate, sizeof(player_state_t));
|
||||
|
||||
flags = MSG_ReadShort (net_message);
|
||||
state->flags = TranslateFlags(flags);
|
||||
state->messagenum = cl.parsecount;
|
||||
state->command.msec = 0;
|
||||
state->frame = MSG_ReadByte (net_message);
|
||||
state->state_time = parsecounttime;
|
||||
for (i=0; i <3; i++)
|
||||
if (flags & (DF_ORIGIN << i))
|
||||
state->origin[i] = MSG_ReadCoord (net_message);
|
||||
for (i=0; i <3; i++)
|
||||
if (flags & (DF_ANGLES << i))
|
||||
state->command.angles[i] = MSG_ReadAngle16 (net_message);
|
||||
if (flags & DF_MODEL)
|
||||
state->modelindex = MSG_ReadByte (net_message);
|
||||
if (flags & DF_SKINNUM)
|
||||
state->skinnum = MSG_ReadByte (net_message);
|
||||
if (flags & DF_EFFECTS)
|
||||
state->effects = MSG_ReadByte (net_message);
|
||||
if (flags & DF_WEAPONFRAME)
|
||||
state->weaponframe = MSG_ReadByte (net_message);
|
||||
VectorCopy (state->command.angles, state->viewangles);
|
||||
return;
|
||||
}
|
||||
|
||||
flags = state->flags = MSG_ReadShort (net_message);
|
||||
|
||||
state->messagenum = cl.parsecount;
|
||||
|
@ -830,7 +918,7 @@ CL_LinkPlayers (void)
|
|||
|
||||
// only predict half the move to minimize overruns
|
||||
msec = 500 * (playertime - state->state_time);
|
||||
if (msec <= 0 || (!cl_predict_players->int_val)) {
|
||||
if (msec <= 0 || (!cl_predict_players->int_val) || cls.demoplayback2) {
|
||||
VectorCopy (state->origin, ent->origin);
|
||||
} else { // predict players movement
|
||||
state->command.msec = msec = min (msec, 255);
|
||||
|
@ -927,6 +1015,13 @@ CL_SetSolidEntities (void)
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
CL_ClearPredict (void)
|
||||
{
|
||||
memset (predicted_players, 0, sizeof (predicted_players));
|
||||
//fixangle = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
Calculate the new position of players, without other player clipping
|
||||
|
||||
|
|
|
@ -670,7 +670,7 @@ CL_SendCmd (void)
|
|||
sizebuf_t buf;
|
||||
usercmd_t *cmd, *oldcmd;
|
||||
|
||||
if (cls.demoplayback)
|
||||
if (cls.demoplayback && !cls.demoplayback2)
|
||||
return; // sendcmds come from the demo
|
||||
|
||||
// save this command off for prediction
|
||||
|
@ -684,6 +684,11 @@ CL_SendCmd (void)
|
|||
|
||||
build_cmd (cmd);
|
||||
|
||||
if (cls.demoplayback2) {
|
||||
cls.netchan.outgoing_sequence++;
|
||||
return;
|
||||
}
|
||||
|
||||
// send this and the previous cmds in the message, so
|
||||
// if the last packet was dropped, it can be recovered
|
||||
buf.maxsize = 128;
|
||||
|
|
|
@ -471,6 +471,7 @@ CL_Disconnect (void)
|
|||
CL_SetState (ca_disconnected);
|
||||
|
||||
cls.demoplayback = cls.demorecording = cls.timedemo = false;
|
||||
cls.demoplayback2 = false;
|
||||
|
||||
CL_RemoveQFInfoKeys ();
|
||||
}
|
||||
|
@ -1049,7 +1050,7 @@ CL_ReadPackets (void)
|
|||
SL_CheckPing (NET_AdrToString (net_from));
|
||||
continue;
|
||||
}
|
||||
if (net_message->message->cursize < 8) {
|
||||
if (net_message->message->cursize < 8 && !cls.demoplayback2) {
|
||||
Con_Printf ("%s: Runt packet\n", NET_AdrToString (net_from));
|
||||
continue;
|
||||
}
|
||||
|
@ -1061,14 +1062,18 @@ CL_ReadPackets (void)
|
|||
NET_AdrToString (net_from));
|
||||
continue;
|
||||
}
|
||||
if (!Netchan_Process (&cls.netchan))
|
||||
continue; // wasn't accepted for some reason
|
||||
if (!cls.demoplayback2) {
|
||||
if (!Netchan_Process (&cls.netchan))
|
||||
continue; // wasn't accepted for some reason
|
||||
} else {
|
||||
MSG_BeginReading (net_message);
|
||||
}
|
||||
if (cls.state != ca_disconnected)
|
||||
CL_ParseServerMessage ();
|
||||
}
|
||||
|
||||
// check timeout
|
||||
if (cls.state >= ca_connected
|
||||
if (!cls.demoplayback && cls.state >= ca_connected
|
||||
&& realtime - cls.netchan.last_received > cl_timeout->value) {
|
||||
Con_Printf ("\nServer connection timed out.\n");
|
||||
CL_Disconnect ();
|
||||
|
@ -1543,6 +1548,23 @@ Host_Frame (float time)
|
|||
// fetch results from server
|
||||
CL_ReadPackets ();
|
||||
|
||||
if (cls.demoplayback2) {
|
||||
player_state_t *self, *oldself;
|
||||
|
||||
self = &cl.frames[cl.parsecount
|
||||
& UPDATE_MASK].playerstate[cl.playernum];
|
||||
oldself = &cl.frames[(cls.netchan.outgoing_sequence - 1)
|
||||
& UPDATE_MASK].playerstate[cl.playernum];
|
||||
self->messagenum = cl.parsecount;
|
||||
VectorCopy (oldself->origin, self->origin);
|
||||
VectorCopy (oldself->velocity, self->velocity);
|
||||
VectorCopy (oldself->viewangles, self->viewangles);
|
||||
|
||||
CL_ParseClientdata ();
|
||||
|
||||
cls.netchan.outgoing_sequence = cl.parsecount + 1;
|
||||
}
|
||||
|
||||
// send intentions now
|
||||
// resend a connection request if necessary
|
||||
if (cls.state == ca_disconnected) {
|
||||
|
|
|
@ -60,6 +60,7 @@ static const char rcsid[] =
|
|||
#include "QF/gib_thread.h"
|
||||
|
||||
#include "bothdefs.h"
|
||||
#include "cl_cam.h"
|
||||
#include "cl_ents.h"
|
||||
#include "cl_input.h"
|
||||
#include "cl_main.h"
|
||||
|
@ -138,7 +139,7 @@ char *svc_strings[] = {
|
|||
"svc_setinfo",
|
||||
"svc_serverinfo",
|
||||
"svc_updatepl",
|
||||
"NEW PROTOCOL",
|
||||
"svc_nails2", // FIXME from qwex
|
||||
"NEW PROTOCOL",
|
||||
"NEW PROTOCOL",
|
||||
"NEW PROTOCOL",
|
||||
|
@ -658,11 +659,18 @@ CL_ParseServerData (void)
|
|||
snprintf (fn, sizeof (fn), "cmd_warncmd %d\n", cmd_warncmd_val);
|
||||
Cbuf_AddText (cl_cbuf, fn);
|
||||
}
|
||||
// parse player slot, high bit means spectator
|
||||
cl.playernum = MSG_ReadByte (net_message);
|
||||
if (cl.playernum & 128) {
|
||||
|
||||
if (cls.demoplayback2) {
|
||||
realtime = cls.basetime = MSG_ReadFloat (net_message);
|
||||
cl.playernum = 31;
|
||||
cl.spectator = true;
|
||||
cl.playernum &= ~128;
|
||||
} else {
|
||||
// parse player slot, high bit means spectator
|
||||
cl.playernum = MSG_ReadByte (net_message);
|
||||
if (cl.playernum & 128) {
|
||||
cl.spectator = true;
|
||||
cl.playernum &= ~128;
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: evil hack so NQ and QW can share sound code
|
||||
|
@ -915,10 +923,13 @@ CL_ParseClientdata (void)
|
|||
oldparsecountmod = parsecountmod;
|
||||
|
||||
i = cls.netchan.incoming_acknowledged;
|
||||
|
||||
cl.parsecount = i;
|
||||
i &= UPDATE_MASK;
|
||||
parsecountmod = i;
|
||||
frame = &cl.frames[i];
|
||||
if (cls.demoplayback2)
|
||||
frame->senttime = realtime - host_frametime;//realtime;
|
||||
parsecounttime = cl.frames[i].senttime;
|
||||
|
||||
frame->receivedtime = realtime;
|
||||
|
@ -1064,6 +1075,12 @@ CL_SetStat (int stat, int value)
|
|||
if (stat < 0 || stat >= MAX_CL_STATS)
|
||||
Host_Error ("CL_SetStat: %i is invalid", stat);
|
||||
|
||||
if (cls.demoplayback2) {
|
||||
cl.players[cls.lastto].stats[stat] = value;
|
||||
if (Cam_TrackNum () != cls.lastto)
|
||||
return;
|
||||
}
|
||||
|
||||
Sbar_Changed ();
|
||||
|
||||
switch (stat) {
|
||||
|
@ -1211,7 +1228,8 @@ CL_ParseServerMessage (void)
|
|||
// other commands
|
||||
switch (cmd) {
|
||||
default:
|
||||
Host_Error ("CL_ParseServerMessage: Illegible server message");
|
||||
Host_Error ("CL_ParseServerMessage: Illegible server "
|
||||
"message: %d\n", cmd);
|
||||
break;
|
||||
|
||||
case svc_nop:
|
||||
|
@ -1287,7 +1305,19 @@ CL_ParseServerMessage (void)
|
|||
break;
|
||||
|
||||
case svc_setangle:
|
||||
MSG_ReadAngleV (net_message, cl.viewangles);
|
||||
if (!cls.demoplayback2) {
|
||||
MSG_ReadAngleV (net_message, cl.viewangles);
|
||||
} else {
|
||||
j = MSG_ReadByte(net_message);
|
||||
//fixangle |= 1 << j;
|
||||
if (j != Cam_TrackNum()) {
|
||||
MSG_ReadAngle (net_message);
|
||||
MSG_ReadAngle (net_message);
|
||||
MSG_ReadAngle (net_message);
|
||||
} else {
|
||||
MSG_ReadAngleV (net_message, cl.viewangles);
|
||||
}
|
||||
}
|
||||
// FIXME cl.viewangles[PITCH] = cl.viewangles[ROLL] = 0;
|
||||
break;
|
||||
|
||||
|
@ -1453,7 +1483,11 @@ CL_ParseServerMessage (void)
|
|||
break;
|
||||
|
||||
case svc_nails:
|
||||
CL_ParseProjectiles ();
|
||||
CL_ParseProjectiles (false);
|
||||
break;
|
||||
|
||||
case svc_nails2: // FIXME from qwex
|
||||
CL_ParseProjectiles (true);
|
||||
break;
|
||||
|
||||
case svc_chokecount: // some preceding packets were choked
|
||||
|
|
Loading…
Reference in a new issue