osb/source/game/StarUniverseServer.hpp
Kae ddab1db73f Server: Log TCP error and carry on
Fixes the Singleplayer server thread locking up forever and leaving us in an eternal loading screen if the TCP socket fails to bind. Something on my PC is hogging the default server port, which is what made me encounter this.
2023-07-26 18:47:23 +10:00

259 lines
9.7 KiB
C++

#ifndef STAR_UNIVERSE_SERVER_HPP
#define STAR_UNIVERSE_SERVER_HPP
#include "StarLockFile.hpp"
#include "StarIdMap.hpp"
#include "StarWorkerPool.hpp"
#include "StarGameTypes.hpp"
#include "StarCelestialCoordinate.hpp"
#include "StarServerClientContext.hpp"
#include "StarWorldServerThread.hpp"
#include "StarSystemWorldServerThread.hpp"
#include "StarUniverseConnection.hpp"
#include "StarUniverseSettings.hpp"
namespace Star {
STAR_CLASS(Clock);
STAR_CLASS(File);
STAR_CLASS(Player);
STAR_CLASS(ChatProcessor);
STAR_CLASS(CommandProcessor);
STAR_CLASS(TeamManager);
STAR_CLASS(UniverseServer);
STAR_CLASS(WorldTemplate);
STAR_CLASS(WorldServer);
STAR_CLASS(UniverseSettings);
STAR_EXCEPTION(UniverseServerException, StarException);
// Manages all running worlds, listens for new client connections and marshalls
// between all the different worlds and all the different client connections
// and routes packets between them.
class UniverseServer : public Thread {
public:
UniverseServer(String const& storageDir);
~UniverseServer();
// If enabled, will listen on the configured server port for incoming
// connections.
void setListeningTcp(bool listenTcp);
// Connects an arbitrary UniverseConnection to this server
void addClient(UniverseConnection remoteConnection);
// Constructs an in-process connection to a UniverseServer for a
// UniverseClient, and returns the other side of the connection.
UniverseConnection addLocalClient();
// Signals the UniverseServer to stop and then joins the thread.
void stop();
void setPause(bool pause);
List<WorldId> activeWorlds() const;
bool isWorldActive(WorldId const& worldId) const;
List<ConnectionId> clientIds() const;
size_t numberOfClients() const;
uint32_t maxClients() const;
bool isConnectedClient(ConnectionId clientId) const;
String clientDescriptor(ConnectionId clientId) const;
String clientNick(ConnectionId clientId) const;
Maybe<ConnectionId> findNick(String const& nick) const;
Maybe<Uuid> uuidForClient(ConnectionId clientId) const;
Maybe<ConnectionId> clientForUuid(Uuid const& uuid) const;
void adminBroadcast(String const& text);
void adminWhisper(ConnectionId clientId, String const& text);
String adminCommand(String text);
bool isAdmin(ConnectionId clientId) const;
bool canBecomeAdmin(ConnectionId clientId) const;
void setAdmin(ConnectionId clientId, bool admin);
bool isLocal(ConnectionId clientId) const;
bool isPvp(ConnectionId clientId) const;
void setPvp(ConnectionId clientId, bool pvp);
RpcThreadPromise<Json> sendWorldMessage(WorldId const& worldId, String const& message, JsonArray const& args = {});
void clientWarpPlayer(ConnectionId clientId, WarpAction action, bool deploy = false);
void clientFlyShip(ConnectionId clientId, Vec3I const& system, SystemLocation const& location);
WorldId clientWorld(ConnectionId clientId) const;
CelestialCoordinate clientShipCoordinate(ConnectionId clientId) const;
ClockPtr universeClock() const;
UniverseSettingsPtr universeSettings() const;
CelestialDatabase& celestialDatabase();
// If the client exists and is in a valid connection state, executes the
// given function on the client world and player object in a thread safe way.
// Returns true if function was called, false if client was not found or in
// an invalid connection state.
bool executeForClient(ConnectionId clientId, function<void(WorldServer*, PlayerPtr)> action);
void disconnectClient(ConnectionId clientId, String const& reason);
void banUser(ConnectionId clientId, String const& reason, pair<bool, bool> banType, Maybe<int> timeout);
bool unbanIp(String const& addressString);
bool unbanUuid(String const& uuidString);
bool updatePlanetType(CelestialCoordinate const& coordinate, String const& newType, String const& weatherBiome);
protected:
virtual void run();
private:
struct TimeoutBan {
int64_t banExpiry;
String reason;
Maybe<HostAddress> ip;
Maybe<Uuid> uuid;
};
enum class TcpState : uint8_t { No, Yes, Fuck };
void processUniverseFlags();
void sendPendingChat();
void updateTeams();
void updateShips();
void sendClockUpdates();
void sendClientContextUpdate(ServerClientContextPtr clientContext);
void sendClientContextUpdates();
void kickErroredPlayers();
void reapConnections();
void processPlanetTypeChanges();
void warpPlayers();
void flyShips();
void arriveShips();
void respondToCelestialRequests();
void processChat();
void clearBrokenWorlds();
void handleWorldMessages();
void shutdownInactiveWorlds();
void doTriggeredStorage();
void saveSettings();
void loadSettings();
// Either returns the default configured starter world, or a new randomized
// starter world, or if a randomized world is not yet available, starts a job
// to find a randomized starter world and returns nothing until it is ready.
Maybe<CelestialCoordinate> nextStarterWorld();
void loadTempWorldIndex();
void saveTempWorldIndex();
String tempWorldFile(InstanceWorldId const& worldId) const;
Maybe<String> isBannedUser(Maybe<HostAddress> hostAddress, Uuid playerUuid) const;
void doTempBan(ConnectionId clientId, String const& reason, pair<bool, bool> banType, int timeout);
void doPermBan(ConnectionId clientId, String const& reason, pair<bool, bool> banType);
void removeTimedBan();
void addCelestialRequests(ConnectionId clientId, List<CelestialRequest> requests);
void worldUpdated(WorldServerThread* worldServer);
void systemWorldUpdated(SystemWorldServerThread* systemWorldServer);
void packetsReceived(UniverseConnectionServer* connectionServer, ConnectionId clientId, List<PacketPtr> packets);
void acceptConnection(UniverseConnection connection, Maybe<HostAddress> remoteAddress);
// Main lock and clients read lock must be held when calling
WarpToWorld resolveWarpAction(WarpAction warpAction, ConnectionId clientId, bool deploy) const;
// Main lock and clients write lock must be held when calling
void doDisconnection(ConnectionId clientId, String const& reason);
// Clients read lock must be held when calling
Maybe<ConnectionId> getClientForUuid(Uuid const& uuid) const;
// Get the world only if it is already loaded, Main lock must be held when
// calling.
WorldServerThreadPtr getWorld(WorldId const& worldId);
// If the world is not created, block and load it, otherwise just return the
// loaded world. Main lock and Clients read lock must be held when calling.
WorldServerThreadPtr createWorld(WorldId const& worldId);
// Trigger off-thread world creation, returns a value when the creation is
// finished, either successfully or with an error. Main lock and Clients
// read lock must be held when calling.
Maybe<WorldServerThreadPtr> triggerWorldCreation(WorldId const& worldId);
// Main lock and clients read lock must be held when calling world promise
// generators
Maybe<WorkerPoolPromise<WorldServerThreadPtr>> makeWorldPromise(WorldId const& worldId);
Maybe<WorkerPoolPromise<WorldServerThreadPtr>> shipWorldPromise(ClientShipWorldId const& uuid);
Maybe<WorkerPoolPromise<WorldServerThreadPtr>> celestialWorldPromise(CelestialWorldId const& coordinate);
Maybe<WorkerPoolPromise<WorldServerThreadPtr>> instanceWorldPromise(InstanceWorldId const& instanceWorld);
// If the system world is not created, initialize it, otherwise return the
// already initialized one
SystemWorldServerThreadPtr createSystemWorld(Vec3I const& location);
bool instanceWorldStoredOrActive(InstanceWorldId const& worldId) const;
// Signal that a world either failed to load, or died due to an exception,
// kicks clients if that world is a ship world. Main lock and clients read
// lock must be held when calling.
void worldDiedWithError(WorldId world);
// Get SkyParameters if the coordinate is a valid world, and empty
// SkyParameters otherwise.
SkyParameters celestialSkyParameters(CelestialCoordinate const& coordinate) const;
mutable RecursiveMutex m_mainLock;
String m_storageDirectory;
ByteArray m_assetsDigest;
Maybe<LockFile> m_storageDirectoryLock;
StringMap<StringList> m_speciesShips;
CelestialMasterDatabasePtr m_celestialDatabase;
ClockPtr m_universeClock;
UniverseSettingsPtr m_universeSettings;
WorkerPool m_workerPool;
int64_t m_storageTriggerDeadline;
int64_t m_clearBrokenWorldsDeadline;
int64_t m_lastClockUpdateSent;
atomic<bool> m_stop;
atomic<TcpState> m_tcpState;
mutable ReadersWriterMutex m_clientsLock;
unsigned m_maxPlayers;
IdMap<ConnectionId, ServerClientContextPtr> m_clients;
shared_ptr<atomic<bool>> m_pause;
Map<WorldId, Maybe<WorkerPoolPromise<WorldServerThreadPtr>>> m_worlds;
Map<InstanceWorldId, pair<int64_t, int64_t>> m_tempWorldIndex;
Map<Vec3I, SystemWorldServerThreadPtr> m_systemWorlds;
UniverseConnectionServerPtr m_connectionServer;
List<ThreadFunction<void>> m_connectionAcceptThreads;
LinkedList<pair<UniverseConnection, int64_t>> m_deadConnections;
ChatProcessorPtr m_chatProcessor;
CommandProcessorPtr m_commandProcessor;
TeamManagerPtr m_teamManager;
HashMap<ConnectionId, pair<WarpAction, bool>> m_pendingPlayerWarps;
HashMap<ConnectionId, pair<pair<Vec3I, SystemLocation>, Maybe<double>>> m_queuedFlights;
HashMap<ConnectionId, pair<Vec3I, SystemLocation>> m_pendingFlights;
HashMap<ConnectionId, CelestialCoordinate> m_pendingArrivals;
HashMap<ConnectionId, String> m_pendingDisconnections;
HashMap<ConnectionId, List<WorkerPoolPromise<CelestialResponse>>> m_pendingCelestialRequests;
List<pair<WorldId, UniverseFlagAction>> m_pendingFlagActions;
HashMap<ConnectionId, List<pair<String, ChatSendMode>>> m_pendingChat;
Maybe<WorkerPoolPromise<CelestialCoordinate>> m_nextRandomizedStarterWorld;
Map<WorldId, List<WorldServerThread::Message>> m_pendingWorldMessages;
List<TimeoutBan> m_tempBans;
};
}
#endif