foxygit / doom Log in
commit 005747a6174d2d5b72e1af196a72cafb9b801a58
Author:     Simon Howard <fraggle@gmail.com>
AuthorDate: Sat Nov 20 16:45:27 2010 +0000
Commit:     Simon Howard <fraggle@gmail.com>
CommitDate: Sat Nov 20 16:45:27 2010 +0000

    Remove dependency of network code on Doom code.

    Subversion-branch: /branches/raven-branch
    Subversion-revision: 2163
---
 src/doom/d_net.c   | 354 ++++++++++++++++++++++++++++++++++++++---------------
 src/doom/d_net.h   |   1 -
 src/net_client.c   | 183 +++++++--------------------
 src/net_client.h   |   6 +-
 src/net_defs.h     |  25 ++++
 src/net_gui.c      |  14 ++-
 src/net_gui.h      |   2 +-
 src/net_server.c   |  31 +++--
 src/net_structrw.c |  22 +++-
 src/net_structrw.h |   3 +
 10 files changed, 372 insertions(+), 269 deletions(-)

diff --git a/src/doom/d_net.c b/src/doom/d_net.c
index a15d3b1a..05d1875f 100644
--- a/src/doom/d_net.c
+++ b/src/doom/d_net.c
@@ -25,7 +25,7 @@
 //
 //-----------------------------------------------------------------------------

-
+#include <stdlib.h>

 #include "doomfeatures.h"

@@ -76,8 +76,7 @@ int             recvtic;

 // Used for original sync code.

-int		lastnettic;
-int             skiptics = 0;
+static int      skiptics = 0;

 // Reduce the bandwidth needed by sampling game input less and transmitting
 // less.  If ticdup is 2, sample half normal, 3 = one third normal, etc.
@@ -94,11 +93,7 @@ fixed_t         offsetms;

 // Use new client syncronisation code

-boolean         net_cl_new_sync = true;
-
-// Connected but not participating in the game (observer)
-
-boolean drone = false;
+boolean         new_sync = true;

 // 35 fps clock adjusted by offsetms milliseconds

@@ -108,7 +103,7 @@ static int GetAdjustedTime(void)

     time_ms = I_GetTimeMS();

-    if (net_cl_new_sync)
+    if (new_sync)
     {
 	// Use the adjustments from net_client.c only if we are
 	// using the new sync mode.
@@ -186,7 +181,7 @@ void NetUpdate (void)
             continue;
         }

-        if (net_cl_new_sync)
+        if (new_sync)
         {
            // If playing single player, do not allow tics to buffer
            // up very far
@@ -309,21 +304,144 @@ void D_StartGameLoop(void)
     lasttime = GetAdjustedTime() / ticdup;
 }

+// Load game settings from the specified structure and
+// set global variables.

