Fixes calls to SetVertexAttributeFormat with zero stride.
[0ad.git] / source / network / NetServer.h
blob74e03d4180de7727e583c94b6489d3c9cfd5766e
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/>.
18 #ifndef NETSERVER_H
19 #define NETSERVER_H
21 #include "NetFileTransfer.h"
22 #include "NetHost.h"
23 #include "lib/config2.h"
24 #include "lib/types.h"
25 #include "scriptinterface/ScriptTypes.h"
27 #include <ctime>
28 #include <mutex>
29 #include <string>
30 #include <utility>
31 #include <unordered_map>
32 #include <vector>
33 #include <thread>
35 class CNetServerSession;
36 class CNetServerTurnManager;
37 class CFsmEvent;
38 class CPlayerAssignmentMessage;
39 class CNetStatsTable;
40 class CSimulationMessage;
41 class ScriptInterface;
42 class ScriptRequest;
44 class CNetServerWorker;
46 enum NetServerState
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
54 // and stuff.
55 SERVER_STATE_PREGAME,
57 // All the hosts are connected and are loading the game
58 SERVER_STATE_LOADING,
60 // The one with all the killing ;-)
61 SERVER_STATE_INGAME,
63 // The game is over and someone has won. Players might linger to chat or
64 // download the replay log.
65 SERVER_STATE_POSTGAME
68 /**
69 * Server session representation of client state
71 enum NetServerSessionState
73 // The client has disconnected or been disconnected
74 NSS_UNCONNECTED,
76 // The client has just connected and we're waiting for its handshake message,
77 // to agree on the protocol version
78 NSS_HANDSHAKE,
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
85 NSS_AUTHENTICATE,
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.
90 NSS_PREGAME,
92 // The client has authenticated but the game was already started,
93 // so it's synchronising with the game state from other clients
94 NSS_JOIN_SYNCING,
96 // The client is running the game.
97 // Server must be in SERVER_STATE_LOADING or SERVER_STATE_INGAME.
98 NSS_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.
107 class CNetServer
109 NONCOPYABLE(CNetServer);
110 public:
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);
117 ~CNetServer();
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.
130 void StartGame();
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);
190 private:
191 CNetServerWorker* m_Worker;
192 const bool m_LobbyAuth;
193 bool m_UseSTUN;
194 u16 m_PublicPort;
195 CStr m_PublicIp;
196 CStr m_Password;
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.)
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.
212 class CNetServerWorker
214 NONCOPYABLE(CNetServerWorker);
216 public:
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);
234 private:
235 friend class CNetServer;
236 friend class CNetFileReceiveTask_ServerRejoin;
238 CNetServerWorker(bool useLobbyAuth);
239 ~CNetServerWorker();
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;
357 ENetHost* m_Host;
358 std::vector<CNetServerSession*> m_Sessions;
360 CNetStatsTable* m_Stats;
362 NetServerState m_State;
364 CStrW m_ServerName;
366 std::vector<u32> m_BannedIPs;
367 std::vector<CStrW> m_BannedPlayers;
369 CStr m_Password;
372 * Holds the GUIDs of all currently paused players.
374 std::vector<CStr> m_PausingPlayers;
376 u32 m_NextHostID;
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;
408 private:
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;
417 #endif
419 static void RunThread(CNetServerWorker* data);
420 void Run();
421 bool RunStep();
423 std::thread m_WorkerThread;
424 std::mutex m_WorkerMutex;
426 // protected by m_WorkerMutex
427 bool m_Shutdown;
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