1 /* Copyright (C) 2022 Wildfire Games.
2 * This file is part of 0 A.D.
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.
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.
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/>.
21 #include "NetFileTransfer.h"
23 #include "lib/config2.h"
24 #include "lib/types.h"
25 #include "scriptinterface/ScriptTypes.h"
31 #include <unordered_map>
35 class CNetServerSession
;
36 class CNetServerTurnManager
;
38 class CPlayerAssignmentMessage
;
40 class CSimulationMessage
;
41 class ScriptInterface
;
44 class CNetServerWorker
;
48 // We haven't opened the port yet, we're just setting some stuff up.
49 // The worker thread has not been started.
50 SERVER_STATE_UNCONNECTED
,
52 // The server is open and accepting connections. This is the screen where
53 // rules are set up by the operator and where players join and select civs
57 // All the hosts are connected and are loading the game
60 // The one with all the killing ;-)
63 // The game is over and someone has won. Players might linger to chat or
64 // download the replay log.
69 * Server session representation of client state
71 enum NetServerSessionState
73 // The client has disconnected or been disconnected
76 // The client has just connected and we're waiting for its handshake message,
77 // to agree on the protocol version
80 // The client has handshook and we're waiting for its lobby authentication message
81 NSS_LOBBY_AUTHENTICATE
,
83 // The client has handshook and we're waiting for its authentication message,
84 // to find its name and check its password etc
87 // The client has fully joined, and is in the pregame setup stage
88 // or is loading the game.
89 // Server must be in SERVER_STATE_PREGAME or SERVER_STATE_LOADING.
92 // The client has authenticated but the game was already started,
93 // so it's synchronising with the game state from other clients
96 // The client is running the game.
97 // Server must be in SERVER_STATE_LOADING or SERVER_STATE_INGAME.
102 * Network server interface. Handles all the coordination between players.
103 * One person runs this object, and every player (including the host) connects their CNetClient to it.
105 * The actual work is performed by CNetServerWorker in a separate thread.
109 NONCOPYABLE(CNetServer
);
112 * Construct a new network server.
113 * once this many players are connected (intended for the command-line testing mode).
115 CNetServer(bool useLobbyAuth
= false);
120 * Begin listening for network connections.
121 * This function is synchronous (it won't return until the connection is established).
122 * @return true on success, false on error (e.g. port already in use)
124 bool SetupConnection(const u16 port
);
127 * Call from the GUI to asynchronously notify all clients that they should start loading the game.
128 * UpdateInitAttributes must be called at least once.
133 * Call from the GUI to update the game setup attributes.
134 * The changes won't be propagated to clients until game start.
135 * @param attrs init attributes, in the script context of rq
137 void UpdateInitAttributes(JS::MutableHandleValue attrs
, const ScriptRequest
& rq
);
140 * Set the turn length to a fixed value.
141 * TODO: we should replace this with some adapative lag-dependent computation.
143 void SetTurnLength(u32 msecs
);
145 bool UseLobbyAuth() const;
147 void OnLobbyAuth(const CStr
& name
, const CStr
& token
);
149 void SendHolePunchingMessage(const CStr
& ip
, u16 port
);
151 void SetConnectionData(const CStr
& ip
, u16 port
);
152 bool SetConnectionDataViaSTUN();
154 bool GetUseSTUN() const;
157 * Return the externally accessible IP.
159 CStr
GetPublicIp() const;
162 * Return the externally accessible port.
164 u16
GetPublicPort() const;
167 * Return the serving port on the local machine.
169 u16
GetLocalPort() const;
172 * Check if password is valid. If is not, increase number of failed attempts of the lobby user.
173 * This is used without established direct session with the client, to prevent brute force attacks
174 * when guessing password trying to get connection data from the host.
175 * @return true iff password is valid
177 bool CheckPasswordAndIncrement(const std::string
& username
, const std::string
& password
, const std::string
& salt
);
180 * Check if user reached certain number of failed attempts.
181 * @see m_BanAfterNumberOfTries
182 * @see CheckPasswordAndBan
184 bool IsBanned(const std::string
& username
) const;
186 void SetPassword(const CStr
& password
);
188 void SetControllerSecret(const std::string
& secret
);
191 CNetServerWorker
* m_Worker
;
192 const bool m_LobbyAuth
;
197 std::unordered_map
<std::string
, int> m_FailedAttempts
;
201 * Network server worker thread.
202 * (This is run in a thread so that client/server communication is not delayed
203 * by the host player's framerate - the only delay should be the network latency.)
206 * - SetupConnection and constructor/destructor must be called from the main thread.
207 * - The main thread may push commands onto the Queue members,
208 * while holding the m_WorkerMutex lock.
209 * - Public functions (SendMessage, Broadcast) must be called from the network
212 class CNetServerWorker
214 NONCOPYABLE(CNetServerWorker
);
217 // Public functions for CNetSession/CNetServerTurnManager to use:
220 * Send a message to the given network peer.
222 bool SendMessage(ENetPeer
* peer
, const CNetMessage
* message
);
225 * Disconnects a player from gamesetup or session.
227 void KickPlayer(const CStrW
& playerName
, const bool ban
);
230 * Send a message to all clients who match one of the given states.
232 bool Broadcast(const CNetMessage
* message
, const std::vector
<NetServerSessionState
>& targetStates
);
235 friend class CNetServer
;
236 friend class CNetFileReceiveTask_ServerRejoin
;
238 CNetServerWorker(bool useLobbyAuth
);
241 bool CheckPassword(const std::string
& password
, const std::string
& salt
) const;
243 void SetPassword(const CStr
& hashedPassword
);
245 void SetControllerSecret(const std::string
& secret
);
248 * Begin listening for network connections.
249 * @return true on success, false on error (e.g. port already in use)
251 bool SetupConnection(const u16 port
);
254 * The given GUID will be (re)assigned to the given player ID.
255 * Any player currently using that ID will be unassigned.
257 void AssignPlayer(int playerID
, const CStr
& guid
);
260 * Switch in game mode and notify all clients to start the game.
262 void StartGame(const CStr
& initAttribs
);
265 * Make a player name 'nicer' by limiting the length and removing forbidden characters etc.
267 static CStrW
SanitisePlayerName(const CStrW
& original
);
270 * Make a player name unique, if it matches any existing session's name.
272 CStrW
DeduplicatePlayerName(const CStrW
& original
);
275 * Get the script context used for init attributes.
277 const ScriptInterface
& GetScriptInterface();
280 * Set the turn length to a fixed value.
281 * TODO: we should replace this with some adaptive lag-dependent computation.
283 void SetTurnLength(u32 msecs
);
285 void ProcessLobbyAuth(const CStr
& name
, const CStr
& token
);
287 void AddPlayer(const CStr
& guid
, const CStrW
& name
);
288 void RemovePlayer(const CStr
& guid
);
289 void SendPlayerAssignments();
290 void ClearAllPlayerReady();
292 void SetupSession(CNetServerSession
* session
);
293 bool HandleConnect(CNetServerSession
* session
);
295 void OnUserJoin(CNetServerSession
* session
);
296 void OnUserLeave(CNetServerSession
* session
);
298 static bool OnClientHandshake(void* context
, CFsmEvent
* event
);
299 static bool OnAuthenticate(void* context
, CFsmEvent
* event
);
300 static bool OnSimulationCommand(void* context
, CFsmEvent
* event
);
301 static bool OnSyncCheck(void* context
, CFsmEvent
* event
);
302 static bool OnEndCommandBatch(void* context
, CFsmEvent
* event
);
303 static bool OnChat(void* context
, CFsmEvent
* event
);
304 static bool OnReady(void* context
, CFsmEvent
* event
);
305 static bool OnClearAllReady(void* context
, CFsmEvent
* event
);
306 static bool OnGameSetup(void* context
, CFsmEvent
* event
);
307 static bool OnAssignPlayer(void* context
, CFsmEvent
* event
);
308 static bool OnGameStart(void* context
, CFsmEvent
* event
);
309 static bool OnLoadedGame(void* context
, CFsmEvent
* event
);
310 static bool OnJoinSyncingLoadedGame(void* context
, CFsmEvent
* event
);
311 static bool OnRejoined(void* context
, CFsmEvent
* event
);
312 static bool OnKickPlayer(void* context
, CFsmEvent
* event
);
313 static bool OnDisconnect(void* context
, CFsmEvent
* event
);
314 static bool OnClientPaused(void* context
, CFsmEvent
* event
);
317 * Checks if all clients have finished loading.
318 * If so informs the clients about that and change the server state.
320 * Returns if all clients finished loading.
322 bool CheckGameLoadStatus(CNetServerSession
* changedSession
);
324 void ConstructPlayerAssignmentMessage(CPlayerAssignmentMessage
& message
);
326 void HandleMessageReceive(const CNetMessage
* message
, CNetServerSession
* session
);
329 * Send a network warning if the connection to a client is being lost or has bad latency.
331 void CheckClientConnections();
333 void SendHolePunchingMessage(const CStr
& ip
, u16 port
);
336 * Internal script context for (de)serializing script messages,
337 * and for storing init attributes.
338 * (TODO: we shouldn't bother deserializing (except for debug printing of messages),
339 * we should just forward messages blindly and efficiently.)
341 ScriptInterface
* m_ScriptInterface
;
343 PlayerAssignmentMap m_PlayerAssignments
;
346 * Stores the most current init attributes.
347 * NB: this is not guaranteed to be up-to-date until the server is LOADING or INGAME.
348 * At that point, the settings are frozen and ought to be identical to the simulation Init Attributes.
350 JS::PersistentRootedValue m_InitAttributes
;
353 * Whether this match requires lobby authentication.
355 const bool m_LobbyAuth
;
358 std::vector
<CNetServerSession
*> m_Sessions
;
360 CNetStatsTable
* m_Stats
;
362 NetServerState m_State
;
366 std::vector
<u32
> m_BannedIPs
;
367 std::vector
<CStrW
> m_BannedPlayers
;
372 * Holds the GUIDs of all currently paused players.
374 std::vector
<CStr
> m_PausingPlayers
;
378 CNetServerTurnManager
* m_ServerTurnManager
;
381 * The GUID of the client in control of the game (the 'host' from the players' perspective).
383 CStr m_ControllerGUID
;
386 * The 'secret' used to identify the controller of the game.
388 std::string m_ControllerSecret
;
391 * A copy of all simulation commands received so far, indexed by
392 * turn number, to simplify support for rejoining etc.
393 * TODO: verify this doesn't use too much RAM.
395 std::vector
<std::vector
<CSimulationMessage
>> m_SavedCommands
;
398 * The latest copy of the simulation state, received from an existing
399 * client when a new client has asked to rejoin the game.
401 std::string m_JoinSyncFile
;
404 * Time when the clients connections were last checked for timeouts and latency.
406 std::time_t m_LastConnectionCheck
;
409 // Thread-related stuff:
411 #if CONFIG2_MINIUPNPC
413 * Try to find a UPnP root on the network and setup port forwarding.
415 static void SetupUPnP();
416 std::thread m_UPnPThread
;
419 static void RunThread(CNetServerWorker
* data
);
423 std::thread m_WorkerThread
;
424 std::mutex m_WorkerMutex
;
426 // protected by m_WorkerMutex
429 // Queues for messages sent by the game thread (protected by m_WorkerMutex):
430 std::vector
<bool> m_StartGameQueue
;
431 std::vector
<std::string
> m_InitAttributesQueue
;
432 std::vector
<std::pair
<CStr
, CStr
>> m_LobbyAuthQueue
;
433 std::vector
<u32
> m_TurnLengthQueue
;
436 /// Global network server for the standard game
437 extern CNetServer
*g_NetServer
;
439 #endif // NETSERVER_H