foxygit / doom Log in
commit 915053b5e7a885dbc6be6f150455eb153cc41309
Author:     Simon Howard <fraggle@gmail.com>
AuthorDate: Wed Feb 22 18:35:55 2006 +0000
Commit:     Simon Howard <fraggle@gmail.com>
CommitDate: Wed Feb 22 18:35:55 2006 +0000

    Packet resends for server->client gamedata

    Subversion-branch: /trunk/chocolate-doom
    Subversion-revision: 375
---
 src/net_client.c | 240 ++++++++++++++++++++++++++++++++++++++++++++++++++++---
 src/net_server.c | 112 ++++++++++++++++----------
 2 files changed, 299 insertions(+), 53 deletions(-)

diff --git a/src/net_client.c b/src/net_client.c
index fba35cad..2d0622a5 100644
--- a/src/net_client.c
+++ b/src/net_client.c
@@ -1,7 +1,7 @@
 // Emacs style mode select   -*- C++ -*-
 //-----------------------------------------------------------------------------
 //
-// $Id: net_client.c 374 2006-02-19 13:42:27Z fraggle $
+// $Id: net_client.c 375 2006-02-22 18:35:55Z fraggle $
 //
 // Copyright(C) 2005 Simon Howard
 //
@@ -21,6 +21,9 @@
 // 02111-1307, USA.
 //
 // $Log$
+// Revision 1.27  2006/02/22 18:35:55  fraggle
+// Packet resends for server->client gamedata
+//
 // Revision 1.26  2006/02/19 13:42:27  fraggle
 // Move tic number expansion code to common code.  Parse game data packets
 // received from the server.
@@ -121,10 +124,13 @@
 //

 #include <stdlib.h>
+#include <string.h>

 #include "config.h"
 #include "doomdef.h"
 #include "doomstat.h"
+#include "deh_main.h"
+#include "g_game.h"
 #include "i_system.h"
 #include "net_client.h"
 #include "net_common.h"
@@ -165,6 +171,27 @@ typedef struct

 } net_server_recv_t;

+// Type of structure used in the send window
+
+typedef struct
+{
+    // Whether this slot is active yet
+
+    boolean active;
+
+    // The tic number
+
+    unsigned int seq;
+
+    // Time the command was generated
+
+    unsigned int time;
+
+    // Ticcmd diff
+
+    net_ticdiff_t cmd;
+} net_server_send_t;
+
 static net_connection_t client_connection;
 static net_clientstate_t client_state;
 static net_addr_t *server_addr;
@@ -205,7 +232,7 @@ static ticcmd_t last_ticcmd;

 // Buffer of ticcmd diffs being sent to the server

-static net_ticdiff_t ticcmd_send_queue[NET_TICCMD_QUEUE_SIZE];
+static net_server_send_t send_queue[NET_TICCMD_QUEUE_SIZE];

 // Receive window

@@ -215,7 +242,35 @@ static net_server_recv_t recvwindow[BACKUPTICS];

 #define NET_CL_ExpandTicNum(b) NET_ExpandTicNum(recvwindow_start, (b))

-static void NET_CL_ExpandTiccmds(net_full_ticcmd_t *cmd)
+// Called when a player leaves the game
+
+static void NET_CL_PlayerQuitGame(player_t *player)
+{
+    static char exitmsg[80];
+
+    // Do this the same way as Vanilla Doom does, to allow dehacked
+    // replacements of this message
+
+    strcpy(exitmsg, DEH_String("Player 1 left the game"));
+
+    exitmsg[7] += player - players;
+
+    players[consoleplayer].message = exitmsg;
+
+    // TODO: check if it is sensible to do this:
+
+    if (demorecording)
+    {
+        G_CheckDemoStatus ();
+    }
+}
+
+// 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)
 {
     int i;

@@ -226,6 +281,11 @@ static void NET_CL_ExpandTiccmds(net_full_ticcmd_t *cmd)
             continue;
         }

+        if (playeringame[i] && !cmd->playeringame[i])
+        {
+            NET_CL_PlayerQuitGame(&players[i]);
+        }
+
         playeringame[i] = cmd->playeringame[i];

         if (playeringame[i])
