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