/
NetworkManager.h
470 lines (431 loc) · 15 KB
/
NetworkManager.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
/*
* Copyright (c) 2018 Isetta
*/
#pragma once
#include <list>
#include <typeindex>
#include <unordered_map>
#include <utility>
#include "Core/DataStructures/HandleBin.h"
#include "Core/Debug/Logger.h"
#include "Core/IsettaAlias.h"
#include "ISETTA_API.h"
#include "Networking/ClientInfo.h"
#include "yojimbo/yojimbo.h"
namespace Isetta {
class Entity;
class NetworkId;
/**
* @brief Wrapper class for NetworkingModule so that other engine components can
* use networking features.
*/
class ISETTA_API_DECLARE NetworkManager {
public:
static NetworkManager& Instance();
/**
* @brief Enum that defines the current client connection state
*
*/
enum class ClientState {
ConnectTokenExpired = -6,
InvalidConnectToken = -5,
ConnectionTimedOut = -4,
ConnectionResponseTimedOut = -3,
ConnectionRequestTimedOut = -2,
ConnectionDenied = -1,
SendingConnectionRequest = 1,
SendingConnectionResponse = 2,
Connected = 3,
};
/**
* @brief Populates a message using a
* lambda function and sends it from the client to the server.
*
* @tparam T Class of the message to be sent
* @param messageInitializer Lambda function that takes a message pointer and
* populates its data
*/
template <typename T>
void SendMessageFromClient(Action<T*> messageInitializer);
/**
* @brief Populates a message using a
* lambda function and sends it from the server to a client.
*
* @tparam T Class of the message to be sent
* @param clientIndex Index of the client that we are sending the message to
* @param messageInitializer Lambda function that takes a message pointer and
* populates its data
*/
template <typename T>
void SendMessageFromServer(int clientIndex, Action<T*> messageInitializer);
/**
* @brief Populates a message using a
* lambda function and sends it from the server to all clients.
*
* @tparam T Class of the message to be sent
* @param messageInitializer Lambda function that takes a message pointer and
* populates its data
*/
template <typename T>
void SendMessageFromServerToAll(Action<T*> messageInitializer);
// The following two functions are called when the user already have a message
/**
* @brief Populates a message using a reference message and sends it from the
* server to all clients.
*
* @tparam T Class of the message to be sent
* @param refMessage Message to populate our own message with
*/
template <typename T>
void SendMessageFromServerToAll(yojimbo::Message* refMessage);
/**
* @brief Populates a message using a reference message and sends it from the
* server to all clients but the client at the given index.
*
* @tparam T Class of the message to be sent
* @param clientIdx Index of the client that we are sending the message to
* @param refMessage Message to populate our own message with
*/
template <typename T>
void SendMessageFromServerToAllButClient(int clientIdx,
yojimbo::Message* refMessage);
/**
* @brief Registers a callback function on the server for when we receive a
* message.
*
* @tparam T Class of the message to run the callback for
* @param func Function to run when the message is received
* @return int Handle of the function in the registry to unregister with
*/
template <typename T>
int RegisterServerCallback(Action<int, yojimbo::Message*> func);
/**
* @brief Unregisters a callback function on the server corresponding to the
* handle.
*
* @tparam T Class of the message corresponding to the callback we want to
* remove
* @param handle Handle of the callback function in the registry that we want
* to remove
*/
template <typename T>
void UnregisterServerCallback(int handle);
/**
* @brief Registers a callback function on the client for when we receive a
* message.
*
* @tparam T Class of the message to run the callback for
* @param func Function to run when the message is received
* @return int Handle of the function in the registry to unregister with
*/
template <typename T>
int RegisterClientCallback(Action<yojimbo::Message*> func);
/**
* @brief Unregisters a callback function on the client corresponding to the
* handle.
*
* @tparam T Class of the message corresponding to the callback we want to
* remove
* @param handle Handle of the callback function in the registry that we want
* to remove
*/
template <typename T>
void UnregisterClientCallback(int handle);
/**
* @brief Get the entity corresonding to the given network ID
*
* @param id The network ID of the entity that we want
* @return Entity* A pointer to the entity that we want; null if no such
* entity exists
*/
Entity* GetNetworkEntity(const U32 id);
/**
* @brief Get the NetworkID component corresponding to the given network ID
*
* @param id The network ID of the component that we want
* @return NetworkId* A pointer to the component that we want; null if no such
* component exists
*/
NetworkId* GetNetworkId(const U32 id);
/**
* @brief Initializes the local Server object with the given address and port.
*
* @param serverIP Address of the server.
*/
void StartServer(std::string_view serverIP) const;
/**
* @brief Closes the local Server object and deallocates its allocated memory.
*
*/
void StopServer() const;
/**
* @brief Connects the local Client to a server at the given address.
*
* @param serverIP Address of the server to connect the local Client to.
* @param onStarted Function to call when the connection request either
* succeeds or fails. Passes a boolean indicating the success of the
* connection.
*/
void StartClient(std::string_view serverIP,
const Action<ClientState>& onStarted = nullptr) const;
/**
* @brief Disconnects the local Client from the server it is connected to.
*/
void StopClient() const;
/**
* @brief Starts the server then starts a client and hooks it into it.
*
* @param hostIP The IP of the computer to host the server from
*/
void StartHost(std::string_view hostIP) const;
/**
* @brief Disconnects the client then stops the server.
*
*/
void StopHost() const;
// Following three checks "pure role". i.e. a Host is not a Server
bool IsClient() const;
bool IsHost() const;
bool IsServer() const;
/**
* @brief Can only be run on the server, and tells all of the clients to load
* the given level.
*
* @param levelName Name of the level to be loaded by the clients
*/
void NetworkLoadLevel(std::string_view levelName);
bool IsClientRunning() const;
bool IsServerRunning() const;
bool IsClientConnected(int clientIdx) const;
static int GetMaxClients();
int GetClientIndex() const;
~NetworkManager() = default;
/**
* @brief Registers a callback function that is called when the client
* connects to the server.
*
* @param listener Callback function
* @return U64 Handle of the callback function in the registry
*/
U64 RegisterConnectedToServerCallback(const Action<>& listener) const;
/**
* @brief Unregisters the corresponding callback function on the client for
* connecting to the server.
*
* @param handle Handle of the callback function to unregister
*/
void UnregisterConnectedToServerCallback(U64& handle) const;
/**
* @brief Registers a callback function that is called when the client
* disconnects from the server.
*
* @param listener Callback function
* @return U64 Handle of the callback function in the registry
*/
U64 RegisterDisconnectedFromServerCallback(const Action<>& listener) const;
/**
* @brief Unregisters the corresponding callback function on the client for
* disconnecting from the server.
*
* @param handle Handle of the callback function to unregister
*/
void UnregisterDisconnectedFromServerCallback(U64& handle) const;
/**
* @brief Registers a callback function that is called when the server gets a
* client connected to it.
*
* @param listener Callback function
* @return U64 Handle of the callback function in the registry
*/
U64 RegisterClientConnectedCallback(const Action<ClientInfo>& listener) const;
/**
* @brief Unregisters the corresponding callback function on the server for
* connecting to the server.
*
* @param handle Handle of the callback function to unregister
*/
void UnregisterClientConnectedCallback(U64& handle) const;
/**
* @brief Registers a callback function that is called when the server gets a
* client disconnected from it.
*
* @param listener Callback function
* @return U64 Handle of the callback function in the registry
*/
U64 RegisterClientDisconnectedCallback(
const Action<ClientInfo>& listener) const;
/**
* @brief Unregisters the corresponding callback function on the server for
* disconnecting from the server.
*
* @param handle Handle of the callback function to unregister
*/
void UnregisterClientDisconnectedCallback(U64& handle) const;
private:
NetworkManager();
template <typename T>
T* GenerateMessageFromServer(int clientIdx);
void SendMessageFromServer(int clientIdx, yojimbo::Message* message) const;
template <typename T>
T* GenerateMessageFromClient();
void SendMessageFromClient(yojimbo::Message* message) const;
template <typename T>
bool RegisterMessageType(U64 size, Func<yojimbo::Message*, void*> factory);
U16 GetMessageTypeCount() const { return messageTypeCount; }
yojimbo::Message* CreateClientMessage(int messageId) const;
yojimbo::Message* CreateServerMessage(int clientIdx, int messageId) const;
template <typename T>
int GetMessageTypeId();
std::list<std::pair<U16, Action<yojimbo::Message*>>> GetClientFunctions(
int type);
std::list<std::pair<U16, Action<int, yojimbo::Message*>>> GetServerFunctions(
int type);
U32 CreateNetworkId(NetworkId* networkId);
U32 AssignNetworkId(U32 netId, NetworkId* networkId);
void RemoveNetworkId(NetworkId* networkId);
static U16 GetServerPort();
class NetworkingModule* networkingModule{nullptr};
int messageTypeCount = 0;
U16 functionCount = 0;
HandleBin networkIds;
std::unordered_map<int, std::pair<U64, Func<yojimbo::Message*, void*>>>
factories;
std::unordered_map<std::type_index, int> typeMap;
std::unordered_map<int, std::list<std::pair<U16, Action<yojimbo::Message*>>>>
clientCallbacks;
std::unordered_map<int,
std::list<std::pair<U16, Action<int, yojimbo::Message*>>>>
serverCallbacks;
std::unordered_map<U32, NetworkId*> networkIdToComponentMap;
friend class NetworkId;
friend class NetworkingModule;
friend class NetworkMessageFactory;
friend class CustomAdapter;
template <typename T>
friend class NetworkMessageRegistry;
};
template <typename T>
T* NetworkManager::GenerateMessageFromClient() {
return reinterpret_cast<T*>(CreateClientMessage(GetMessageTypeId<T>()));
}
template <typename T>
T* NetworkManager::GenerateMessageFromServer(int clientIdx) {
return reinterpret_cast<T*>(
CreateServerMessage(clientIdx, GetMessageTypeId<T>()));
}
template <typename T>
void NetworkManager::SendMessageFromClient(Action<T*> messageInitializer) {
if (!IsClientRunning()) {
LOG_ERROR(Debug::Channel::Networking,
"Cannot send message from client, client not running");
return;
}
yojimbo::Message* newMessage = GenerateMessageFromClient<T>();
messageInitializer(reinterpret_cast<T*>(newMessage));
SendMessageFromClient(newMessage);
}
template <typename T>
void NetworkManager::SendMessageFromServer(const int clientIndex,
Action<T*> messageInitializer) {
if (!IsServerRunning()) {
LOG_ERROR(Debug::Channel::Networking,
"Cannot send message from server, server not running");
return;
}
yojimbo::Message* newMessage = GenerateMessageFromServer<T>(clientIndex);
messageInitializer(reinterpret_cast<T*>(newMessage));
SendMessageFromServer(clientIndex, newMessage);
}
template <typename T>
int NetworkManager::GetMessageTypeId() {
return typeMap[std::type_index(typeid(T))];
}
template <typename T>
void NetworkManager::SendMessageFromServerToAll(yojimbo::Message* refMessage) {
if (!IsServerRunning()) {
LOG_ERROR(Debug::Channel::Networking,
"Cannot send message from server to all, server not running");
return;
}
for (int i = 0; i < GetMaxClients(); ++i) {
if (!IsClientConnected(i)) {
continue;
}
yojimbo::Message* newMessage = GenerateMessageFromServer<T>(i);
newMessage->Copy(refMessage);
SendMessageFromServer(i, newMessage);
}
}
template <typename T>
void NetworkManager::SendMessageFromServerToAll(Action<T*> messageInitializer) {
if (!IsServerRunning()) {
LOG_ERROR(Debug::Channel::Networking,
"Cannot send message from server to all, server not running");
return;
}
for (int i = 0; i < GetMaxClients(); ++i) {
if (!IsClientConnected(i)) {
continue;
}
yojimbo::Message* newMessage = GenerateMessageFromServer<T>(i);
messageInitializer(reinterpret_cast<T*>(newMessage));
SendMessageFromServer(i, newMessage);
}
}
template <typename T>
void NetworkManager::SendMessageFromServerToAllButClient(
int clientIdx, yojimbo::Message* refMessage) {
if (!IsServerRunning()) {
LOG_ERROR(Debug::Channel::Networking,
"Cannot send message from server to all but client %d, server "
"not running",
clientIdx);
return;
}
for (int i = 0; i < GetMaxClients(); ++i) {
if (!IsClientConnected(i) || i == clientIdx) {
continue;
}
yojimbo::Message* newMessage = GenerateMessageFromServer<T>(i);
newMessage->Copy(refMessage);
SendMessageFromServer(i, newMessage);
}
}
template <typename T>
bool NetworkManager::RegisterMessageType(
U64 size, Func<yojimbo::Message*, void*> factory) {
factories[messageTypeCount] = std::pair(size, factory);
return typeMap
.insert_or_assign(std::type_index(typeid(T)), messageTypeCount++)
.second;
}
template <typename T>
int NetworkManager::RegisterServerCallback(
Action<int, yojimbo::Message*> func) {
serverCallbacks[GetMessageTypeId<T>()].push_back(
std::pair(functionCount, func));
return functionCount++;
}
template <typename T>
void NetworkManager::UnregisterServerCallback(int handle) {
int messageId = GetMessageTypeId<T>();
serverCallbacks[messageId].remove_if(
[handle](const std::pair<U16, Action<U16, yojimbo::Message*>> item) {
return item.first == handle;
});
}
template <typename T>
int NetworkManager::RegisterClientCallback(Action<yojimbo::Message*> func) {
clientCallbacks[GetMessageTypeId<T>()].push_back(
std::pair(functionCount, func));
return functionCount++;
}
template <typename T>
void NetworkManager::UnregisterClientCallback(int handle) {
int messageId = GetMessageTypeId<T>();
clientCallbacks[messageId].remove_if(
[handle](const std::pair<U16, Action<yojimbo::Message*>> item) {
return item.first == handle;
});
}
} // namespace Isetta