Maelstrom: Unified the single player and multiplayer game info case

https://github.com/libsdl-org/Maelstrom/commit/3c7a644315d7e4ac7153363a1e4b964d352bfd81

From 3c7a644315d7e4ac7153363a1e4b964d352bfd81 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Sat, 19 Nov 2011 18:55:27 -0500
Subject: [PATCH] Unified the single player and multiplayer game info case Keep
 the node and player arrays compact

---
 game/gameinfo.cpp | 242 ++++++++++++++++++++++++++++++++++++----------
 game/gameinfo.h   |  64 +++++++-----
 game/lobby.cpp    |  88 +++++------------
 game/main.cpp     |  10 +-
 game/netplay.cpp  |   3 +-
 5 files changed, 265 insertions(+), 142 deletions(-)

diff --git a/game/gameinfo.cpp b/game/gameinfo.cpp
index 34c9d420..2467d937 100644
--- a/game/gameinfo.cpp
+++ b/game/gameinfo.cpp
@@ -28,48 +28,106 @@
 #include "gameinfo.h"
 
 
+GameInfo::GameInfo()
+{
+	localID = rand();
+	Reset();
+}
+
 void
 GameInfo::Reset()
 {
-	SDL_zero(*this);
+	gameID = 0;
+	seed = 0;
+	wave = 0;
+	lives = 0;
+	turbo = 0;
+	deathMatch = 0;
+	numNodes = 0;
+	SDL_zero(nodes);
+	numPlayers = 0;
+	SDL_zero(players);
 }
 
 void
