Line data Source code
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 NETCLIENT_H
19 : #define NETCLIENT_H
20 :
21 : #include "network/FSM.h"
22 : #include "network/NetFileTransfer.h"
23 : #include "network/NetHost.h"
24 : #include "scriptinterface/Object.h"
25 :
26 : #include "ps/CStr.h"
27 :
28 : #include <ctime>
29 : #include <deque>
30 : #include <thread>
31 :
32 : class CGame;
33 : class CNetClientSession;
34 : class CNetClientTurnManager;
35 : class ScriptInterface;
36 :
37 : typedef struct _ENetHost ENetHost;
38 :
39 : // NetClient session FSM states
40 : enum
41 : {
42 : NCS_UNCONNECTED,
43 : NCS_CONNECT,
44 : NCS_HANDSHAKE,
45 : NCS_AUTHENTICATE,
46 : NCS_PREGAME,
47 : NCS_LOADING,
48 : NCS_JOIN_SYNCING,
49 : NCS_INGAME
50 : };
51 :
52 : /**
53 : * Network client.
54 : * This code is run by every player (including the host, if they are not
55 : * a dedicated server).
56 : * It provides an interface between the GUI, the network (via CNetClientSession),
57 : * and the game (via CGame and CNetClientTurnManager).
58 : */
59 : class CNetClient : public CFsm
60 : {
61 : NONCOPYABLE(CNetClient);
62 :
63 : friend class CNetFileReceiveTask_ClientRejoin;
64 :
65 : public:
66 : /**
67 : * Construct a client associated with the given game object.
68 : * The game must exist for the lifetime of this object.
69 : */
70 : CNetClient(CGame* game);
71 :
72 : virtual ~CNetClient();
73 :
74 : /**
75 : * We assume that adding a tracing function that's only called
76 : * during GC is better for performance than using a
77 : * PersistentRooted<T> where each value needs to be added to
78 : * the root set.
79 : */
80 0 : static void Trace(JSTracer *trc, void *data)
81 : {
82 0 : reinterpret_cast<CNetClient*>(data)->TraceMember(trc);
83 0 : }
84 :
85 : void TraceMember(JSTracer *trc);
86 :
87 : /**
88 : * Set the user's name that will be displayed to all players.
89 : * This must not be called after the connection setup.
90 : */
91 : void SetUserName(const CStrW& username);
92 :
93 : /**
94 : * Store the JID of the host.
95 : * This is needed for the secure lobby authentication.
96 : */
97 : void SetHostJID(const CStr& jid);
98 :
99 : void SetControllerSecret(const std::string& secret);
100 :
101 0 : bool IsController() const { return m_IsController; }
102 :
103 : /**
104 : * Set the game password.
105 : * Must be called after SetUserName, as that is used to hash further.
106 : */
107 : void SetGamePassword(const CStr& hashedPassword);
108 :
109 : /**
110 : * Returns the GUID of the local client.
111 : * Used for distinguishing observers.
112 : */
113 0 : CStr GetGUID() const { return m_GUID; }
114 :
115 : /**
116 : * Set connection data to the remote networked server.
117 : * @param address IP address or host name to connect to
118 : */
119 : void SetupServerData(CStr address, u16 port, bool stun);
120 :
121 : /**
122 : * Set up a connection to the remote networked server.
123 : * Must call SetupServerData first.
124 : * @return true on success, false on connection failure
125 : */
126 : bool SetupConnection(ENetHost* enetClient);
127 :
128 : /**
129 : * Request connection information over the lobby.
130 : */
131 : void SetupConnectionViaLobby();
132 :
133 : /**
134 : * Connect to the remote networked server using lobby.
135 : * Push netstatus messages on failure.
136 : * @param localNetwork - if true, assume we are trying to connect on the local network.
137 : * @return true on success, false on connection failure
138 : */
139 : bool TryToConnect(const CStr& hostJID, bool localNetwork);
140 :
141 : /**
142 : * Destroy the connection to the server.
143 : * This client probably cannot be used again.
144 : */
145 : void DestroyConnection();
146 :
147 : /**
148 : * Poll the connection for messages from the server and process them, and send
149 : * any queued messages.
150 : * This must be called frequently (i.e. once per frame).
151 : */
152 : void Poll();
153 :
154 : /**
155 : * Locally triggers a GUI message if the connection to the server is being lost or has bad latency.
156 : */
157 : void CheckServerConnection();
158 :
159 : /**
160 : * Retrieves the next queued GUI message, and removes it from the queue.
161 : * The returned value is in the GetScriptInterface() JS context.
162 : *
163 : * This is the only mechanism for the networking code to send messages to
164 : * the GUI - it is pull-based (instead of push) so the engine code does not
165 : * need to know anything about the code structure of the GUI scripts.
166 : *
167 : * The structure of the messages is <code>{ "type": "...", ... }</code>.
168 : * The exact types and associated data are not specified anywhere - the
169 : * implementation and GUI scripts must make the same assumptions.
170 : *
171 : * @return next message, or the value 'undefined' if the queue is empty
172 : */
173 : void GuiPoll(JS::MutableHandleValue);
174 :
175 : /**
176 : * Add a message to the queue, to be read by GuiPoll.
177 : * The script value must be in the GetScriptInterface() JS context.
178 : */
179 : template<typename... Args>
180 0 : void PushGuiMessage(Args const&... args)
181 : {
182 0 : ScriptRequest rq(GetScriptInterface());
183 :
184 0 : JS::RootedValue message(rq.cx);
185 0 : Script::CreateObject(rq, &message, args...);
186 0 : m_GuiMessageQueue.push_back(JS::Heap<JS::Value>(message));
187 0 : }
188 :
189 : /**
190 : * Return a concatenation of all messages in the GUI queue,
191 : * for test cases to easily verify the queue contents.
192 : */
193 : std::string TestReadGuiMessages();
194 :
195 : /**
196 : * Get the script interface associated with this network client,
197 : * which is equivalent to the one used by the CGame in the constructor.
198 : */
199 : const ScriptInterface& GetScriptInterface();
200 :
201 : /**
202 : * Send a message to the server.
203 : * @param message message to send
204 : * @return true on success
205 : */
206 : bool SendMessage(const CNetMessage* message);
207 :
208 : /**
209 : * Call when the network connection has been successfully initiated.
210 : */
211 : void HandleConnect();
212 :
213 : /**
214 : * Call when the network connection has been lost.
215 : */
216 : void HandleDisconnect(u32 reason);
217 :
218 : /**
219 : * Call when a message has been received from the network.
220 : */
221 : bool HandleMessage(CNetMessage* message);
222 :
223 : /**
224 : * Call when the game has started and all data files have been loaded,
225 : * to signal to the server that we are ready to begin the game.
226 : */
227 : void LoadFinished();
228 :
229 : void SendGameSetupMessage(JS::MutableHandleValue attrs, const ScriptInterface& scriptInterface);
230 :
231 : void SendAssignPlayerMessage(const int playerID, const CStr& guid);
232 :
233 : void SendChatMessage(const std::wstring& text);
234 :
235 : void SendReadyMessage(const int status);
236 :
237 : void SendClearAllReadyMessage();
238 :
239 : void SendStartGameMessage(const CStr& initAttribs);
240 :
241 : /**
242 : * Call when the client has rejoined a running match and finished
243 : * the loading screen.
244 : */
245 : void SendRejoinedMessage();
246 :
247 : /**
248 : * Call to kick/ban a client
249 : */
250 : void SendKickPlayerMessage(const CStrW& playerName, bool ban);
251 :
252 : /**
253 : * Call when the client has paused or unpaused the game.
254 : */
255 : void SendPausedMessage(bool pause);
256 :
257 : /**
258 : * @return Whether the NetClient is shutting down.
259 : */
260 : bool ShouldShutdown() const;
261 :
262 : /**
263 : * Called when fetching connection data from the host failed, to inform JS code.
264 : */
265 : void HandleGetServerDataFailed(const CStr& error);
266 : private:
267 :
268 : void SendAuthenticateMessage();
269 :
270 : // Net message / FSM transition handlers
271 : static bool OnConnect(void* context, CFsmEvent* event);
272 : static bool OnHandshake(void* context, CFsmEvent* event);
273 : static bool OnHandshakeResponse(void* context, CFsmEvent* event);
274 : static bool OnAuthenticateRequest(void* context, CFsmEvent* event);
275 : static bool OnAuthenticate(void* context, CFsmEvent* event);
276 : static bool OnChat(void* context, CFsmEvent* event);
277 : static bool OnReady(void* context, CFsmEvent* event);
278 : static bool OnGameSetup(void* context, CFsmEvent* event);
279 : static bool OnPlayerAssignment(void* context, CFsmEvent* event);
280 : static bool OnInGame(void* context, CFsmEvent* event);
281 : static bool OnGameStart(void* context, CFsmEvent* event);
282 : static bool OnJoinSyncStart(void* context, CFsmEvent* event);
283 : static bool OnJoinSyncEndCommandBatch(void* context, CFsmEvent* event);
284 : static bool OnRejoined(void* context, CFsmEvent* event);
285 : static bool OnKicked(void* context, CFsmEvent* event);
286 : static bool OnClientTimeout(void* context, CFsmEvent* event);
287 : static bool OnClientPerformance(void* context, CFsmEvent* event);
288 : static bool OnClientsLoading(void* context, CFsmEvent* event);
289 : static bool OnClientPaused(void* context, CFsmEvent* event);
290 : static bool OnLoadedGame(void* context, CFsmEvent* event);
291 :
292 : /**
293 : * Take ownership of a session object, and use it for all network communication.
294 : */
295 : void SetAndOwnSession(CNetClientSession* session);
296 :
297 : /**
298 : * Push a message onto the GUI queue listing the current player assignments.
299 : */
300 : void PostPlayerAssignmentsToScript();
301 :
302 : CGame *m_Game;
303 : CStrW m_UserName;
304 :
305 : CStr m_HostJID;
306 : CStr m_ServerAddress;
307 : u16 m_ServerPort;
308 : bool m_UseSTUN;
309 :
310 : /**
311 : * Password to join the game.
312 : */
313 : CStr m_Password;
314 :
315 : /// The 'secret' used to identify the controller of the game.
316 : std::string m_ControllerSecret;
317 :
318 : /// Note that this is just a "gui hint" with no actual impact on being controller.
319 : bool m_IsController = false;
320 :
321 : /// Current network session (or NULL if not connected)
322 : CNetClientSession* m_Session;
323 :
324 : std::thread m_PollingThread;
325 :
326 : /// Turn manager associated with the current game (or NULL if we haven't started the game yet)
327 : CNetClientTurnManager* m_ClientTurnManager;
328 :
329 : /// Unique-per-game identifier of this client, used to identify the sender of simulation commands
330 : u32 m_HostID;
331 :
332 : /// True if the player is currently rejoining or has already rejoined the game.
333 : bool m_Rejoin;
334 :
335 : /// Latest copy of player assignments heard from the server
336 : PlayerAssignmentMap m_PlayerAssignments;
337 :
338 : /// Globally unique identifier to distinguish users beyond the lifetime of a single network session
339 : CStr m_GUID;
340 :
341 : /// Queue of messages for GuiPoll
342 : std::deque<JS::Heap<JS::Value>> m_GuiMessageQueue;
343 :
344 : /// Serialized game state received when joining an in-progress game
345 : std::string m_JoinSyncBuffer;
346 :
347 : /// Time when the server was last checked for timeouts and bad latency
348 : std::time_t m_LastConnectionCheck;
349 : };
350 :
351 : /// Global network client for the standard game
352 : extern CNetClient *g_NetClient;
353 :
354 : #endif // NETCLIENT_H
|