shithub: choc

Download patch

ref: 4af352a213431e577a48dd8b3ece7822cb7168bc
parent: b693c7f12dbbf367ef41c8aba08774968bfe0604
author: Simon Howard <fraggle@gmail.com>
date: Wed Feb 15 20:12:28 EST 2006

Define a new type net_full_ticcmd_t, a structure containing all ticcmds
for a given tic. Store received game data in a receive window. Add
send queues for clients and add data from the receive window to
generate complete sets of ticcmds.

Subversion-branch: /trunk/chocolate-doom
Subversion-revision: 369

--- a/src/net_defs.h
+++ b/src/net_defs.h
@@ -1,7 +1,7 @@
 // Emacs style mode select   -*- C++ -*- 
 //-----------------------------------------------------------------------------
 //
-// $Id: net_defs.h 323 2006-01-22 22:29:42Z fraggle $
+// $Id: net_defs.h 369 2006-02-16 01:12:28Z fraggle $
 //
 // Copyright(C) 2005 Simon Howard
 //
@@ -21,6 +21,12 @@
 // 02111-1307, USA.
 //
 // $Log$
+// Revision 1.12  2006/02/16 01:12:28  fraggle
+// Define a new type net_full_ticcmd_t, a structure containing all ticcmds
+// for a given tic.  Store received game data in a receive window.  Add
+// send queues for clients and add data from the receive window to
+// generate complete sets of ticcmds.
+//
 // Revision 1.11  2006/01/22 22:29:42  fraggle
 // Periodically request the time from clients to estimate their offset to
 // the server time.
@@ -69,6 +75,7 @@
 #ifndef NET_DEFS_H
 #define NET_DEFS_H 
 
+#include "doomdef.h"
 #include "doomtype.h"
 #include "d_ticcmd.h"
 
@@ -179,6 +186,15 @@
     unsigned int diff;
     ticcmd_t cmd;
 } net_ticdiff_t;
+
+// Complete set of ticcmds from all players
+
+typedef struct 
+{
+    unsigned int seq;
+    boolean playeringame[MAXPLAYERS];
+    net_ticdiff_t cmds[MAXPLAYERS];
+} net_full_ticcmd_t;
 
 #endif /* #ifndef NET_DEFS_H */
 
--- a/src/net_server.c
+++ b/src/net_server.c
@@ -1,7 +1,7 @@
 // Emacs style mode select   -*- C++ -*- 
 //-----------------------------------------------------------------------------
 //
-// $Id: net_server.c 323 2006-01-22 22:29:42Z fraggle $
+// $Id: net_server.c 369 2006-02-16 01:12:28Z fraggle $
 //
 // Copyright(C) 2005 Simon Howard
 //
@@ -21,6 +21,12 @@
 // 02111-1307, USA.
 //
 // $Log$
+// Revision 1.24  2006/02/16 01:12:28  fraggle
+// Define a new type net_full_ticcmd_t, a structure containing all ticcmds
+// for a given tic.  Store received game data in a receive window.  Add
+// send queues for clients and add data from the receive window to
+// generate complete sets of ticcmds.
+//
 // Revision 1.23  2006/01/22 22:29:42  fraggle
 // Periodically request the time from clients to estimate their offset to
 // the server time.
@@ -141,6 +147,7 @@
 typedef struct 
 {
     boolean active;
+    int player_number;
     net_addr_t *addr;
     net_connection_t connection;
     int last_send_time;
@@ -151,16 +158,56 @@
     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
+
+    int sendseq;
+    net_full_ticcmd_t sendqueue[BACKUPTICS];
+
 } net_client_t;
 
+// structure used for the recv window
+
+typedef struct 
+{
+    boolean active;
+    unsigned int time;
+    net_ticdiff_t diff;
+} net_client_recv_t;
+
 static net_server_state_t server_state;
 static boolean server_initialised = false;
 static net_client_t clients[MAXNETNODES];
+static net_client_t *sv_players[MAXPLAYERS];
 static net_context_t *server_context;
 static int sv_gamemode;
 static int sv_gamemission;
 static net_gamesettings_t sv_settings;
 
+// receive window
+
+static unsigned int recvwindow_start;
+static net_client_recv_t recvwindow[BACKUPTICS][MAXPLAYERS];
+
+static unsigned int NET_SV_ExpandTicNum(int i)
+{
+    int l, h;
+    unsigned int result;
+
+    h = recvwindow_start & ~0xff;
+    l = recvwindow_start & 0xff;
+    
+    result = h | i;
+
+    if (i - l > 0x80)
+        result -= 0x100;
+    else if (l - i > 0x80)
+        result += 0x100;
+
+    return result;
+}
+
 static void NET_SV_DisconnectClient(net_client_t *client)
 {
     if (client->active)
@@ -178,6 +225,29 @@
         && client->connection.state == NET_CONN_STATE_CONNECTED;
 }
 