-//
-// D_CheckNetGame
-// Works out player numbers among the net participants
-//
-extern	int			viewangleoffset;
+static void LoadGameSettings(net_gamesettings_t *settings)
+{
+    unsigned int i;

-void D_CheckNetGame (void)
+    deathmatch = settings->deathmatch;
+    ticdup = settings->ticdup;
+    extratics = settings->extratics;
+    startepisode = settings->episode;
+    startmap = settings->map;
+    startskill = settings->skill;
+    startloadgame = settings->loadgame;
+    lowres_turn = settings->lowres_turn;
+    nomonsters = settings->nomonsters;
+    fastparm = settings->fast_monsters;
+    respawnparm = settings->respawn_monsters;
+    timelimit = settings->timelimit;
+
+    if (lowres_turn)
+    {
+        printf("NOTE: Turning resolution is reduced; this is probably "
+               "because there is a client recording a Vanilla demo.\n");
+    }
+
+    new_sync = settings->new_sync;
+
+    if (new_sync == false)
+    {
+	printf("Syncing netgames like Vanilla Doom.\n");
+    }
+
+    netgame = true;
+    autostart = true;
+
+    if (!drone)
+    {
+        consoleplayer = settings->consoleplayer;
+    }
+    else
+    {
+        consoleplayer = 0;
+    }
+
+    for (i=0; i<MAXPLAYERS; ++i)
+    {
+        playeringame[i] = i < settings->num_players;
+    }
+}
+
+// Save the game settings from global variables to the specified
+// game settings structure.
+
+static void SaveGameSettings(net_gamesettings_t *settings,
+                             net_connect_data_t *connect_data)
 {
     int i;
-    int num_players;

-    // Call D_QuitNetGame on exit
+    // Fill in game settings structure with appropriate parameters
+    // for the new game
+
+    settings->deathmatch = deathmatch;
+    settings->episode = startepisode;
+    settings->map = startmap;
+    settings->skill = startskill;
+    settings->loadgame = startloadgame;
+    settings->gameversion = gameversion;
+    settings->nomonsters = nomonsters;
+    settings->fast_monsters = fastparm;
+    settings->respawn_monsters = respawnparm;
+    settings->timelimit = timelimit;
+
+    settings->lowres_turn = M_CheckParm("-record") > 0
+                         && M_CheckParm("-longtics") == 0;
+
+    //!
+    // @category net
+    //
+    // Use original game sync code.
+    //
+
+    if (M_CheckParm("-oldsync") > 0)
+	settings->new_sync = 0;
+    else
+	settings->new_sync = 1;
+
+    //!
+    // @category net
+    // @arg <n>
+    //
+    // Send n extra tics in every packet as insurance against dropped
+    // packets.
+    //
+
+    i = M_CheckParm("-extratics");
+
+    if (i > 0)
+        settings->extratics = atoi(myargv[i+1]);
+    else
+        settings->extratics = 1;

-    I_AtExit(D_QuitNetGame, true);
+    //!
+    // @category net
+    // @arg <n>
+    //
+    // Reduce the resolution of the game by a factor of n, reducing
+    // the amount of network bandwidth needed.
+    //
+
+    i = M_CheckParm("-dup");
+
+    if (i > 0)
+        settings->ticdup = atoi(myargv[i+1]);
+    else
+        settings->ticdup = 1;
+
+    //
+    // Connect data
+    //
+
+    // Game type fields:
+
+    connect_data->gamemode = gamemode;
+    connect_data->gamemission = gamemission;
+
+    // Drone mode?
+
+    connect_data->drone = M_CheckParm("-drone") > 0;
+
+    // Are we recording a demo? Possibly set lowres turn mode
+
+    connect_data->lowres_turn = settings->lowres_turn;
+}
+
+void D_InitSinglePlayerGame(void)
+{
+    int i;

     // default values for single player

@@ -342,128 +460,160 @@ void D_CheckNetGame (void)
     recvtic = 0;

     playeringame[0] = true;
+}
+
+boolean D_InitNetGame(net_connect_data_t *connect_data,
+                      net_gamesettings_t *settings)
+{
+    net_addr_t *addr = NULL;
+    int i;

 #ifdef FEATURE_MULTIPLAYER

+    //!
+    // @category net
+    //
+    // Start a multiplayer server, listening for connections.
+    //
+
+    if (M_CheckParm("-server") > 0)
     {
-        net_addr_t *addr = NULL;
+        NET_SV_Init();
+        NET_SV_AddModule(&net_loop_server_module);
+        NET_SV_AddModule(&net_sdl_module);

-        //!
+        net_loop_client_module.InitClient();
+        addr = net_loop_client_module.ResolveAddress(NULL);
+    }
+    else
+    {
+        //!
         // @category net
         //
-        // Start a multiplayer server, listening for connections.
+        // Automatically search the local LAN for a multiplayer
+        // server and join it.
         //

-        if (M_CheckParm("-server") > 0)
+        i = M_CheckParm("-autojoin");
+
+        if (i > 0)
         {
-            NET_SV_Init();
-            NET_SV_AddModule(&net_loop_server_module);
-            NET_SV_AddModule(&net_sdl_module);
+            addr = NET_FindLANServer();

-            net_loop_client_module.InitClient();
-            addr = net_loop_client_module.ResolveAddress(NULL);
+            if (addr == NULL)
+            {
+                I_Error("No server found on local LAN");
+            }
         }
-        else
-        {
-            //!
-            // @category net
-            //
-            // Automatically search the local LAN for a multiplayer
-            // server and join it.
-            //

-            i = M_CheckParm("-autojoin");
+        //!
+        // @arg <address>
+        // @category net
+        //
+        // Connect to a multiplayer server running on the given
+        // address.
+        //
+
+        i = M_CheckParm("-connect");

-            if (i > 0)
-            {
-                addr = NET_FindLANServer();
+        if (i > 0)
+        {
+            net_sdl_module.InitClient();
+            addr = net_sdl_module.ResolveAddress(myargv[i+1]);

-                if (addr == NULL)
-                {
-                    I_Error("No server found on local LAN");
-                }
+            if (addr == NULL)
+            {
+                I_Error("Unable to resolve '%s'\n", myargv[i+1]);
             }
+        }
+    }

-            //!
-            // @arg <address>
-            // @category net
-            //
-            // Connect to a multiplayer server running on the given
-            // address.
-            //
-
-            i = M_CheckParm("-connect");
-
-            if (i > 0)
-            {
-                net_sdl_module.InitClient();
-                addr = net_sdl_module.ResolveAddress(myargv[i+1]);
+    if (addr != NULL)
+    {
+        if (M_CheckParm("-drone") > 0)
+        {
+            connect_data->drone = true;
+        }

-                if (addr == NULL)
-                {
-                    I_Error("Unable to resolve '%s'\n", myargv[i+1]);
-                }
-            }
+        //!
+        // @category net
+        //
+        // Run as the left screen in three screen mode.
+        //
+
+        if (M_CheckParm("-left") > 0)
+        {
+            viewangleoffset = ANG90;
+            connect_data->drone = true;
         }

-        if (addr != NULL)
+        //!
+        // @category net
+        //
+        // Run as the right screen in three screen mode.
+        //
+
+        if (M_CheckParm("-right") > 0)
         {
-            if (M_CheckParm("-drone") > 0)
-            {
-                drone = true;
-            }
+            viewangleoffset = ANG270;
+            connect_data->drone = true;
+        }

-            //!
-            // @category net
-            //
-            // Run as the left screen in three screen mode.
-            //
+        if (!NET_CL_Connect(addr, connect_data))
+        {
+            I_Error("D_CheckNetGame: Failed to connect to %s\n",
+                    NET_AddrToString(addr));
+        }

-            if (M_CheckParm("-left") > 0)
-            {
-                viewangleoffset = ANG90;
-                drone = true;
-            }
+        printf("D_CheckNetGame: Connected to %s\n", NET_AddrToString(addr));

-            //!
-            // @category net
-            //
-            // Run as the right screen in three screen mode.
-            //
+        // Wait for game start message received from server.

-            if (M_CheckParm("-right") > 0)
-            {
-                viewangleoffset = ANG270;
-                drone = true;
-            }
+        NET_WaitForStart(settings);

-            if (!NET_CL_Connect(addr))
-            {
-                I_Error("D_CheckNetGame: Failed to connect to %s\n",
-                        NET_AddrToString(addr));
-            }
+        // Read the game settings that were received.

-            printf("D_CheckNetGame: Connected to %s\n", NET_AddrToString(addr));
+        NET_CL_GetSettings(settings);

-            NET_WaitForStart();
-        }
+        return true;
     }

 #endif

-    num_players = 0;
+    return false;
+}
+
+//
+// D_CheckNetGame
+// Works out player numbers among the net participants
+//
+extern	int			viewangleoffset;
+
+void D_CheckNetGame (void)
+{
+    net_connect_data_t connect_data;
+    net_gamesettings_t settings;
+
+    // Call D_QuitNetGame on exit

-    for (i=0; i<MAXPLAYERS; ++i)
+    I_AtExit(D_QuitNetGame, true);
+
+    SaveGameSettings(&settings, &connect_data);
+
+    if (D_InitNetGame(&connect_data, &settings))
     {
-        if (playeringame[i])
-            ++num_players;
+        LoadGameSettings(&settings);
+    }
+    else
+    {
+        D_InitSinglePlayerGame();
     }

     DEH_printf("startskill %i  deathmatch: %i  startmap: %i  startepisode: %i\n",
                startskill, deathmatch, startmap, startepisode);

     DEH_printf("player %i of %i (%i nodes)\n",
-               consoleplayer+1, num_players, num_players);
+               consoleplayer+1, settings.num_players, settings.num_players);

     // Show players here; the server might have specified a time limit

@@ -687,7 +837,7 @@ void TryRunTics (void)

     // decide how many tics to run

-    if (net_cl_new_sync)
+    if (new_sync)
     {
 	counts = availabletics;
     }
diff --git a/src/doom/d_net.h b/src/doom/d_net.h
index f801d216..a3c1a43c 100644
--- a/src/doom/d_net.h
+++ b/src/doom/d_net.h
@@ -45,7 +45,6 @@ void TryRunTics (void);
 // Called at start of game loop to initialize timers
 void D_StartGameLoop(void);

-extern boolean drone;
 extern boolean net_cl_new_sync;

 #endif
diff --git a/src/net_client.c b/src/net_client.c
index 12bda6fd..ffc9b49b 100644
--- a/src/net_client.c
+++ b/src/net_client.c
@@ -33,6 +33,7 @@
 #include "i_system.h"
 #include "i_timer.h"
 #include "m_argv.h"
+#include "m_fixed.h"
 #include "m_config.h"
 #include "net_client.h"
 #include "net_common.h"
@@ -45,8 +46,6 @@
 #include "w_checksum.h"
 #include "w_wad.h"

-#include "doom/doomstat.h"
-
 extern void D_ReceiveTic(ticcmd_t *ticcmds, boolean *playeringame);

 typedef enum
@@ -107,6 +106,10 @@ static net_clientstate_t client_state;
 static net_addr_t *server_addr;
 static net_context_t *client_context;

+// game settings, as received from the server when the game started
+
+static net_gamesettings_t settings;
+
 // true if the client code is in use

 boolean net_client_connected;
@@ -154,6 +157,10 @@ boolean net_waiting_for_start = false;

 char *net_player_name = NULL;

+// Connected but not participating in the game (observer)
+
+boolean drone = false;
+
 // The last ticcmd constructed

 static ticcmd_t last_ticcmd;
@@ -264,7 +271,7 @@ static void NET_CL_ExpandFullTiccmd(net_full_ticcmd_t *cmd, unsigned int seq,

     for (i=0; i<MAXPLAYERS; ++i)
     {
-        if (i == consoleplayer && !drone)
+        if (i == settings.consoleplayer && !drone)
         {
             continue;
         }
@@ -327,66 +334,9 @@ static void NET_CL_Shutdown(void)
     }
 }

-void NET_CL_StartGame(void)
+void NET_CL_StartGame(net_gamesettings_t *settings)
 {
     net_packet_t *packet;
-    net_gamesettings_t settings;
-    int i;
-
-    // Fill in game settings structure with appropriate parameters
-    // for the new game
-
-    settings.deathmatch = deathmatch;
-    settings.episode = startepisode;
-    settings.map = startmap;
-    settings.skill = startskill;
-    settings.loadgame = startloadgame;
-    settings.gameversion = gameversion;
-    settings.nomonsters = nomonsters;
-    settings.fast_monsters = fastparm;
-    settings.respawn_monsters = respawnparm;
-    settings.timelimit = timelimit;
-
-    //!
-    // @category net
-    //
-    // Use original game sync code.
-    //
-
-    if (M_CheckParm("-oldsync") > 0)
-	settings.new_sync = 0;
-    else
-	settings.new_sync = 1;
-
-    //!
-    // @category net
-    // @arg <n>
-    //
-    // Send n extra tics in every packet as insurance against dropped
-    // packets.
-    //
-
-    i = M_CheckParm("-extratics");
-
-    if (i > 0)
-        settings.extratics = atoi(myargv[i+1]);
-    else
-        settings.extratics = 1;
-
-    //!
-    // @category net
-    // @arg <n>
-    //
-    // Reduce the resolution of the game by a factor of n, reducing
-    // the amount of network bandwidth needed.
-    //
-
-    i = M_CheckParm("-dup");
-
-    if (i > 0)
-        settings.ticdup = atoi(myargv[i+1]);
-    else
-        settings.ticdup = 1;

     // Start from a ticcmd of all zeros

@@ -397,7 +347,7 @@ void NET_CL_StartGame(void)
     packet = NET_Conn_NewReliable(&client_connection,
                                   NET_PACKET_TYPE_GAMESTART);

-    NET_WriteSettings(packet, &settings);
+    NET_WriteSettings(packet, settings);
 }

 static void NET_CL_SendGameDataACK(void)
@@ -407,7 +357,7 @@ static void NET_CL_SendGameDataACK(void)
     packet = NET_NewPacket(10);

     NET_WriteInt16(packet, NET_PACKET_TYPE_GAMEDATA_ACK);
-    NET_WriteInt8(packet, (gametic / ticdup) & 0xff);
+    NET_WriteInt8(packet, recvwindow_start & 0xff);

     NET_Conn_SendPacket(&client_connection, packet);

@@ -439,7 +389,7 @@ static void NET_CL_SendTics(int start, int end)
     // Write the start tic and number of tics.  Send only the low byte
     // of start - it can be inferred by the server.

-    NET_WriteInt8(packet, (gametic / ticdup) & 0xff);
+    NET_WriteInt8(packet, recvwindow_start & 0xff);
     NET_WriteInt8(packet, start & 0xff);
     NET_WriteInt8(packet, end - start + 1);

@@ -453,7 +403,7 @@ static void NET_CL_SendTics(int start, int end)

         NET_WriteInt16(packet, average_latency / FRACUNIT);

-        NET_WriteTiccmdDiff(packet, &sendobj->cmd, lowres_turn);
+        NET_WriteTiccmdDiff(packet, &sendobj->cmd, settings.lowres_turn);
     }

     // Send the packet
