foxygit / doom Log in
commit 3dfc0893fae028cdcb21ffa7a18ccf3796c68e6a
Author:     Simon Howard <fraggle@gmail.com>
AuthorDate: Thu Oct 5 17:19:43 2006 +0000
Commit:     Simon Howard <fraggle@gmail.com>
CommitDate: Thu Oct 5 17:19:43 2006 +0000

    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
---
 src/net_client.c | 47 ++++++++++++++++++++++++++++++++---
 src/net_server.c | 76 +++++++++++++++++++++++++++++++++++++++++++++++++++++---
 2 files changed, 116 insertions(+), 7 deletions(-)

diff --git a/src/net_client.c b/src/net_client.c
index e13b8ea3..a3281ac8 100644
--- 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 @@ static void NET_CL_SendTics(int start, int end)
         return;
     }

-
     if (start < 0)
         start = 0;

@@ -748,10 +747,16 @@ static void NET_CL_ParseGameStart(net_packet_t *packet)
 	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,19 +978,53 @@ static void NET_CL_ParseGameData(net_packet_t *packet)
 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))
     {
         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
diff --git a/src/net_server.c b/src/net_server.c
index fb676843..87180793 100644
--- 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 @@ typedef struct
     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 @@ static void NET_SV_InitNewClient(net_client_t *client,
     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 @@ static void NET_SV_ParseGameStart(net_packet_t *packet, net_client_t *client)
 {
     net_gamesettings_t settings;
     net_packet_t *startpacket;
+    int nowtime;
     int i;

     if (client != NET_SV_Controller())
@@ -757,6 +765,8 @@ static void NET_SV_ParseGameStart(net_packet_t *packet, net_client_t *client)
         }
     }

+    nowtime = I_GetTimeMS();
+
     // Send start packets to each connected node

     for (i=0; i<MAXNETNODES; ++i)
@@ -764,6 +774,8 @@ static void NET_SV_ParseGameStart(net_packet_t *packet, net_client_t *client)
         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 @@ static void NET_SV_ParseGameData(net_packet_t *packet, net_client_t *client)
         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 @@ static void NET_SV_ParseGameData(net_packet_t *packet, net_client_t *client)
         recvobj->active = true;
         recvobj->diff = diff;
         recvobj->latency = latency;
+
+        client->last_gamedata_time = nowtime;
     }

     // Higher acknowledgement point?
@@ -977,8 +995,6 @@ static void NET_SV_ParseGameData(net_packet_t *packet, net_client_t *client)
     if (resend_end >= BACKUPTICS)
         resend_end = BACKUPTICS - 1;

-    nowtime = I_GetTimeMS();
-
     index = resend_end - 1;
     resend_start = resend_end;

@@ -1008,6 +1024,12 @@ static void NET_SV_ParseGameData(net_packet_t *packet, net_client_t *client)

     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 @@ static void NET_SV_PumpSendQueue(net_client_t *client)
     ++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 @@ static void NET_SV_RunClient(net_client_t *client)
     if (server_state == SERVER_IN_GAME)
     {
         NET_SV_PumpSendQueue(client);
+        NET_SV_CheckDeadlock(client);
     }
 }