Con_Printf("no slot left for weapon definition; increase IN_BESTWEAPON_MAX\n");
return; // sorry
}
- strlcpy(in_bestweapon_info[i].name, name, sizeof(in_bestweapon_info[i].name));
+ dp_strlcpy(in_bestweapon_info[i].name, name, sizeof(in_bestweapon_info[i].name));
in_bestweapon_info[i].impulse = impulse;
if(weaponbit != -1)
in_bestweapon_info[i].weaponbit = weaponbit;
IN_Move ();
// send mouse move to csqc
- if (cl.csqc_loaded && cl_csqc_generatemousemoveevents.integer)
+ if (CLVM_prog->loaded && cl_csqc_generatemousemoveevents.integer)
{
if (cl.csqc_wantsmousemove)
{
static int oldwindowmouse[2];
if (oldwindowmouse[0] != in_windowmouse_x || oldwindowmouse[1] != in_windowmouse_y)
{
- CL_VM_InputEvent(3, in_windowmouse_x * vid_conwidth.value / vid.width, in_windowmouse_y * vid_conheight.value / vid.height);
+ CL_VM_InputEvent(3, in_windowmouse_x * vid_conwidth.value / vid.mode.width, in_windowmouse_y * vid_conheight.value / vid.mode.height);
oldwindowmouse[0] = in_windowmouse_x;
oldwindowmouse[1] = in_windowmouse_y;
}
// mouse interacting with the scene, mostly stationary view
V_StopPitchDrift();
// update prydon cursor
- cl.cmd.cursor_screen[0] = in_windowmouse_x * 2.0 / vid.width - 1.0;
- cl.cmd.cursor_screen[1] = in_windowmouse_y * 2.0 / vid.height - 1.0;
+ cl.cmd.cursor_screen[0] = in_windowmouse_x * 2.0 / vid.mode.width - 1.0;
+ cl.cmd.cursor_screen[1] = in_windowmouse_y * 2.0 / vid.mode.height - 1.0;
}
if(v_flipped.integer)
else
{
cl.moveflags = 0;
- cl.movevars_ticrate = (cls.demoplayback ? 1.0f : host_timescale.value) / bound(10.0f, cl_netfps.value, 1000.0f);
- cl.movevars_timescale = (cls.demoplayback ? 1.0f : host_timescale.value);
+ cl.movevars_ticrate = 0; // bones_was_here: no guessing, unavailable ticrate triggers better fallbacks
+ cl.movevars_timescale = (cls.demoplayback || host_timescale.value <= 0) ? 1.0f : host_timescale.value;
cl.movevars_gravity = sv_gravity.value;
cl.movevars_stopspeed = cl_movement_stopspeed.value;
cl.movevars_maxspeed = cl_movement_maxspeed.value;
usercmd_t *cmd;
sizebuf_t buf;
unsigned char data[1024];
- float packettime;
+ float packettime, sv_frametime, lag, frames_per_tic;
+ qbool opportune_moment;
qbool quemove;
qbool important;
if (in_button7.state & 3) bits |= 64;
if (in_button8.state & 3) bits |= 128;
if (in_use.state & 3) bits |= 256;
- if (key_dest != key_game || key_consoleactive) bits |= 512;
+ if (key_dest != key_game || key_consoleactive || !vid_activewindow) bits |= 512;
if (cl_prydoncursor.integer > 0) bits |= 1024;
if (in_button9.state & 3) bits |= 2048;
if (in_button10.state & 3) bits |= 4096;
if (quemove)
cl.movecmd[0] = cl.cmd;
- // don't predict more than 200fps
- if (cl.timesincepacket >= 0.005)
+ /* Accumulating cl.realframetime to prevent low packet rates,
+ * previously with cl_maxfps == cl_netfps it did not send every frame because
+ * host.realtime - cl.lastpackettime was often well below (or above) cl_packetinterval.
+ */
+ cl.timesincepacket += cl.realframetime;
+
+ // don't predict more than 256fps
+ if (cl.timesincepacket >= 1/256)
cl.movement_replay = true; // redo the prediction
// now decide whether to actually send this move
// don't send too often or else network connections can get clogged by a
// high renderer framerate
packettime = 1.0f / bound(10.0f, cl_netfps.value, 1000.0f);
- if (cl.movevars_timescale && cl.movevars_ticrate)
+ if (cl.movevars_ticrate)
{
- // try to ensure at least 1 packet per server frame
- // and apply soft limit of 2, hard limit < 4 (packettime reduced further below)
- float maxtic = cl.movevars_ticrate / cl.movevars_timescale;
- packettime = bound(maxtic * 0.5f, packettime, maxtic);
+ packettime = bound(cl.movevars_ticrate * 0.5f, packettime, cl.movevars_ticrate);
+ sv_frametime = cl.movevars_ticrate;
}
- // bones_was_here: reduce packettime to (largest multiple of realframetime) <= packettime
- // prevents packet rates lower than cl_netfps or server frame rate
- // eg: cl_netfps 60 and cl_maxfps 250 would otherwise send only 50 netfps
- // with this line that config sends 62.5 netfps
- // (this causes it to emit packets at a steady beat)
- packettime = floor(packettime / (float)cl.realframetime) * (float)cl.realframetime;
+ else // fallback, may be affected by server->client network problems
+ sv_frametime = (cl.mtime[0] - cl.mtime[1]) / cl.movevars_timescale;
// always send if buttons changed or an impulse is pending
// even if it violates the rate limit!
important = (cl.cmd.impulse || (cl_netimmediatebuttons.integer && cl.cmd.buttons != cl.movecmd[1].buttons));
- // don't send too often (cl_netfps), allowing a small margin for float error
- // bones_was_here: accumulate realframetime to prevent low packet rates
- // previously with cl_maxfps == cl_netfps it did not send every frame as
- // host.realtime - cl.lastpackettime was often well below (or above) packettime
- if (!important && cl.timesincepacket < packettime * 0.99999f)
+ lag = cl.mtime[0] - cl.cmd.time;
+ frames_per_tic = sv_frametime / cl.realframetime;
+// opportune_moment = lag <= cl.realframetime * 2; // FAIL: can miss the moment with uncapped fps
+// opportune_moment = lag <= cl.realframetime * frames_per_tic * 0.5; // FAIL: too early at high fps, reducing multi causes misses at moderate fps
+ opportune_moment = lag <= cl.realframetime * (frames_per_tic <= 1 ? 1 : sqrt(frames_per_tic)); // perfect
+
+ // Two methods for deciding when to send
+ if (!important)
{
- cl.timesincepacket += cl.realframetime;
- return;
+ // Traditional time interval, now used as fallback
+ if (sv_frametime <= 0 || lag > sv_frametime || lag < 0) // unknown ticrate || lag/PL || still connecting
+ {
+ if (cl.timesincepacket < packettime * 0.99999f)
+ {
+// Con_Printf("^6moveft %f realft %f lag %f tic %f inputsince %d\n", cl.cmd.frametime, cl.realframetime, lag, sv_frametime, cl.opt_inputs_since_update);
+ return;
+ }
+ }
+ // Server-synchronised, for better pings
+ // Sends at least once per server frame
+ else // cl.opt_inputs_since_update is usable
+ {
+ if (!opportune_moment || cl.opt_inputs_since_update >= sv_frametime / packettime)
+ {
+// Con_Printf("^1moveft %f realft %f lag %f tic %f inputsince %d\n", cl.cmd.frametime, cl.realframetime, lag, sv_frametime, cl.opt_inputs_since_update);
+ return;
+ }
+ }
}
// don't choke the connection with packets (obey rate limit)
if (!NetConn_CanSend(cls.netcon) && !important)
return;
+// Con_Printf("%smoveft %f realft %f lag %f tic %f inputsince %d important %d\n", (lag < 0.0005 || !opportune_moment) ? "^3" : "^2", cl.cmd.frametime, cl.realframetime, lag, sv_frametime, cl.opt_inputs_since_update, important);
+
+ if (opportune_moment) // this check is needed for optimal timing especially with cl_netimmediatebuttons
+ ++cl.opt_inputs_since_update;
+
// reset the packet timing accumulator
- cl.timesincepacket = cl.realframetime;
+ cl.timesincepacket = 0;
buf.maxsize = sizeof(data);
buf.cursize = 0;