@@ -260,7 +320,7 @@ static void NET_CL_AdvanceWindow(void)
     {
         // Expand tic diff data into d_net.c structures

-        NET_CL_ExpandTiccmds(&recvwindow[0].cmd);
+        NET_CL_ExpandFullTiccmd(&recvwindow[0].cmd);

         // Advance the window

@@ -335,15 +395,17 @@ static void NET_CL_SendTics(int start, int end)
     NET_WriteInt8(packet, start & 0xff);
     NET_WriteInt8(packet, end - start + 1);

-    // TODO: Include ticcmd construction time for sync.
-
     // Add the tics.

     for (i=start; i<=end; ++i)
     {
-        NET_WriteTiccmdDiff(packet,
-                            &ticcmd_send_queue[i % NET_TICCMD_QUEUE_SIZE],
-                            false);
+        net_server_send_t *sendobj;
+
+        sendobj = &send_queue[i % NET_TICCMD_QUEUE_SIZE];
+
+        NET_WriteInt16(packet, sendobj->time);
+
+        NET_WriteTiccmdDiff(packet, &sendobj->cmd, false);
     }

     // Send the packet
@@ -360,6 +422,7 @@ static void NET_CL_SendTics(int start, int end)
 void NET_CL_SendTiccmd(ticcmd_t *ticcmd, int maketic)
 {
     net_ticdiff_t diff;
+    net_server_send_t *sendobj;

     // Calculate the difference to the last ticcmd

@@ -367,7 +430,11 @@ void NET_CL_SendTiccmd(ticcmd_t *ticcmd, int maketic)

     // Store in the send queue

-    ticcmd_send_queue[maketic % NET_TICCMD_QUEUE_SIZE] = diff;
+    sendobj = &send_queue[maketic % NET_TICCMD_QUEUE_SIZE];
+    sendobj->active = true;
+    sendobj->seq = maketic;
+    sendobj->time = I_GetTimeMS();
+    sendobj->cmd = diff;

     last_ticcmd = *ticcmd;

@@ -480,10 +547,107 @@ static void NET_CL_ParseGameStart(net_packet_t *packet)
     autostart = true;
 }

+static void NET_CL_SendResendRequest(int start, int end)
+{
+    net_packet_t *packet;
+    unsigned int nowtime;
+    int i;
+
+    packet = NET_NewPacket(64);
+    NET_WriteInt16(packet, NET_PACKET_TYPE_GAMEDATA_RESEND);
+    NET_WriteInt32(packet, start);
+    NET_WriteInt8(packet, end - start - 1);
+    NET_Conn_SendPacket(&client_connection, packet);
+
+    nowtime = I_GetTimeMS();
+
+    // Save the time we sent the resend request
+
+    for (i=start; i<=end; ++i)
+    {
+        int index;
+
+        index = start - recvwindow_start;
+
+        if (index < 0 || index >= BACKUPTICS)
+            continue;
+
+        recvwindow[index].resend_time = nowtime;
+    }
+}
+
+// Check for expired resend requests
+
+static void NET_CL_CheckResends(void)
+{
+    int i;
+    int resend_start, resend_end;
+    unsigned int nowtime;
+
+    nowtime = I_GetTimeMS();
+
+    resend_start = -1;
+    resend_end = -1;
+
+    for (i=0; i<BACKUPTICS; ++i)
+    {
+        net_server_recv_t *recvobj;
+        boolean need_resend;
+
+        recvobj = &recvwindow[i];
+
+        // if need_resend is true, this tic needs another retransmit
+        // request (300ms timeout)
+
+        need_resend = !recvobj->active
+                   && recvobj->resend_time != 0
+                   && nowtime > recvobj->resend_time + 300;
+
+        if (need_resend)
+        {
+            // Start a new run of resend tics?
+
+            if (resend_start < 0)
+            {
+                resend_start = i;
+            }
+
+            resend_end = i;
+        }
+        else
+        {
+            if (resend_start >= 0)
+            {
+                // End of a run of resend tics
+
+                //printf("CL: resend request timed out: %i-%i\n", resend_start, resend_end);
+                NET_CL_SendResendRequest(recvwindow_start + resend_start,
+                                         recvwindow_start + resend_end);
+
+                resend_start = -1;
+            }
+        }
+    }
+
+    if (resend_start >= 0)
+    {
+        NET_CL_SendResendRequest(recvwindow_start + resend_start,
+                                 recvwindow_start + resend_end);
+    }
+}
+
+
+// Parsing of NET_PACKET_TYPE_GAMEDATA packets
+// (packets containing the actual ticcmd data)
+
 static void NET_CL_ParseGameData(net_packet_t *packet)
 {
+    net_server_recv_t *recvobj;
     unsigned int seq, num_tics;
+    unsigned int nowtime;
+    int resend_start, resend_end;
     int i;
+    int index;

     // Read header

@@ -493,15 +657,15 @@ static void NET_CL_ParseGameData(net_packet_t *packet)
         return;
     }

+    nowtime = I_GetTimeMS();
+
     // Expand byte value into the full tic number

     seq = NET_CL_ExpandTicNum(seq);

     for (i=0; i<num_tics; ++i)
     {
-        net_server_recv_t *recvobj;
         net_full_ticcmd_t cmd;
-        int index;

         index = seq - recvwindow_start + i;

@@ -524,6 +688,53 @@ static void NET_CL_ParseGameData(net_packet_t *packet)
         recvobj->active = true;
         recvobj->cmd = cmd;
     }
+
+    // Has this been received out of sequence, ie. have we not received
+    // all tics before the first tic in this packet?  If so, send a
+    // resend request.
+
+    //printf("CL: %p: %i\n", client, seq);
+
+    resend_end = seq - recvwindow_start;
+
+    if (resend_end <= 0)
+        return;
+
+    if (resend_end >= BACKUPTICS)
+        resend_end = BACKUPTICS - 1;
+
+    index = resend_end - 1;
+    resend_start = resend_end;
+
+    while (index >= 0)
+    {
+        recvobj = &recvwindow[index];
+
+        if (recvobj->active)
+        {
+            // ended our run of unreceived tics
+
+            break;
+        }
+
+        if (recvobj->resend_time != 0)
+        {
+            // Already sent a resend request for this tic
+
+            break;
+        }
+
+        resend_start = index;
+        --index;
+    }
+
+    // Possibly send a resend request
+
+    if (resend_start < resend_end)
+    {
+        NET_CL_SendResendRequest(recvwindow_start + resend_start,
+                                 recvwindow_start + resend_end - 1);
+    }
 }

 static void NET_CL_ParseTimeRequest(net_packet_t *packet)