-GameInfo::SetSinglePlayer(Uint8 wave, Uint8 lives, Uint8 turbo)
+GameInfo::SetHost(const char *name,
+		  Uint8 wave, Uint8 lives, Uint8 turbo, Uint8 deathMatch)
 {
 	Reset();
-	this->gameID = 1;
+
+	this->gameID = localID;
 	this->seed = GetRandSeed();
 	this->wave = wave;
 	this->lives = lives;
 	this->turbo = turbo;
-	this->deathMatch = 0;
-}
-
-void
-GameInfo::SetMultiplayerHost(Uint8 deathMatch, const char *name)
-{
-	this->gameID = localID;
-	this->seed = GetRandSeed();
-	this->wave = DEFAULT_START_WAVE;
-	this->lives = DEFAULT_START_LIVES;
-	this->turbo = DEFAULT_START_TURBO;
 	this->deathMatch = deathMatch;
 
 	// We are the host node
+	assert(HOST_NODE == 0);
 	nodes[HOST_NODE].nodeID = localID;
+	numNodes = 1;
 
 	// We are the first player
-	GameInfoPlayer *player = GetPlayer(0);
+	AddLocalPlayer(name, CONTROL_LOCAL);
+}
+
+bool
+GameInfo::AddLocalPlayer(const char *name, Uint8 controlMask)
+{
+	int slot;
+
+	if (IsFull()) {
+		return false;
+	}
+
+	if (!name) {
+		name = "";
+	}
+
+	slot = numPlayers;
+	GameInfoPlayer *player = &players[slot];
 	player->nodeID = localID;
-	SDL_strlcpy(player->name, name ? name : "", sizeof(player->name));
-	player->controlMask = (CONTROL_KEYBOARD|CONTROL_JOYSTICK1);
+	SDL_strlcpy(player->name, name, sizeof(player->name));
+	player->controlMask = controlMask;
+	++numPlayers;
+
+	UpdateUI(player);
+
+	return true;
+}
+
+bool
+GameInfo::AddNetworkPlayer(Uint32 nodeID, const IPaddress &address, const char *name)
+{
+	int slot;
+
+	if (IsFull()) {
+		return false;
+	}
+
+	slot = numNodes;
+	GameInfoNode *node = &nodes[slot];
+	node->nodeID = nodeID;
+	node->address = address;
+	InitializePing(slot);
+	++numNodes;
+
+	slot = numPlayers;
+	GameInfoPlayer *player = &players[slot];
+	player->nodeID = nodeID;
+	SDL_strlcpy(player->name, name, sizeof(player->name));
+	player->controlMask = CONTROL_NETWORK;
+	++numPlayers;
+
+	UpdateUI(player);
+
+	return true;
 }
 
 void
 GameInfo::CopyFrom(const GameInfo &rhs)
 {
-	int i;
+	int i, j;
 
 	gameID = rhs.gameID;
 	seed = rhs.seed;
@@ -79,20 +137,39 @@ GameInfo::CopyFrom(const GameInfo &rhs)
 	deathMatch = rhs.deathMatch;
 
 	for (i = 0; i < MAX_NODES; ++i) {
-		nodes[i].nodeID = rhs.nodes[i].nodeID;
-		if (nodes[i].address != rhs.nodes[i].address) {
-			nodes[i].address = rhs.nodes[i].address;
-
-			// Reset the ping info
-			InitializePing(i);
+		const GameInfoNode *node = rhs.GetNode(i);
+		if (nodes[i].nodeID != node->nodeID ||
+		    nodes[i].address != node->address) {
+			// See if this node was just slid down
+			for (j = i+1; j < MAX_NODES; ++j) {
+				if (nodes[j].nodeID == node->nodeID &&
+				    nodes[j].address == node->address) {
+					nodes[i].ping = nodes[j].ping;
+					break;
+				}
+			}
+			if (j == MAX_NODES) {
+				// Reset the ping info
+				InitializePing(i);
+			}
 		}
+		nodes[i].nodeID = node->nodeID;
+		nodes[i].address = node->address;
 	}
+	numNodes = rhs.numNodes;
 
 	for (i = 0; i < MAX_PLAYERS; ++i) {
-		players[i].nodeID = rhs.players[i].nodeID;
-		SDL_strlcpy(players[i].name, rhs.players[i].name,
+		const GameInfoPlayer *player = rhs.GetPlayer(i);
+		players[i].nodeID = player->nodeID;
+		SDL_memcpy(players[i].name, player->name,
 			sizeof(players[i].name));
+		if (players[i].nodeID == localID) {
+			players[i].controlMask = CONTROL_LOCAL;
+		} else {
+			players[i].controlMask = CONTROL_NETWORK;
+		}
 	}
+	numPlayers = rhs.numPlayers;
 
 	UpdateUI();
 }
@@ -121,6 +198,9 @@ GameInfo::ReadFromPacket(DynamicPacket &packet)
 		return false;
 	}
 
+	if (!packet.Read(numNodes)) {
+		return false;
+	}
 	for (i = 0; i < MAX_NODES; ++i) {
 		if (!packet.Read(nodes[i].nodeID)) {
 			return false;
@@ -133,6 +213,9 @@ GameInfo::ReadFromPacket(DynamicPacket &packet)
 		}
 	}
 
+	if (!packet.Read(numPlayers)) {
+		return false;
+	}
 	for (i = 0; i < MAX_PLAYERS; ++i) {
 		if (!packet.Read(players[i].nodeID)) {
 			return false;
@@ -140,6 +223,11 @@ GameInfo::ReadFromPacket(DynamicPacket &packet)
 		if (!packet.Read(players[i].name, sizeof(players[i].name))) {
 			return false;
 		}
+		if (players[i].nodeID) {
+			players[i].controlMask = CONTROL_REPLAY;
+		} else {
+			players[i].controlMask = CONTROL_NONE;
+		}
 	}
 
 	// We want to get the public address of the server
@@ -163,12 +251,14 @@ GameInfo::WriteToPacket(DynamicPacket &packet)
 	packet.Write(turbo);
 	packet.Write(deathMatch);
 
+	packet.Write(numNodes);
 	for (i = 0; i < MAX_NODES; ++i) {
 		packet.Write(nodes[i].nodeID);
 		packet.Write(nodes[i].address.host);
 		packet.Write(nodes[i].address.port);
 	}
 
+	packet.Write(numPlayers);
 	for (i = 0; i < MAX_PLAYERS; ++i) {
 		packet.Write(players[i].nodeID);
 		packet.Write(players[i].name);
@@ -176,7 +266,7 @@ GameInfo::WriteToPacket(DynamicPacket &packet)
 }
 
 bool
-GameInfo::HasNode(Uint32 nodeID)
+GameInfo::HasNode(Uint32 nodeID) const
 {
 	for (int i = 0; i < MAX_NODES; ++i) {
 		if (nodes[i].nodeID == nodeID) {
@@ -187,7 +277,7 @@ GameInfo::HasNode(Uint32 nodeID)
 }
 
 bool
-GameInfo::HasNode(const IPaddress &address)
+GameInfo::HasNode(const IPaddress &address) const
 {
 	for (int i = 0; i < MAX_NODES; ++i) {
 		if (nodes[i].address == address) {
@@ -201,22 +291,62 @@ void
 GameInfo::RemoveNode(Uint32 nodeID)
 {
 	int i;
-	for (i = 0; i < MAX_NODES; ++i) {
+
+	i = 0;
+	while (i < GetNumNodes()) {
 		if (nodeID == nodes[i].nodeID) {
-			SDL_zero(nodes[i]);
+			SDL_memcpy(&nodes[i], &nodes[i+1],
+					(MAX_NODES-i-1)*sizeof(nodes[i]));
+			SDL_zero(nodes[MAX_NODES-1]);
+			--numNodes;
+		} else {
+			++i;
 		}
 	}
-	for (i = 0; i < MAX_PLAYERS; ++i) {
+
+	i = 0;
+	while (i < GetNumPlayers()) {
 		if (nodeID == players[i].nodeID) {
-			SDL_zero(players[i]);
+			RemovePlayer(i);
+		} else {
+			++i;
 		}
 	}
+
+	UpdateUI();
+}
+
+void
+GameInfo::RemovePlayer(int index)
+{
+	GameInfoPlayer *player;
+
+	for (int i = index; i < MAX_PLAYERS-1; ++i) {
+		player = &players[i];
+		player->nodeID = players[i+1].nodeID;
+		SDL_memcpy(player->name, players[i+1].name, sizeof(player->name));
+		player->controlMask = players[i+1].controlMask;
+	}
+	player = &players[MAX_PLAYERS-1];
+	player->nodeID = 0;
+	SDL_zero(player->name);
+	player->controlMask = 0;
+
+	--numPlayers;
+
+	UpdateUI();
 }
 
 bool
-GameInfo::IsNetworkNode(int index)
+GameInfo::IsHosting() const
 {
-	if (!nodes[index].nodeID) {
+	return localID == gameID;
+}
+
+bool
+GameInfo::IsNetworkNode(int index) const
+{
+	if (index >= GetNumNodes()) {
 		return false;
 	}
 	if (nodes[index].nodeID == localID) {
@@ -226,16 +356,23 @@ GameInfo::IsNetworkNode(int index)
 }
 
 bool
-GameInfo::IsFull()
+GameInfo::IsNetworkPlayer(int index) const
 {
-	for (int i = 0; i < MAX_PLAYERS; ++i) {
-		if (!players[i].nodeID) {
-			return false;
-		}
+	if (index >= GetNumPlayers()) {
+		return false;
+	}
+	if (players[index].nodeID == localID) {
+		return false;
 	}
 	return true;
 }
 
+bool
+GameInfo::IsFull() const
+{
+	return GetNumPlayers() == MAX_PLAYERS;
+}
+
 void
 GameInfo::BindPlayerToUI(int index, UIElement *element)
 {
@@ -274,6 +411,10 @@ GameInfo::UpdateUI()
 void
 GameInfo::UpdateUI(GameInfoPlayer *player)
 {
+	if (!player->UI.element) {
+		return;
+	}
+
 	if (player->UI.name) {
 		if (player->name[0]) {
 			player->UI.name->Show();
@@ -283,16 +424,16 @@ GameInfo::UpdateUI(GameInfoPlayer *player)
 		}
 	}
 	if (player->UI.host) {
-		GameInfoNode *node = GetNodeByID(player->nodeID);
-		if (node->nodeID == localID) {
+		const GameInfoNode *node = GetNodeByID(player->nodeID);
+		if (!node) {
+			player->UI.host->Hide();
+		} else if (node->nodeID == localID) {
 			//player->UI.host->Show();
 			//player->UI.host->SetText("localhost");
 			player->UI.host->Hide();
-		} else if (node->address.host) {
+		} else {
 			player->UI.host->Show();
 			player->UI.host->SetText(SDLNet_ResolveIP(&node->address));
-		} else {
-			player->UI.host->Hide();
 		}
 	}
 	if (player->UI.control) {
@@ -326,7 +467,8 @@ GameInfo::UpdateUI(GameInfoPlayer *player)
 	for (int i = 0; i < NUM_PING_STATES; ++i) {
 		UIElement *element = player->UI.ping_states[i];
 		if (element) {
-			if (GetNodeByID(player->nodeID)->ping.status == i) {
+			const GameInfoNode *node = GetNodeByID(player->nodeID);
+			if (node && node->ping.status == i) {
 				element->Show();
 			} else {
 				element->Hide();
@@ -346,7 +488,7 @@ GameInfo::InitializePing()
 void
 GameInfo::InitializePing(int index)
 {
-	GameInfoNode *node = GetNode(index);
+	GameInfoNode *node = &nodes[index];
 
 	if (node->nodeID != localID) {
 		node->ping.lastPing = SDL_GetTicks();
@@ -365,7 +507,7 @@ GameInfo::UpdatePingTime(int index, Uint32 timestamp)
 	now = SDL_GetTicks();
 	elapsed = (now - timestamp);
 
-	node = GetNode(index);
+	node = &nodes[index];
 	node->ping.lastPing = now;
 	if (!node->ping.roundTripTime) {
 		node->ping.roundTripTime = elapsed;
@@ -378,7 +520,7 @@ GameInfo::UpdatePingTime(int index, Uint32 timestamp)
 void
 GameInfo::UpdatePingStatus()
 {
-	for (int i = 0; i < MAX_NODES; ++i) {
+	for (int i = 0; i < GetNumNodes(); ++i) {
 		UpdatePingStatus(i);
 	}
 }
@@ -386,7 +528,7 @@ GameInfo::UpdatePingStatus()
 void
 GameInfo::UpdatePingStatus(int index)
 {
-	GameInfoNode *node = GetNode(index);
+	GameInfoNode *node = &nodes[index];
 
 	if (!IsNetworkNode(index)) {
 		node->ping.status = PING_LOCAL;
@@ -430,7 +572,7 @@ printf("Game 0x%8.8x: node 0x%8.8x since last ping %d (TIMEDOUT)\n",
 	}
 
 	// Update the UI for matching players
-	for (int i = 0; i < MAX_PLAYERS; ++i) {
+	for (int i = 0; i < GetNumPlayers(); ++i) {
 		if (players[i].nodeID == node->nodeID) {
 			UpdateUI(&players[i]);
 		}
diff --git a/game/gameinfo.h b/game/gameinfo.h
index 8792331b..79f9fa0a 100644
--- a/game/gameinfo.h
+++ b/game/gameinfo.h
@@ -31,9 +31,14 @@ class UIElementCheckbox;
 class UIElementRadioGroup;
 
 enum PLAYER_CONTROL {
-	CONTROL_KEYBOARD = 1,
-	CONTROL_JOYSTICK,
-	CONTROL_NETWORK,
+	CONTROL_NONE,
+	CONTROL_KEYBOARD  = 0x01,
+	CONTROL_JOYSTICK1 = 0x02,
+	CONTROL_JOYSTICK2 = 0x04,
+	CONTROL_JOYSTICK3 = 0x08,
+	CONTROL_NETWORK   = 0x10,
+	CONTROL_REPLAY    = 0x20,
+	CONTROL_LOCAL     = (CONTROL_KEYBOARD|CONTROL_JOYSTICK1)
 };
 
 enum PING_STATUS {
@@ -87,7 +92,7 @@ struct GameInfoPlayer
 class GameInfo
 {
 public:
-	GameInfo() { Reset(); }
+	GameInfo();
 
 	// Equality operator for array operations
 	bool operator ==(const GameInfo &rhs) {
@@ -96,46 +101,55 @@ class GameInfo
 
 	void Reset();
 
-	void SetSinglePlayer(Uint8 wave, Uint8 lives, Uint8 turbo);
-
-	void SetMultiplayerHost(Uint8 deathMatch, const char *name);
-
 	void SetLocalID(Uint32 uniqueID) {
 		localID = uniqueID;
 	}
 
+	void SetHost(const char *name, Uint8 wave, Uint8 lives, Uint8 turbo, Uint8 deathMatch);
+
+	bool AddLocalPlayer(const char *name, Uint8 controlMask);
+	bool AddNetworkPlayer(Uint32 nodeID, const IPaddress &address, const char *name);
+
 	void CopyFrom(const GameInfo &rhs);
 
 	bool ReadFromPacket(DynamicPacket &packet);
 	void WriteToPacket(DynamicPacket &packet);
 
-	GameInfoNode *GetHost() {
+	int GetNumNodes() const {
+		return numNodes;
+	}
+	const GameInfoNode *GetHost() const {
 		return GetNode(HOST_NODE);
 	}
-	GameInfoNode *GetNode(int index) {
+	const GameInfoNode *GetNode(int index) const {
 		return &nodes[index];
 	}
-	GameInfoNode *GetNodeByID(Uint32 nodeID) {
-		for (int i = 0; i < MAX_NODES; ++i) {
+	const GameInfoNode *GetNodeByID(Uint32 nodeID) const {
+		for (int i = 0; i < GetNumNodes(); ++i) {
 			if (nodeID == nodes[i].nodeID) {
 				return &nodes[i];
 			}
 		}
-	}
-	GameInfoPlayer *GetPlayer(int index) {
-		return &players[index];
+		return NULL;
 	}
 
-	bool HasNode(Uint32 nodeID);
-	bool HasNode(const IPaddress &address);
+	bool HasNode(Uint32 nodeID) const;
+	bool HasNode(const IPaddress &address) const;
 	void RemoveNode(Uint32 nodeID);
 
-	bool IsHosting() {
-		return localID == gameID;
+	int GetNumPlayers() const {
+		return numPlayers;
 	}
-	bool IsNetworkNode(int index);
+	const GameInfoPlayer *GetPlayer(int index) const {
+		return &players[index];
+	}
+	void RemovePlayer(int index);
+
+	bool IsHosting() const;
+	bool IsNetworkNode(int index) const;
+	bool IsNetworkPlayer(int index) const;
 
-	bool IsFull();
+	bool IsFull() const;
 
 	void BindPlayerToUI(int index, UIElement *element);
 	void UpdateUI();
@@ -155,10 +169,14 @@ class GameInfo
 	Uint8 lives;
 	Uint8 turbo;
 	Uint8 deathMatch;
-	GameInfoNode nodes[MAX_NODES];
-	GameInfoPlayer players[MAX_PLAYERS];
 
 	Uint32 localID;
+
+protected:
+	Uint8 numNodes;
+	GameInfoNode nodes[MAX_NODES];
+	Uint8 numPlayers;
+	GameInfoPlayer players[MAX_PLAYERS];
 };
 
 #endif // _gameinfo_h
diff --git a/game/lobby.cpp b/game/lobby.cpp
index 2702e720..bde91b47 100644
--- a/game/lobby.cpp
+++ b/game/lobby.cpp
@@ -156,15 +156,13 @@ LobbyDialogDelegate::OnHide()
 	if (m_dialog->GetDialogStatus() > 0) {
 		SetState(STATE_PLAYING);
 
-		for (int i = 0; i < MAX_PLAYERS; ++i) {
-			GameInfoPlayer *player = m_game.GetPlayer(i);
-			if (player->nodeID) {
-				if (player->nodeID == m_game.localID) {
-					AddLocalPlayer(i);
-				} else {
-					GameInfoNode *node = m_game.GetNodeByID(player->nodeID);
-					AddNetworkPlayer(i, node->address);
-				}
+		for (int i = 0; i < m_game.GetNumPlayers(); ++i) {
+			const GameInfoPlayer *player = m_game.GetPlayer(i);
+			if (player->nodeID == m_game.localID) {
+				AddLocalPlayer(i);
+			} else {
+				const GameInfoNode *node = m_game.GetNodeByID(player->nodeID);
+				AddNetworkPlayer(i, node->address);
 			}
 		}
 		NewGame();
@@ -318,7 +316,7 @@ LobbyDialogDelegate::SetState(LOBBY_STATE state)
 	if (state == STATE_NONE) {
 		if (m_state == STATE_HOSTING) {
 			// Notify the players that the game is gone
-			for (int i = 0; i < MAX_NODES; ++i) {
+			for (int i = 0; i < m_game.GetNumNodes(); ++i) {
 				SendKick(i);
 			}
 		} else if (m_state == STATE_JOINING ||
@@ -327,9 +325,11 @@ LobbyDialogDelegate::SetState(LOBBY_STATE state)
 			SendLeaveRequest();
 		}
 	} else if (state == STATE_HOSTING) {
-		m_game.SetMultiplayerHost(
-			prefs->GetNumber(PREFERENCES_DEATHMATCH),
-			prefs->GetString(PREFERENCES_HANDLE));
+		m_game.SetHost(prefs->GetString(PREFERENCES_HANDLE),
+				DEFAULT_START_WAVE,
+				DEFAULT_START_LIVES,
+				DEFAULT_START_TURBO,
+				prefs->GetNumber(PREFERENCES_DEATHMATCH));
 	} else if (state == STATE_LISTING) {
 		ClearGameList();
 	}
@@ -370,7 +370,7 @@ LobbyDialogDelegate::CheckPings()
 		}
 	} else if (m_state == STATE_HOSTING) {
 		m_game.UpdatePingStatus();
-		for (int i = 0; i < MAX_NODES; ++i) {
+		for (int i = 0; i < m_game.GetNumNodes(); ++i) {
 			if (m_game.GetPingStatus(i) == PING_TIMEDOUT) {
 //printf("Player timed out, removing from lobby\n");
 				SendKick(i);
@@ -392,7 +392,7 @@ LobbyDialogDelegate::CheckPings()
 		m_packet.Write(m_game.localID);
 		m_packet.Write(SDL_GetTicks());
 
-		for (int i = 0; i < MAX_NODES; ++i) {
+		for (int i = 0; i < m_game.GetNumNodes(); ++i) {
 			if (m_game.IsNetworkNode(i)) {
 				m_packet.address = m_game.GetNode(i)->address;
 				
@@ -488,7 +488,7 @@ LobbyDialogDelegate::SendLeaveRequest()
 void
 LobbyDialogDelegate::SendKick(int index)
 {
-	GameInfoNode *node;
+	const GameInfoNode *node;
 
 	if (!m_game.IsNetworkNode(index)) {
 		return;
@@ -504,9 +504,6 @@ LobbyDialogDelegate::SendKick(int index)
 
 	// Now remove them from the game list
 	m_game.RemoveNode(node->nodeID);
-
-	// Update our own UI
-	UpdateUI();
 }
 
 void
@@ -650,7 +647,7 @@ LobbyDialogDelegate::ProcessPong(DynamicPacket &packet)
 		return;
 	}
 
-	for (int i = 0; i < MAX_NODES; ++i) {
+	for (int i = 0; i < m_game.GetNumNodes(); ++i) {
 		if (packet.address == m_game.GetNode(i)->address) {
 			m_game.UpdatePingTime(i, timestamp);
 		}
@@ -736,54 +733,23 @@ LobbyDialogDelegate::ProcessRequestJoin(DynamicPacket &packet)
 	if (!packet.Read(name, sizeof(name))) {
 		return;
 	}
-
-	// Find an empty slot
-	int slot;
-	for (slot = 0; slot < MAX_NODES; ++slot) {
-		if (nodeID == m_game.nodes[slot].nodeID) {
-			// We already have this node, ignore it
-			return;
-		}
-	}
-	if (slot == MAX_NODES) {
-		for (slot = 0; slot < MAX_NODES; ++slot) {
-			if (!m_game.nodes[slot].nodeID) {
-				break;
-			}
-		}
-	}
-	assert(slot < MAX_NODES);
-
-	GameInfoNode *node = m_game.GetNode(slot);
-	node->nodeID = nodeID;
-	node->address = packet.address;
-	m_game.InitializePing(slot);
-
-	for (slot = 0; slot < MAX_PLAYERS; ++slot) {
-		if (!m_game.players[slot].nodeID) {
-			break;
-		}
+	if (m_game.HasNode(nodeID)) {
+		// We already have this node, ignore it
+		return;
 	}
-	assert(slot < MAX_PLAYERS);
 
-	GameInfoPlayer *player = m_game.GetPlayer(slot);
-	player->nodeID = nodeID;
-	SDL_strlcpy(player->name, name, sizeof(player->name));
-	player->controlMask = CONTROL_NETWORK;
+	m_game.AddNetworkPlayer(nodeID, packet.address, name);
 
 	// Let everybody know!
 	m_reply.StartLobbyMessage(LOBBY_GAME_INFO);
 	m_reply.Write((Uint32)0);
 	m_game.WriteToPacket(m_reply);
-	for (slot = 0; slot < MAX_NODES; ++slot) {
-		if (m_game.IsNetworkNode(slot)) {
-			m_reply.address = m_game.nodes[slot].address;
+	for (int i = 0; i < m_game.GetNumNodes(); ++i) {
+		if (m_game.IsNetworkNode(i)) {
+			m_reply.address = m_game.GetNode(i)->address;
 			SDLNet_UDP_Send(gNetFD, -1, &m_reply);
 		}
 	}
-
-	// Update our own UI
-	UpdateUI();
 }
 
 void
@@ -804,9 +770,6 @@ LobbyDialogDelegate::ProcessRequestLeave(DynamicPacket &packet)
 
 	// Okay, clear them from the list!
 	m_game.RemoveNode(nodeID);
-
-	// Update our own UI
-	UpdateUI();
 }
 
 void
@@ -840,6 +803,7 @@ LobbyDialogDelegate::ProcessGameInfo(DynamicPacket &packet)
 			m_gameList[i].UpdatePingTime(HOST_NODE, timestamp);
 			m_gameList[i].UpdatePingStatus(HOST_NODE);
 		}
+		UpdateUI();
 	} else {
 		if (game.gameID != m_game.gameID) {
 			// Probably an old packet...
@@ -860,8 +824,6 @@ LobbyDialogDelegate::ProcessGameInfo(DynamicPacket &packet)
 			}
 		}
 	}
-
-	UpdateUI();
 }
 
 void
diff --git a/game/main.cpp b/game/main.cpp
index 37ae7c3a..10a3b102 100644
--- a/game/main.cpp
+++ b/game/main.cpp
@@ -73,9 +73,10 @@ static void RunSinglePlayerGame()
 }
 static void RunPlayGame(void*)
 {
-	gGameInfo.SetSinglePlayer(DEFAULT_START_WAVE,
-	                          DEFAULT_START_LIVES,
-	                          DEFAULT_START_TURBO);
+	gGameInfo.SetHost(prefs->GetString(PREFERENCES_HANDLE),
+				DEFAULT_START_WAVE,
+				DEFAULT_START_LIVES,
+				DEFAULT_START_TURBO, 0);
 	RunSinglePlayerGame();
 }
 static void RunQuitGame(void*)
@@ -162,7 +163,8 @@ static void CheatDialogDone(UIDialog *dialog, int status)
 		Delay(SOUND_DELAY);
 		sound->PlaySound(gNewLife, 5);
 		Delay(SOUND_DELAY);
-		gGameInfo.SetSinglePlayer(wave, lives, turbo);
+		gGameInfo.SetHost(prefs->GetString(PREFERENCES_HANDLE),
+					wave, lives, turbo, 0);
 		RunSinglePlayerGame();
 	}
 }
diff --git a/game/netplay.cpp b/game/netplay.cpp
index 633e3c8b..6bebbf2b 100644
--- a/game/netplay.cpp
+++ b/game/netplay.cpp
@@ -545,9 +545,8 @@ int Send_NewGame()
 int Await_NewGame()
 {
 	unsigned char netbuf[BUFSIZ];
-	int len, gameon;
+	int gameon;
 	UDPpacket sent;
-	Uint32 lives, seed;
 
 	/* Get ready to wait for server */
 	Message("Awaiting Player 1 (server)");