diff --git a/engine/client/cl_pred.c b/engine/client/cl_pred.c index 7c03b513a..115807bc2 100644 --- a/engine/client/cl_pred.c +++ b/engine/client/cl_pred.c @@ -906,40 +906,10 @@ void CL_PredictEntityMovement(entity_state_t *estate, float age) } } -/* -============== -CL_PredictMove -============== -*/ -void CL_PredictMovePNum (int seat) +float CL_GetPredictionRealtime(playerview_t *pv) { - //when this is called, the entity states have been interpolated. - //interpolation state should be updated to match prediction state, so entities move correctly in mirrors/portals. - - //this entire function is pure convolouted bollocks. - struct { - int frame; - double time; - player_state_t *state; - usercmd_t *cmd; - } from, to; - playerview_t *pv = &cl.playerview[seat]; - int i; - float f; - outframe_t *backdate; - player_state_t framebuf[2]; //need two framebufs so we can interpolate between two states. - static player_state_t nullstate; - int oldphysent; - double simtime; //this is server time if nopred is set (lerp-only), and local time if we're predicting - extern cvar_t cl_netfps; - lerpents_t *le; - qboolean nopred; - qboolean lerpangles = false; - int trackent; - qboolean cam_nowlocked = false; - usercmd_t indcmd; - - //these are to make svc_viewentity work better + float simtime; +//these are to make svc_viewentity work better float netfps = cl_netfps.value; if (!netfps) @@ -978,6 +948,43 @@ void CL_PredictMovePNum (int seat) simtime -= cls.latency; //push back when playing demos. simtime += bound(-0.5, cl_predict_timenudge.value, 0.5); + return simtime; +} +/* +============== +CL_PredictMove +============== +*/ +void CL_PredictMovePNum (int seat) +{ + //when this is called, the entity states have been interpolated. + //interpolation state should be updated to match prediction state, so entities move correctly in mirrors/portals. + + //this entire function is pure convolouted bollocks. + struct { + int frame; + double time; + player_state_t *state; + usercmd_t *cmd; + } from, to; + playerview_t *pv = &cl.playerview[seat]; + int i; + float f; + outframe_t *backdate; + player_state_t framebuf[2]; //need two framebufs so we can interpolate between two states. + static player_state_t nullstate; + int oldphysent; + double simtime; //this is server time if nopred is set (lerp-only), and local time if we're predicting + extern cvar_t cl_netfps; + lerpents_t *le; + qboolean nopred; + qboolean lerpangles = false; + int trackent; + qboolean cam_nowlocked = false; + usercmd_t indcmd; + + simtime = CL_GetPredictionRealtime(pv); + pv->nolocalplayer = !!(cls.fteprotocolextensions2 & PEXT2_REPLACEMENTDELTAS) || (cls.protocol != CP_QUAKEWORLD); if (!pv->spectator && (pv->cam_state != CAM_FREECAM || pv->cam_spec_track != -1)) //just in case diff --git a/engine/client/pr_csqc.c b/engine/client/pr_csqc.c index b14cadb31..841119c9a 100644 --- a/engine/client/pr_csqc.c +++ b/engine/client/pr_csqc.c @@ -3941,6 +3941,8 @@ static void cs_get_input_state (usercmd_t *cmd) cmd->fservertime = *csqcg.input_servertime; cmd->servertime = *csqcg.input_servertime*1000; } + if (csqcg.input_clienttime) + cmd->fclienttime = *csqcg.input_clienttime; if (csqcg.input_cursor_screen) Vector2Copy(csqcg.input_cursor_screen, cmd->cursor_screen); diff --git a/engine/common/common.c b/engine/common/common.c index 41b97da62..eb2d29289 100644 --- a/engine/common/common.c +++ b/engine/common/common.c @@ -1454,21 +1454,21 @@ void MSGFTE_WriteDeltaUsercmd (sizebuf_t *buf, const usercmd_t *from, const user if (bits & UC_FORWARD) { if (bits & UC_BIGMOVES) - MSG_WriteShort(buf, cmd->forwardmove); + MSG_WriteInt64(buf, cmd->forwardmove); else MSG_WriteChar(buf, cmd->forwardmove/5); } if (bits & UC_RIGHT) { if (bits & UC_BIGMOVES) - MSG_WriteShort(buf, cmd->sidemove); + MSG_WriteInt64(buf, cmd->sidemove); else MSG_WriteChar(buf, cmd->sidemove/5); } if (bits & UC_UP) { if (bits & UC_BIGMOVES) - MSG_WriteShort(buf, cmd->upmove); + MSG_WriteInt64(buf, cmd->upmove); else MSG_WriteChar(buf, cmd->upmove/5); } @@ -1548,6 +1548,7 @@ void MSGFTE_ReadDeltaUsercmd (const usercmd_t *from, usercmd_t *cmd) } *cmd = *from; cmd->servertime = from->servertime+MSG_ReadUInt64(); + cmd->fservertime = cmd->servertime/1000.0; for (i = 0; i < 3; i++) { if (bits & (UC_ANGLE1<forwardmove = MSG_ReadShort(); + cmd->forwardmove = MSG_ReadInt64(); else cmd->forwardmove = MSG_ReadChar()*5; } if (bits & UC_RIGHT) { if (bits & UC_BIGMOVES) - cmd->sidemove = MSG_ReadShort(); + cmd->sidemove = MSG_ReadInt64(); else cmd->sidemove = MSG_ReadChar()*5; } if (bits & UC_UP) { if (bits & UC_BIGMOVES) - cmd->upmove = MSG_ReadShort(); + cmd->upmove = MSG_ReadInt64(); else cmd->upmove = MSG_ReadChar()*5; } diff --git a/engine/server/pr_cmds.c b/engine/server/pr_cmds.c index 139cb1c9f..faca578c3 100644 --- a/engine/server/pr_cmds.c +++ b/engine/server/pr_cmds.c @@ -1008,6 +1008,7 @@ void PR_LoadGlabalStruct(qboolean muted) globalvec (false, input_right_angles) \ globalvec (false, input_right_velocity) \ globalvec (false, input_right_avelocity) \ + globalfloat (false, input_servertime) \ \ globalint (false, serverid) \ globalvec (false, global_gravitydir) \ @@ -10321,6 +10322,10 @@ static void SV_SetSSQCInputs(usercmd_t *ucmd) (pr_global_struct->input_movevalues)[1] = ucmd->sidemove; (pr_global_struct->input_movevalues)[2] = ucmd->upmove; } + if (pr_global_ptrs->input_servertime) + pr_global_struct->input_servertime = ucmd->fservertime; + if (pr_global_ptrs->input_clienttime) + pr_global_struct->input_clienttime = ucmd->fclienttime; if (pr_global_ptrs->input_buttons) pr_global_struct->input_buttons = ucmd->buttons; if (pr_global_ptrs->input_weapon) @@ -10462,6 +10467,7 @@ qboolean SV_RunFullQCMovement(client_t *client, usercmd_t *ucmd) } //prethink should be consistant with what the engine normally does + pr_global_struct->time = sv.time; pr_global_struct->self = EDICT_TO_PROG(svprogfuncs, client->edict); PR_ExecuteProgram (svprogfuncs, pr_global_struct->PlayerPreThink); WPhys_RunThink (&sv.world, (wedict_t*)client->edict); @@ -12706,6 +12712,8 @@ void PR_DumpPlatform_f(void) {"end_sys_fields", "void", QW|NQ|CS|MENU}, {"time", "float", MENU, D("The current local time. Increases while paused.")}, + {"input_servertime", "float", QW|NQ|CS, D("Server's timestamp of the client's interpolation state.")}, +// {"input_clienttime", "float", QW|NQ|CS, D("This is the timestamp that player prediction is simulating.")}, {"input_timelength", "float", QW|NQ}, {"input_angles", "vector", QW|NQ, D("+x=DOWN")}, {"input_movevalues", "vector", QW|NQ}, diff --git a/engine/server/progdefs.h b/engine/server/progdefs.h index 8d2f7597f..44cfea4a9 100644 --- a/engine/server/progdefs.h +++ b/engine/server/progdefs.h @@ -104,6 +104,8 @@ typedef struct nqglobalvars_s pvec_t *physics_mode; pvec_t *clientcommandframe; + pvec_t *input_servertime; + pvec_t *input_clienttime; pvec_t *input_timelength; pvec_t *input_impulse; pvec3_t *input_angles; diff --git a/engine/server/sv_user.c b/engine/server/sv_user.c index 0c8708f74..58fb2c3ce 100644 --- a/engine/server/sv_user.c +++ b/engine/server/sv_user.c @@ -7946,56 +7946,54 @@ void SV_ExecuteClientMessage (client_t *cl) qbyte checksum, calculatedChecksum; int seq_hash; - // calc ping time - if (cl->frameunion.frames) - { //split screen doesn't always have frames. - frame = &cl->frameunion.frames[cl->netchan.incoming_acknowledged & UPDATE_MASK]; - - if (cl->lastsequence_acknowledged + UPDATE_BACKUP > cl->netchan.incoming_acknowledged) - { - /*note that if there is packetloss, we can change a single frame's ping_time multiple times - this means that the 'ping' is more latency than ping times*/ - if (frame->ping_time == -1 || !sv_ping_ignorepl.ival) - frame->ping_time = realtime - frame->senttime; //no more phenomanally low pings please - - if (cl->spectator) - cl->delay = 0; - else - { - float diff = frame->ping_time*1000 - sv_minping.value; - if (fabs(diff) > 1) - { - //FIXME: we should use actual arrival times instead, so we don't get so much noise and seesawing. - diff = bound(-25, diff, 25); //don't swing wildly - cl->delay -= 0.001*(diff/25); //scale towards the ideal value - cl->delay = bound(0, cl->delay, 1); //but make sure things don't go crazy - } - } - if (cl->penalties & BAN_LAGGED) - if (cl->delay < 0.2) - cl->delay = 0.2; - } - - if (sv_antilag.ival || !*sv_antilag.string) - { -#ifdef warningmsg -#pragma warningmsg("FIXME: make antilag optionally support non-player ents too") -#endif - cl->laggedents_count = sv.allocated_client_slots; - memcpy(cl->laggedents, frame->laggedplayer, sizeof(*cl->laggedents)*cl->laggedents_count); - cl->laggedents_time = frame->laggedtime; - cl->laggedents_frac = !*sv_antilag_frac.string?1:sv_antilag_frac.value; - } - else - cl->laggedents_count = 0; - } - else + if (!cl->frameunion.frames) { Con_Printf("Server bug: No frames!\n"); cl->send_message = false; return; //shouldn't happen... } +// calc ping time + frame = &cl->frameunion.frames[cl->netchan.incoming_acknowledged & UPDATE_MASK]; + + if (cl->lastsequence_acknowledged + UPDATE_BACKUP > cl->netchan.incoming_acknowledged) + { + /*note that if there is packetloss, we can change a single frame's ping_time multiple times + this means that the 'ping' is more latency than ping times*/ + if (frame->ping_time == -1 || !sv_ping_ignorepl.ival) + frame->ping_time = realtime - frame->senttime; //no more phenomanally low pings please + + if (cl->spectator) + cl->delay = 0; + else + { + float diff = frame->ping_time*1000 - sv_minping.value; + if (fabs(diff) > 1) + { + //FIXME: we should use actual arrival times instead, so we don't get so much noise and seesawing. + diff = bound(-25, diff, 25); //don't swing wildly + cl->delay -= 0.001*(diff/25); //scale towards the ideal value + cl->delay = bound(0, cl->delay, 1); //but make sure things don't go crazy + } + } + if (cl->penalties & BAN_LAGGED) + if (cl->delay < 0.2) + cl->delay = 0.2; + } + + if (sv_antilag.ival || !*sv_antilag.string) + { +#ifdef warningmsg +#pragma warningmsg("FIXME: make antilag optionally support non-player ents too") +#endif + cl->laggedents_count = sv.allocated_client_slots; + memcpy(cl->laggedents, frame->laggedplayer, sizeof(*cl->laggedents)*cl->laggedents_count); + cl->laggedents_time = frame->laggedtime; + cl->laggedents_frac = !*sv_antilag_frac.string?1:sv_antilag_frac.value; + } + else + cl->laggedents_count = 0; + // make sure the reply sequence number matches the incoming // sequence number if (cl->netchan.incoming_sequence >= cl->netchan.outgoing_sequence) @@ -8076,6 +8074,8 @@ void SV_ExecuteClientMessage (client_t *cl) 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 Vector2Copy(split->lastcmd.cursor_screen, oldest.cursor_screen); VectorCopy(split->lastcmd.cursor_start, oldest.cursor_start); VectorCopy(split->lastcmd.cursor_impact, oldest.cursor_impact);