Update VR-Inputs extension. Should now be more resilient against impulse loss, should also report better ping times.

git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@5966 fc73d0e0-1445-4013-8a0c-d673dee63da5
This commit is contained in:
Spoike 2021-07-17 15:10:01 +00:00
parent 0fd629f2d2
commit e90a0b3945
7 changed files with 432 additions and 235 deletions

View File

@ -34,9 +34,10 @@ static cvar_t cl_movement = CVARD("cl_movement","1", "Specifies whether to send
cvar_t cl_nodelta = CVAR("cl_nodelta","0");
cvar_t cl_c2sdupe = CVAR("cl_c2sdupe", "0");
cvar_t cl_c2sdupe = CVARD("cl_c2sdupe", "0", "Send duplicate copies of packets to the server. This avoids extra latency caused by packetloss, but could also make the problem worse.");
cvar_t cl_c2spps = CVARD("cl_c2spps", "0", "Reduces outgoing packet rates by dropping up to a third of outgoing packets.");
cvar_t cl_c2sImpulseBackup = CVARD("cl_c2sImpulseBackup","3", "Prevents the cl_c2spps setting from dropping redundant packets that contain impulses, in an attempt to keep impulses more reliable.");
static cvar_t cl_c2sMaxRedundancy = CVARD("cl_c2sMaxRedundancy","5", "This is the maximum number of input frames to send in each input packet. Values greater than 1 provide redundancy and avoid prediction misses, though you might find cl_c2sdupe provides equivelent result and at lower latency. It is locked at 3 for vanilla quakeworld, and locked at 1 for vanilla netquake.");
cvar_t cl_netfps = CVARD("cl_netfps", "150", "Send up to this many packets to the server per second. The rate used is also limited by the server which usually forces a cap to this setting of 77. Low packet rates can result in extra extrapolation to try to hide the resulting latencies.");
cvar_t cl_sparemsec = CVARCD("cl_sparemsec", "10", CL_SpareMsec_Callback, "Allows the 'banking' of a little extra time, so that one slow frame will not delay the timing of the following frame so much.");
cvar_t cl_queueimpulses = CVARD("cl_queueimpulses", "0", "Queues unsent impulses instead of replacing them. This avoids the need for extra wait commands (and the timing issues of such commands), but potentially increases latency and can cause scripts to be desynced with regard to buttons and impulses.");
@ -1213,6 +1214,82 @@ static void CL_FinishMove (usercmd_t *cmd, int pnum)
cmd->impulse = 0;
}
static qboolean CLFTE_SendVRCmd (sizebuf_t *buf, unsigned int seats)
{
//compute the delay between receiving the frame we're acking and when we're sending the new frame
unsigned int cldelay = (realtime - cl.inframes[cls.netchan.incoming_sequence&UPDATE_MASK].receivedtime)*10000; //this is to report actual network latency instead of just reporting our packet rate (framerates may still be a factor).
unsigned int lost = CL_CalcNet(r_netgraph.value); //report packetloss
unsigned int flags = 0;
unsigned int first = cl.ackedmovesequence+1; //no point resending that which has already been acked.
unsigned int last = cl.movesequence+1; //we want to ignore moveseq itself
unsigned int frame, seat, count, i;
const usercmd_t *from, *to;
qboolean dontdrop = false;
if (first > last)
first = last-1;
if (first < last-(countof(cl.outframes)-2))
first = last-(countof(cl.outframes)-2);
if (first < 1)
first = 1;
if (last < first)
count = 0;
else
count = last-first;
if (count > max(1,cl_c2sMaxRedundancy.ival))
count = max(1,cl_c2sMaxRedundancy.ival);
if (cl.inframes[cls.netchan.incoming_sequence&UPDATE_MASK].receivedtime<0)
cldelay = 0; //erk?
MSG_WriteByte (buf, clcfte_move);
#ifdef NQPROT
if (cls.protocol == CP_NETQUAKE) //nq uses fully separate packet+movement sequences (unlike qw).
MSG_WriteShort(buf, (last-1)&0xffff);
#endif
if (seats!=1)
flags |= VRM_SEATS;
if (lost)
flags |= VRM_LOSS;
if (cldelay)
flags |= VRM_DELAY;
if (count!=3)
flags |= VRM_FRAMES;
if (cl.numackframes)
flags |= VRM_ACKS;
MSG_WriteUInt64 (buf, flags);
if (flags & VRM_SEATS)
MSG_WriteUInt64 (buf, seats);
if (flags & VRM_FRAMES)
MSG_WriteUInt64 (buf, count);
if (flags & VRM_LOSS)
MSG_WriteByte (buf, (qbyte)lost);
if (flags & VRM_DELAY)
MSG_WriteByte (buf, bound(0,cldelay,255)); //a byte should always be enough for any framerate above 40.
if (flags & VRM_ACKS)
{
MSG_WriteUInt64(buf, cl.numackframes);
for (i = 0; i < cl.numackframes; i++)
MSG_WriteLong(buf, cl.ackframes[i]);
cl.numackframes = 0;
}
from = &nullcmd;
for (seat = 0; seat < seats; seat++)
for (frame = last-count; frame < last; frame++)
{
to = &cl.outframes[frame&UPDATE_MASK].cmd[seat];
MSGFTE_WriteDeltaUsercmd (buf, from, to);
if (to->impulse && (int)(last-frame)>=cl_c2sImpulseBackup.ival)
dontdrop = true;
from = to;
}
return dontdrop;
}
void CL_UpdatePrydonCursor(usercmd_t *from, int pnum)
{
int hit;
@ -1318,59 +1395,54 @@ void CLNQ_SendMove (usercmd_t *cmd, int pnum, sizebuf_t *buf)
else if (cls.fteprotocolextensions2 & PEXT2_PREDINFO)
MSG_WriteShort(buf, cl.movesequence&0xffff);
if (cls.fteprotocolextensions2 & PEXT2_VRINPUTS)
MSGFTE_WriteDeltaUsercmd(buf, &nullcmd, cmd);
else
MSG_WriteFloat (buf, cmd->fservertime); // use latest time. because ping reports!
for (i=0 ; i<3 ; i++)
{
MSG_WriteFloat (buf, cmd->fservertime); // use latest time. because ping reports!
for (i=0 ; i<3 ; i++)
if (cls.protocol_nq == CPNQ_FITZ666 || (cls.proquake_angles_hack && buf->prim.anglesize <= 1))
{
if (cls.protocol_nq == CPNQ_FITZ666 || (cls.proquake_angles_hack && buf->prim.anglesize <= 1))
{
//fitz/proquake protocols are always 16bit for this angle and 8bit elsewhere. rmq is always at least 16bit
//the above logic should satify everything.
MSG_WriteAngle16 (buf, cl.playerview[pnum].viewangles[i]);
}
else
MSG_WriteAngle (buf, cl.playerview[pnum].viewangles[i]);
}
MSG_WriteShort (buf, cmd->forwardmove);
MSG_WriteShort (buf, cmd->sidemove);
MSG_WriteShort (buf, cmd->upmove);
bits = cmd->buttons;
if (cls.fteprotocolextensions2 & PEXT2_PRYDONCURSOR)
{
if (cmd->cursor_screen[0] || cmd->cursor_screen[1] ||
cmd->cursor_start[0] || cmd->cursor_start[1] || cmd->cursor_start[2] ||
cmd->cursor_impact[0] || cmd->cursor_impact[1] || cmd->cursor_impact[2] ||
cmd->cursor_entitynumber)
bits |= (1u<<31); //set it if there's actually something to send.
MSG_WriteLong (buf, bits);
}
else if (cls.protocol_nq >= CPNQ_DP6)
{
MSG_WriteLong (buf, bits);
bits |= (1u<<31); //unconditionally set it (without writing it)
//fitz/proquake protocols are always 16bit for this angle and 8bit elsewhere. rmq is always at least 16bit
//the above logic should satify everything.
MSG_WriteAngle16 (buf, cl.playerview[pnum].viewangles[i]);
}
else
MSG_WriteByte (buf, cmd->buttons);
MSG_WriteByte (buf, cmd->impulse);
MSG_WriteAngle (buf, cl.playerview[pnum].viewangles[i]);
}
if (bits & (1u<<31))
{
MSG_WriteShort (buf, cmd->cursor_screen[0] * 32767.0f);
MSG_WriteShort (buf, cmd->cursor_screen[1] * 32767.0f);
MSG_WriteFloat (buf, cmd->cursor_start[0]);
MSG_WriteFloat (buf, cmd->cursor_start[1]);
MSG_WriteFloat (buf, cmd->cursor_start[2]);
MSG_WriteFloat (buf, cmd->cursor_impact[0]);
MSG_WriteFloat (buf, cmd->cursor_impact[1]);
MSG_WriteFloat (buf, cmd->cursor_impact[2]);
MSG_WriteEntity (buf, cmd->cursor_entitynumber);
}
MSG_WriteShort (buf, cmd->forwardmove);
MSG_WriteShort (buf, cmd->sidemove);
MSG_WriteShort (buf, cmd->upmove);
bits = cmd->buttons;
if (cls.fteprotocolextensions2 & PEXT2_PRYDONCURSOR)
{
if (cmd->cursor_screen[0] || cmd->cursor_screen[1] ||
cmd->cursor_start[0] || cmd->cursor_start[1] || cmd->cursor_start[2] ||
cmd->cursor_impact[0] || cmd->cursor_impact[1] || cmd->cursor_impact[2] ||
cmd->cursor_entitynumber)
bits |= (1u<<31); //set it if there's actually something to send.
MSG_WriteLong (buf, bits);
}
else if (cls.protocol_nq >= CPNQ_DP6)
{
MSG_WriteLong (buf, bits);
bits |= (1u<<31); //unconditionally set it (without writing it)
}
else
MSG_WriteByte (buf, cmd->buttons);
MSG_WriteByte (buf, cmd->impulse);
if (bits & (1u<<31))
{
MSG_WriteShort (buf, cmd->cursor_screen[0] * 32767.0f);
MSG_WriteShort (buf, cmd->cursor_screen[1] * 32767.0f);
MSG_WriteFloat (buf, cmd->cursor_start[0]);
MSG_WriteFloat (buf, cmd->cursor_start[1]);
MSG_WriteFloat (buf, cmd->cursor_start[2]);
MSG_WriteFloat (buf, cmd->cursor_impact[0]);
MSG_WriteFloat (buf, cmd->cursor_impact[1]);
MSG_WriteFloat (buf, cmd->cursor_impact[2]);
MSG_WriteEntity (buf, cmd->cursor_entitynumber);
}
}
@ -1411,26 +1483,31 @@ void CLNQ_SendCmd(sizebuf_t *buf)
CL_ClearPendingCommands();
//inputs are only sent once we receive an entity.
if (cls.signon == 4)
{
for (seat = 0; seat < cl.splitclients; seat++)
{
// send the unreliable message
// if (independantphysics[seat].impulse && !cls.netchan.message.cursize)
// CLNQ_SendMove (&cl.outframes[i].cmd[seat], seat, &cls.netchan.message);
// else
CLNQ_SendMove (&cl.outframes[i].cmd[seat], seat, buf);
}
}
if (cls.fteprotocolextensions2 & PEXT2_VRINPUTS)
CLFTE_SendVRCmd(buf, (cls.signon != 4 || cls.state == ca_connected)?0:cl.splitclients);
else
MSG_WriteByte (buf, clc_nop);
for (i = 0; i < cl.numackframes; i++)
{
MSG_WriteByte(buf, clcdp_ackframe);
MSG_WriteLong(buf, cl.ackframes[i]);
if (cls.signon == 4)
{
for (seat = 0; seat < cl.splitclients; seat++)
{
// send the unreliable message
// if (independantphysics[seat].impulse && !cls.netchan.message.cursize)
// CLNQ_SendMove (&cl.outframes[i].cmd[seat], seat, &cls.netchan.message);
// else
CLNQ_SendMove (&cl.outframes[i].cmd[seat], seat, buf);
}
}
else
MSG_WriteByte (buf, clc_nop);
for (i = 0; i < cl.numackframes; i++)
{
MSG_WriteByte(buf, clcdp_ackframe);
MSG_WriteLong(buf, cl.ackframes[i]);
}
cl.numackframes = 0;
}
cl.numackframes = 0;
}
#else
void Name_Callback(struct cvar_s *var, char *oldvalue)
@ -1924,53 +2001,58 @@ qboolean CLQW_SendCmd (sizebuf_t *buf, qboolean actuallysend)
}
CL_ClearPendingCommands();
cmd = &cl.outframes[curframe].cmd[0];
if (cmd->cursor_screen[0] || cmd->cursor_screen[1] || cmd->cursor_entitynumber ||
cmd->cursor_start[0] || cmd->cursor_start[1] || cmd->cursor_start[2] ||
cmd->cursor_impact[0] || cmd->cursor_impact[1] || cmd->cursor_impact[2])
if (cls.fteprotocolextensions2 & PEXT2_VRINPUTS)
dontdrop = CLFTE_SendVRCmd(buf, clientcount);
else
{
MSG_WriteByte (buf, clcfte_prydoncursor);
MSG_WriteShort(buf, cmd->cursor_screen[0] * 32767.0f);
MSG_WriteShort(buf, cmd->cursor_screen[1] * 32767.0f);
MSG_WriteFloat(buf, cmd->cursor_start[0]);
MSG_WriteFloat(buf, cmd->cursor_start[1]);
MSG_WriteFloat(buf, cmd->cursor_start[2]);
MSG_WriteFloat(buf, cmd->cursor_impact[0]);
MSG_WriteFloat(buf, cmd->cursor_impact[1]);
MSG_WriteFloat(buf, cmd->cursor_impact[2]);
MSG_WriteEntity(buf, cmd->cursor_entitynumber);
cmd = &cl.outframes[curframe].cmd[0];
if (cmd->cursor_screen[0] || cmd->cursor_screen[1] || cmd->cursor_entitynumber ||
cmd->cursor_start[0] || cmd->cursor_start[1] || cmd->cursor_start[2] ||
cmd->cursor_impact[0] || cmd->cursor_impact[1] || cmd->cursor_impact[2])
{
MSG_WriteByte (buf, clcfte_prydoncursor);
MSG_WriteShort(buf, cmd->cursor_screen[0] * 32767.0f);
MSG_WriteShort(buf, cmd->cursor_screen[1] * 32767.0f);
MSG_WriteFloat(buf, cmd->cursor_start[0]);
MSG_WriteFloat(buf, cmd->cursor_start[1]);
MSG_WriteFloat(buf, cmd->cursor_start[2]);
MSG_WriteFloat(buf, cmd->cursor_impact[0]);
MSG_WriteFloat(buf, cmd->cursor_impact[1]);
MSG_WriteFloat(buf, cmd->cursor_impact[2]);
MSG_WriteEntity(buf, cmd->cursor_entitynumber);
}
MSG_WriteByte (buf, clc_move);
// save the position for a checksum qbyte
checksumIndex = buf->cursize;
MSG_WriteByte (buf, 0);
// write our lossage percentage
lost = CL_CalcNet(r_netgraph.value);
MSG_WriteByte (buf, (qbyte)lost);
firstsize=0;
for (plnum = 0; plnum<clientcount; plnum++)
{
cmd = &cl.outframes[curframe].cmd[plnum];
if (plnum)
MSG_WriteByte (buf, clc_move);
dontdrop = CL_WriteDeltas(plnum, buf) || dontdrop;
if (!firstsize)
firstsize = buf->cursize;
}
// calculate a checksum over the move commands
buf->data[checksumIndex] = COM_BlockSequenceCRCByte(
buf->data + checksumIndex + 1, firstsize - checksumIndex - 1,
seq_hash);
}
MSG_WriteByte (buf, clc_move);
// save the position for a checksum qbyte
checksumIndex = buf->cursize;
MSG_WriteByte (buf, 0);
// write our lossage percentage
lost = CL_CalcNet(r_netgraph.value);
MSG_WriteByte (buf, (qbyte)lost);
firstsize=0;
for (plnum = 0; plnum<clientcount; plnum++)
{
cmd = &cl.outframes[curframe].cmd[plnum];
if (plnum)
MSG_WriteByte (buf, clc_move);
dontdrop = CL_WriteDeltas(plnum, buf) || dontdrop;
if (!firstsize)
firstsize = buf->cursize;
}
// calculate a checksum over the move commands
buf->data[checksumIndex] = COM_BlockSequenceCRCByte(
buf->data + checksumIndex + 1, firstsize - checksumIndex - 1,
seq_hash);
// request delta compression of entities
if (cls.netchan.outgoing_sequence - cl.validsequence >= UPDATE_BACKUP-1)
cl.validsequence = 0;
@ -2599,6 +2681,7 @@ void CL_InitInput (void)
Cvar_Register (&cl_c2sdupe, inputnetworkcvargroup);
Cvar_Register (&cl_c2sImpulseBackup, inputnetworkcvargroup);
Cvar_Register (&cl_c2sMaxRedundancy, inputnetworkcvargroup);
Cvar_Register (&cl_c2spps, inputnetworkcvargroup);
Cvar_Register (&cl_queueimpulses, inputnetworkcvargroup);
Cvar_Register (&cl_netfps, inputnetworkcvargroup);

