summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon Howard2013-04-03 23:47:43 +0000
committerSimon Howard2013-04-03 23:47:43 +0000
commit2840213616978c2d806c35b586ff1c4beff0f187 (patch)
treea79cc3c625e5e9da42bad63d942698b63904f356
parent48b1728d9145baf1912aa6566d8e64abea65f83b (diff)
downloadchocolate-doom-2840213616978c2d806c35b586ff1c4beff0f187.tar.gz
chocolate-doom-2840213616978c2d806c35b586ff1c4beff0f187.tar.bz2
chocolate-doom-2840213616978c2d806c35b586ff1c4beff0f187.zip
Don't start the game until all players are ready. Send waiting data to
players that are ready for startup progress screens. Subversion-branch: /branches/v2-branch Subversion-revision: 2581
-rw-r--r--src/net_client.c13
-rw-r--r--src/net_defs.h1
-rw-r--r--src/net_server.c349
-rw-r--r--src/net_structrw.c2
4 files changed, 233 insertions, 132 deletions
diff --git a/src/net_client.c b/src/net_client.c
index f258eb44..239691c8 100644
--- a/src/net_client.c
+++ b/src/net_client.c
@@ -446,6 +446,7 @@ static void NET_CL_ParseWaitingData(net_packet_t *packet)
}
if (wait_data.num_players > wait_data.max_players
+ || wait_data.ready_players > wait_data.num_players
|| wait_data.max_players > NET_MAXPLAYERS)
{
// insane data
@@ -468,11 +469,23 @@ static void NET_CL_ParseWaitingData(net_packet_t *packet)
static void NET_CL_ParseLaunch(net_packet_t *packet)
{
+ unsigned int num_players;
+
if (client_state != CLIENT_STATE_WAITING_LAUNCH)
{
return;
}
+ // The launch packet contains the number of players that will be
+ // in the game when it starts, so that we can do the startup
+ // progress indicator (the wait data is unreliable).
+
+ if (!NET_ReadInt8(packet, &num_players))
+ {
+ return;
+ }
+
+ net_client_wait_data.num_players = num_players;
client_state = CLIENT_STATE_WAITING_START;
}
diff --git a/src/net_defs.h b/src/net_defs.h
index a5804e53..38114efc 100644
--- a/src/net_defs.h
+++ b/src/net_defs.h
@@ -241,6 +241,7 @@ typedef struct
{
int num_players;
int num_drones;
+ int ready_players;
int max_players;
int is_controller;
int consoleplayer;
diff --git a/src/net_server.c b/src/net_server.c
index 67f49236..ff91c71f 100644
--- a/src/net_server.c
+++ b/src/net_server.c
@@ -80,6 +80,11 @@ typedef struct
int last_send_time;
char *name;
+ // If true, the client has sent the NET_PACKET_TYPE_GAMESTART
+ // message indicating that it is ready for the game to start.
+
+ boolean ready;
+
// Time that this client connected to the server.
// This is used to determine the controller (oldest client).
@@ -280,6 +285,25 @@ static int NET_SV_NumPlayers(void)
return result;
}
+// Returns the number of players ready to start the game.
+
+static int NET_SV_NumReadyPlayers(void)
+{
+ int result = 0;
+ int i;
+
+ for (i = 0; i < MAXNETNODES; ++i)
+ {
+ if (ClientConnected(&clients[i])
+ && !clients[i].drone && clients[i].ready)
+ {
+ ++result;
+ }
+ }
+
+ return result;
+}
+
// Returns the maximum number of players that can play.
static int NET_SV_MaxPlayers(void)
@@ -337,6 +361,95 @@ static int NET_SV_NumClients(void)
return count;
}
+// returns a pointer to the client which controls the server
+
+static net_client_t *NET_SV_Controller(void)
+{
+ net_client_t *best;
+ int i;
+
+ // Find the oldest client (first to connect).
+
+ best = NULL;
+
+ for (i=0; i<MAXNETNODES; ++i)
+ {
+ // Can't be controller?
+
+ if (!ClientConnected(&clients[i]) || clients[i].drone)
+ {
+ continue;
+ }
+
+ if (best == NULL || clients[i].connect_time < best->connect_time)
+ {
+ best = &clients[i];
+ }
+ }
+
+ return best;
+}
+
+static void NET_SV_SendWaitingData(net_client_t *client)
+{
+ net_waitdata_t wait_data;
+ net_packet_t *packet;
+ net_client_t *controller;
+ int i;
+
+ NET_SV_AssignPlayers();
+
+ controller = NET_SV_Controller();
+
+ wait_data.num_players = NET_SV_NumPlayers();
+ wait_data.num_drones = NET_SV_NumDrones();
+ wait_data.ready_players = NET_SV_NumReadyPlayers();
+ wait_data.max_players = NET_SV_MaxPlayers();
+ wait_data.is_controller = (client == controller);
+ wait_data.consoleplayer = client->player_number;
+
+ // Send the WAD and dehacked checksums of the controlling client.
+ // If no controller found (?), send the details that the client
+ // is expecting anyway.
+
+ if (controller != NULL)
+ {
+ controller = client;
+ }
+
+ memcpy(&wait_data.wad_sha1sum, &controller->wad_sha1sum,
+ sizeof(sha1_digest_t));
+ memcpy(&wait_data.deh_sha1sum, &controller->deh_sha1sum,
+ sizeof(sha1_digest_t));
+ wait_data.is_freedoom = controller->is_freedoom;
+
+ // set name and address of each player:
+
+ for (i = 0; i < wait_data.num_players; ++i)
+ {
+ strncpy(wait_data.player_names[i],
+ sv_players[i]->name,
+ MAXPLAYERNAME);
+ wait_data.player_names[i][MAXPLAYERNAME-1] = '\0';
+
+ strncpy(wait_data.player_addrs[i],
+ NET_AddrToString(sv_players[i]->addr),
+ MAXPLAYERNAME);
+ wait_data.player_addrs[i][MAXPLAYERNAME-1] = '\0';
+ }
+
+ // Construct packet:
+
+ packet = NET_NewPacket(10);
+ NET_WriteInt16(packet, NET_PACKET_TYPE_WAITING_DATA);
+ NET_WriteWaitData(packet, &wait_data);
+
+ // Send packet to client and free
+
+ NET_Conn_SendPacket(&client->connection, packet);
+ NET_FreePacket(packet);
+}
+
// Find the latest tic which has been acknowledged as received by
// all clients.
@@ -419,35 +532,6 @@ static void NET_SV_AdvanceWindow(void)
}
}
-// returns a pointer to the client which controls the server
-
-static net_client_t *NET_SV_Controller(void)
-{
- net_client_t *best;
- int i;
-
- // Find the oldest client (first to connect).
-
- best = NULL;
-
- for (i=0; i<MAXNETNODES; ++i)
- {
- // Can't be controller?
-
- if (!ClientConnected(&clients[i]) || clients[i].drone)
- {
- continue;
- }
-
- if (best == NULL || clients[i].connect_time < best->connect_time)
- {
- best = &clients[i];
- }
- }
-
- return best;
-}
-
// Given an address, find the corresponding client
static net_client_t *NET_SV_FindClient(net_addr_t *addr)
@@ -496,6 +580,7 @@ static void NET_SV_InitNewClient(net_client_t *client,
client->sendseq = 0;
client->acknowledged = 0;
client->drone = false;
+ client->ready = false;
client->last_gamedata_time = 0;
@@ -694,6 +779,8 @@ static void NET_SV_ParseSYN(net_packet_t *packet,
static void NET_SV_ParseLaunch(net_packet_t *packet, net_client_t *client)
{
+ net_packet_t *launchpacket;
+ int num_players;
unsigned int i;
// Only the controller can launch the game.
@@ -712,13 +799,17 @@ static void NET_SV_ParseLaunch(net_packet_t *packet, net_client_t *client)
// Forward launch on to all clients.
+ NET_SV_AssignPlayers();
+ num_players = NET_SV_NumPlayers();
+
for (i=0; i<MAXNETNODES; ++i)
{
if (!ClientConnected(&clients[i]))
continue;
- NET_Conn_NewReliable(&clients[i].connection,
- NET_PACKET_TYPE_LAUNCH);
+ launchpacket = NET_Conn_NewReliable(&clients[i].connection,
+ NET_PACKET_TYPE_LAUNCH);
+ NET_WriteInt8(launchpacket, num_players);
}
// Now in launch state.
@@ -726,42 +817,15 @@ static void NET_SV_ParseLaunch(net_packet_t *packet, net_client_t *client)
server_state = SERVER_WAITING_START;
}
-// Parse a game start packet
+// Transition to the in-game state and send all players the start game
+// message. Invoked once all players have indicated they are ready to
+// start the game.
-static void NET_SV_ParseGameStart(net_packet_t *packet, net_client_t *client)
+static void StartGame(void)
{
- net_gamesettings_t settings;
net_packet_t *startpacket;
+ unsigned int i;
int nowtime;
- int i;
-
- if (client != NET_SV_Controller())
- {
- // Only the controller can start a new game
-
- return;
- }
-
- if (!NET_ReadSettings(packet, &settings))
- {
- // Malformed packet
-
- return;
- }
-
- // Check the game settings are valid
-
- if (!NET_ValidGameSettings(sv_gamemode, sv_gamemission, &settings))
- {
- return;
- }
-
- // Can only start a game if we are in the waiting start state.
-
- if (server_state != SERVER_WAITING_START)
- {
- return;
- }
// Assign player numbers
@@ -769,17 +833,17 @@ static void NET_SV_ParseGameStart(net_packet_t *packet, net_client_t *client)
// Check if anyone is recording a demo and set lowres_turn if so.
- settings.lowres_turn = false;
+ sv_settings.lowres_turn = false;
- for (i=0; i<NET_MAXPLAYERS; ++i)
+ for (i = 0; i < NET_MAXPLAYERS; ++i)
{
if (sv_players[i] != NULL && sv_players[i]->recording_lowres)
{
- settings.lowres_turn = true;
+ sv_settings.lowres_turn = true;
}
}
- settings.num_players = NET_SV_NumPlayers();
+ sv_settings.num_players = NET_SV_NumPlayers();
// Copy player classes:
@@ -787,11 +851,11 @@ static void NET_SV_ParseGameStart(net_packet_t *packet, net_client_t *client)
{
if (sv_players[i] != NULL)
{
- settings.player_classes[i] = sv_players[i]->player_class;
+ sv_settings.player_classes[i] = sv_players[i]->player_class;
}
else
{
- settings.player_classes[i] = 0;
+ sv_settings.player_classes[i] = 0;
}
}
@@ -799,7 +863,7 @@ static void NET_SV_ParseGameStart(net_packet_t *packet, net_client_t *client)
// Send start packets to each connected node
- for (i=0; i<MAXNETNODES; ++i)
+ for (i = 0; i < MAXNETNODES; ++i)
{
if (!ClientConnected(&clients[i]))
continue;
@@ -809,20 +873,100 @@ static void NET_SV_ParseGameStart(net_packet_t *packet, net_client_t *client)
startpacket = NET_Conn_NewReliable(&clients[i].connection,
NET_PACKET_TYPE_GAMESTART);
- settings.consoleplayer = clients[i].player_number;
+ sv_settings.consoleplayer = clients[i].player_number;
- NET_WriteSettings(startpacket, &settings);
+ NET_WriteSettings(startpacket, &sv_settings);
}
// Change server state
server_state = SERVER_IN_GAME;
- sv_settings = settings;
memset(recvwindow, 0, sizeof(recvwindow));
recvwindow_start = 0;
}
+// Returns true when all nodes have indicated readiness to start the game.
+
+static boolean AllNodesReady(void)
+{
+ unsigned int i;
+
+ for (i = 0; i < MAXNETNODES; ++i)
+ {
+ if (ClientConnected(&clients[i]) && !clients[i].ready)
+ {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+// Send waiting data with current status to all nodes that are ready to
+// start the game.
+
+static void SendAllWaitingData(void)
+{
+ unsigned int i;
+
+ for (i = 0; i < MAXNETNODES; ++i)
+ {
+ if (ClientConnected(&clients[i]) && clients[i].ready)
+ {
+ NET_SV_SendWaitingData(&clients[i]);
+ }
+ }
+}
+
+// Parse a game start packet
+
+static void NET_SV_ParseGameStart(net_packet_t *packet, net_client_t *client)
+{
+ net_gamesettings_t settings;
+
+ // Can only start a game if we are in the waiting start state.
+
+ if (server_state != SERVER_WAITING_START)
+ {
+ return;
+ }
+
+ if (client == NET_SV_Controller())
+ {
+ if (!NET_ReadSettings(packet, &settings))
+ {
+ // Malformed packet
+
+ return;
+ }
+
+ // Check the game settings are valid
+
+ if (!NET_ValidGameSettings(sv_gamemode, sv_gamemission, &settings))
+ {
+ return;
+ }
+
+ sv_settings = settings;
+ }
+
+ client->ready = true;
+
+ // Start the game once all clients are ready.
+
+ if (AllNodesReady())
+ {
+ StartGame();
+ }
+
+ // Update all ready clients with the current state (number of players
+ // ready, etc.). This is used by games that show startup progress
+ // (eg. Hexen's spinal loading)
+
+ SendAllWaitingData();
+}
+
// Send a resend request to a client
static void NET_SV_SendResendRequest(net_client_t *client, int start, int end)
@@ -1312,65 +1456,6 @@ static void NET_SV_Packet(net_packet_t *packet, net_addr_t *addr)
}
-static void NET_SV_SendWaitingData(net_client_t *client)
-{
- net_waitdata_t wait_data;
- net_packet_t *packet;
- net_client_t *controller;
- int i;
-
- NET_SV_AssignPlayers();
-
- controller = NET_SV_Controller();
-
- wait_data.num_players = NET_SV_NumPlayers();
- wait_data.num_drones = NET_SV_NumDrones();
- wait_data.max_players = NET_SV_MaxPlayers();
- wait_data.is_controller = (client == controller);
- wait_data.consoleplayer = client->player_number;
-
- // Send the WAD and dehacked checksums of the controlling client.
- // If no controller found (?), send the details that the client
- // is expecting anyway.
-
- if (controller != NULL)
- {
- controller = client;
- }
-
- memcpy(&wait_data.wad_sha1sum, &controller->wad_sha1sum,
- sizeof(sha1_digest_t));
- memcpy(&wait_data.deh_sha1sum, &controller->deh_sha1sum,
- sizeof(sha1_digest_t));
- wait_data.is_freedoom = controller->is_freedoom;
-
- // set name and address of each player:
-
- for (i = 0; i < wait_data.num_players; ++i)
- {
- strncpy(wait_data.player_names[i],
- sv_players[i]->name,
- MAXPLAYERNAME);
- wait_data.player_names[i][MAXPLAYERNAME-1] = '\0';
-
- strncpy(wait_data.player_addrs[i],
- NET_AddrToString(sv_players[i]->addr),
- MAXPLAYERNAME);
- wait_data.player_addrs[i][MAXPLAYERNAME-1] = '\0';
- }
-
- // Construct packet:
-
- packet = NET_NewPacket(10);
- NET_WriteInt16(packet, NET_PACKET_TYPE_WAITING_DATA);
- NET_WriteWaitData(packet, &wait_data);
-
- // Send packet to client and free
-
- NET_Conn_SendPacket(&client->connection, packet);
- NET_FreePacket(packet);
-}
-
static void NET_SV_PumpSendQueue(net_client_t *client)
{
net_full_ticcmd_t cmd;
diff --git a/src/net_structrw.c b/src/net_structrw.c
index 8f6f8d57..17556ccd 100644
--- a/src/net_structrw.c
+++ b/src/net_structrw.c
@@ -451,6 +451,7 @@ void NET_WriteWaitData(net_packet_t *packet, net_waitdata_t *data)
NET_WriteInt8(packet, data->num_players);
NET_WriteInt8(packet, data->num_drones);
+ NET_WriteInt8(packet, data->ready_players);
NET_WriteInt8(packet, data->max_players);
NET_WriteInt8(packet, data->is_controller);
NET_WriteInt8(packet, data->consoleplayer);
@@ -473,6 +474,7 @@ boolean NET_ReadWaitData(net_packet_t *packet, net_waitdata_t *data)
if (!NET_ReadInt8(packet, (unsigned int *) &data->num_players)
|| !NET_ReadInt8(packet, (unsigned int *) &data->num_drones)
+ || !NET_ReadInt8(packet, (unsigned int *) &data->ready_players)
|| !NET_ReadInt8(packet, (unsigned int *) &data->max_players)
|| !NET_ReadInt8(packet, (unsigned int *) &data->is_controller)
|| !NET_ReadSInt8(packet, &data->consoleplayer))