@@ -493,7 +443,7 @@ void NET_CL_SendTiccmd(ticcmd_t *ticcmd, int maketic)

     // Send to server.

-    starttic = maketic - extratics;
+    starttic = maketic - settings.extratics;
     endtic = maketic;

     if (starttic < 0)
@@ -584,14 +534,7 @@ static void NET_CL_ParseWaitingData(net_packet_t *packet)

 static void NET_CL_ParseGameStart(net_packet_t *packet)
 {
-    net_gamesettings_t settings;
-    unsigned int num_players;
-    signed int player_number;
-    unsigned int i;
-
-    if (!NET_ReadInt8(packet, &num_players)
-     || !NET_ReadSInt8(packet, &player_number)
-     || !NET_ReadSettings(packet, &settings))
+    if (!NET_ReadSettings(packet, &settings))
     {
         return;
     }
@@ -601,14 +544,15 @@ static void NET_CL_ParseGameStart(net_packet_t *packet)
         return;
     }

-    if (num_players > MAXPLAYERS || player_number >= (signed int) num_players)
+    if (settings.num_players > MAXPLAYERS
+     || settings.consoleplayer >= (signed int) settings.num_players)
     {
         // insane values
         return;
     }

-    if ((drone && player_number >= 0)
-     || (!drone && player_number < 0))
+    if ((drone && settings.consoleplayer >= 0)
+     || (!drone && settings.consoleplayer < 0))
     {
         // Invalid player number: must be positive for real players,
         // negative for drones
@@ -616,49 +560,8 @@ static void NET_CL_ParseGameStart(net_packet_t *packet)
         return;
     }