View File

@ -500,7 +500,7 @@ void CL_AckedInputFrame(int inseq, int outseq, qboolean worldstateokay)
cls.latency += 0.001; // drift up, so correction are needed
}
if (cl.inframes[inseq&UPDATE_MASK].invalid)
if (cls.protocol != CP_NETQUAKE && cl.inframes[inseq&UPDATE_MASK].invalid)
frame->latency = -4;
//and mark any missing ones as dropped

View File

@ -1674,9 +1674,6 @@ void MSGCL_WriteDeltaUsercmd (sizebuf_t *buf, const usercmd_t *from, const userc
MSGQ2_WriteDeltaUsercmd(buf, from, cmd);
else
#endif
if (cls.fteprotocolextensions2 & PEXT2_VRINPUTS)
MSGFTE_WriteDeltaUsercmd(buf, from, cmd);
else
MSGQW_WriteDeltaUsercmd(buf, from, cmd);
}
#endif

View File

@ -236,7 +236,7 @@ unsigned int Net_PextMask(unsigned int protover, qboolean fornq)
if (fornq)
{
//only ones that are tested
mask &= PEXT2_PRYDONCURSOR | PEXT2_VOICECHAT | PEXT2_SETANGLEDELTA | PEXT2_REPLACEMENTDELTAS | PEXT2_MAXPLAYERS | PEXT2_PREDINFO | PEXT2_NEWSIZEENCODING /*| PEXT2_VRINPUTS - FIXME: do we want multiple per packet, to cover packetloss?*/;
mask &= PEXT2_PRYDONCURSOR | PEXT2_VOICECHAT | PEXT2_SETANGLEDELTA | PEXT2_REPLACEMENTDELTAS | PEXT2_MAXPLAYERS | PEXT2_PREDINFO | PEXT2_NEWSIZEENCODING | PEXT2_VRINPUTS;
}
// else
// mask &= ~PEXT2_PREDINFO;

