1 /* Copyright (C) 2017 Wildfire Games. 2 * This file is part of 0 A.D. 3 * 4 * 0 A.D. is free software: you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation, either version 2 of the License, or 7 * (at your option) any later version. 8 * 9 * 0 A.D. is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with 0 A.D. If not, see <http://www.gnu.org/licenses/>. 16 */ 17 18 #ifndef NETSERVER_H 19 #define NETSERVER_H 20 21 #include "NetFileTransfer.h" 22 #include "NetHost.h" 23 #include "lib/config2.h" 24 #include "lib/types.h" 25 #include "ps/ThreadUtil.h" 26 #include "scriptinterface/ScriptTypes.h" 27 28 #include <string> 29 #include <utility> 30 #include <vector> 31 32 class CNetServerSession; 33 class CNetServerTurnManager; 34 class CFsmEvent; 35 class ScriptInterface; 36 class CPlayerAssignmentMessage; 37 class CNetStatsTable; 38 class CSimulationMessage; 39 40 class CNetServerWorker; 41 42 enum NetServerState 43 { 44 // We haven't opened the port yet, we're just setting some stuff up. 45 // The worker thread has not been started. 46 SERVER_STATE_UNCONNECTED, 47 48 // The server is open and accepting connections. This is the screen where 49 // rules are set up by the operator and where players join and select civs 50 // and stuff. 51 SERVER_STATE_PREGAME, 52 53 // All the hosts are connected and are loading the game 54 SERVER_STATE_LOADING, 55 56 // The one with all the killing ;-) 57 SERVER_STATE_INGAME, 58 59 // The game is over and someone has won. Players might linger to chat or 60 // download the replay log. 61 SERVER_STATE_POSTGAME 62 }; 63 64 /** 65 * Server session representation of client state 66 */ 67 enum NetServerSessionState 68 { 69 // The client has disconnected or been disconnected 70 NSS_UNCONNECTED, 71 72 // The client has just connected and we're waiting for its handshake message, 73 // to agree on the protocol version 74 NSS_HANDSHAKE, 75 76 // The client has handshook and we're waiting for its lobby authentication message 77 NSS_LOBBY_AUTHENTICATE, 78 79 // The client has handshook and we're waiting for its authentication message, 80 // to find its name and check its password etc 81 NSS_AUTHENTICATE, 82 83 // The client has fully joined, and is in the pregame setup stage 84 // or is loading the game. 85 // Server must be in SERVER_STATE_PREGAME or SERVER_STATE_LOADING. 86 NSS_PREGAME, 87 88 // The client has authenticated but the game was already started, 89 // so it's synchronising with the game state from other clients 90 NSS_JOIN_SYNCING, 91 92 // The client is running the game. 93 // Server must be in SERVER_STATE_LOADING or SERVER_STATE_INGAME. 94 NSS_INGAME 95 }; 96 97 /** 98 * Network server interface. Handles all the coordination between players. 99 * One person runs this object, and every player (including the host) connects their CNetClient to it. 100 * 101 * The actual work is performed by CNetServerWorker in a separate thread. 102 */ 103 class CNetServer 104 { 105 NONCOPYABLE(CNetServer); 106 public: 107 /** 108 * Construct a new network server. 109 * @param autostartPlayers if positive then StartGame will be called automatically 110 * once this many players are connected (intended for the command-line testing mode). 111 */ 112 CNetServer(bool useLobbyAuth = false, int autostartPlayers = -1); 113 114 ~CNetServer(); 115 116 /** 117 * Begin listening for network connections. 118 * This function is synchronous (it won't return until the connection is established). 119 * @return true on success, false on error (e.g. port already in use) 120 */ 121 bool SetupConnection(const u16 port); 122 123 /** 124 * Call from the GUI to asynchronously notify all clients that they should start loading the game. 125 */ 126 void StartGame(); 127 128 /** 129 * Call from the GUI to update the game setup attributes. 130 * This must be called at least once before starting the game. 131 * The changes will be asynchronously propagated to all clients. 132 * @param attrs game attributes, in the script context of scriptInterface 133 */ 134 void UpdateGameAttributes(JS::MutableHandleValue attrs, const ScriptInterface& scriptInterface); 135 136 /** 137 * Set the turn length to a fixed value. 138 * TODO: we should replace this with some adapative lag-dependent computation. 139 */ 140 void SetTurnLength(u32 msecs); 141 142 bool UseLobbyAuth() const; 143 144 void OnLobbyAuth(const CStr& name, const CStr& token); 145 146 void SendHolePunchingMessage(const CStr& ip, u16 port); 147 148 private: 149 CNetServerWorker* m_Worker; 150 const bool m_LobbyAuth; 151 }; 152 153 /** 154 * Network server worker thread. 155 * (This is run in a thread so that client/server communication is not delayed 156 * by the host player's framerate - the only delay should be the network latency.) 157 * 158 * Thread-safety: 159 * - SetupConnection and constructor/destructor must be called from the main thread. 160 * - The main thread may push commands onto the Queue members, 161 * while holding the m_WorkerMutex lock. 162 * - Public functions (SendMessage, Broadcast) must be called from the network 163 * server thread. 164 */ 165 class CNetServerWorker 166 { 167 NONCOPYABLE(CNetServerWorker); 168 169 public: 170 // Public functions for CNetSession/CNetServerTurnManager to use: 171 172 /** 173 * Send a message to the given network peer. 174 */ 175 bool SendMessage(ENetPeer* peer, const CNetMessage* message); 176 177 /** 178 * Disconnects a player from gamesetup or session. 179 */ 180 void KickPlayer(const CStrW& playerName, const bool ban); 181 182 /** 183 * Send a message to all clients who match one of the given states. 184 */ 185 bool Broadcast(const CNetMessage* message, const std::vector<NetServerSessionState>& targetStates); 186 187 private: 188 friend class CNetServer; 189 friend class CNetFileReceiveTask_ServerRejoin; 190 191 CNetServerWorker(bool useLobbyAuth, int autostartPlayers); 192 ~CNetServerWorker(); 193 194 /** 195 * Begin listening for network connections. 196 * @return true on success, false on error (e.g. port already in use) 197 */ 198 bool SetupConnection(const u16 port); 199 200 /** 201 * Call from the GUI to update the player assignments. 202 * The given GUID will be (re)assigned to the given player ID. 203 * Any player currently using that ID will be unassigned. 204 * The changes will be propagated to all clients. 205 */ 206 void AssignPlayer(int playerID, const CStr& guid); 207 208 /** 209 * Call from the GUI to notify all clients that they should start loading the game. 210 */ 211 void StartGame(); 212 213 /** 214 * Call from the GUI to update the game setup attributes. 215 * This must be called at least once before starting the game. 216 * The changes will be propagated to all clients. 217 * @param attrs game attributes, in the script context of GetScriptInterface() 218 */ 219 void UpdateGameAttributes(JS::MutableHandleValue attrs); 220 221 /** 222 * Make a player name 'nicer' by limiting the length and removing forbidden characters etc. 223 */ 224 static CStrW SanitisePlayerName(const CStrW& original); 225 226 /** 227 * Make a player name unique, if it matches any existing session's name. 228 */ 229 CStrW DeduplicatePlayerName(const CStrW& original); 230 231 /** 232 * Get the script context used for game attributes. 233 */ 234 const ScriptInterface& GetScriptInterface(); 235 236 /** 237 * Set the turn length to a fixed value. 238 * TODO: we should replace this with some adaptive lag-dependent computation. 239 */ 240 void SetTurnLength(u32 msecs); 241 242 void ProcessLobbyAuth(const CStr& name, const CStr& token); 243 244 void AddPlayer(const CStr& guid, const CStrW& name); 245 void RemovePlayer(const CStr& guid); 246 void SendPlayerAssignments(); 247 void ClearAllPlayerReady(); 248 249 void SetupSession(CNetServerSession* session); 250 bool HandleConnect(CNetServerSession* session); 251 252 void OnUserJoin(CNetServerSession* session); 253 void OnUserLeave(CNetServerSession* session); 254 255 static bool OnClientHandshake(void* context, CFsmEvent* event); 256 static bool OnAuthenticate(void* context, CFsmEvent* event); 257 static bool OnInGame(void* context, CFsmEvent* event); 258 static bool OnChat(void* context, CFsmEvent* event); 259 static bool OnReady(void* context, CFsmEvent* event); 260 static bool OnClearAllReady(void* context, CFsmEvent* event); 261 static bool OnGameSetup(void* context, CFsmEvent* event); 262 static bool OnAssignPlayer(void* context, CFsmEvent* event); 263 static bool OnStartGame(void* context, CFsmEvent* event); 264 static bool OnLoadedGame(void* context, CFsmEvent* event); 265 static bool OnJoinSyncingLoadedGame(void* context, CFsmEvent* event); 266 static bool OnRejoined(void* context, CFsmEvent* event); 267 static bool OnKickPlayer(void* context, CFsmEvent* event); 268 static bool OnDisconnect(void* context, CFsmEvent* event); 269 static bool OnClientPaused(void* context, CFsmEvent* event); 270 271 /** 272 * Checks if all clients have finished loading. 273 * If so informs the clients about that and change the server state. 274 * 275 * Returns if all clients finished loading. 276 */ 277 bool CheckGameLoadStatus(CNetServerSession* changedSession); 278 279 void ConstructPlayerAssignmentMessage(CPlayerAssignmentMessage& message); 280 281 void HandleMessageReceive(const CNetMessage* message, CNetServerSession* session); 282 283 /** 284 * Send a network warning if the connection to a client is being lost or has bad latency. 285 */ 286 void CheckClientConnections(); 287 288 void SendHolePunchingMessage(const CStr& ip, u16 port); 289 290 /** 291 * Internal script context for (de)serializing script messages, 292 * and for storing game attributes. 293 * (TODO: we shouldn't bother deserializing (except for debug printing of messages), 294 * we should just forward messages blindly and efficiently.) 295 */ 296 ScriptInterface* m_ScriptInterface; 297 298 PlayerAssignmentMap m_PlayerAssignments; 299 300 /** 301 * Stores the most current game attributes. 302 */ 303 JS::PersistentRootedValue m_GameAttributes; 304 305 int m_AutostartPlayers; 306 307 /** 308 * Whether this match requires lobby authentication. 309 */ 310 const bool m_LobbyAuth; 311 312 ENetHost* m_Host; 313 std::vector<CNetServerSession*> m_Sessions; 314 315 CNetStatsTable* m_Stats; 316 317 NetServerState m_State; 318 319 CStrW m_ServerName; 320 321 std::vector<u32> m_BannedIPs; 322 std::vector<CStrW> m_BannedPlayers; 323 324 /** 325 * Holds the GUIDs of all currently paused players. 326 */ 327 std::vector<CStr> m_PausingPlayers; 328 329 u32 m_NextHostID; 330 331 CNetServerTurnManager* m_ServerTurnManager; 332 333 CStr m_HostGUID; 334 335 /** 336 * A copy of all simulation commands received so far, indexed by 337 * turn number, to simplify support for rejoining etc. 338 * TODO: verify this doesn't use too much RAM. 339 */ 340 std::vector<std::vector<CSimulationMessage>> m_SavedCommands; 341 342 /** 343 * The latest copy of the simulation state, received from an existing 344 * client when a new client has asked to rejoin the game. 345 */ 346 std::string m_JoinSyncFile; 347 348 /** 349 * Time when the clients connections were last checked for timeouts and latency. 350 */ 351 std::time_t m_LastConnectionCheck; 352 353 private: 354 // Thread-related stuff: 355 356 #if CONFIG2_MINIUPNPC 357 /** 358 * Try to find a UPnP root on the network and setup port forwarding. 359 */ 360 static void* SetupUPnP(void*); 361 pthread_t m_UPnPThread; 362 #endif 363 364 static void* RunThread(void* data); 365 void Run(); 366 bool RunStep(); 367 368 pthread_t m_WorkerThread; 369 CMutex m_WorkerMutex; 370 371 // protected by m_WorkerMutex 372 bool m_Shutdown; 373 374 // Queues for messages sent by the game thread (protected by m_WorkerMutex): 375 std::vector<bool> m_StartGameQueue; 376 std::vector<std::string> m_GameAttributesQueue; 377 std::vector<std::pair<CStr, CStr>> m_LobbyAuthQueue; 378 std::vector<u32> m_TurnLengthQueue; 379 }; 380 381 /// Global network server for the standard game 382 extern CNetServer *g_NetServer; 383 384 #endif // NETSERVER_H 385