-    // Start the game
-
-    if (!drone)
-    {
-        consoleplayer = player_number;
-    }
-    else
-    {
-        consoleplayer = 0;
-    }
-
-    for (i=0; i<MAXPLAYERS; ++i)
-    {
-        playeringame[i] = i < num_players;
-    }
-
     client_state = CLIENT_STATE_IN_GAME;

-    deathmatch = settings.deathmatch;
-    ticdup = settings.ticdup;
-    extratics = settings.extratics;
-    startepisode = settings.episode;
-    startmap = settings.map;
-    startskill = settings.skill;
-    startloadgame = settings.loadgame;
-    lowres_turn = settings.lowres_turn;
-    nomonsters = settings.nomonsters;
-    fastparm = settings.fast_monsters;
-    respawnparm = settings.respawn_monsters;
-    net_cl_new_sync = settings.new_sync != 0;
-    timelimit = settings.timelimit;
-
-    if (net_cl_new_sync == false)
-    {
-	printf("Syncing netgames like Vanilla Doom.\n");
-    }
-
-    if (lowres_turn)
-    {
-        printf("NOTE: Turning resolution is reduced; this is probably "
-               "because there is a client recording a Vanilla demo.\n");
-    }
-
     // Clear the receive window

     memset(recvwindow, 0, sizeof(recvwindow));