+static void NET_SV_AssignPlayers(void)
+{
+    int i;
+    int pl;
+
+    pl = 0;
+
+    for (i=0; i<MAXNETNODES; ++i)
+    {
+        if (ClientConnected(&clients[i]))
+        {
+            sv_players[pl] = &clients[i];
+            sv_players[pl]->player_number = pl;
+            ++pl;
+        }
+    }
+
+    for (; pl<MAXPLAYERS; ++pl)
+    {
+        sv_players[pl] = NULL;
+    }
+}
+
 // returns the number of clients connected
 
 static int NET_SV_NumClients(void)
@@ -223,6 +293,45 @@
     return -1;
 }
 
+// Possibly advance the recv window if all connected clients have
+// used the data in the window
+
+static void NET_SV_AdvanceWindow(void)
+{
+    int i;
+    int lowtic = -1;
+
+    // Find the smallest value of player->sendseq for all connected
+    // players
+
+    for (i=0; i<MAXPLAYERS; ++i) 
+    {
+        if (sv_players[i] == NULL || !ClientConnected(sv_players[i]))
+        {
+            continue;
+        }
+
+        if (lowtic < 0 || sv_players[i]->sendseq < lowtic)
+        {
+            lowtic = sv_players[i]->sendseq;
+        }
+    }
+
+    if (lowtic < 0)
+    {
+        return;
+    }
+
+    // Advance the recv window until it catches up with lowtic
+
+    while (recvwindow_start < lowtic)
+    {    
+        memcpy(recvwindow, recvwindow + 1, sizeof(*recvwindow) * (BACKUPTICS - 1));
+        memset(&recvwindow[BACKUPTICS-1], 0, sizeof(*recvwindow));
+        ++recvwindow_start;
+    }
+}
+
 // returns a pointer to the client which controls the server
 
 static net_client_t *NET_SV_Controller(void)
@@ -274,6 +383,25 @@
     NET_FreePacket(packet);
 }
 
+static void NET_SV_InitNewClient(net_client_t *client, 
+                                 net_addr_t *addr,
+                                 char *player_name)
+{
+    client->active = true;
+    NET_Conn_InitServer(&client->connection, addr);
+    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
+
+    client->sendseq = 0;
+    memset(client->sendqueue, 0xff, sizeof(client->sendqueue));
+}
+
 // parse a SYN from a client(initiating a connection)
 
 static void NET_SV_ParseSYN(net_packet_t *packet, 
@@ -406,14 +534,7 @@
         
         // Activate, initialise connection
 
-        client->active = true;
-        NET_Conn_InitServer(&client->connection, addr);
-        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;
+        NET_SV_InitNewClient(client, addr, player_name);
     }
 
     if (client->connection.state == NET_CONN_STATE_WAITING_ACK)
@@ -459,18 +580,23 @@
 
     // Send start packets to each connected node
 
-    for (i=0; i<MAXNETNODES; ++i) 
+    NET_SV_AssignPlayers();
+
+    for (i=0; i<MAXPLAYERS; ++i) 
     {
-        if (ClientConnected(&clients[i]))
-        {
-            startpacket = NET_Conn_NewReliable(&clients[i].connection,
-                                               NET_PACKET_TYPE_GAMESTART);
+        if (sv_players[i] == NULL)
+            break;
 
-            NET_WriteInt8(startpacket, NET_SV_NumClients());
-            NET_WriteInt8(startpacket, NET_SV_ClientIndex(&clients[i]));
-            NET_WriteSettings(startpacket, &settings);
-        }
+        startpacket = NET_Conn_NewReliable(&sv_players[i]->connection,
+                                           NET_PACKET_TYPE_GAMESTART);
+
+        NET_WriteInt8(startpacket, NET_SV_NumClients());
+        NET_WriteInt8(startpacket, sv_players[i]->player_number);
+        NET_WriteSettings(startpacket, &settings);
     }
+
+    memset(recvwindow, 0, sizeof(recvwindow));
+    recvwindow_start = 0;
 }
 
 static void NET_SV_ParseTimeResponse(net_packet_t *packet, net_client_t *client)
@@ -527,6 +653,62 @@
     printf("client %p time offset: %i(%i)->%i\n", client, time_offset, rtt, client->time_offset);
 }
 
