shithub: choc

Download patch

ref: 005747a6174d2d5b72e1af196a72cafb9b801a58
parent: 4dac08c8694f9b347b2b5c0ca12a0d26b1ae6589
author: Simon Howard <[email protected]>
date: Sat Nov 20 11:45:27 EST 2010

Remove dependency of network code on Doom code.

Subversion-branch: /branches/raven-branch
Subversion-revision: 2163

--- a/src/doom/d_net.c
+++ b/src/doom/d_net.c
@@ -25,8 +25,8 @@
 //
 //-----------------------------------------------------------------------------
 
+#include <stdlib.h>
 
-
 #include "doomfeatures.h"
 
 #include "d_main.h"
@@ -76,8 +76,7 @@
 
 // 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,12 +93,8 @@
 
 // Use new client syncronisation code
 
-boolean         net_cl_new_sync = true;
+boolean         new_sync = true;
 
-// Connected but not participating in the game (observer)
-
-boolean drone = false;
-
 // 35 fps clock adjusted by offsetms milliseconds
 
 static int GetAdjustedTime(void)
@@ -108,7 +103,7 @@
 
     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 @@
             continue;
         }
 	
-        if (net_cl_new_sync)
+        if (new_sync)
         { 
            // If playing single player, do not allow tics to buffer
            // up very far
@@ -309,22 +304,145 @@
     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
 
-    I_AtExit(D_QuitNetGame, true);
+    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;
+
+    //!
+    // @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
 
     consoleplayer = 0;
@@ -342,128 +460,160 @@
     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 (addr != NULL)
+    {
+        if (M_CheckParm("-drone") > 0)
+        {
+            connect_data->drone = true;
+        }
 
-            if (i > 0)
-            {
-                net_sdl_module.InitClient();
-                addr = net_sdl_module.ResolveAddress(myargv[i+1]);
+        //!
+        // @category net
+        //
+        // Run as the left screen in three screen mode.
+        //
 
-                if (addr == NULL)
-                {
-                    I_Error("Unable to resolve '%s'\n", myargv[i+1]);
-                }
-            }
+        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;
+}
 
-    for (i=0; i<MAXPLAYERS; ++i)
+//
+// 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 
+
+    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 @@
     
     // decide how many tics to run
     
-    if (net_cl_new_sync)
+    if (new_sync)
     {
 	counts = availabletics;
     }
--- a/src/doom/d_net.h
+++ b/src/doom/d_net.h
@@ -45,7 +45,6 @@
 // Called at start of game loop to initialize timers
 void D_StartGameLoop(void);
 
-extern boolean drone;
 extern boolean net_cl_new_sync;
 
 #endif
--- 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_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 @@
 
 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 @@
     
     for (i=0; i<MAXPLAYERS; ++i)
     {
-        if (i == consoleplayer && !drone)
+        if (i == settings.consoleplayer && !drone)
         {
             continue;
         }
@@ -327,67 +334,10 @@
     }
 }
 
-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
 
     memset(&last_ticcmd, 0, sizeof(ticcmd_t));
@@ -397,7 +347,7 @@
     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 @@
     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 @@
     // 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 @@
 
         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 @@
 
     // Send to server.
 
-    starttic = maketic - extratics;
+    starttic = maketic - settings.extratics;
     endtic = maketic;
 
     if (starttic < 0)
@@ -584,14 +534,7 @@
 
 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 @@
         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 @@
         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 @@
     // 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 @@
 
         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 @@
     }
 }
 
-static void NET_CL_SendSYN(void)
+static void NET_CL_SendSYN(net_connect_data_t *data)
 {
     net_packet_t *packet;
 
@@ -1066,10 +966,7 @@
     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,7 +977,7 @@
 
 // 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;
@@ -1087,13 +984,6 @@
 
     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 @@
     // necessary module
 
     client_context = NET_NewContext();
-    
+
     // initialize module for client mode
 
     if (!addr->module->InitClient())
@@ -1137,7 +1027,7 @@
 
         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 @@
         // run client code
 
         NET_CL_Run();
-        
+
         // run the server, just incase we are doing a loopback
         // connect
 
@@ -1167,6 +1057,7 @@
         // connected ok!
 
         client_state = CLIENT_STATE_WAITING_START;
+        drone = data->drone;
 
         return true;
     }
@@ -1175,9 +1066,23 @@
         // 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
--- 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_deh_md5sum;
 extern unsigned int net_local_is_freedoom;
 
+extern boolean drone;
 
 #endif /* #ifndef NET_CLIENT_H */
 
--- a/src/net_defs.h
+++ b/src/net_defs.h
@@ -128,6 +128,22 @@
     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 @@
     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)
--- a/src/net_gui.c
+++ b/src/net_gui.c
@@ -53,9 +53,11 @@
     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 @@
     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 @@
     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 @@
     had_warning = true;
 }
 
-void NET_WaitForStart(void)
+void NET_WaitForStart(net_gamesettings_t *settings)
 {
     if (!TXT_Init())
     {
@@ -274,7 +276,7 @@
 
     while (net_waiting_for_start)
     {
-        UpdateGUI();
+        UpdateGUI(settings);
         CheckMD5Sums();
 
         TXT_DispatchEvents();
--- 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 */
 
--- a/src/net_server.c
+++ b/src/net_server.c
@@ -447,10 +447,8 @@
                             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 @@
 
     // 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 @@
         return;
     }
 
-    if (!D_ValidGameMode(cl_gamemission, cl_gamemode))
+    if (!D_ValidGameMode(data.gamemission, data.gamemode))
     {
         return;
     }
@@ -579,7 +574,7 @@
         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 @@
 
         // 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 @@
         // 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 @@
 
         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 @@
         }
     }
 
+    settings.num_players = NET_SV_NumPlayers();
+
     nowtime = I_GetTimeMS();
 
     // Send start packets to each connected node
@@ -695,8 +692,8 @@
         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);
     }
 
--- 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 @@
     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 @@
         && 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)
--- 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);