@@ -668,9 +571,6 @@ static void NET_CL_ParseGameStart(net_packet_t *packet)
     // Clear the send queue

     memset(&send_queue, 0x00, sizeof(send_queue));
-
-    netgame = true;
-    autostart = true;
 }

 static void NET_CL_SendResendRequest(int start, int end)
@@ -817,7 +717,7 @@ static void NET_CL_ParseGameData(net_packet_t *packet)

         index = seq - recvwindow_start + i;

-        if (!NET_ReadFullTiccmd(packet, &cmd, lowres_turn))
+        if (!NET_ReadFullTiccmd(packet, &cmd, settings.lowres_turn))
         {
             return;
         }
@@ -1058,7 +958,7 @@ void NET_CL_Run(void)
     }
 }

-static void NET_CL_SendSYN(void)
+static void NET_CL_SendSYN(net_connect_data_t *data)
 {
     net_packet_t *packet;

@@ -1066,10 +966,7 @@ static void NET_CL_SendSYN(void)
     NET_WriteInt16(packet, NET_PACKET_TYPE_SYN);
     NET_WriteInt32(packet, NET_MAGIC_NUMBER);
     NET_WriteString(packet, PACKAGE_STRING);
-    NET_WriteInt16(packet, gamemode);
-    NET_WriteInt16(packet, gamemission);
-    NET_WriteInt8(packet, lowres_turn);
-    NET_WriteInt8(packet, drone);
+    NET_WriteConnectData(packet, data);
     NET_WriteMD5Sum(packet, net_local_wad_md5sum);
     NET_WriteMD5Sum(packet, net_local_deh_md5sum);
     NET_WriteInt8(packet, net_local_is_freedoom);
@@ -1080,20 +977,13 @@ static void NET_CL_SendSYN(void)

 // connect to a server

-boolean NET_CL_Connect(net_addr_t *addr)
+boolean NET_CL_Connect(net_addr_t *addr, net_connect_data_t *data)
 {
     int start_time;
     int last_send_time;

     server_addr = addr;

-    // Are we recording a demo? Possibly set lowres turn mode
-
-    if (M_CheckParm("-record") > 0 && M_CheckParm("-longtics") == 0)
-    {
-        lowres_turn = true;
-    }
-
     // Read checksums of our WAD directory and dehacked information

     W_Checksum(net_local_wad_md5sum);
@@ -1107,7 +997,7 @@ boolean NET_CL_Connect(net_addr_t *addr)
     // necessary module

     client_context = NET_NewContext();
-
+
     // initialize module for client mode

     if (!addr->module->InitClient())
@@ -1137,7 +1027,7 @@ boolean NET_CL_Connect(net_addr_t *addr)

         if (nowtime - last_send_time > 1000 || last_send_time < 0)
         {
-            NET_CL_SendSYN();
+            NET_CL_SendSYN(data);
             last_send_time = nowtime;
         }

@@ -1151,7 +1041,7 @@ boolean NET_CL_Connect(net_addr_t *addr)
         // run client code

         NET_CL_Run();
-
+
         // run the server, just incase we are doing a loopback
         // connect

@@ -1167,6 +1057,7 @@ boolean NET_CL_Connect(net_addr_t *addr)
         // connected ok!

         client_state = CLIENT_STATE_WAITING_START;
+        drone = data->drone;

         return true;
     }
@@ -1175,11 +1066,25 @@ boolean NET_CL_Connect(net_addr_t *addr)
         // failed to connect

         NET_CL_Shutdown();
-
+
         return false;
     }
 }