View File

@ -501,6 +501,13 @@ enum {
#define clcfte_prydoncursor 82
#define clcfte_voicechat 83
#define clcfte_brushedit 84
#define clcfte_move 85 //part of PEXT2_VRINPUTS. replaces clc_move+clcfte_prydoncursor+clcdp_ackframe
#define VRM_LOSS (1u<<0) //for server packetloss reports
#define VRM_DELAY (1u<<1) //for server to compute lag properly.
#define VRM_SEATS (1u<<2) //for splitscreen to work properly
#define VRM_FRAMES (1u<<3) //number of input frames in this packet.
#define VRM_ACKS (1u<<4) //number of sequence acks included in message.
//==============================================

View File

@ -2783,8 +2783,9 @@ qboolean SV_SendClientDatagram (client_t *client)
sentbytes = Netchan_Transmit (&client->netchan, msg.cursize, buf, SV_RateForClient(client));
if (ISNQCLIENT(client))
{
client_frame_t *frame = &client->frameunion.frames[client->netchan.outgoing_sequence & UPDATE_MASK];
client_frame_t *frame = &client->frameunion.frames[outframeseq & UPDATE_MASK];
frame->packetsizeout += sentbytes;
frame->ping_time = -1;
frame->senttime = realtime;
}
else if (ISQWCLIENT(client))

View File

@ -7931,6 +7931,124 @@ done:
SV_ClientPrintf(host_client, PRINT_HIGH, "qcrequest \"%s\" not supported\n", fname);
}
static double SVFTE_ExecuteClientMove(client_t *controller)
{
client_t *split = controller;
unsigned int flags = MSG_ReadUInt64();
unsigned int seats = (flags & VRM_SEATS)?MSG_ReadUInt64():1;
unsigned int frames = (flags & VRM_FRAMES)?MSG_ReadUInt64():3;
unsigned int loss = (flags & VRM_LOSS)?MSG_ReadByte():0;
double delay = (flags & VRM_DELAY)?MSG_ReadByte()/10000.0:0; //networked as 10ths of a millisecond.
unsigned int numacks = (flags & VRM_ACKS)?MSG_ReadUInt64():0;
usercmd_t old;
unsigned int seat, frame, a;
qboolean ran;
for (a = 0; a < numacks; a++)
{
controller->delta_sequence = MSG_ReadLong();
if (controller->delta_sequence == -1)
{
unsigned int e;
if (controller->pendingdeltabits)
controller->pendingdeltabits[0] = UF_REMOVE;
if (host_client->pendingcsqcbits)
for (e = 1; e < host_client->max_net_ents; e++)
if (host_client->pendingcsqcbits[e] & SENDFLAGS_PRESENT)
host_client->pendingcsqcbits[e] |= SENDFLAGS_USABLE;
}
SV_AckEntityFrame(controller, controller->delta_sequence);
}
for (seat = 0; seat < seats; seat++)
{
if (!split)
{ //err, they sent too many seats... assume we kicked one.
for (frame = 0; frame < frames; frame++)
MSGFTE_ReadDeltaUsercmd(&nullcmd, &old);
continue;
}
host_client = split;
sv_player = split->edict;
split->lossage = loss;
split->localtime = loss;
//all sorts of reasons why we might not want to do physics here and now.
split->isindependant = !(sv_nqplayerphysics.ival || split->state < cs_spawned || SV_PlayerPhysicsQC || sv.paused || !sv.world.worldmodel || sv.world.worldmodel->loadstate != MLS_LOADED);
ran = false;
old = nullcmd;
for (frame = 0; frame < frames; frame++)
{
MSGFTE_ReadDeltaUsercmd(&old, &split->lastcmd);
old = split->lastcmd;
if (split->penalties & BAN_CRIPPLED)
{
split->lastcmd.forwardmove = 0;
split->lastcmd.sidemove = 0;
split->lastcmd.upmove = 0;
}
if (split->state == cs_spawned)
{
if (split->lastcmd.impulse)
split->edict->v->impulse = split->lastcmd.impulse;
if (split->isindependant)
{ //this protocol uses bigger timestamps instead of msecs
unsigned int curtime = sv.time*1000;
if (split->lastcmd.servertime < split->lastruncmd)
{
if (sv_showpredloss.ival)
Con_Printf("%s: client jumped %u msecs backwards (anti speed cheat)\n", split->name, split->lastruncmd - split->lastcmd.servertime);
}
else if (split->lastruncmd < split->lastcmd.servertime)
{
if (split->lastcmd.servertime > curtime)
{
//from last map?... attempted speedcheat?
if (sv_showpredloss.ival)
Con_Printf("%s: client is %u msecs in the future (anti speed cheat)\n", split->name, split->lastcmd.servertime - curtime);
split->lastcmd.servertime = curtime;
}
if (!ran)
{
SV_PreRunCmd();
ran=true;
}
split->lastcmd.msec = split->lastcmd.servertime - split->lastruncmd;
SV_RunCmd (&split->lastcmd, false);
split->lastruncmd = split->lastcmd.servertime;
}
}
else
{
SV_SetEntityButtons(split->edict, split->lastcmd.buttons);
split->lastcmd.buttons = 0;
}
}
}
if (ran)
SV_PostRunCmd();
//for framerate calcs
if (split->frameunion.frames)
split->frameunion.frames[split->netchan.outgoing_sequence&UPDATE_MASK].move_msecs = split->lastcmd.msec;
split->lastcmd.msec = 0;
split = split->controlled;
}
host_client = controller;
sv_player = host_client->edict;
return delay;
}
/*
===================
SV_ExecuteClientMessage
@ -8053,6 +8171,9 @@ void SV_ExecuteClientMessage (client_t *cl)
cl->delta_sequence = MSG_ReadByte ();
break;
case clcfte_move:
frame->ping_time -= SVFTE_ExecuteClientMove(cl);
break;
case clc_move:
if (split == cl)
{
@ -8069,27 +8190,18 @@ void SV_ExecuteClientMessage (client_t *cl)
if (split)
split->lossage = cl->lossage;
}
if (cl->fteprotocolextensions2 & PEXT2_VRINPUTS)
MSGQW_ReadDeltaUsercmd (&nullcmd, &oldest, PROTOCOL_VERSION_QW);
oldest.fservertime = frame->laggedtime; //not very accurate, but our best guess.
oldest.servertime = frame->laggedtime*1000; //not very accurate
if (split)
{
MSGFTE_ReadDeltaUsercmd (&nullcmd, &oldest);
MSGFTE_ReadDeltaUsercmd (&oldest, &oldcmd);
MSGFTE_ReadDeltaUsercmd (&oldcmd, &newcmd);
}
else
{
MSGQW_ReadDeltaUsercmd (&nullcmd, &oldest, PROTOCOL_VERSION_QW);
oldest.fservertime = frame->laggedtime; //not very accurate, but our best guess.
oldest.servertime = frame->laggedtime*1000; //not very accurate
if (split)
{
Vector2Copy(split->lastcmd.cursor_screen, oldest.cursor_screen);
VectorCopy(split->lastcmd.cursor_start, oldest.cursor_start);
VectorCopy(split->lastcmd.cursor_impact, oldest.cursor_impact);
oldest.cursor_entitynumber = split->lastcmd.cursor_entitynumber;
}
MSGQW_ReadDeltaUsercmd (&oldest, &oldcmd, PROTOCOL_VERSION_QW);
MSGQW_ReadDeltaUsercmd (&oldcmd, &newcmd, PROTOCOL_VERSION_QW);
Vector2Copy(split->lastcmd.cursor_screen, oldest.cursor_screen);
VectorCopy(split->lastcmd.cursor_start, oldest.cursor_start);
VectorCopy(split->lastcmd.cursor_impact, oldest.cursor_impact);
oldest.cursor_entitynumber = split->lastcmd.cursor_entitynumber;
}
MSGQW_ReadDeltaUsercmd (&oldest, &oldcmd, PROTOCOL_VERSION_QW);
MSGQW_ReadDeltaUsercmd (&oldcmd, &newcmd, PROTOCOL_VERSION_QW);
if (!split)
break; // either someone is trying to cheat, or they sent input commands for splitscreen clients they no longer own.
@ -8168,54 +8280,20 @@ void SV_ExecuteClientMessage (client_t *cl)
split->isindependant = true;
SV_PreRunCmd();
if (cl->fteprotocolextensions2 & PEXT2_VRINPUTS)
{ //this protocol uses bigger timestamps instead of msecs
usercmd_t *c;
unsigned int curtime = sv.time*1000;
if (newcmd.servertime < split->lastruncmd)
{
if (sv_showpredloss.ival)
Con_Printf("%s: client jumped %u msecs backwards (anti speed cheat)\n", split->name, split->lastruncmd - newcmd.servertime);
}
else while (split->lastruncmd < newcmd.servertime)
{
//try to find the oldest (valid) command.
if (split->lastruncmd < oldest.servertime)
c = &oldest;
else if (split->lastruncmd < oldcmd.servertime)
c = &oldcmd;
else
c = &newcmd;
if (c->servertime > curtime)
{
if (sv_showpredloss.ival)
Con_Printf("%s: client is %u msecs in the future (anti speed cheat)\n", split->name, c->servertime - curtime);
break; //from last map?... attempted speedcheat?
}
c->msec = c->servertime - split->lastruncmd;
SV_RunCmd (c, false);
split->lastruncmd = c->servertime;
}
}
else
if (net_drop < 20)
{
if (net_drop < 20)
while (net_drop > 2)
{
while (net_drop > 2)
{
SV_RunCmd (&split->lastcmd, false);
net_drop--;
}
if (net_drop > 1)
SV_RunCmd (&oldest, false);
if (net_drop > 0)
SV_RunCmd (&oldcmd, false);
SV_RunCmd (&split->lastcmd, false);
net_drop--;
}
SV_RunCmd (&newcmd, false);
host_client->lastruncmd = sv.time*1000;
if (net_drop > 1)
SV_RunCmd (&oldest, false);
if (net_drop > 0)
SV_RunCmd (&oldcmd, false);
}
SV_RunCmd (&newcmd, false);
host_client->lastruncmd = sv.time*1000;
if (!SV_PlayerPhysicsQC || host_client->spectator)
SV_PostRunCmd();
@ -8542,67 +8620,59 @@ void SVNQ_ReadClientMove (qboolean forceangle16)
else
host_client->last_sequence = 0;
if (host_client->fteprotocolextensions2 & PEXT2_VRINPUTS)
{ //this actually drops from 37 to 23 bytes (according to showpackets), so that's cool. obviously it goes up when vr inputs are actually networked...
MSGFTE_ReadDeltaUsercmd(&nullcmd, &cmd);
cmd.fservertime = cmd.servertime/1000.0;
}
else
cmd = nullcmd;
//read the time, woo... should be an ack of our serverside time.
cmd.fservertime = MSG_ReadFloat ();
if (cmd.fservertime < from->fservertime)
cmd.fservertime = from->fservertime;
if (cmd.fservertime > sv.time)
cmd.fservertime = sv.time;
if (cmd.fservertime < sv.time - 2) //if you do lag more than this, you won't get your free time.
cmd.fservertime = sv.time - 2;
cmd.servertime = cmd.fservertime*1000;
//read angles
for (i=0 ; i<3 ; i++)
{
cmd = nullcmd;
//read the time, woo... should be an ack of our serverside time.
cmd.fservertime = MSG_ReadFloat ();
if (cmd.fservertime < from->fservertime)
cmd.fservertime = from->fservertime;
if (cmd.fservertime > sv.time)
cmd.fservertime = sv.time;
if (cmd.fservertime < sv.time - 2) //if you do lag more than this, you won't get your free time.
cmd.fservertime = sv.time - 2;
cmd.servertime = cmd.fservertime*1000;
//read angles
for (i=0 ; i<3 ; i++)
{
float a;
if (forceangle16)
a = MSG_ReadAngle16 ();
else
a = MSG_ReadAngle ();
cmd.angles[i] = ANGLE2SHORT(a);
}
// read movement
cmd.forwardmove = MSG_ReadShort ();
cmd.sidemove = MSG_ReadShort ();
cmd.upmove = MSG_ReadShort ();
// read buttons
if (host_client->protocol == SCP_DARKPLACES6 || host_client->protocol == SCP_DARKPLACES7)
cmd.buttons = MSG_ReadLong() | (1u<<31);
else if (host_client->fteprotocolextensions2 & PEXT2_PRYDONCURSOR)
cmd.buttons = MSG_ReadLong();
float a;
if (forceangle16)
a = MSG_ReadAngle16 ();
else
cmd.buttons = MSG_ReadByte ();
a = MSG_ReadAngle ();
//impulse...
cmd.impulse = MSG_ReadByte ();
//weapon extension
if (cmd.buttons & (1u<<30))
cmd.weapon = MSG_ReadLong();
else
cmd.weapon = 0;
//cursor extension
if (cmd.buttons & (1u<<31))
SV_ReadPrydonCursor(&cmd);
//clear out extension buttons that are part of the protocol rather than actual buttons..
cmd.buttons &= ~((1u<<30)|(1u<<31));
cmd.angles[i] = ANGLE2SHORT(a);
}
// read movement
cmd.forwardmove = MSG_ReadShort ();
cmd.sidemove = MSG_ReadShort ();
cmd.upmove = MSG_ReadShort ();
// read buttons
if (host_client->protocol == SCP_DARKPLACES6 || host_client->protocol == SCP_DARKPLACES7)
cmd.buttons = MSG_ReadLong() | (1u<<31);
else if (host_client->fteprotocolextensions2 & PEXT2_PRYDONCURSOR)
cmd.buttons = MSG_ReadLong();
else
cmd.buttons = MSG_ReadByte ();
//impulse...
cmd.impulse = MSG_ReadByte ();
//weapon extension
if (cmd.buttons & (1u<<30))
cmd.weapon = MSG_ReadLong();
else
cmd.weapon = 0;
//cursor extension
if (cmd.buttons & (1u<<31))
SV_ReadPrydonCursor(&cmd);
//clear out extension buttons that are part of the protocol rather than actual buttons..
cmd.buttons &= ~((1u<<30)|(1u<<31));
//figure out ping
frame->ping_time = sv.time - cmd.fservertime;
@ -8784,6 +8854,45 @@ void SVNQ_ExecuteClientMessage (client_t *cl)
// cl->delta_sequence = MSG_ReadByte ();
// break;
case clcfte_move:
{
int seq = (unsigned short)MSG_ReadShort ();
unsigned int oldservertime = cl->lastcmd.servertime;
float delay = SVFTE_ExecuteClientMove(cl);
client_frame_t *frame;
//this is the input sequence that we'll need to ack later (no
if (seq < (host_client->last_sequence&0xffff))
host_client->last_sequence += 0x10000; //wrapped
host_client->last_sequence = (host_client->last_sequence&0xffff0000) | seq;
if (cl->lastsequence_acknowledged)
{
frame = &host_client->frameunion.frames[cl->netchan.incoming_acknowledged & UPDATE_MASK];
if (frame->ping_time == -1)
frame->ping_time = (realtime - frame->senttime) - delay;
}
else
{
frame = &host_client->frameunion.frames[cl->netchan.incoming_acknowledged & UPDATE_MASK];
frame->ping_time = (sv.time - cl->lastcmd.servertime/1000.0) - delay;
}
frame->move_msecs = cl->lastcmd.servertime - oldservertime;
if (frame->ping_time*1000 > sv_minping.value+1)
{
host_client->delay -= 0.001;
if (host_client->delay < 0)
host_client->delay = 0;
}
if (frame->ping_time*1000 < sv_minping.value)
{
host_client->delay += 0.001;
if (host_client->delay > 1)
host_client->delay = 1;
}
}
break;
case clc_move: //bytes: 16(nq), 19(proquake/fitz), 56(dp7)
if (cl->state != cs_spawned)
return; //shouldn't be sending moves at this point. typically they're stale, left from the previous map. this results in crashes if the protocol is different.