Maelstrom: The NEW_GAME packet sends the entire game info so we're guaranteed to have all the info when we start the game.

https://github.com/libsdl-org/Maelstrom/commit/08819dabe176db0d72645258fef2dd3abafd3b87

From 08819dabe176db0d72645258fef2dd3abafd3b87 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Sat, 19 Nov 2011 21:28:48 -0500
Subject: [PATCH] The NEW_GAME packet sends the entire game info so we're
 guaranteed to have all the info when we start the game. Other players respond
 with a simple ack and start the game.

---
 game/game.cpp    |  10 +--
 game/lobby.cpp   |  19 ++++-
 game/netplay.cpp | 186 +++++++++++++----------------------------------
 game/netplay.h   |   1 -
 game/protocol.h  |  10 ++-
 5 files changed, 74 insertions(+), 152 deletions(-)

diff --git a/game/game.cpp b/game/game.cpp
index e4de1107..797a565c 100644
--- a/game/game.cpp
+++ b/game/game.cpp
@@ -71,13 +71,9 @@ void NewGame(void)
 	SeedRandom(gGameInfo.seed);
 
 	/* Send a "NEW_GAME" packet onto the network */
-	if ( gGameInfo.IsMultiplayer() ) {
-		if ( gGameInfo.IsHosting() ) {
-			if ( Send_NewGame() < 0)
-				return;
-		} else {
-			if ( Await_NewGame() < 0 )
-				return;
+	if ( gGameInfo.IsMultiplayer() && gGameInfo.IsHosting() ) {
+		if ( Send_NewGame() < 0) {
+			return;
 		}
 	}
 	for (int i = 0; i < MAX_PLAYERS; ++i) {
diff --git a/game/lobby.cpp b/game/lobby.cpp
index 1b8af021..ed5e2ed0 100644
--- a/game/lobby.cpp
+++ b/game/lobby.cpp
@@ -647,13 +647,13 @@ LobbyDialogDelegate::ProcessPong(DynamicPacket &packet)
 void
 LobbyDialogDelegate::ProcessNewGame(DynamicPacket &packet)
 {
-	Uint8 playerIndex;
-	Uint32 gameID;
+	GameInfo game;
 
-	if (!packet.Read(playerIndex)) {
+	if (!game.ReadFromPacket(packet)) {
 		return;
 	}
-	if (!packet.Read(gameID) || gameID != m_game.gameID) {
+	if (game.gameID != m_game.gameID) {
+		// Probably an old packet...
 		return;
 	}
 	if (m_game.IsHosting()) {
@@ -662,6 +662,17 @@ LobbyDialogDelegate::ProcessNewGame(DynamicPacket &packet)
 	}
 
 	// Ooh, ooh, they're starting!
+	m_game.CopyFrom(game);
+
+	// Send a response
+	m_reply.Reset();
+	m_reply.Write((Uint8)NEW_GAME_ACK);
+	m_reply.Write(m_game.gameID);
+	m_reply.Write(m_game.localID);
+	m_reply.address = packet.address;
+
+	SDLNet_UDP_Send(gNetFD, -1, &m_reply);
+
 	if (m_game.HasNode(packet.address)) {
 		m_playButton->OnClick();
 	}
diff --git a/game/netplay.cpp b/game/netplay.cpp
index 0d25eb52..3d3d9af4 100644
--- a/game/netplay.cpp
+++ b/game/netplay.cpp
@@ -109,6 +109,7 @@ int InitNetData(bool hosting)
 
 	/* Initialize network game variables */
 	gOurPlayer  = -1;
+	NextFrame = 0;
 	for ( i=0; i<MAX_PLAYERS; ++i ) {
 		SyncPtrs[0][i] = NULL;
 		SyncPtrs[1][i] = NULL;
@@ -275,11 +276,12 @@ int SyncNetwork(void)
 			continue;
 		}
 		if ( buf[0] == NEW_GAME ) {
-			/* Send it back if we are not the server.. */
-			if ( gOurPlayer != 0 ) {
-				buf[1] = gOurPlayer;
-				SDLNet_UDP_Send(gNetFD, -1, &sent);
-			}
+			/* FIXME: Convert this to the DynamicPacket */
+			buf[0] = NEW_GAME_ACK;
+			SDLNet_Write32(gGameInfo.gameID, &buf[1]);
+			SDLNet_Write32(gGameInfo.localID, &buf[4]);
+			sent.len = 9;
+			SDLNet_UDP_Send(gNetFD, -1, &sent);
 //error("NEW_GAME packet!\r\n");
 			continue;
 		}
@@ -391,13 +393,6 @@ inline void SuckPackets(void)
 	}
 }
 
-static inline void MakeNewPacket(Uint32 gameID, unsigned char *packet)
-{
-	*packet++ = NEW_GAME;
-	*packet++ = gOurPlayer;
-	SDLNet_Write32(gameID, packet);
-}
-
 /* Flash an error up on the screen and pause for 3 seconds */
 static void ErrorMessage(const char *message)
 {
@@ -409,42 +404,48 @@ static void ErrorMessage(const char *message)
 }
 
 /* This function sends a NEW_GAME packet, and waits for all other players
-   to respond in kind.
-   This function is not very robust in handling errors such as multiple
-   machines thinking they are the same player.  The address server is
-   supposed to handle such things gracefully.
+   to respond with NEW_GAME_ACK
 */
 int Send_NewGame()
 {
-	Uint8 netbuf[BUFSIZ], sendbuf[NEW_PACKETLEN];
 	char message[BUFSIZ];
-	int  nleft, n;
-	int  acked[MAX_PLAYERS];
-	int  i;
-	UDPpacket newgame, sent;
+	int  nleft;
+	Uint32 waiting[MAX_NODES];
+	int  i, j;
+	DynamicPacket newgame, packet;
 
 	/* Send all the packets */
-	MakeNewPacket(gGameInfo.gameID, sendbuf);
-	newgame.data = sendbuf;
-	newgame.len = sizeof(sendbuf);
+	newgame.Write((Uint8)NEW_GAME);
+	gGameInfo.WriteToPacket(newgame);
 	SDLNet_UDP_Send(gNetFD, 0, &newgame);
 
 	/* Get ready for responses */
-	memset(acked, 0, (sizeof acked));
-	sent.data = netbuf;
-	sent.maxlen = sizeof(netbuf);
+	nleft = 0;
+	for (i = 0; i < gGameInfo.GetNumNodes(); ++i) {
+		if (gGameInfo.IsNetworkNode(i)) {
+			++nleft;
+			waiting[i] = gGameInfo.GetNode(i)->nodeID;
+		} else {
+			waiting[i] = 0;
+		}
+	}
 
 	/* Wait for Ack's */
-	for ( nleft=gNumPlayers, n=0; nleft; ) {
+	while (nleft > 0) {
 		/* Show a status */
 		strcpy(message, "Waiting for players:");
-		for ( i=0; i<gNumPlayers; ++i ) {
-			if ( ! acked[i] )
-				sprintf(&message[strlen(message)], " %d", i+1);
+		for (i = 0; i < MAX_PLAYERS; ++i) {
+			const GameInfoPlayer *player = gGameInfo.GetPlayer(i);
+			for (j = 0; j < MAX_NODES; ++j) {
+				if (player->nodeID == waiting[j]) {
+					sprintf(&message[strlen(message)], " %d", i+1);
+					break;
+				}
+			}
 		}
 		Message(message);
 
-		if ( SDLNet_CheckSockets(SocketSet, 1000) <= 0 ) {
+		if ( SDLNet_CheckSockets(SocketSet, 100) <= 0 ) {
 			HandleEvents(0);
 			/* Peek at key buffer for Quit key */
 			for ( i=(PDATA_OFFSET+1); i<OutLen; i += 2 ) {
@@ -455,12 +456,8 @@ int Send_NewGame()
 			}
 			OutLen = PDATA_OFFSET;
 
-			/* Every three seconds...resend the new game packet */
-			if ( (n++)%3 != 0 )
-				continue;
-
-			for ( i=gNumPlayers; i--; ) {
-				if ( ! acked[i] ) {
+			for (i = 0; i < MAX_NODES; ++i) {
+				if ( waiting[i] ) {
 					SDLNet_UDP_Send(gNetFD, i+1, &newgame);
 				}
 			}
@@ -468,120 +465,37 @@ int Send_NewGame()
 		}
 
 		/* We are guaranteed that there is data here */
-		if ( SDLNet_UDP_Recv(gNetFD, &sent) <= 0 ) {
+		packet.Reset();
+		if ( SDLNet_UDP_Recv(gNetFD, &packet) <= 0 ) {
 			ErrorMessage("Network error receiving packets");
 			return(-1);
 		}
 
 		/* We have a packet! */
-		if ( netbuf[0] == LOBBY_MSG ) {
+		Uint8 cmd;
+		Uint32 gameID;
+		Uint32 nodeID;
+		if (!packet.Read(cmd) || cmd != NEW_GAME_ACK) {
+			/* Continue waiting */
 			continue;
 		}
-		if ( netbuf[0] != NEW_GAME ) {
-			/* Continue waiting */
-#ifdef VERBOSE
-			error("Unknown packet: 0x%x\r\n", netbuf[0]);
-#endif
+		if (!packet.Read(gameID) || !packet.Read(nodeID)) {
 			continue;
 		}
-
-		Uint32 gameID = SDLNet_Read32(&netbuf[2]);
 		if (gameID != gGameInfo.gameID) {
 			/* This must be for a different game */
 			continue;
 		}
-
-		/* Loop, check the address */
-		for ( i=gNumPlayers; i--; ) {
-			if ( acked[i] )
-				continue;
-
-			/* Check both the host AND port!! :-) */
-			if ( (sent.address.host != PlayAddr[i].host) ||
-			     (sent.address.port != PlayAddr[i].port) )
-				continue;
-
-			/* Check the player... */
-			if ( (i != gOurPlayer) && (netbuf[1] == gOurPlayer) ) {
-				/* Print message, sleep 3 seconds absolutely */
-				sprintf(message, 
-	"Error: Another player (%d) thinks they are player 1!\r\n", i+1);
-				ErrorMessage(message);
-				/* Suck up retransmission packets */
-				SuckPackets();
-				return(-1);
-			}
-
-			/* Check them off our list.. */
-			--nleft;
-			acked[i] = 1;
-			break;
-		}
-	}
-	NextFrame = 0L;
-	return(0);
-}
-
-int Await_NewGame()
-{
-	unsigned char netbuf[BUFSIZ];
-	int gameon;
-	UDPpacket sent;
-
-	/* Get ready to wait for server */
-	Message("Awaiting Player 1 (server)");
-	sent.data = netbuf;
-	sent.maxlen = sizeof(netbuf);
-
-	gameon = 0;
-	while ( ! gameon ) {
-		if ( SDLNet_CheckSockets(SocketSet, 1000) <= 0 ) {
-			HandleEvents(0);
-			/* Peek at key buffer for Quit key */
-			for ( int i=(PDATA_OFFSET+1); i<OutLen; i += 2 ) {
-				if ( OutBuf[i] == ABORT_KEY ) {
-					OutLen = PDATA_OFFSET;
-					return(-1);
-				}
-			}
-			OutLen = PDATA_OFFSET;
-			continue;
-		}
-
-		/* We are guaranteed that there is data here */
-		if ( SDLNet_UDP_Recv(gNetFD, &sent) <= 0 ) {
-			ErrorMessage("Network error receiving packets");
-			return(-1);
-		}
-
-		/* We have a packet! */
-		if ( netbuf[0] == LOBBY_MSG ) {
-			continue;
-		}
-		if ( netbuf[0] != NEW_GAME ) {
-#ifdef VERBOSE
-			error(
-			"Await_NewGame(): Unknown packet: 0x%x\r\n", netbuf[0]);
-#endif
+		if (!nodeID) {
 			continue;
 		}
-
-		Uint32 gameID = SDLNet_Read32(&netbuf[2]);
-		if (gameID != gGameInfo.gameID) {
-			/* This must be for a different game */
-			continue;
+		for (i = 0; i < MAX_NODES; ++i) {
+			if (nodeID == waiting[i]) {
+				waiting[i] = 0;
+				--nleft;
+				break;
+			}
 		}
-
-		netbuf[1] = gOurPlayer;
-		SDLNet_UDP_Send(gNetFD, 1, &sent);
-
-		/* Note that we don't guarantee delivery of the NEW_GAME ack.
-		   That's okay, we have the checksum.  We will hang on the very
-		   first frame, and we echo back all NEW_GAME packets at that
-		   point as well.
-		*/
-		NextFrame = 0L;
-		gameon = 1;
 	}
 	return(0);
 }
diff --git a/game/netplay.h b/game/netplay.h
index 74e78b72..3e891d6a 100644
--- a/game/netplay.h
+++ b/game/netplay.h
@@ -30,7 +30,6 @@ extern void  QueueKey(unsigned char Op, unsigned char Type);
 extern int   SyncNetwork(void);
 extern int   GetSyncBuf(int index, unsigned char **bufptr);
 extern int   Send_NewGame();
-extern int   Await_NewGame();
 
 /* Variables from netplay.cpp */
 extern int	gOurPlayer;
diff --git a/game/protocol.h b/game/protocol.h
index 2b89c80e..bdddb1eb 100644
--- a/game/protocol.h
+++ b/game/protocol.h
@@ -181,10 +181,12 @@ enum LobbyProtocol {
 /* Network protocol for synchronization and keystrokes */
 
 #define LOBBY_MSG	0x00			/* Sent before game */
-#define NEW_GAME	0x01			/* Sent by players at start */
-#define SYNC_MSG	0x02			/* Sent during game */
-#define KEY_PRESS	0x04			/* Sent during game */
-#define KEY_RELEASE	0x08			/* Sent during game */
+#define NEW_GAME	0x01			/* Sent by host at start */
+#define NEW_GAME_ACK	0x02			/* Sent by players at start */
+#define SYNC_MSG	0x04			/* Sent during game */
+
+#define KEY_PRESS	0x01			/* Sent during game */
+#define KEY_RELEASE	0x02			/* Sent during game */
 
 /* The default port for Maelstrom games */
 #define LOBBY_PORT	0xAE00			/* port 44544 */