+// Process game data from a client
+
+static void NET_SV_ParseGameData(net_packet_t *packet, net_client_t *client)
+{
+    net_client_recv_t *recvobj;
+    unsigned int seq;
+    unsigned int num_tics;
+    int i;
+    int player;
+
+    if (server_state != SERVER_IN_GAME)
+    {
+        return;
+    }
+    
+    player = client->player_number;
+
+    // Read header
+
+    if (!NET_ReadInt8(packet, &seq)
+     || !NET_ReadInt8(packet, &num_tics))
+    {
+        return;
+    }
+
+    // Expand 8-bit value to the full sequence number
+
+    seq = NET_SV_ExpandTicNum(seq);
+
+    // Sanity checks
+
+    for (i=0; i<num_tics; ++i)
+    {
+        net_ticdiff_t diff;
+        int index;
+
+        if (!NET_ReadTiccmdDiff(packet, &diff, false))
+        {
+            return;
+        }
+
+        index = seq + i - recvwindow_start;
+
+        if (index < 0 || index >= BACKUPTICS)
+        {
+            // Not in range of the recv window
+
+            continue;
+        }
+
+        recvobj = &recvwindow[index][player];
+        recvobj->active = true;
+        recvobj->diff = diff;
+    }
+}
+
 // Process a packet received by the server
 
 static void NET_SV_Packet(net_packet_t *packet, net_addr_t *addr)
@@ -568,6 +750,9 @@
             case NET_PACKET_TYPE_GAMESTART:
                 NET_SV_ParseGameStart(packet, client);
                 break;
+            case NET_PACKET_TYPE_GAMEDATA:
+                NET_SV_ParseGameData(packet, client);
+                break;
 	    case NET_PACKET_TYPE_TIME_RESP:
 		NET_SV_ParseTimeResponse(packet, client);
 		break;
@@ -655,6 +840,72 @@
     client->last_time_req_time = I_GetTimeMS();
 }
 
+static void NET_SV_PumpSendQueue(net_client_t *client)
+{
+    net_full_ticcmd_t cmd;
+    int recv_index;
+    int i;
+
+    recv_index = client->sendseq - recvwindow_start;
+
+    if (recv_index < 0 || recv_index >= BACKUPTICS)
+    {
+        return;
+    }
+
+    // Check if we can generate a new entry for the send queue
+    // using the data in recvwindow.
+
+    for (i=0; i<MAXPLAYERS; ++i)
+    {
+        if (sv_players[i] == client)
+        {
+            // Client does not rely on itself for data
+
+            continue;
+        }
+
+        if (sv_players[i] == NULL || !ClientConnected(sv_players[i]))
+        {
+            continue;
+        }
+
+        if (!recvwindow[recv_index][i].active)
+        {
+            // We do not have this player's ticcmd, so we cannot
+            // generate a complete command yet.
+
+            return;
+        }
+    }
+
+    //printf("have complete ticcmd for %i\n", client->sendseq);
+
+    // We have all data we need to generate a command for this tic.
+    
+    cmd.seq = client->sendseq;
+
+    // Add ticcmds from all players
+
+    for (i=0; i<MAXPLAYERS; ++i)
+    {
+        if (sv_players[i] == NULL || !recvwindow[recv_index][i].active)
+        {
+            cmd.playeringame[i] = false;
+            continue;
+        }
+
+        cmd.playeringame[i] = true;
+        cmd.cmds[i] = recvwindow[recv_index][i].diff;
+    }
+
+    // Add into the queue
+
+    client->sendqueue[client->sendseq % BACKUPTICS] = cmd;
+
+    ++client->sendseq;
+}
+
 // Perform any needed action on a client
 
 static void NET_SV_RunClient(net_client_t *client)
@@ -706,6 +957,11 @@
 	
 	NET_SV_SendTimeRequest(client);
     }
+
+    if (server_state == SERVER_IN_GAME)
+    {
+        NET_SV_PumpSendQueue(client);
+    }
 }
 
 // Initialise server and wait for connections
@@ -761,6 +1017,11 @@
         {
             NET_SV_RunClient(&clients[i]);
         }
+    }
+
+    if (server_state == SERVER_IN_GAME)
+    {
+        NET_SV_AdvanceWindow();
     }
 }
 
--- a/src/net_structrw.c
+++ b/src/net_structrw.c
@@ -1,7 +1,7 @@
 // Emacs style mode select   -*- C++ -*- 
 //-----------------------------------------------------------------------------
 //
