ref: 3dfc0893fae028cdcb21ffa7a18ccf3796c68e6a
parent: 14a26c3d0825a67eedffa37af5fb463776c4a7bb
author: Simon Howard <fraggle@gmail.com>
date: Thu Oct 5 13:19:43 EDT 2006
Prevent against deadlock where client and server are both stuck waiting for each other to send data. Subversion-branch: /trunk/chocolate-doom Subversion-revision: 685
--- a/src/net_client.c
+++ b/src/net_client.c
@@ -1,7 +1,7 @@
// Emacs style mode select -*- C++ -*-
//-----------------------------------------------------------------------------
//
-// $Id: net_client.c 680 2006-09-29 21:25:13Z fraggle $
+// $Id: net_client.c 685 2006-10-05 17:19:43Z fraggle $
//
// Copyright(C) 2005 Simon Howard
//
@@ -542,7 +542,6 @@
return;
}
-
if (start < 0)
start = 0;
@@ -748,10 +747,16 @@
printf("Syncing netgames like Vanilla Doom.\n");
}
+ // Clear the receive window
+
memset(recvwindow, 0, sizeof(recvwindow));
recvwindow_start = 0;
memset(&recvwindow_cmd_base, 0, sizeof(recvwindow_cmd_base));
+ // Clear the send queue
+
+ memset(&send_queue, 0x00, sizeof(send_queue));
+
netgame = true;
autostart = true;
}
@@ -973,8 +978,16 @@
static void NET_CL_ParseResendRequest(net_packet_t *packet)
{
static unsigned int start;
+ static unsigned int end;
static unsigned int num_tics;
+ if (drone)
+ {
+ // Drones don't send gamedata.
+
+ return;
+ }
+
if (!NET_ReadInt32(packet, &start)
|| !NET_ReadInt8(packet, &num_tics))
{
@@ -981,11 +994,37 @@
return;
}
+ end = start + num_tics - 1;
+
+ //printf("requested resend %i-%i .. ", start, end);
+
+ // Check we have the tics being requested. If not, reduce the
+ // window of tics to only what we have.
+
+ while (start <= end
+ && (!send_queue[start % BACKUPTICS].active
+ || send_queue[start % BACKUPTICS].seq != start))
+ {
+ ++start;
+ }
+
+ while (start <= end
+ && (!send_queue[end % BACKUPTICS].active
+ || send_queue[end % BACKUPTICS].seq != end))
+ {
+ --end;
+ }
+
+ //printf("%i-%i\n", start, end);
+
// Resend those tics
- //printf("CL: resend %i-%i\n", start, start+num_tics-1);
+ if (start <= end)
+ {
+ //printf("CL: resend %i-%i\n", start, start+num_tics-1);
- NET_CL_SendTics(start, start + num_tics - 1);
+ NET_CL_SendTics(start, end);
+ }
}
// Console message that the server wants the client to print
--- a/src/net_server.c
+++ b/src/net_server.c
@@ -1,7 +1,7 @@
// Emacs style mode select -*- C++ -*-
//-----------------------------------------------------------------------------
//
-// $Id: net_server.c 682 2006-09-30 10:22:48Z fraggle $
+// $Id: net_server.c 685 2006-10-05 17:19:43Z fraggle $
//
// Copyright(C) 2005 Simon Howard
//
@@ -199,6 +199,10 @@
int last_send_time;
char *name;
+ // Last time new gamedata was received from this client
+
+ int last_gamedata_time;
+
// recording a demo without -longtics
boolean recording_lowres;
@@ -535,6 +539,9 @@
client->sendseq = 0;
client->acknowledged = 0;
client->drone = false;
+
+ client->last_gamedata_time = 0;
+
memset(client->sendqueue, 0xff, sizeof(client->sendqueue));
}
@@ -707,6 +714,7 @@
{
net_gamesettings_t settings;
net_packet_t *startpacket;
+ int nowtime;
int i;
if (client != NET_SV_Controller())
@@ -757,6 +765,8 @@
}
}
+ nowtime = I_GetTimeMS();
+
// Send start packets to each connected node
for (i=0; i<MAXNETNODES; ++i)
@@ -764,6 +774,8 @@
if (!ClientConnected(&clients[i]))
continue;
+ clients[i].last_gamedata_time = nowtime;
+
startpacket = NET_Conn_NewReliable(&clients[i].connection,
NET_PACKET_TYPE_GAMESTART);
@@ -923,6 +935,10 @@
return;
}
+ // Get the current time
+
+ nowtime = I_GetTimeMS();
+
// Expand 8-bit values to the full sequence number
ackseq = NET_SV_ExpandTicNum(ackseq);
@@ -954,6 +970,8 @@
recvobj->active = true;
recvobj->diff = diff;
recvobj->latency = latency;
+
+ client->last_gamedata_time = nowtime;
}
// Higher acknowledgement point?
@@ -977,8 +995,6 @@
if (resend_end >= BACKUPTICS)
resend_end = BACKUPTICS - 1;
- nowtime = I_GetTimeMS();
-
index = resend_end - 1;
resend_start = resend_end;
@@ -1008,6 +1024,12 @@
if (resend_start < resend_end)
{
+ /*
+ printf("missed %i-%i before %i, send resend\n",
+ recvwindow_start + resend_start,
+ recvwindow_start + resend_end - 1,
+ seq);
+ */
NET_SV_SendResendRequest(client,
recvwindow_start + resend_start,
recvwindow_start + resend_end - 1);
@@ -1391,6 +1413,53 @@
++client->sendseq;
}
+// Prevent against deadlock: resend requests are usually only
+// triggered if we miss a packet and receive the next one.
+// If we miss a whole load of packets, we can end up in a
+// deadlock situation where the client will not send any more.
+// If we don't receive any game data in a while, trigger a resend
+// request for the next tic we're expecting.
+
+void NET_SV_CheckDeadlock(net_client_t *client)
+{
+ int nowtime;
+ int i;
+
+ // Don't expect game data from clients.
+
+ if (client->drone)
+ {
+ return;
+ }
+
+ nowtime = I_GetTimeMS();
+
+ // If we haven't received anything for a long time, it may be a deadlock.
+
+ if (nowtime - client->last_gamedata_time > 1000)
+ {
+ // Search the receive window for the first tic we are expecting
+ // from this player.
+
+ for (i=0; i<BACKUPTICS; ++i)
+ {
+ if (!recvwindow[client->player_number][i].active)
+ {
+ //printf("Possible deadlock: Sending resend request\n");
+
+ // Found a tic we haven't received. Send a resend request.
+
+ NET_SV_SendResendRequest(client,
+ recvwindow_start + i,
+ recvwindow_start + i + 5);
+
+ client->last_gamedata_time = nowtime;
+ break;
+ }
+ }
+ }
+}
+
// Perform any needed action on a client
static void NET_SV_RunClient(net_client_t *client)
@@ -1450,6 +1519,7 @@
if (server_state == SERVER_IN_GAME)
{
NET_SV_PumpSendQueue(client);
+ NET_SV_CheckDeadlock(client);
}
}