From 391f63ac31719d908171b894562de3c48906a048 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Thu, 27 Nov 2025 14:42:40 -0800
Subject: [PATCH] Removed the global lobby server
We don't want multiplayer games to be reliant on a third party service
---
Data/UI/lobby.xml | 4 -
MaelstromLobby.cpp | 373 ---------------------------------------------
game/lobby.cpp | 142 +----------------
game/lobby.h | 4 -
game/protocol.h | 61 --------
5 files changed, 3 insertions(+), 581 deletions(-)
delete mode 100644 MaelstromLobby.cpp
diff --git a/Data/UI/lobby.xml b/Data/UI/lobby.xml
index 05803551..08e97426 100644
--- a/Data/UI/lobby.xml
+++ b/Data/UI/lobby.xml
@@ -19,10 +19,6 @@
</Elements>
</DialogRadioGroup>
- <DialogCheckbox name="globalGame" bindValue="Network.GlobalGame" text="Global">
- <Anchor anchorFrom="TOPLEFT" anchorTo="TOPLEFT" x="290" y="39"/>
- </DialogCheckbox>
-
<Area name="gamelist">
<Elements>
<DialogLabel name="ping_label">
diff --git a/MaelstromLobby.cpp b/MaelstromLobby.cpp
deleted file mode 100644
index 47bfcabd..00000000
--- a/MaelstromLobby.cpp
+++ /dev/null
@@ -1,373 +0,0 @@
-/*
- Maelstrom: Open Source version of the classic game by Ambrosia Software
- Copyright (C) 1997-2011 Sam Lantinga
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-
- Sam Lantinga
- slouken@libsdl.org
-*/
-
-#include <stdio.h>
-#include <stdarg.h>
-
-#include "SDL_net.h"
-
-#include "game/packet.h"
-#include "game/protocol.h"
-#include "utils/array.h"
-
-
-// We'll let games stick around for 10 seconds before aging them out
-#define GAME_LIFETIME 10000
-
-static void log(const char *fmt, ...)
-{
- va_list ap;
-
- va_start(ap, fmt);
- vfprintf(stdout, fmt, ap);
- fflush(stdout);
-}
-
-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_sock = NULL;
- 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 SetSocket(UDPsocket sock) {
- m_sock = sock;
- }
-
- void ProcessList() {
- Game *game, *next;
- Uint32 now;
-
- now = SDL_GetTicks();
- game = m_list;
- while (game) {
- next = game->Next();
- if (game->TimedOut(now)) {
- log("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;
- }
- if (cmd != LOBBY_MSG) {
- return;
- }
- 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)) {
- log("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) {
- //log("Refreshing game from %s:%d\n",
- // SDLNet_ResolveIP(&packet.address),
- // SDL_SwapBE16(packet.address.port));
-
- game->Refresh();
- newGame->Link(m_free);
- return;
- }
- }
-
- log("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) {
- log("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) {
- AddressList addresses;
- Game *game;
-
- if (!addresses.ReadFromPacket(packet)) {
- return;
- }
-
- // Send back a list of all games
- int mark;
- m_reply.StartLobbyMessage(LOBBY_GAME_SERVERS);
- mark = m_reply.Tell();
- m_reply.Write((Uint8)0);
- int count;
- for (game = m_list; game; game = game->Next()) {
- game->WriteToPacket(m_reply);
- ++count;
- if (count == 255) {
- // That's it, I'm cutting you off...
- break;
- }
- }
- m_reply.Seek(mark);
- m_reply.Write((Uint8)count);
- m_reply.address = packet.address;
- SDLNet_UDP_Send(m_sock, -1, &m_reply);
-
- // Send the requesting player to all game servers
- m_reply.StartLobbyMessage(LOBBY_ANNOUNCE_PLAYER);
- addresses.WriteToPacket(m_reply);
- for (game = m_list; game; game = game->Next()) {
- m_reply.address = *game->Address();
- SDLNet_UDP_Send(m_sock, -1, &m_reply);
- }
- }
-
-protected:
- UDPsocket m_sock;
- Game *m_list;
- Game *m_free;
- DynamicPacket m_reply;
-};
-
-int main(int argc, char *argv[])
-{
- UDPsocket sock;
- DynamicPacket packet;
- GameList games;
-
- sock = SDLNet_UDP_Open(LOBBY_PORT);
- if (!sock) {
- fprintf(stderr, "Couldn't create socket on port %d: %s\n",
- LOBBY_PORT, SDL_GetError());
- exit(1);
- }
- games.SetSocket(sock);
-
- for ( ; ; ) {
- games.ProcessList();
-
- while (SDLNet_UDP_Recv(sock, &packet)) {
- games.ProcessPacket(packet);
- packet.Reset();
- }
-
- SDL_Delay(100);
- }
- SDLNet_UDP_Close(sock);
-}
diff --git a/game/lobby.cpp b/game/lobby.cpp
index 6ecc49fb..97dcf1b4 100644
--- a/game/lobby.cpp
+++ b/game/lobby.cpp
@@ -30,13 +30,6 @@
#include "netplay.h"
#include "game.h"
-// Update the game list every 3 seconds
-#define GLOBAL_SERVER_HOST "obelix.dreamhost.com"
-//#define GLOBAL_SERVER_HOST "localhost"
-
-// Define this if you want local broadcast in addition to the global server
-#define LOBBY_BROADCAST
-
class SelectControlCallback : public UIClickCallback
{
@@ -151,11 +144,6 @@ LobbyDialogDelegate::OnLoad()
IPaddress addresses[32];
char name[32];
- // Get the address of the global server
- if (SDLNet_ResolveHost(&m_globalServer, GLOBAL_SERVER_HOST, LOBBY_PORT) < 0) {
- fprintf(stderr, "Warning: Couldn't resolve global server host %s\n", GLOBAL_SERVER_HOST);
- }
-
// Get the addresses for this machine
count = SDLNet_GetLocalAddresses(addresses, SDL_arraysize(addresses));
m_addresses.clear();
@@ -170,17 +158,6 @@ LobbyDialogDelegate::OnLoad()
}
m_hostOrJoin->SetValueCallback(this, &LobbyDialogDelegate::SetHostOrJoin);
- m_globalGame = m_dialog->GetElement<UIElementCheckbox>("globalGame");
- if (!m_globalGame) {
- fprintf(stderr, "Warning: Couldn't find checkbox 'globalGame'\n");
- return false;
- }
- if (m_globalServer.host == INADDR_NONE) {
- m_globalGame->SetChecked(false);
- m_globalGame->SetDisabled(true);
- }
- m_globalGame->SetClickCallback(this, &LobbyDialogDelegate::GlobalGameChanged);
-
m_deathmatch = m_dialog->GetElement<UIElementRadioGroup>("deathmatch");
if (!m_deathmatch) {
fprintf(stderr, "Warning: Couldn't find radio group 'deathmatch'\n");
@@ -273,7 +250,7 @@ LobbyDialogDelegate::OnPoll()
if (!m_lastRefresh ||
(now - m_lastRefresh) > PING_INTERVAL) {
if (m_state == STATE_HOSTING) {
- AdvertiseGame();
+ // Nothing to do
} else if (m_state == STATE_LISTING) {
GetGameList();
} else if (m_state == STATE_JOINING) {
@@ -302,7 +279,7 @@ void
LobbyDialogDelegate::SetHostOrJoin(void*, int value)
{
// Remove the game before shutting down the network
- if (m_state == STATE_HOSTING && m_globalGame->IsChecked()) {
+ if (m_state == STATE_HOSTING) {
RemoveGame();
}
@@ -331,20 +308,6 @@ LobbyDialogDelegate::SetHostOrJoin(void*, int value)
}
}
-void
-LobbyDialogDelegate::GlobalGameChanged(void*)
-{
- m_lastRefresh = 0;
-
- if (!m_globalGame->IsChecked()) {
- if (m_state == STATE_HOSTING) {
- RemoveGame();
- } else {
- ClearGameList();
- }
- }
-}
-
void
LobbyDialogDelegate::SetDeathmatch(void*, int value)
{
@@ -408,7 +371,7 @@ LobbyDialogDelegate::SetState(LOBBY_STATE state)
int i;
// Handle any state transitions here
- if (m_state == STATE_HOSTING && m_globalGame->IsChecked()) {
+ if (m_state == STATE_HOSTING) {
RemoveGame();
}
if (m_state == STATE_HOSTING) {
@@ -532,47 +495,20 @@ LobbyDialogDelegate::CheckPings()
}
}
-void
-LobbyDialogDelegate::AdvertiseGame()
-{
- if (m_globalGame->IsChecked()) {
- m_packet.StartLobbyMessage(LOBBY_ANNOUNCE_GAME);
- PackAddresses(m_packet);
- m_packet.address = m_globalServer;
-
- SDLNet_UDP_Send(gNetFD, -1, &m_packet);
- }
-}
-
void
LobbyDialogDelegate::RemoveGame()
{
- m_packet.StartLobbyMessage(LOBBY_REMOVE_GAME);
- PackAddresses(m_packet);
- m_packet.address = m_globalServer;
-
- SDLNet_UDP_Send(gNetFD, -1, &m_packet);
}
void
LobbyDialogDelegate::GetGameList()
{
- if (m_globalGame->IsChecked()) {
- m_packet.StartLobbyMessage(LOBBY_REQUEST_GAME_SERVERS);
- PackAddresses(m_packet);
- m_packet.address = m_globalServer;
-
- SDLNet_UDP_Send(gNetFD, -1, &m_packet);
- }
-
-#ifdef LOBBY_BROADCAST
// Get game info for local games
m_packet.StartLobbyMessage(LOBBY_REQUEST_GAME_INFO);
m_packet.Write((Uint32)SDL_GetTicks());
m_packet.address.host = INADDR_BROADCAST;
m_packet.address.port = SDL_Swap16BE(NETPLAY_PORT);
SDLNet_UDP_Send(gNetFD, -1, &m_packet);
-#endif
}
void
@@ -681,17 +617,6 @@ LobbyDialogDelegate::ProcessPacket(DynamicPacket &packet)
}
if (m_state == STATE_HOSTING) {
- if (cmd == LOBBY_ANNOUNCE_PLAYER) {
- if (m_globalGame->IsChecked()) {
- ProcessAnnouncePlayer(packet);
- }
- return;
- }
-
- if (m_game.IsFull() && !m_game.HasNode(packet.address)) {
- return;
- }
-
if (cmd == LOBBY_PING) {
ProcessPing(packet);
} else if (cmd == LOBBY_PONG) {
@@ -707,15 +632,6 @@ LobbyDialogDelegate::ProcessPacket(DynamicPacket &packet)
}
- if (m_state == STATE_LISTING) {
- if (cmd == LOBBY_GAME_SERVERS) {
- if (m_globalGame->IsChecked()) {
- ProcessGameServerList(packet);
- }
- return;
- }
- }
-
// These packets we handle in all the join states
if (cmd == LOBBY_PING) {
ProcessPing(packet);
@@ -818,29 +734,6 @@ LobbyDialogDelegate::ProcessNewGame(DynamicPacket &packet)
}
}
-void
-LobbyDialogDelegate::ProcessAnnouncePlayer(DynamicPacket &packet)
-{
- Uint8 count;
- IPaddress address;
-
- // Open the firewall so this player can contact us.
- m_reply.StartLobbyMessage(LOBBY_OPEN_FIREWALL);
-
- if (!packet.Read(count)) {
- return;
- }
- for (Uint8 i = 0; i < count; ++i) {
- if (!packet.Read(address.host) ||
- !packet.Read(address.port)) {
- return;
- }
- m_reply.address = address;
-
- SDLNet_UDP_Send(gNetFD, -1, &m_reply);
- }
-}
-
void
LobbyDialogDelegate::ProcessRequestGameInfo(DynamicPacket &packet)
{
@@ -985,32 +878,3 @@ LobbyDialogDelegate::ProcessKick(DynamicPacket &packet)
SetState(STATE_LISTING);
}
-
-void
-LobbyDialogDelegate::ProcessGameServerList(DynamicPacket &packet)
-{
- Uint8 serverCount, count;
- IPaddress address;
-
- // Request game information from the servers
- m_reply.StartLobbyMessage(LOBBY_REQUEST_GAME_INFO);
- m_reply.Write((Uint32)SDL_GetTicks());
-
- if (!packet.Read(serverCount)) {
- return;
- }
- for (Uint8 i = 0; i < serverCount; ++i) {
- if (!packet.Read(count)) {
- return;
- }
- for (Uint8 j = 0; j < count; ++j) {
- if (!packet.Read(address.host) ||
- !packet.Read(address.port)) {
- return;
- }
- m_reply.address = address;
-
- SDLNet_UDP_Send(gNetFD, -1, &m_reply);
- }
- }
-}
diff --git a/game/lobby.h b/game/lobby.h
index 434259b6..bc0adaa7 100644
--- a/game/lobby.h
+++ b/game/lobby.h
@@ -61,7 +61,6 @@ class LobbyDialogDelegate : public UIDialogDelegate
void UpdateUI();
void CheckPings();
- void AdvertiseGame();
void RemoveGame();
void GetGameList();
void GetGameInfo();
@@ -77,11 +76,9 @@ class LobbyDialogDelegate : public UIDialogDelegate
void ProcessPing(DynamicPacket &packet);
void ProcessPong(DynamicPacket &packet);
void ProcessNewGame(DynamicPacket &packet);
- void ProcessAnnouncePlayer(DynamicPacket &packet);
void ProcessRequestGameInfo(DynamicPacket &packet);
void ProcessRequestJoin(DynamicPacket &packet);
void ProcessRequestLeave(DynamicPacket &packet);
- void ProcessGameServerList(DynamicPacket &packet);
void ProcessGameInfo(DynamicPacket &packet);
void ProcessKick(DynamicPacket &packet);
@@ -108,7 +105,6 @@ class LobbyDialogDelegate : public UIDialogDelegate
DynamicPacket m_packet, m_reply;
UIElementRadioGroup *m_hostOrJoin;
- UIElementCheckbox *m_globalGame;
UIElementRadioGroup *m_deathmatch;
UIElement *m_gameListArea;
UIElement *m_gameListElements[5];
diff --git a/game/protocol.h b/game/protocol.h
index 58e685fe..f7de2c1e 100644
--- a/game/protocol.h
+++ b/game/protocol.h
@@ -37,67 +37,6 @@
enum LobbyProtocol {
LOBBY_NONE,
- /**********/
- /* Messages between the hosting game and the lobby server */
-
- LOBBY_ANNOUNCE_GAME = 1,
- /* Sent by the hosting game to the lobby server
- This is sent periodically to keep the entry refreshed, since
- the server will automatically age out entries after 30 seconds.
-
- Uint8 numaddresses
- {
- Uint32 host;
- Uint16 port;
- } 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.
-
- Uint8 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
- requesting to join, opening the firewall for them.
-
- Uint8 numaddresses
- {
- Uint32 host;
- Uint16 port;
- } addresses[]
- */
-
- /**********/
- /* Messages between the joining game and the lobby server */
-
- LOBBY_REQUEST_GAME_SERVERS = 10,
- /* Sent by the joining game, containing a list of it's addresses
-
- Uint8 numaddresses
- {
- Uint32 host;
- Uint16 port;
- } addresses[]
- */
-
- LOBBY_GAME_SERVERS,
- /* Sent by the lobby server containing all the current game addresses
-
- Uint8 numaddresses
- {
- Uint32 host;
- Uint16 port;
- } addresses[]
- */
-
/**********/
/* Messages between the joining game and the hosting game */