Pyrogenesis  trunk
NetServer.h
Go to the documentation of this file.
1 /* Copyright (C) 2022 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"
26 
27 #include <ctime>
28 #include <mutex>
29 #include <string>
30 #include <utility>
31 #include <unordered_map>
32 #include <vector>
33 #include <thread>
34 
35 class CNetServerSession;
37 class CFsmEvent;
38 class CPlayerAssignmentMessage;
39 class CNetStatsTable;
40 class CSimulationMessage;
41 class ScriptInterface;
42 class ScriptRequest;
43 
44 class CNetServerWorker;
45 
47 {
48  // We haven't opened the port yet, we're just setting some stuff up.
49  // The worker thread has not been started.
51 
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
54  // and stuff.
56 
57  // All the hosts are connected and are loading the game
59 
60  // The one with all the killing ;-)
62 
63  // The game is over and someone has won. Players might linger to chat or
64  // download the replay log.
66 };
67 
68 /**
69  * Server session representation of client state
70  */
72 {
73  // The client has disconnected or been disconnected
75 
76  // The client has just connected and we're waiting for its handshake message,
77  // to agree on the protocol version
79 
80  // The client has handshook and we're waiting for its lobby authentication message
82 
83  // The client has handshook and we're waiting for its authentication message,
84  // to find its name and check its password etc
86 
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.
91 
92  // The client has authenticated but the game was already started,
93  // so it's synchronising with the game state from other clients
95 
96  // The client is running the game.
97  // Server must be in SERVER_STATE_LOADING or SERVER_STATE_INGAME.
99 };
100 
101 /**
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.
104  *
105  * The actual work is performed by CNetServerWorker in a separate thread.
106  */
108 {
110 public:
111  /**
112  * Construct a new network server.
113  * once this many players are connected (intended for the command-line testing mode).
114  */
115  CNetServer(bool useLobbyAuth = false);
116 
117  ~CNetServer();
118 
119  /**
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)
123  */
124  bool SetupConnection(const u16 port);
125 
126  /**
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.
129  */
130  void StartGame();
131 
132  /**
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
136  */
137  void UpdateInitAttributes(JS::MutableHandleValue attrs, const ScriptRequest& rq);
138 
139  /**
140  * Set the turn length to a fixed value.
141  * TODO: we should replace this with some adapative lag-dependent computation.
142  */
143  void SetTurnLength(u32 msecs);
144 
145  bool UseLobbyAuth() const;
146 
147  void OnLobbyAuth(const CStr& name, const CStr& token);
148 
149  void SendHolePunchingMessage(const CStr& ip, u16 port);
150 
151  void SetConnectionData(const CStr& ip, u16 port);
153 
154  bool GetUseSTUN() const;
155 
156  /**
157  * Return the externally accessible IP.
158  */
159  CStr GetPublicIp() const;
160 
161  /**
162  * Return the externally accessible port.
163  */
164  u16 GetPublicPort() const;
165 
166  /**
167  * Return the serving port on the local machine.
168  */
169  u16 GetLocalPort() const;
170 
171  /**
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
176  */
177  bool CheckPasswordAndIncrement(const std::string& username, const std::string& password, const std::string& salt);
178 
179  /**
180  * Check if user reached certain number of failed attempts.
181  * @see m_BanAfterNumberOfTries
182  * @see CheckPasswordAndBan
183  */
184  bool IsBanned(const std::string& username) const;
185 
186  void SetPassword(const CStr& password);
187 
188  void SetControllerSecret(const std::string& secret);
189 
190 private:
192  const bool m_LobbyAuth;
193  bool m_UseSTUN;
197  std::unordered_map<std::string, int> m_FailedAttempts;
198 };
199 
200 /**
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.)
204  *
205  * Thread-safety:
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
210  * server thread.
211  */
213 {
215 
216 public:
217  // Public functions for CNetSession/CNetServerTurnManager to use:
218 
219  /**
220  * Send a message to the given network peer.
221  */
222  bool SendMessage(ENetPeer* peer, const CNetMessage* message);
223 
224  /**
225  * Disconnects a player from gamesetup or session.
226  */
227  void KickPlayer(const CStrW& playerName, const bool ban);
228 
229  /**
230  * Send a message to all clients who match one of the given states.
231  */
232  bool Broadcast(const CNetMessage* message, const std::vector<NetServerSessionState>& targetStates);
233 
234 private:
235  friend class CNetServer;
237 
238  CNetServerWorker(bool useLobbyAuth);
239  ~CNetServerWorker();
240 
241  bool CheckPassword(const std::string& password, const std::string& salt) const;
242 
243  void SetPassword(const CStr& hashedPassword);
244 
245  void SetControllerSecret(const std::string& secret);
246 
247  /**
248  * Begin listening for network connections.
249  * @return true on success, false on error (e.g. port already in use)
250  */
251  bool SetupConnection(const u16 port);
252 
253  /**
254  * The given GUID will be (re)assigned to the given player ID.
255  * Any player currently using that ID will be unassigned.
256  */
257  void AssignPlayer(int playerID, const CStr& guid);
258 
259  /**
260  * Switch in game mode and notify all clients to start the game.
261  */
262  void StartGame(const CStr& initAttribs);
263 
264  /**
265  * Make a player name 'nicer' by limiting the length and removing forbidden characters etc.
266  */
267  static CStrW SanitisePlayerName(const CStrW& original);
268 
269  /**
270  * Make a player name unique, if it matches any existing session's name.
271  */
272  CStrW DeduplicatePlayerName(const CStrW& original);
273 
274  /**
275  * Get the script context used for init attributes.
276  */
277  const ScriptInterface& GetScriptInterface();
278 
279  /**
280  * Set the turn length to a fixed value.
281  * TODO: we should replace this with some adaptive lag-dependent computation.
282  */
283  void SetTurnLength(u32 msecs);
284 
285  void ProcessLobbyAuth(const CStr& name, const CStr& token);
286 
287  void AddPlayer(const CStr& guid, const CStrW& name);
288  void RemovePlayer(const CStr& guid);
289  void SendPlayerAssignments();
290  void ClearAllPlayerReady();
291 
292  void SetupSession(CNetServerSession* session);
293  bool HandleConnect(CNetServerSession* session);
294 
295  void OnUserJoin(CNetServerSession* session);
296  void OnUserLeave(CNetServerSession* session);
297 
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);
315 
316  /**
317  * Checks if all clients have finished loading.
318  * If so informs the clients about that and change the server state.
319  *
320  * Returns if all clients finished loading.
321  */
322  bool CheckGameLoadStatus(CNetServerSession* changedSession);
323 
324  void ConstructPlayerAssignmentMessage(CPlayerAssignmentMessage& message);
325 
326  void HandleMessageReceive(const CNetMessage* message, CNetServerSession* session);
327 
328  /**
329  * Send a network warning if the connection to a client is being lost or has bad latency.
330  */
331  void CheckClientConnections();
332 
333  void SendHolePunchingMessage(const CStr& ip, u16 port);
334 
335  /**
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.)
340  */
342 
344 
345  /**
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.
349  */
350  JS::PersistentRootedValue m_InitAttributes;
351 
352  /**
353  * Whether this match requires lobby authentication.
354  */
355  const bool m_LobbyAuth;
356 
358  std::vector<CNetServerSession*> m_Sessions;
359 
361 
363 
365 
366  std::vector<u32> m_BannedIPs;
367  std::vector<CStrW> m_BannedPlayers;
368 
370 
371  /**
372  * Holds the GUIDs of all currently paused players.
373  */
374  std::vector<CStr> m_PausingPlayers;
375 
377 
379 
380  /**
381  * The GUID of the client in control of the game (the 'host' from the players' perspective).
382  */
384 
385  /**
386  * The 'secret' used to identify the controller of the game.
387  */
388  std::string m_ControllerSecret;
389 
390  /**
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.
394  */
395  std::vector<std::vector<CSimulationMessage>> m_SavedCommands;
396 
397  /**
398  * The latest copy of the simulation state, received from an existing
399  * client when a new client has asked to rejoin the game.
400  */
401  std::string m_JoinSyncFile;
402 
403  /**
404  * Time when the clients connections were last checked for timeouts and latency.
405  */
407 
408 private:
409  // Thread-related stuff:
410 
411 #if CONFIG2_MINIUPNPC
412  /**
413  * Try to find a UPnP root on the network and setup port forwarding.
414  */
415  static void SetupUPnP();
416  std::thread m_UPnPThread;
417 #endif
418 
419  static void RunThread(CNetServerWorker* data);
420  void Run();
421  bool RunStep();
422 
423  std::thread m_WorkerThread;
424  std::mutex m_WorkerMutex;
425 
426  // protected by m_WorkerMutex
428 
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;
434 };
435 
436 /// Global network server for the standard game
437 extern CNetServer *g_NetServer;
438 
439 #endif // NETSERVER_H
void UpdateInitAttributes(JS::MutableHandleValue attrs, const ScriptRequest &rq)
Call from the GUI to update the game setup attributes.
Definition: NetServer.cpp:1723
void SetControllerSecret(const std::string &secret)
Definition: NetServer.cpp:1711
std::thread m_UPnPThread
Definition: NetServer.h:416
ScriptInterface * m_ScriptInterface
Internal script context for (de)serializing script messages, and for storing init attributes...
Definition: NetServer.h:341
void StartGame()
Call from the GUI to asynchronously notify all clients that they should start loading the game...
Definition: NetServer.cpp:1717
std::map< CStr, PlayerAssignment > PlayerAssignmentMap
Definition: NetHost.h:54
CStr m_ControllerGUID
The GUID of the client in control of the game (the &#39;host&#39; from the players&#39; perspective).
Definition: NetServer.h:383
std::vector< CStr > m_PausingPlayers
Holds the GUIDs of all currently paused players.
Definition: NetServer.h:374
CNetServer(bool useLobbyAuth=false)
Construct a new network server.
Definition: NetServer.cpp:1622
u16 GetPublicPort() const
Return the externally accessible port.
Definition: NetServer.cpp:1653
JS::PersistentRootedValue m_InitAttributes
Stores the most current init attributes.
Definition: NetServer.h:350
void SetPassword(const CStr &password)
Definition: NetServer.cpp:1704
std::vector< std::vector< CSimulationMessage > > m_SavedCommands
A copy of all simulation commands received so far, indexed by turn number, to simplify support for re...
Definition: NetServer.h:395
struct _ENetHost ENetHost
Definition: NetClient.h:37
uint16_t u16
Definition: types.h:38
std::string m_JoinSyncFile
The latest copy of the simulation state, received from an existing client when a new client has asked...
Definition: NetServer.h:401
Represents a signal in the state machine that a change has occurred.
Definition: FSM.h:52
bool CheckPasswordAndIncrement(const std::string &username, const std::string &password, const std::string &salt)
Check if password is valid.
Definition: NetServer.cpp:1682
Async task for receiving the initial game state to be forwarded to another client that is rejoining a...
Definition: NetServer.cpp:95
std::vector< CStrW > m_BannedPlayers
Definition: NetServer.h:367
u32 m_NextHostID
Definition: NetServer.h:376
std::vector< std::string > m_InitAttributesQueue
Definition: NetServer.h:431
Definition: NetServer.h:61
u16 GetLocalPort() const
Return the serving port on the local machine.
Definition: NetServer.cpp:1658
Definition: NetServer.h:81
Definition: NetServer.h:85
struct _ENetPeer ENetPeer
Definition: NetHost.h:30
CStr m_Password
Definition: NetServer.h:196
NetServerState
Definition: NetServer.h:46
Definition: NetServer.h:90
std::time_t m_LastConnectionCheck
Time when the clients connections were last checked for timeouts and latency.
Definition: NetServer.h:406
void SetTurnLength(u32 msecs)
Set the turn length to a fixed value.
Definition: NetServer.cpp:1739
uint32_t u32
Definition: types.h:39
bool IsBanned(const std::string &username) const
Check if user reached certain number of failed attempts.
Definition: NetServer.cpp:1698
bool SetConnectionDataViaSTUN()
Definition: NetServer.cpp:1673
Various declarations shared by networking code.
bool m_Shutdown
Definition: NetServer.h:427
~CNetServer()
Definition: NetServer.cpp:1628
ENetHost * m_Host
Definition: NetServer.h:357
void KickPlayer(const CStrW &playerName, bool ban)
Definition: JSInterface_Network.cpp:231
NetServerSessionState
Server session representation of client state.
Definition: NetServer.h:71
void ClearAllPlayerReady()
Definition: JSInterface_Network.cpp:252
std::string m_ControllerSecret
The &#39;secret&#39; used to identify the controller of the game.
Definition: NetServer.h:388
NONCOPYABLE(CNetServer)
Network server interface.
Definition: NetServer.h:107
ENet connection statistics profiler table.
Definition: NetStats.h:38
std::vector< bool > m_StartGameQueue
Definition: NetServer.h:430
const bool m_LobbyAuth
Whether this match requires lobby authentication.
Definition: NetServer.h:355
bool UseLobbyAuth() const
Definition: NetServer.cpp:1638
std::vector< u32 > m_TurnLengthQueue
Definition: NetServer.h:433
std::vector< CNetServerSession * > m_Sessions
Definition: NetServer.h:358
Definition: NetServer.h:98
The base class for all network messages exchanged within the game.
Definition: NetMessage.h:32
CStr m_PublicIp
Definition: NetServer.h:195
void OnLobbyAuth(const CStr &name, const CStr &token)
Definition: NetServer.cpp:1733
Definition: NetServer.h:58
CNetServerWorker * m_Worker
Definition: NetServer.h:191
std::vector< std::pair< CStr, CStr > > m_LobbyAuthQueue
Definition: NetServer.h:432
u16 m_PublicPort
Definition: NetServer.h:194
CStr m_Password
Definition: NetServer.h:369
bool m_UseSTUN
Definition: NetServer.h:193
CNetStatsTable * m_Stats
Definition: NetServer.h:360
void SendHolePunchingMessage(const CStr &ip, u16 port)
Definition: NetServer.cpp:1745
std::vector< u32 > m_BannedIPs
Definition: NetServer.h:366
NetServerState m_State
Definition: NetServer.h:362
std::unordered_map< std::string, int > m_FailedAttempts
Definition: NetServer.h:197
Abstraction around a SpiderMonkey JS::Realm.
Definition: ScriptInterface.h:71
Definition: NetServer.h:50
Definition: NetServer.h:74
bool GetUseSTUN() const
Definition: NetServer.cpp:1633
std::mutex m_WorkerMutex
Definition: NetServer.h:424
CStrW m_ServerName
Definition: NetServer.h:364
Spidermonkey maintains some &#39;local&#39; state via the JSContext* object.
Definition: ScriptRequest.h:59
CNetServerTurnManager * m_ServerTurnManager
Definition: NetServer.h:378
PlayerAssignmentMap m_PlayerAssignments
Definition: NetServer.h:343
Definition: NetServer.h:65
Network server worker thread.
Definition: NetServer.h:212
The server&#39;s end of a network session.
Definition: NetSession.h:154
const bool m_LobbyAuth
Definition: NetServer.h:192
Definition: NetServer.h:94
bool SetupConnection(const u16 port)
Begin listening for network connections.
Definition: NetServer.cpp:1643
std::thread m_WorkerThread
Definition: NetServer.h:423
CStr GetPublicIp() const
Return the externally accessible IP.
Definition: NetServer.cpp:1648
The server-side counterpart to CNetClientTurnManager.
Definition: NetServerTurnManager.h:37
CNetServer * g_NetServer
Global network server for the standard game.
Definition: NetServer.cpp:80
Definition: NetServer.h:55
Special message type for simulation commands.
Definition: NetMessage.h:113
static Status Run(const Operation &op, const Parameters &p=Parameters(), const CompletedHook &completedHook=CompletedHook(), const IssueHook &issueHook=IssueHook())
Definition: io.h:243
Definition: NetServer.h:78
void SetConnectionData(const CStr &ip, u16 port)
Definition: NetServer.cpp:1666