https://github.com/libsdl-org/Maelstrom/commit/5e8f01ffdfb3f5dee955cb424770dc1deee52188
From 5e8f01ffdfb3f5dee955cb424770dc1deee52188 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Sat, 5 Nov 2011 19:02:03 -0400
Subject: [PATCH] The Maelstrom lobby server now maintains a list of games.
---
MaelstromLobby.cpp | 289 ++++++++++++++++++++++++++++++++++++++++++--
netlogic/lobby.cpp | 65 ++++++----
netlogic/lobby.h | 5 +-
netlogic/packet.h | 47 +++++--
netlogic/protocol.h | 17 ++-
5 files changed, 376 insertions(+), 47 deletions(-)
diff --git a/MaelstromLobby.cpp b/MaelstromLobby.cpp
index 5636f0b4..a997fc25 100644
--- a/MaelstromLobby.cpp
+++ b/MaelstromLobby.cpp
@@ -22,19 +22,285 @@
#include "SDL_net.h"
+#include "netlogic/packet.h"
#include "netlogic/protocol.h"
+#include "utils/array.h"
#define MAX_PACKET_SIZE 1024
-void ProcessPacket(UDPpacket *packet)
-{
- printf("Received packet from %s\n", SDLNet_ResolveIP(&packet->address));
+// We'll let games stick around for 10 seconds before aging them out
+#define GAME_LIFETIME 10000
+
+
+bool operator==(const IPaddress &lhs, const IPaddress &rhs) {
+ return lhs.host == rhs.host && lhs.port == rhs.port;
}
+bool operator!=(const IPaddress &lhs, const IPaddress &rhs) {
+ return !operator==(lhs, rhs);
+}
+
+class AddressList : public array<IPaddress>
+{
+public:
+ AddressList() : array<IPaddress>() { }
+
+ bool ReadFromPacket(DynamicPacket &packet) {
+ Uint8 count;
+ IPaddress address;
+
+ if (!packet.Read(count) || !count) {
+ return false;
+ }
+
+ clear();
+ SDL_zero(address);
+ for (Uint8 i = 0; i < count; ++i) {
+ if (!packet.Read(address.host)) {
+ return false;
+ }
+ if (!packet.Read(address.port)) {
+ return false;
+ }
+ add(address);
+ }
+
+ // Add the address that we saw this packet come from
+ if (!find(packet.address)) {
+ insert(packet.address, 0);
+ }
+
+ return true;
+ }
+
+ void WriteToPacket(DynamicPacket &packet) {
+ packet.Write((Uint8)length());
+ for (int i = 0; i < length(); ++i) {
+ packet.Write(m_data[i].host);
+ packet.Write(m_data[i].port);
+ }
+ }
+
+ bool operator ==(const AddressList &rhs) const {
+ if (length() != rhs.length()) {
+ return false;
+ }
+ for (int i = 0; i < length(); ++i) {
+ if (m_data[i] != rhs.m_data[i]) {
+ return false;
+ }
+ }
+ return true;
+ }
+};
+
+class Game
+{
+public:
+ Game() {
+ m_timestamp = 0;
+ m_list = 0;
+ m_prev = 0;
+ m_next = 0;
+ }
+
+ bool ReadFromPacket(DynamicPacket &packet) {
+ return m_addresses.ReadFromPacket(packet);
+ }
+
+ void WriteToPacket(DynamicPacket &packet) {
+ m_addresses.WriteToPacket(packet);
+ }
+
+ bool operator ==(const Game &rhs) {
+ return m_addresses == rhs.m_addresses;
+ }
+
+ bool TimedOut(Uint32 now) {
+ return (now - m_timestamp) > GAME_LIFETIME;
+ }
+
+ void Refresh() {
+ m_timestamp = SDL_GetTicks();
+ }
+
+ const IPaddress *Address() const {
+ return &m_addresses[0];
+ }
+
+ void Link(Game *&list) {
+ Unlink();
+ if (list) {
+ list->m_prev = this;
+ }
+ m_next = list;
+ list = this;
+ m_list = &list;
+ }
+
+ void Unlink() {
+ if (m_prev) {
+ m_prev->m_next = m_next;
+ } else if (m_list) {
+ *m_list = m_next;
+ }
+ if (m_next) {
+ m_next->m_prev = m_prev;
+ }
+ m_list = 0;
+ m_prev = 0;
+ m_next = 0;
+ }
+
+ Game *Prev() {
+ return m_prev;
+ }
+ Game *Next() {
+ return m_next;
+ }
+
+protected:
+ Uint32 m_timestamp;
+ AddressList m_addresses;
+ Game **m_list;
+ Game *m_prev, *m_next;
+};
+
+class GameList
+{
+public:
+ GameList() {
+ m_list = 0;
+ m_free = 0;
+ }
+
+ ~GameList() {
+ Game *game;
+
+ while (m_list) {
+ game = m_list;
+ game->Unlink();
+ delete game;
+ }
+ while (m_free) {
+ game = m_free;
+ game->Unlink();
+ delete game;
+ }
+ }
+
+ void ProcessList() {
+ Game *game, *next;
+ Uint32 now;
+
+ now = SDL_GetTicks();
+ game = m_list;
+ while (game) {
+ next = game->Next();
+ if (game->TimedOut(now)) {
+ printf("Expiring game from %s:%d\n",
+ SDLNet_ResolveIP(game->Address()),
+ SDL_SwapBE16(game->Address()->port));
+ game->Link(m_free);
+ }
+ game = next;
+ }
+ }
+
+ void ProcessPacket(DynamicPacket &packet) {
+ Uint8 cmd;
+
+ if (!packet.Read(cmd)) {
+ return;
+ }
+
+ switch (cmd) {
+ case LOBBY_ANNOUNCE_GAME:
+ ProcessAnnounceGame(packet);
+ break;
+ case LOBBY_REMOVE_GAME:
+ ProcessRemoveGame(packet);
+ break;
+ case LOBBY_REQUEST_GAME_SERVERS:
+ ProcessRequestGames(packet);
+ break;
+ }
+ }
+
+ void ProcessAnnounceGame(DynamicPacket &packet) {
+ Game *newGame;
+
+ if (m_free) {
+ newGame = m_free;
+ } else {
+ newGame = new Game;
+ }
+ if (!newGame->ReadFromPacket(packet)) {
+ printf("Invalid game from %s:%d\n",
+ SDLNet_ResolveIP(&packet.address),
+ SDL_SwapBE16(packet.address.port));
+
+ newGame->Link(m_free);
+ return;
+ }
+
+ for (Game *game = m_list; game; game = game->Next()) {
+ if (*game == *newGame) {
+ //printf("Refreshing game from %s:%d\n",
+ // SDLNet_ResolveIP(&packet.address),
+ // SDL_SwapBE16(packet.address.port));
+
+ game->Refresh();
+ newGame->Link(m_free);
+ return;
+ }
+ }
+
+ printf("Adding game from %s:%d\n",
+ SDLNet_ResolveIP(&packet.address),
+ SDL_SwapBE16(packet.address.port));
+
+ newGame->Refresh();
+ newGame->Link(m_list);
+ }
+
+ void ProcessRemoveGame(DynamicPacket &packet) {
+ Game *newGame;
+
+ if (m_free) {
+ newGame = m_free;
+ } else {
+ newGame = new Game;
+ }
+ if (!newGame->ReadFromPacket(packet)) {
+ newGame->Link(m_free);
+ return;
+ }
+
+ for (Game *game = m_list; game; game = game->Next()) {
+ if (*game == *newGame) {
+ printf("Removing game from %s:%d\n",
+ SDLNet_ResolveIP(&packet.address),
+ SDL_SwapBE16(packet.address.port));
+ game->Link(m_free);
+ break;
+ }
+ }
+ newGame->Link(m_free);
+ }
+
+ void ProcessRequestGames(DynamicPacket &packet) {
+ }
+
+protected:
+ Game *m_list;
+ Game *m_free;
+};
int main(int argc, char *argv[])
{
UDPsocket sock;
- UDPpacket *packet;
+ DynamicPacket packet;
+ GameList games;
sock = SDLNet_UDP_Open(LOBBY_PORT);
if (!sock) {
@@ -43,17 +309,14 @@ int main(int argc, char *argv[])
exit(1);
}
- packet = SDLNet_AllocPacket(MAX_PACKET_SIZE);
- if (!packet) {
- fprintf(stderr, "Couldn't allocate packet of size %d: %s\n",
- MAX_PACKET_SIZE, SDL_GetError());
- SDLNet_UDP_Close(sock);
- exit(1);
- }
+ packet.Expand(MAX_PACKET_SIZE);
for ( ; ; ) {
- while (SDLNet_UDP_Recv(sock, packet)) {
- ProcessPacket(packet);
+ games.ProcessList();
+
+ while (SDLNet_UDP_Recv(sock, &packet)) {
+ games.ProcessPacket(packet);
+ packet.Reset();
}
SDL_Delay(100);
diff --git a/netlogic/lobby.cpp b/netlogic/lobby.cpp
index f1af81cc..0f97890d 100644
--- a/netlogic/lobby.cpp
+++ b/netlogic/lobby.cpp
@@ -107,17 +107,17 @@ LobbyDialogDelegate::OnTick()
return;
}
- if (m_globalGame->IsChecked()) {
- Uint32 now = SDL_GetTicks();
- if (!m_lastGlobalCheck ||
- (now - m_lastGlobalCheck) > GLOBAL_CHECK_INTERVAL) {
+ Uint32 now = SDL_GetTicks();
+ if (!m_lastRefresh ||
+ (now - m_lastRefresh) > GLOBAL_CHECK_INTERVAL) {
+ if (m_globalGame->IsChecked()) {
if (m_hosting) {
AdvertiseGame();
} else {
GetGameList();
}
- m_lastGlobalCheck = now;
}
+ m_lastRefresh = now;
}
}
@@ -133,28 +133,41 @@ LobbyDialogDelegate::SetHostOrJoin(void*, int value)
if (InitNetData(m_hosting) < 0) {
m_hostOrJoin->SetValue(0);
}
- m_lastGlobalCheck = 0;
+ m_lastRefresh = 0;
}
}
void
LobbyDialogDelegate::GlobalGameChanged(void*)
{
- m_lastGlobalCheck = 0;
+ m_lastRefresh = 0;
+
+ if (!m_globalGame->IsChecked()) {
+ if (m_hosting) {
+ RemoveGame();
+ } else {
+ ClearGameList();
+ }
+ }
}
void
LobbyDialogDelegate::AdvertiseGame()
{
- int i;
+ m_packet.Reset();
+ m_packet.Write((Uint8)LOBBY_ANNOUNCE_GAME);
+ PackAddresses(m_packet);
+ m_packet.address = m_globalServer;
+ SDLNet_UDP_Send(gNetFD, -1, &m_packet);
+}
+
+void
+LobbyDialogDelegate::RemoveGame()
+{
m_packet.Reset();
- m_packet << (Uint8)LOBBY_ANNOUNCE_GAME;
- m_packet << (Uint8)m_addresses.length();
- for (i = 0; i < m_addresses.length(); ++i) {
- m_packet << m_addresses[i].host;
- m_packet << m_addresses[i].port;
- }
+ m_packet.Write((Uint8)LOBBY_REMOVE_GAME);
+ PackAddresses(m_packet);
m_packet.address = m_globalServer;
SDLNet_UDP_Send(gNetFD, -1, &m_packet);
@@ -163,15 +176,9 @@ LobbyDialogDelegate::AdvertiseGame()
void
LobbyDialogDelegate::GetGameList()
{
- int i;
-
m_packet.Reset();
- m_packet << (Uint8)LOBBY_REQUEST_GAME_SERVERS;
- m_packet << (Uint8)m_addresses.length();
- for (i = 0; i < m_addresses.length(); ++i) {
- m_packet << m_addresses[i].host;
- m_packet << m_addresses[i].port;
- }
+ m_packet.Write((Uint8)LOBBY_REQUEST_GAME_SERVERS);
+ PackAddresses(m_packet);
m_packet.address = m_globalServer;
SDLNet_UDP_Send(gNetFD, -1, &m_packet);
@@ -181,3 +188,17 @@ void
LobbyDialogDelegate::ClearGameList()
{
}
+
+void
+LobbyDialogDelegate::PackAddresses(DynamicPacket &packet)
+{
+ Uint16 port;
+
+ port = SDLNet_UDP_GetPeerAddress(gNetFD, -1)->port;
+
+ m_packet.Write((Uint8)m_addresses.length());
+ for (int i = 0; i < m_addresses.length(); ++i) {
+ m_packet.Write(m_addresses[i].host);
+ m_packet.Write(port);
+ }
+}
diff --git a/netlogic/lobby.h b/netlogic/lobby.h
index 027e2b88..258d0c2f 100644
--- a/netlogic/lobby.h
+++ b/netlogic/lobby.h
@@ -52,15 +52,18 @@ class LobbyDialogDelegate : public UIDialogDelegate
void GlobalGameChanged(void*);
void AdvertiseGame();
+ void RemoveGame();
void GetGameList();
void ClearGameList();
+ void PackAddresses(DynamicPacket &packet);
+
protected:
IPaddress m_globalServer;
array<IPaddress> m_addresses;
DynamicPacket m_packet;
bool m_hosting;
- Uint32 m_lastGlobalCheck;
+ Uint32 m_lastRefresh;
UIElementRadioGroup *m_hostOrJoin;
UIElementCheckbox *m_globalGame;
};
diff --git a/netlogic/packet.h b/netlogic/packet.h
index 2ef0dae5..0c4bb736 100644
--- a/netlogic/packet.h
+++ b/netlogic/packet.h
@@ -32,7 +32,7 @@ class DynamicPacket : public UDPpacket
{
public:
DynamicPacket(int minSize = 32) {
- len = 0;
+ SDL_zero(*this);
maxlen = minSize;
data = (Uint8*)SDL_malloc(minSize);
}
@@ -40,35 +40,66 @@ class DynamicPacket : public UDPpacket
SDL_free(data);
}
+ void Expand(size_t size) {
+ CheckSize(size - len);
+ }
+
void Reset() {
len = 0;
+ pos = 0;
}
- DynamicPacket& operator <<(Uint8 value) {
+ void Write(Uint8 value) {
CheckSize(sizeof(value));
data[len++] = value;
- return *this;
}
- DynamicPacket& operator <<(Uint16 value) {
+ void Write(Uint16 value) {
CheckSize(sizeof(value));
SDLNet_Write16(value, &data[len]);
len += sizeof(value);
- return *this;
}
- DynamicPacket& operator <<(Uint32 value) {
+ void Write(Uint32 value) {
CheckSize(sizeof(value));
SDLNet_Write32(value, &data[len]);
len += sizeof(value);
- return *this;
+ }
+
+ bool Read(Uint8 &value) {
+ if (pos+sizeof(value) > (size_t)len) {
+ return false;
+ }
+ value = data[pos++];
+ return true;
+ }
+ bool Read(Uint16 &value) {
+ if (pos+sizeof(value) > (size_t)len) {
+ return false;
+ }
+ value = SDLNet_Read16(&data[pos]);
+ pos += sizeof(value);
+ return true;
+ }
+ bool Read(Uint32 &value) {
+ if (pos+sizeof(value) > (size_t)len) {
+ return false;
+ }
+ value = SDLNet_Read32(&data[pos]);
+ pos += sizeof(value);
+ return true;
}
protected:
void CheckSize(size_t additionalSize) {
if (len+additionalSize > (size_t)maxlen) {
- maxlen *= 2;
+ while (len+additionalSize > (size_t)maxlen) {
+ maxlen *= 2;
+ }
data = (Uint8*)SDL_realloc(data, maxlen);
}
}
+
+protected:
+ int pos;
};
#endif // _packet_h
diff --git a/netlogic/protocol.h b/netlogic/protocol.h
index 8095028d..d0a7f8cb 100644
--- a/netlogic/protocol.h
+++ b/netlogic/protocol.h
@@ -50,6 +50,17 @@ enum LobbyProtocol {
} addresses[]
*/
+ LOBBY_REMOVE_GAME,
+ /* Sent by the hosting game to the lobby server
+ This is sent when the game is no longer available to join.
+
+ BYTE numaddresses
+ {
+ Uint32 host;
+ Uint16 port;
+ } addresses[]
+ */
+
LOBBY_ANNOUNCE_PLAYER,
/* Sent by the lobby server when a player requests the game list.
This allows the hosting game to send a packet to the player
@@ -148,9 +159,9 @@ enum LobbyProtocol {
#define KEY_PRESS 0x80 /* Sent during game */
#define KEY_RELEASE 0xF0 /* Sent during game */
-/* The default port for Maelstrom games. What is 0xAEAE?? *shrug* :) */
-#define LOBBY_PORT 0xAFAF /* port 44975 */
-#define NETPLAY_PORT 0xAEAE /* port 44718 */
+/* The default port for Maelstrom games */
+#define LOBBY_PORT 0xAE00 /* port 44544 */
+#define NETPLAY_PORT 0xAF00 /* port 44800 */
/* The minimum length of a new packet buffer */
#define NEW_PACKETLEN (3+3*4)