+// read game settings received from server
+
+boolean NET_CL_GetSettings(net_gamesettings_t *_settings)
+{
+    if (client_state != CLIENT_STATE_IN_GAME)
+    {
+        return false;
+    }
+
+    memcpy(_settings, &settings, sizeof(net_gamesettings_t));
+
+    return true;
+}
+
 // disconnect from the server

 void NET_CL_Disconnect(void)
diff --git a/src/net_client.h b/src/net_client.h
index 078a19a2..b071d32a 100644
--- a/src/net_client.h
+++ b/src/net_client.h
@@ -31,12 +31,13 @@

 #define MAXPLAYERNAME 30

-boolean NET_CL_Connect(net_addr_t *addr);
+boolean NET_CL_Connect(net_addr_t *addr, net_connect_data_t *data);
 void NET_CL_Disconnect(void);
 void NET_CL_Run(void);
 void NET_CL_Init(void);
-void NET_CL_StartGame();
+void NET_CL_StartGame(net_gamesettings_t *settings);
 void NET_CL_SendTiccmd(ticcmd_t *ticcmd, int maketic);
+boolean NET_CL_GetSettings(net_gamesettings_t *_settings);
 void NET_Init(void);

 void NET_BindVariables(void);
@@ -59,6 +60,7 @@ extern md5_digest_t net_local_wad_md5sum;
 extern md5_digest_t net_local_deh_md5sum;
 extern unsigned int net_local_is_freedoom;

+extern boolean drone;

 #endif /* #ifndef NET_CLIENT_H */

diff --git a/src/net_defs.h b/src/net_defs.h
index a5fddc4d..72e0e6d2 100644
--- a/src/net_defs.h
+++ b/src/net_defs.h
@@ -128,6 +128,22 @@ typedef enum
     NET_PACKET_TYPE_QUERY_RESPONSE,
 } net_packet_type_t;

+// Settings specified when the client connects to the server.
+
+typedef struct
+{
+    int gamemode;
+    int gamemission;
+    int lowres_turn;
+    int drone;
+    // TODO: is_freedoom in here?  WAD/DEH checksums?
+    // TODO: [Hexen] Requested player class
+
+} net_connect_data_t;
+
+// Game settings sent by client to server when initiating game start,
+// and received from the server by clients when the game starts.
+
 typedef struct
 {
     int ticdup;
@@ -144,6 +160,15 @@ typedef struct
     int new_sync;
     int timelimit;
     int loadgame;
+
+    // These fields are only used by the server when sending a game
+    // start message:
+
+    int num_players;
+    int consoleplayer;
+
+    // TODO: [Hexen] Array of player classes, one for each player.
+
 } net_gamesettings_t;

 #define NET_TICDIFF_FORWARD      (1 << 0)
diff --git a/src/net_gui.c b/src/net_gui.c
index 913b083d..7473c948 100644
--- a/src/net_gui.c
+++ b/src/net_gui.c
@@ -53,9 +53,11 @@ static void EscapePressed(TXT_UNCAST_ARG(widget), void *unused)
     I_Quit();
 }