@@ -605,6 +816,7 @@ static void NET_CL_ParsePacket(net_packet_t *packet)

             case NET_PACKET_TYPE_GAMEDATA_RESEND:
                 NET_CL_ParseResendRequest(packet);
+                break;

             default:
                 break;
@@ -661,6 +873,10 @@ void NET_CL_Run(void)
         // Possibly advance the receive window

         NET_CL_AdvanceWindow();
+
+        // Check if our resend requests have timed out
+
+        NET_CL_CheckResends();
     }
 }

diff --git a/src/net_server.c b/src/net_server.c
index 6da52a3f..6cc35350 100644
--- a/src/net_server.c
+++ b/src/net_server.c
@@ -1,7 +1,7 @@
 // Emacs style mode select   -*- C++ -*-
 //-----------------------------------------------------------------------------
 //
-// $Id: net_server.c 374 2006-02-19 13:42:27Z fraggle $
+// $Id: net_server.c 375 2006-02-22 18:35:55Z fraggle $
 //
 // Copyright(C) 2005 Simon Howard
 //
@@ -21,6 +21,9 @@
 // 02111-1307, USA.
 //
 // $Log$
+// Revision 1.29  2006/02/22 18:35:55  fraggle
+// Packet resends for server->client gamedata
+//
 // Revision 1.28  2006/02/19 13:42:27  fraggle
 // Move tic number expansion code to common code.  Parse game data packets
 // received from the server.
@@ -724,6 +727,7 @@ static void NET_SV_CheckResends(net_client_t *client)

     player = client->player_number;
     resend_start = -1;
