diff --git a/engine/client/cl_input.c b/engine/client/cl_input.c index eb2105ad4..775160a2d 100644 --- a/engine/client/cl_input.c +++ b/engine/client/cl_input.c @@ -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; plnumcursize; + } + + // 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; plnumcursize; - } - -// 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); diff --git a/engine/client/cl_parse.c b/engine/client/cl_parse.c index 364058ba2..aad18c280 100644 --- a/engine/client/cl_parse.c +++ b/engine/client/cl_parse.c @@ -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 diff --git a/engine/common/common.c b/engine/common/common.c index 477b526ee..f2324a56b 100644 --- a/engine/common/common.c +++ b/engine/common/common.c @@ -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 diff --git a/engine/common/net_chan.c b/engine/common/net_chan.c index 6811a12f0..243070916 100644 --- a/engine/common/net_chan.c +++ b/engine/common/net_chan.c @@ -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; diff --git a/engine/common/protocol.h b/engine/common/protocol.h index 0bf5eb40b..02b447c85 100644 --- a/engine/common/protocol.h +++ b/engine/common/protocol.h @@ -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. //============================================== diff --git a/engine/server/sv_send.c b/engine/server/sv_send.c index 1bed6c34b..078659dbf 100644 --- a/engine/server/sv_send.c +++ b/engine/server/sv_send.c @@ -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)) diff --git a/engine/server/sv_user.c b/engine/server/sv_user.c index e207fc2c6..d283b28e8 100644 --- a/engine/server/sv_user.c +++ b/engine/server/sv_user.c @@ -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.