shithub: choc

Download patch

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);
     }
 }