ref: 94ed513e58e869818ef8c088fa2c07c71f97ced4
parent: b00a10ae3398b04c6d6ad890fad80bd53aa7d763
author: Simon Howard <[email protected]>
date: Fri Nov 9 19:55:34 EST 2018
net: Improve -newsync to use a PID controller. The previous -newsync code wasn't too bad but was basically a hacky version of a PID controller. So replace this with an actual PID controller with tuning parameters manually tuned based on an evening of experimentation. So far this seems to be a significant improvement over the old -newsync code.
--- a/src/net_client.c
+++ b/src/net_client.c
@@ -150,6 +150,12 @@
static boolean need_to_acknowledge;
static unsigned int gamedata_recv_time;
+// The latency (time between when we sent our command and we got all
+// the other players' commands from the server) for the last tic we
+// received. We include this latency in tics we send to the server so
+// that they can adjust to us.
+static int last_latency;
+
// Hash checksums of our wad directory and dehacked data.
sha1_digest_t net_local_wad_sha1sum;
@@ -159,10 +165,6 @@
unsigned int net_local_is_freedoom;
-// Average time between sending our ticcmd and receiving from the server
-
-static fixed_t average_latency;
-
#define NET_CL_ExpandTicNum(b) NET_ExpandTicNum(recvwindow_start, (b))
// Called when we become disconnected from the server
@@ -172,20 +174,15 @@
D_ReceiveTic(NULL, NULL);
}
-// Expand a net_full_ticcmd_t, applying the diffs in cmd->cmds as
-// patches against recvwindow_cmd_base. Place the results into
-// the d_net.c structures (netcmds/nettics) and save the new ticcmd
-// back into recvwindow_cmd_base.
-
-static void NET_CL_ExpandFullTiccmd(net_full_ticcmd_t *cmd, unsigned int seq,
- ticcmd_t *ticcmds)
+// Called when a packet is received from the server containing game
+// data. This updates the clock synchronization variable (offsetms)
+// using a PID filter that keeps client clocks in sync.
+static void UpdateClockSync(unsigned int seq,
+ unsigned int remote_latency)
{
- int latency;
- fixed_t adjustment;
- int i;
+ static int last_error, cumul_error;
+ int latency, error;
- // Update average_latency
-
if (seq == send_queue[seq % BACKUPTICS].seq)
{
latency = I_GetTimeMS() - send_queue[seq % BACKUPTICS].time;
@@ -199,43 +196,38 @@
}
else
{
- latency = -1;
+ return;
}
- if (latency >= 0)
- {
- if (seq <= 20)
- {
- average_latency = latency * FRACUNIT;
- }
- else
- {
- // Low level filter
+ // PID filter. These are manually trained parameters.
+#define KP 0.1
+#define KI 0.01
+#define KD 0.02
- average_latency = (fixed_t)((average_latency * 0.9)
- + (latency * FRACUNIT * 0.1));
- }
- }
+ // How does our latency compare to the worst other player?
+ error = latency - remote_latency;
+ cumul_error += error;
- //printf("latency: %i\tremote:%i\n", average_latency / FRACUNIT,
- // cmd->latency);
+ offsetms = KP * (FRACUNIT * error)
+ - KI * (FRACUNIT * cumul_error)
+ + (KD * FRACUNIT) * (last_error - error);
- // Possibly adjust offsetms in d_net.c, try to make players all have
- // the same lag. Don't adjust in the first few tics of play, as
- // we don't have an accurate value for average_latency yet.
+ last_error = error;
+ last_latency = latency;
- if (seq > TICRATE)
- {
- adjustment = (cmd->latency * FRACUNIT) - average_latency;
+ //printf("%i,%i,%i\n", latency, remote_latency, offsetms);
+}
- // Only adjust very slightly; the cumulative effect over
- // multiple tics will sort it out.
+// Expand a net_full_ticcmd_t, applying the diffs in cmd->cmds as
+// patches against recvwindow_cmd_base. Place the results into
+// the d_net.c structures (netcmds/nettics) and save the new ticcmd
+// back into recvwindow_cmd_base.
- adjustment = adjustment / 100;
+static void NET_CL_ExpandFullTiccmd(net_full_ticcmd_t *cmd, unsigned int seq,
+ ticcmd_t *ticcmds)
+{
+ int i;
- offsetms += adjustment;
- }
-
// Expand tic diffs for all players
for (i=0; i<NET_MAXPLAYERS; ++i)
@@ -375,7 +367,7 @@
sendobj = &send_queue[i % BACKUPTICS];
- NET_WriteInt16(packet, average_latency / FRACUNIT);
+ NET_WriteInt16(packet, last_latency);
NET_WriteTiccmdDiff(packet, &sendobj->cmd, settings.lowres_turn);
}
@@ -715,11 +707,21 @@
}
// Store in the receive window
-
+
recvobj = &recvwindow[index];
recvobj->active = true;
recvobj->cmd = cmd;
+
+ // If a packet is lost or arrives out of order, we might get
+ // the tic in the next packet instead (because of extratic).
+ // If that's the case then the latency for receiving that tic
+ // now will be bogus. So we only use the last tic in the packet
+ // to trigger a clock sync update.
+ if (i == num_tics - 1)
+ {
+ UpdateClockSync(seq + i, cmd.latency);
+ }
}
// Has this been received out of sequence, ie. have we not received