-static void StartGame(TXT_UNCAST_ARG(widget), void *unused)
+static void StartGame(TXT_UNCAST_ARG(widget), TXT_UNCAST_ARG(settings))
 {
-    NET_CL_StartGame();
+    TXT_CAST_ARG(net_gamesettings_t, settings);
+
+    NET_CL_StartGame(settings);
 }

 static void BuildGUI(void)
@@ -101,7 +103,7 @@ static void BuildGUI(void)
     TXT_SetWindowAction(window, TXT_HORIZ_LEFT, cancel);
 }

-static void UpdateGUI(void)
+static void UpdateGUI(net_gamesettings_t *settings)
 {
     txt_window_action_t *startgame;
     char buf[50];
@@ -144,7 +146,7 @@ static void UpdateGUI(void)
     if (net_client_controller)
     {
         startgame = TXT_NewWindowAction(' ', "Start game");
-        TXT_SignalConnect(startgame, "pressed", StartGame, NULL);
+        TXT_SignalConnect(startgame, "pressed", StartGame, settings);
     }
     else
     {
@@ -259,7 +261,7 @@ static void CheckMD5Sums(void)
     had_warning = true;
 }

-void NET_WaitForStart(void)
+void NET_WaitForStart(net_gamesettings_t *settings)
 {
     if (!TXT_Init())
     {
@@ -274,7 +276,7 @@ void NET_WaitForStart(void)

     while (net_waiting_for_start)
     {
-        UpdateGUI();
+        UpdateGUI(settings);
         CheckMD5Sums();

         TXT_DispatchEvents();
diff --git a/src/net_gui.h b/src/net_gui.h
index fdcc81be..9d40b0d0 100644
--- a/src/net_gui.h
+++ b/src/net_gui.h
@@ -30,7 +30,7 @@

 #include "doomtype.h"

-extern void NET_WaitForStart();
+extern void NET_WaitForStart(net_gamesettings_t *settings);

 #endif /* #ifndef NET_GUI_H */

diff --git a/src/net_server.c b/src/net_server.c
index 904d932e..058e61f7 100644
--- a/src/net_server.c
+++ b/src/net_server.c
@@ -447,10 +447,8 @@ static void NET_SV_ParseSYN(net_packet_t *packet,
                             net_addr_t *addr)
 {
     unsigned int magic;
-    unsigned int cl_gamemode, cl_gamemission;
-    unsigned int cl_recording_lowres;
-    unsigned int cl_drone;
     unsigned int is_freedoom;
+    net_connect_data_t data;
     md5_digest_t deh_md5sum, wad_md5sum;
     char *player_name;
     char *client_version;
@@ -501,10 +499,7 @@ static void NET_SV_ParseSYN(net_packet_t *packet,

     // read the game mode and mission

-    if (!NET_ReadInt16(packet, &cl_gamemode)
-     || !NET_ReadInt16(packet, &cl_gamemission)
-     || !NET_ReadInt8(packet, &cl_recording_lowres)
-     || !NET_ReadInt8(packet, &cl_drone)
+    if (!NET_ReadConnectData(packet, &data)
      || !NET_ReadMD5Sum(packet, wad_md5sum)
      || !NET_ReadMD5Sum(packet, deh_md5sum)
      || !NET_ReadInt8(packet, &is_freedoom))
@@ -512,7 +507,7 @@ static void NET_SV_ParseSYN(net_packet_t *packet,
         return;
     }

-    if (!D_ValidGameMode(cl_gamemission, cl_gamemode))
+    if (!D_ValidGameMode(data.gamemission, data.gamemode))
     {
         return;
     }
@@ -579,7 +574,7 @@ static void NET_SV_ParseSYN(net_packet_t *packet,
         NET_SV_AssignPlayers();
         num_players = NET_SV_NumPlayers();

-        if ((!cl_drone && num_players >= MAXPLAYERS)
+        if ((!data.drone && num_players >= MAXPLAYERS)
          || NET_SV_NumClients() >= MAXNETNODES)
         {
             NET_SV_SendReject(addr, "Server is full!");
@@ -592,10 +587,10 @@ static void NET_SV_ParseSYN(net_packet_t *packet,

         // Adopt the game mode and mission of the first connecting client

-        if (num_players == 0 && !cl_drone)
+        if (num_players == 0 && !data.drone)
         {
-            sv_gamemode = cl_gamemode;
-            sv_gamemission = cl_gamemission;
+            sv_gamemode = data.gamemode;
+            sv_gamemission = data.gamemission;
         }

         // Save the MD5 checksums
@@ -607,7 +602,7 @@ static void NET_SV_ParseSYN(net_packet_t *packet,
         // Check the connecting client is playing the same game as all
         // the other clients

-        if (cl_gamemode != sv_gamemode || cl_gamemission != sv_gamemission)
+        if (data.gamemode != sv_gamemode || data.gamemission != sv_gamemission)
         {
             NET_SV_SendReject(addr, "You are playing the wrong game!");
             return;
@@ -617,8 +612,8 @@ static void NET_SV_ParseSYN(net_packet_t *packet,

         NET_SV_InitNewClient(client, addr, player_name);

-        client->recording_lowres = cl_recording_lowres;
-        client->drone = cl_drone;
+        client->recording_lowres = data.lowres_turn;
+        client->drone = data.drone;
     }

     if (client->connection.state == NET_CONN_STATE_WAITING_ACK)
@@ -681,6 +676,8 @@ static void NET_SV_ParseGameStart(net_packet_t *packet, net_client_t *client)
         }
     }

+    settings.num_players = NET_SV_NumPlayers();
+
     nowtime = I_GetTimeMS();

     // Send start packets to each connected node
@@ -695,8 +692,8 @@ static void NET_SV_ParseGameStart(net_packet_t *packet, net_client_t *client)
         startpacket = NET_Conn_NewReliable(&clients[i].connection,
                                            NET_PACKET_TYPE_GAMESTART);

-        NET_WriteInt8(startpacket, NET_SV_NumPlayers());
-        NET_WriteInt8(startpacket, clients[i].player_number);
+        settings.consoleplayer = clients[i].player_number;
+
         NET_WriteSettings(startpacket, &settings);
     }

diff --git a/src/net_structrw.c b/src/net_structrw.c
index 01933ebb..7380e334 100644
--- a/src/net_structrw.c
+++ b/src/net_structrw.c
@@ -30,6 +30,22 @@
 #include "net_packet.h"
 #include "net_structrw.h"

+void NET_WriteConnectData(net_packet_t *packet, net_connect_data_t *data)
+{
+    NET_WriteInt8(packet, data->gamemode);
+    NET_WriteInt8(packet, data->gamemission);
+    NET_WriteInt8(packet, data->lowres_turn);
+    NET_WriteInt8(packet, data->drone);
+}
+
+boolean NET_ReadConnectData(net_packet_t *packet, net_connect_data_t *data)
+{
+    return NET_ReadInt8(packet, (unsigned int *) &data->gamemode)
+        && NET_ReadInt8(packet, (unsigned int *) &data->gamemission)
+        && NET_ReadInt8(packet, (unsigned int *) &data->lowres_turn)
+        && NET_ReadInt8(packet, (unsigned int *) &data->drone);
+}
+
 void NET_WriteSettings(net_packet_t *packet, net_gamesettings_t *settings)
 {
     NET_WriteInt8(packet, settings->ticdup);
@@ -46,6 +62,8 @@ void NET_WriteSettings(net_packet_t *packet, net_gamesettings_t *settings)
     NET_WriteInt8(packet, settings->new_sync);
     NET_WriteInt32(packet, settings->timelimit);
     NET_WriteInt8(packet, settings->loadgame);
+    NET_WriteInt8(packet, settings->num_players);
+    NET_WriteInt8(packet, settings->consoleplayer);
 }

 boolean NET_ReadSettings(net_packet_t *packet, net_gamesettings_t *settings)
@@ -63,7 +81,9 @@ boolean NET_ReadSettings(net_packet_t *packet, net_gamesettings_t *settings)
         && NET_ReadInt8(packet, (unsigned int *) &settings->lowres_turn)
         && NET_ReadInt8(packet, (unsigned int *) &settings->new_sync)
         && NET_ReadInt32(packet, (unsigned int *) &settings->timelimit)
-        && NET_ReadSInt8(packet, (signed int *) &settings->loadgame);
+        && NET_ReadSInt8(packet, (signed int *) &settings->loadgame)
+        && NET_ReadInt8(packet, (unsigned int *) &settings->num_players)
+        && NET_ReadSInt8(packet, (signed int *) &settings->consoleplayer);
 }

 boolean NET_ReadQueryData(net_packet_t *packet, net_querydata_t *query)
diff --git a/src/net_structrw.h b/src/net_structrw.h
index 13209778..68971cd3 100644
--- a/src/net_structrw.h
+++ b/src/net_structrw.h
@@ -26,6 +26,9 @@
 #include "net_defs.h"
 #include "net_packet.h"

+void NET_WriteConnectData(net_packet_t *packet, net_connect_data_t *data);
+boolean NET_ReadConnectData(net_packet_t *packet, net_connect_data_t *data);
+
 extern void NET_WriteSettings(net_packet_t *packet, net_gamesettings_t *settings);
 extern boolean NET_ReadSettings(net_packet_t *packet, net_gamesettings_t *settings);