ref: 41cdd5785305a28912fa2d6231ec8f518a850ec9
parent: a747cf67119b5934147093aea150043403ffb4d2
author: Simon Howard <fraggle@gmail.com>
date: Mon Feb 27 11:31:08 EST 2006
Working client sync: adjust the clock to try to match the latency of other players. Allow the menu ticker to run even if the main game ticker doesn't run. Remove time request/response code (now using game latency). Subversion-branch: /trunk/chocolate-doom Subversion-revision: 394
--- a/src/d_net.c
+++ b/src/d_net.c
@@ -1,7 +1,7 @@
// Emacs style mode select -*- C++ -*-
//-----------------------------------------------------------------------------
//
-// $Id: d_net.c 388 2006-02-24 19:14:22Z fraggle $
+// $Id: d_net.c 394 2006-02-27 16:31:08Z fraggle $
//
// Copyright(C) 1993-1996 Id Software, Inc.
// Copyright(C) 2005 Simon Howard
@@ -117,7 +117,7 @@
//-----------------------------------------------------------------------------
-static const char rcsid[] = "$Id: d_net.c 388 2006-02-24 19:14:22Z fraggle $";
+static const char rcsid[] = "$Id: d_net.c 394 2006-02-27 16:31:08Z fraggle $";
#include "d_main.h"
@@ -137,6 +137,8 @@
#include "net_loop.h"
+#define FPS 35
+
//
// NETWORKING
//
@@ -155,6 +157,7 @@
int lastnettic;
int ticdup;
int extratics;
+fixed_t offsetms;
void D_ProcessEvents (void);
@@ -162,12 +165,23 @@
void D_DoAdvanceDemo (void);
+// 35 fps clock adjusted by offsetms milliseconds
+
+static int GetAdjustedTime(void)
+{
+ int time_ms;
+
+ time_ms = I_GetTimeMS() + (offsetms / FRACUNIT);
+
+ return (time_ms * FPS) / 1000;
+}
+
//
// NetUpdate
// Builds ticcmds for console player,
// sends out a packet
//
-int gametime;
+int lasttime;
void NetUpdate (void)
{
@@ -183,9 +197,9 @@
NET_SV_Run();
// check time
- nowtime = I_GetTime ()/ticdup;
- newtics = nowtime - gametime;
- gametime = nowtime;
+ nowtime = GetAdjustedTime()/ticdup;
+ newtics = nowtime - lasttime;
+ lasttime = nowtime;
if (newtics <= 0) // nothing new to update
return;
@@ -196,7 +210,14 @@
for (i=0 ; i<newtics ; i++)
{
ticcmd_t cmd;
-
+
+ I_StartTic ();
+ D_ProcessEvents ();
+
+ // Always run the menu
+
+ M_Ticker ();
+
// If playing single player, do not allow tics to buffer
// up very far
@@ -208,9 +229,6 @@
if (maketic - gameticdiv > 35)
break;
- I_StartTic ();
- D_ProcessEvents ();
-
//printf ("mk:%i ",maketic);
G_BuildTiccmd(&cmd);
@@ -247,6 +265,7 @@
ticdup = 1;
extratics = 1;
lowres_turn = false;
+ offsetms = 0;
for (i=0; i<MAXPLAYERS; i++)
{
@@ -384,6 +403,8 @@
else
counts = availabletics;
+ counts = availabletics;
+
if (counts < 1)
counts = 1;
@@ -402,10 +423,11 @@
if (lowtic < gametic/ticdup)
I_Error ("TryRunTics: lowtic < gametic");
- // don't stay in here forever -- give the menu a chance to work
- if (I_GetTime ()/ticdup - entertic >= 20)
+ // Don't stay in this loop forever. The menu is still running,
+ // so return to update the screen
+
+ if (I_GetTime ()/ticdup - entertic > 0)
{
- M_Ticker ();
return;
}
--- a/src/net_client.c
+++ b/src/net_client.c
@@ -1,7 +1,7 @@
// Emacs style mode select -*- C++ -*-
//-----------------------------------------------------------------------------
//
-// $Id: net_client.c 389 2006-02-24 19:14:59Z fraggle $
+// $Id: net_client.c 394 2006-02-27 16:31:08Z fraggle $
//
// Copyright(C) 2005 Simon Howard
//
@@ -213,6 +213,8 @@
net_ticdiff_t cmd;
} net_server_send_t;
+extern fixed_t offsetms;
+
static net_connection_t client_connection;
static net_clientstate_t client_state;
static net_addr_t *server_addr;
@@ -261,6 +263,10 @@
static int recvwindow_start;
static net_server_recv_t recvwindow[BACKUPTICS];
+// 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 a player leaves the game
@@ -308,10 +314,66 @@
// 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)
+static void NET_CL_ExpandFullTiccmd(net_full_ticcmd_t *cmd, int seq)
{
+ int latency;
+ fixed_t adjustment;
int i;
+ // Update average_latency
+
+ if (seq == send_queue[seq % NET_TICCMD_QUEUE_SIZE].seq)
+ {
+ latency = I_GetTimeMS() - send_queue[seq % NET_TICCMD_QUEUE_SIZE].time;
+ }
+ else if (seq > send_queue[seq % NET_TICCMD_QUEUE_SIZE].seq)
+ {
+ // We have received the ticcmd from the server before we have
+ // even sent ours
+
+ latency = 0;
+ }
+ else
+ {
+ latency = -1;
+ }
+
+ if (latency >= 0)
+ {
+ if (seq <= 20)
+ {
+ average_latency = latency * FRACUNIT;
+ }
+ else
+ {
+ // Low level filter
+
+ average_latency = (average_latency * 0.9)
+ + (latency * FRACUNIT * 0.1);
+ }
+ }
+
+ //printf("latency: %i\tremote:%i\n", average_latency / FRACUNIT,
+ // cmd->latency);
+
+ // 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.
+
+ if (seq > 35)
+ {
+ adjustment = (cmd->latency * FRACUNIT) - average_latency;
+
+ // Only adjust very slightly; the cumulative effect over
+ // multiple tics will sort it out.
+
+ adjustment = adjustment / 100;
+
+ offsetms += adjustment;
+ }
+
+ // Expand tic diffs for all players
+
for (i=0; i<MAXPLAYERS; ++i)
{
if (i == consoleplayer)
@@ -358,7 +420,7 @@
{
// Expand tic diff data into d_net.c structures
- NET_CL_ExpandFullTiccmd(&recvwindow[0].cmd);
+ NET_CL_ExpandFullTiccmd(&recvwindow[0].cmd, recvwindow_start);
// Advance the window
@@ -454,7 +516,7 @@
sendobj = &send_queue[i % NET_TICCMD_QUEUE_SIZE];
- NET_WriteInt16(packet, sendobj->time);
+ NET_WriteInt16(packet, average_latency / FRACUNIT);
NET_WriteTiccmdDiff(packet, &sendobj->cmd, lowres_turn);
}
@@ -800,28 +862,6 @@
}
}
-static void NET_CL_ParseTimeRequest(net_packet_t *packet)
-{
- net_packet_t *reply;
- unsigned int seq;
-
- // Received a request from the server for our current time.
-
- if (!NET_ReadInt32(packet, &seq))
- {
- return;
- }
-
- // Send a response with our current time.
-
- reply = NET_NewPacket(10);
- NET_WriteInt16(reply, NET_PACKET_TYPE_TIME_RESP);
- NET_WriteInt32(reply, seq);
- NET_WriteInt32(reply, I_GetTimeMS());
- NET_Conn_SendPacket(&client_connection, reply);
- NET_FreePacket(reply);
-}
-
// Parse a resend request from the server due to a dropped packet
static void NET_CL_ParseResendRequest(net_packet_t *packet)
@@ -872,10 +912,6 @@
case NET_PACKET_TYPE_GAMEDATA:
NET_CL_ParseGameData(packet);
break;
-
- case NET_PACKET_TYPE_TIME_REQ:
- NET_CL_ParseTimeRequest(packet);
- break;
case NET_PACKET_TYPE_GAMEDATA_RESEND:
NET_CL_ParseResendRequest(packet);
--- a/src/net_defs.h
+++ b/src/net_defs.h
@@ -1,7 +1,7 @@
// Emacs style mode select -*- C++ -*-
//-----------------------------------------------------------------------------
//
-// $Id: net_defs.h 376 2006-02-23 18:19:05Z fraggle $
+// $Id: net_defs.h 394 2006-02-27 16:31:08Z fraggle $
//
// Copyright(C) 2005 Simon Howard
//
@@ -165,8 +165,6 @@
NET_PACKET_TYPE_DISCONNECT,
NET_PACKET_TYPE_DISCONNECT_ACK,
NET_PACKET_TYPE_RELIABLE_ACK,
- NET_PACKET_TYPE_TIME_REQ,
- NET_PACKET_TYPE_TIME_RESP,
NET_PACKET_TYPE_GAMEDATA_RESEND,
} net_packet_type_t;
@@ -199,6 +197,7 @@
typedef struct
{
+ signed int latency;
unsigned int seq;
boolean playeringame[MAXPLAYERS];
net_ticdiff_t cmds[MAXPLAYERS];
--- a/src/net_server.c
+++ b/src/net_server.c
@@ -1,7 +1,7 @@
// Emacs style mode select -*- C++ -*-
//-----------------------------------------------------------------------------
//
-// $Id: net_server.c 389 2006-02-24 19:14:59Z fraggle $
+// $Id: net_server.c 394 2006-02-27 16:31:08Z fraggle $
//
// Copyright(C) 2005 Simon Howard
//
@@ -200,12 +200,6 @@
boolean recording_lowres;
- // time query variables
-
- int last_time_req_time;
- int time_req_seq;
- signed int time_offset;
-
// send queue: items to send to the client
// this is a circular buffer
@@ -222,11 +216,10 @@
boolean active;
- // Time this tic was generated (adjusted for time offset between
- // client and server
-
- unsigned int time;
+ // Latency value received from the client
+ signed int latency;
+
// Last time we sent a resend request for this tic
unsigned int resend_time;
@@ -470,9 +463,6 @@
client->addr = addr;
client->last_send_time = -1;
client->name = strdup(player_name);
- client->last_time_req_time = -1;
- client->time_req_seq = 0;
- client->time_offset = 0;
// init the ticcmd send queue
@@ -700,60 +690,6 @@
recvwindow_start = 0;
}
-static void NET_SV_ParseTimeResponse(net_packet_t *packet, net_client_t *client)
-{
- unsigned int seq;
- unsigned int remote_time;
- unsigned int rtt;
- unsigned int nowtime;
- signed int time_offset;
-
- if (!NET_ReadInt32(packet, &seq)
- || !NET_ReadInt32(packet, &remote_time))
- {
- return;
- }
-
- if (seq != client->time_req_seq)
- {
- // Not the time response we are expecting
-
- return;
- }
-
- // Calculate the round trip time
-
- nowtime = I_GetTimeMS();
- rtt = nowtime - client->last_time_req_time;
-
- // Adjust the remote time based on the round trip time
-
- remote_time += rtt / 2;
-
- // Calculate the offset to our own time
-
- time_offset = remote_time - nowtime;
-
- // Update the time offset
-
- if (client->time_req_seq == 1)
- {
- // This is the first reply, so this is the only sample we have
- // so far
-
- client->time_offset = time_offset;
- }
- else
- {
- // Apply a low level filter to the time offset adjustments
-
- client->time_offset = ((client->time_offset * 3) / 4)
- + (time_offset / 4);
- }
-
- //printf("SV: client %p time offset: %i(%i)->%i\n", client, time_offset, rtt, client->time_offset);
-}
-
// Send a resend request to a client
static void NET_SV_SendResendRequest(net_client_t *client, int start, int end)
@@ -900,9 +836,9 @@
for (i=0; i<num_tics; ++i)
{
net_ticdiff_t diff;
- unsigned int time;
+ signed int latency;
- if (!NET_ReadInt16(packet, &time)
+ if (!NET_ReadSInt16(packet, &latency)
|| !NET_ReadTiccmdDiff(packet, &diff, sv_settings.lowres_turn))
{
return;
@@ -920,6 +856,7 @@
recvobj = &recvwindow[index][player];
recvobj->active = true;
recvobj->diff = diff;
+ recvobj->latency = latency;
}
// Has this been received out of sequence, ie. have we not received
@@ -1082,9 +1019,6 @@
case NET_PACKET_TYPE_GAMEDATA_RESEND:
NET_SV_ParseResendRequest(packet, client);
break;
- case NET_PACKET_TYPE_TIME_RESP:
- NET_SV_ParseTimeResponse(packet, client);
- break;
default:
// unknown packet type
@@ -1150,25 +1084,6 @@
NET_FreePacket(packet);
}
-static void NET_SV_SendTimeRequest(net_client_t *client)
-{
- net_packet_t *packet;
-
- ++client->time_req_seq;
-
- // Transmit the request packet
-
- packet = NET_NewPacket(10);
- NET_WriteInt16(packet, NET_PACKET_TYPE_TIME_REQ);
- NET_WriteInt32(packet, client->time_req_seq);
- NET_Conn_SendPacket(&client->connection, packet);
- NET_FreePacket(packet);
-
- // Save the time we send the request
-
- client->last_time_req_time = I_GetTimeMS();
-}
-
static void NET_SV_PumpSendQueue(net_client_t *client)
{
net_full_ticcmd_t cmd;
@@ -1217,8 +1132,12 @@
// Add ticcmds from all players
+ cmd.latency = 0;
+
for (i=0; i<MAXPLAYERS; ++i)
{
+ net_client_recv_t *recvobj;
+
if (sv_players[i] == client)
{
// Not the player we are sending to
@@ -1234,9 +1153,17 @@
}
cmd.playeringame[i] = true;
- cmd.cmds[i] = recvwindow[recv_index][i].diff;
+
+ recvobj = &recvwindow[recv_index][i];
+
+ cmd.cmds[i] = recvobj->diff;
+
+ if (recvobj->latency > cmd.latency)
+ cmd.latency = recvobj->latency;
}
+ //printf("SV: %i: latency %i\n", client->player_number, cmd.latency);
+
// Add into the queue
client->sendqueue[client->sendseq % BACKUPTICS] = cmd;
@@ -1292,18 +1219,6 @@
NET_SV_SendWaitingData(client);
client->last_send_time = I_GetTimeMS();
}
- }
-
- if (client->last_time_req_time < 0)
- {
- client->last_time_req_time = I_GetTimeMS() - 5000;
- }
-
- if (I_GetTimeMS() - client->last_time_req_time > 10000)
- {
- // Query the clients' times once every ten seconds.
-
- NET_SV_SendTimeRequest(client);
}
if (server_state == SERVER_IN_GAME)
--- a/src/net_structrw.c
+++ b/src/net_structrw.c
@@ -1,7 +1,7 @@
// Emacs style mode select -*- C++ -*-
//-----------------------------------------------------------------------------
//
-// $Id: net_structrw.c 378 2006-02-23 19:12:02Z fraggle $
+// $Id: net_structrw.c 394 2006-02-27 16:31:08Z fraggle $
//
// Copyright(C) 2005 Simon Howard
//
@@ -242,6 +242,13 @@
unsigned int bitfield;
int i;
+ // Latency
+
+ if (!NET_ReadSInt16(packet, &cmd->latency))
+ {
+ return false;
+ }
+
// Regenerate playeringame from the "header" bitfield
if (!NET_ReadInt8(packet, &bitfield))
@@ -274,6 +281,10 @@
{
unsigned int bitfield;
int i;
+
+ // Write the latency
+
+ NET_WriteInt16(packet, cmd->latency);
// Write "header" byte indicating which players are active
// in this ticcmd