-// $Id: net_structrw.c 295 2006-01-14 02:06:48Z fraggle $
+// $Id: net_structrw.c 369 2006-02-16 01:12:28Z fraggle $
 //
 // Copyright(C) 2005 Simon Howard
 //
@@ -21,6 +21,12 @@
 // 02111-1307, USA.
 //
 // $Log$
+// Revision 1.6  2006/02/16 01:12:28  fraggle
+// Define a new type net_full_ticcmd_t, a structure containing all ticcmds
+// for a given tic.  Store received game data in a receive window.  Add
+// send queues for clients and add data from the receive window to
+// generate complete sets of ticcmds.
+//
 // Revision 1.5  2006/01/14 02:06:48  fraggle
 // Include the game version in the settings structure.
 //
@@ -47,6 +53,8 @@
 #include <stdlib.h>
 #include <string.h>
 
+#include "doomdef.h"
+
 #include "net_packet.h"
 #include "net_structrw.h"
 
@@ -211,5 +219,73 @@
         dest->chatchar = diff->cmd.chatchar;
     else
         dest->chatchar = 0;
+}
+
+// 
+// net_full_ticcmd_t
+// 
+
+boolean NET_ReadFullTiccmd(net_packet_t *packet, net_full_ticcmd_t *cmd)
+{
+    unsigned int bitfield;
+    int i;
+
+    // Regenerate playeringame from the "header" bitfield
+
+    if (!NET_ReadInt8(packet, &bitfield))
+    {
+        return false;
+    }
+          
+    for (i=0; i<MAXPLAYERS; ++i)
+    {
+        cmd->playeringame[i] = (bitfield & (1 << i)) != 0;
+    }
+        
+    // Read cmds
+
+    for (i=0; i<MAXPLAYERS; ++i)
+    {
+        if (cmd->playeringame[i])
+        {
+            if (!NET_ReadTiccmdDiff(packet, &cmd->cmds[i], false))
+            {
+                return false;
+            }
+        }
+    }
+
+    return true;
+}
+
+void NET_WriteFullTiccmd(net_packet_t *packet, net_full_ticcmd_t *cmd)
+{
+    unsigned int bitfield;
+    int i;
+
+    // Write "header" byte indicating which players are active
+    // in this ticcmd
+
+    bitfield = 0;
+    
+    for (i=0; i<MAXPLAYERS; ++i)
+    {
+        if (cmd->playeringame[i])
+        {
+            bitfield |= 1 << i;
+        }
+    }
+    
+    NET_WriteInt8(packet, bitfield);
+
+    // Write player ticcmds
+
+    for (i=0; i<MAXPLAYERS; ++i)
+    {
+        if (cmd->playeringame[i])
+        {
+            NET_WriteTiccmdDiff(packet, &cmd->cmds[i], false);
+        }
+    }
 }
 
--- a/src/net_structrw.h
+++ b/src/net_structrw.h
@@ -1,7 +1,7 @@
 // Emacs style mode select   -*- C++ -*- 
 //-----------------------------------------------------------------------------
 //
-// $Id: net_structrw.h 288 2006-01-13 02:22:47Z fraggle $
+// $Id: net_structrw.h 369 2006-02-16 01:12:28Z fraggle $
 //
 // Copyright(C) 2005 Simon Howard
 //
@@ -21,6 +21,12 @@
 // 02111-1307, USA.
 //
 // $Log$
+// Revision 1.4  2006/02/16 01:12:28  fraggle
+// Define a new type net_full_ticcmd_t, a structure containing all ticcmds
+// for a given tic.  Store received game data in a receive window.  Add
+// send queues for clients and add data from the receive window to
+// generate complete sets of ticcmds.
+//
 // Revision 1.3  2006/01/13 02:22:47  fraggle
 // Update prototypes to match header.  Make sure we include the header in the
 // source file.
@@ -48,6 +54,10 @@
 extern boolean NET_ReadTiccmdDiff(net_packet_t *packet, net_ticdiff_t *diff, boolean lowres_turn);
 extern void NET_TiccmdDiff(ticcmd_t *tic1, ticcmd_t *tic2, net_ticdiff_t *diff);
 extern void NET_TiccmdPatch(ticcmd_t *src, net_ticdiff_t *diff, ticcmd_t *dest);
+
+boolean NET_ReadFullTiccmd(net_packet_t *packet, net_full_ticcmd_t *cmd);
+void NET_WriteFullTiccmd(net_packet_t *packet, net_full_ticcmd_t *cmd);
+
 
 #endif /* #ifndef NET_STRUCTRW_H */