+    resend_end = -1;

     for (i=0; i<BACKUPTICS; ++i)
     {
@@ -814,8 +818,10 @@ static void NET_SV_ParseGameData(net_packet_t *packet, net_client_t *client)
     for (i=0; i<num_tics; ++i)
     {
         net_ticdiff_t diff;
+        unsigned int time;

-        if (!NET_ReadTiccmdDiff(packet, &diff, false))
+        if (!NET_ReadInt16(packet, &time)
+         || !NET_ReadTiccmdDiff(packet, &diff, false))
         {
             return;
         }
@@ -885,6 +891,66 @@ static void NET_SV_ParseGameData(net_packet_t *packet, net_client_t *client)
     }
 }

+static void NET_SV_SendTics(net_client_t *client, int start, int end)
+{
+    net_packet_t *packet;
+    int i;
+
+    packet = NET_NewPacket(500);
+
+    NET_WriteInt16(packet, NET_PACKET_TYPE_GAMEDATA);
+
+    // Send the start tic and number of tics
+
+    NET_WriteInt8(packet, start & 0xff);
+    NET_WriteInt8(packet, end-start + 1);
+
+    // Write the tics
+
+    for (i=start; i<=end; ++i)
+    {
+        net_full_ticcmd_t *cmd;
+
+        cmd = &client->sendqueue[i % BACKUPTICS];
+
+        if (i != cmd->seq)
+        {
+            I_Error("Wanted to send %i, but %i is in its place", i, cmd->seq);
+        }
+
+        // Add command
+
+        NET_WriteFullTiccmd(packet, cmd);
+    }
+
+    // Send packet
+
+    NET_Conn_SendPacket(&client->connection, packet);
+
+    NET_FreePacket(packet);
+}
+
+// Parse a retransmission request from a client
+
+static void NET_SV_ParseResendRequest(net_packet_t *packet, net_client_t *client)
+{
+    static unsigned int start;
+    static unsigned int num_tics;
+
+    // Read the starting tic and number of tics
+
+    if (!NET_ReadInt32(packet, &start)
+     || !NET_ReadInt8(packet, &num_tics))
+    {
+        return;
+    }
+
+    // Resend those tics
+
+    NET_SV_SendTics(client, start, start + num_tics - 1);
+}
+
+
 // Process a packet received by the server

 static void NET_SV_Packet(net_packet_t *packet, net_addr_t *addr)
@@ -929,6 +995,9 @@ static void NET_SV_Packet(net_packet_t *packet, net_addr_t *addr)
             case NET_PACKET_TYPE_GAMEDATA:
                 NET_SV_ParseGameData(packet, client);
                 break;
+            case NET_PACKET_TYPE_GAMEDATA_RESEND:
+                NET_SV_ParseResendRequest(packet, client);
+                break;
 	    case NET_PACKET_TYPE_TIME_RESP:
 		NET_SV_ParseTimeResponse(packet, client);
 		break;
@@ -1016,45 +1085,6 @@ static void NET_SV_SendTimeRequest(net_client_t *client)
     client->last_time_req_time = I_GetTimeMS();
 }

-static void NET_SV_SendTics(net_client_t *client, int start, int end)
-{
-    net_packet_t *packet;
-    int i;
-
-    packet = NET_NewPacket(500);
-
-    NET_WriteInt16(packet, NET_PACKET_TYPE_GAMEDATA);
-
-    // Send the start tic and number of tics
-
-    NET_WriteInt8(packet, start & 0xff);
-    NET_WriteInt8(packet, end-start + 1);
-
-    // Write the tics
-
-    for (i=start; i<=end; ++i)
-    {
-        net_full_ticcmd_t *cmd;
-
-        cmd = &client->sendqueue[i % BACKUPTICS];
-
-        if (i != cmd->seq)
-        {
-            I_Error("Wanted to send %i, but %i is in its place", i, cmd->seq);
-        }
-
-        // Add command
-
-        NET_WriteFullTiccmd(packet, cmd);
-    }
-
-    // Send packet
-
-    NET_Conn_SendPacket(&client->connection, packet);
-
-    NET_FreePacket(packet);
-}
-
 static void NET_SV_PumpSendQueue(net_client_t *client)
 {
     net_full_ticcmd_t cmd;