1 //
2 // Copyright (c) 2008-2017 the Urho3D project.
3 //
4 // Permission is hereby granted, free of charge, to any person obtaining a copy
5 // of this software and associated documentation files (the "Software"), to deal
6 // in the Software without restriction, including without limitation the rights
7 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 // copies of the Software, and to permit persons to whom the Software is
9 // furnished to do so, subject to the following conditions:
10 //
11 // The above copyright notice and this permission notice shall be included in
12 // all copies or substantial portions of the Software.
13 //
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 // THE SOFTWARE.
21 //
22
23 #include "../Precompiled.h"
24
25 #include "../Core/Context.h"
26 #include "../Core/CoreEvents.h"
27 #include "../Core/Profiler.h"
28 #include "../Engine/EngineEvents.h"
29 #include "../IO/FileSystem.h"
30 #include "../Input/InputEvents.h"
31 #include "../IO/IOEvents.h"
32 #include "../IO/Log.h"
33 #include "../IO/MemoryBuffer.h"
34 #include "../Network/HttpRequest.h"
35 #include "../Network/Network.h"
36 #include "../Network/NetworkEvents.h"
37 #include "../Network/NetworkPriority.h"
38 #include "../Network/Protocol.h"
39 #include "../Scene/Scene.h"
40
41 #include <kNet/kNet.h>
42
43 #include "../DebugNew.h"
44
45 namespace Urho3D
46 {
47
48 static const int DEFAULT_UPDATE_FPS = 30;
49
Network(Context * context)50 Network::Network(Context* context) :
51 Object(context),
52 updateFps_(DEFAULT_UPDATE_FPS),
53 simulatedLatency_(0),
54 simulatedPacketLoss_(0.0f),
55 updateInterval_(1.0f / (float)DEFAULT_UPDATE_FPS),
56 updateAcc_(0.0f)
57 {
58 network_ = new kNet::Network();
59
60 // Register Network library object factories
61 RegisterNetworkLibrary(context_);
62
63 SubscribeToEvent(E_BEGINFRAME, URHO3D_HANDLER(Network, HandleBeginFrame));
64 SubscribeToEvent(E_RENDERUPDATE, URHO3D_HANDLER(Network, HandleRenderUpdate));
65
66 // Blacklist remote events which are not to be allowed to be registered in any case
67 blacklistedRemoteEvents_.Insert(E_CONSOLECOMMAND);
68 blacklistedRemoteEvents_.Insert(E_LOGMESSAGE);
69 blacklistedRemoteEvents_.Insert(E_BEGINFRAME);
70 blacklistedRemoteEvents_.Insert(E_UPDATE);
71 blacklistedRemoteEvents_.Insert(E_POSTUPDATE);
72 blacklistedRemoteEvents_.Insert(E_RENDERUPDATE);
73 blacklistedRemoteEvents_.Insert(E_ENDFRAME);
74 blacklistedRemoteEvents_.Insert(E_MOUSEBUTTONDOWN);
75 blacklistedRemoteEvents_.Insert(E_MOUSEBUTTONUP);
76 blacklistedRemoteEvents_.Insert(E_MOUSEMOVE);
77 blacklistedRemoteEvents_.Insert(E_MOUSEWHEEL);
78 blacklistedRemoteEvents_.Insert(E_KEYDOWN);
79 blacklistedRemoteEvents_.Insert(E_KEYUP);
80 blacklistedRemoteEvents_.Insert(E_TEXTINPUT);
81 blacklistedRemoteEvents_.Insert(E_JOYSTICKCONNECTED);
82 blacklistedRemoteEvents_.Insert(E_JOYSTICKDISCONNECTED);
83 blacklistedRemoteEvents_.Insert(E_JOYSTICKBUTTONDOWN);
84 blacklistedRemoteEvents_.Insert(E_JOYSTICKBUTTONUP);
85 blacklistedRemoteEvents_.Insert(E_JOYSTICKAXISMOVE);
86 blacklistedRemoteEvents_.Insert(E_JOYSTICKHATMOVE);
87 blacklistedRemoteEvents_.Insert(E_TOUCHBEGIN);
88 blacklistedRemoteEvents_.Insert(E_TOUCHEND);
89 blacklistedRemoteEvents_.Insert(E_TOUCHMOVE);
90 blacklistedRemoteEvents_.Insert(E_GESTURERECORDED);
91 blacklistedRemoteEvents_.Insert(E_GESTUREINPUT);
92 blacklistedRemoteEvents_.Insert(E_MULTIGESTURE);
93 blacklistedRemoteEvents_.Insert(E_DROPFILE);
94 blacklistedRemoteEvents_.Insert(E_INPUTFOCUS);
95 blacklistedRemoteEvents_.Insert(E_MOUSEVISIBLECHANGED);
96 blacklistedRemoteEvents_.Insert(E_EXITREQUESTED);
97 blacklistedRemoteEvents_.Insert(E_SERVERCONNECTED);
98 blacklistedRemoteEvents_.Insert(E_SERVERDISCONNECTED);
99 blacklistedRemoteEvents_.Insert(E_CONNECTFAILED);
100 blacklistedRemoteEvents_.Insert(E_CLIENTCONNECTED);
101 blacklistedRemoteEvents_.Insert(E_CLIENTDISCONNECTED);
102 blacklistedRemoteEvents_.Insert(E_CLIENTIDENTITY);
103 blacklistedRemoteEvents_.Insert(E_CLIENTSCENELOADED);
104 blacklistedRemoteEvents_.Insert(E_NETWORKMESSAGE);
105 blacklistedRemoteEvents_.Insert(E_NETWORKUPDATE);
106 blacklistedRemoteEvents_.Insert(E_NETWORKUPDATESENT);
107 blacklistedRemoteEvents_.Insert(E_NETWORKSCENELOADFAILED);
108 }
109
~Network()110 Network::~Network()
111 {
112 // If server connection exists, disconnect, but do not send an event because we are shutting down
113 Disconnect(100);
114 serverConnection_.Reset();
115
116 clientConnections_.Clear();
117 }
118
HandleMessage(kNet::MessageConnection * source,kNet::packet_id_t packetId,kNet::message_id_t msgId,const char * data,size_t numBytes)119 void Network::HandleMessage(kNet::MessageConnection* source, kNet::packet_id_t packetId, kNet::message_id_t msgId, const char* data,
120 size_t numBytes)
121 {
122 // Only process messages from known sources
123 Connection* connection = GetConnection(source);
124 if (connection)
125 {
126 MemoryBuffer msg(data, (unsigned)numBytes);
127 if (connection->ProcessMessage((int)msgId, msg))
128 return;
129
130 // If message was not handled internally, forward as an event
131 using namespace NetworkMessage;
132
133 VariantMap& eventData = GetEventDataMap();
134 eventData[P_CONNECTION] = connection;
135 eventData[P_MESSAGEID] = (int)msgId;
136 eventData[P_DATA].SetBuffer(msg.GetData(), msg.GetSize());
137 connection->SendEvent(E_NETWORKMESSAGE, eventData);
138 }
139 else
140 URHO3D_LOGWARNING("Discarding message from unknown MessageConnection " + ToString((void*)source));
141 }
142
ComputeContentID(kNet::message_id_t msgId,const char * data,size_t numBytes)143 u32 Network::ComputeContentID(kNet::message_id_t msgId, const char* data, size_t numBytes)
144 {
145 switch (msgId)
146 {
147 case MSG_CONTROLS:
148 // Return fixed content ID for controls
149 return CONTROLS_CONTENT_ID;
150
151 case MSG_NODELATESTDATA:
152 case MSG_COMPONENTLATESTDATA:
153 {
154 // Return the node or component ID, which is first in the message
155 MemoryBuffer msg(data, (unsigned)numBytes);
156 return msg.ReadNetID();
157 }
158
159 default:
160 // By default return no content ID
161 return 0;
162 }
163 }
164
NewConnectionEstablished(kNet::MessageConnection * connection)165 void Network::NewConnectionEstablished(kNet::MessageConnection* connection)
166 {
167 connection->RegisterInboundMessageHandler(this);
168
169 // Create a new client connection corresponding to this MessageConnection
170 SharedPtr<Connection> newConnection(new Connection(context_, true, kNet::SharedPtr<kNet::MessageConnection>(connection)));
171 newConnection->ConfigureNetworkSimulator(simulatedLatency_, simulatedPacketLoss_);
172 clientConnections_[connection] = newConnection;
173 URHO3D_LOGINFO("Client " + newConnection->ToString() + " connected");
174
175 using namespace ClientConnected;
176
177 VariantMap& eventData = GetEventDataMap();
178 eventData[P_CONNECTION] = newConnection;
179 newConnection->SendEvent(E_CLIENTCONNECTED, eventData);
180 }
181
ClientDisconnected(kNet::MessageConnection * connection)182 void Network::ClientDisconnected(kNet::MessageConnection* connection)
183 {
184 connection->Disconnect(0);
185
186 // Remove the client connection that corresponds to this MessageConnection
187 HashMap<kNet::MessageConnection*, SharedPtr<Connection> >::Iterator i = clientConnections_.Find(connection);
188 if (i != clientConnections_.End())
189 {
190 Connection* connection = i->second_;
191 URHO3D_LOGINFO("Client " + connection->ToString() + " disconnected");
192
193 using namespace ClientDisconnected;
194
195 VariantMap& eventData = GetEventDataMap();
196 eventData[P_CONNECTION] = connection;
197 connection->SendEvent(E_CLIENTDISCONNECTED, eventData);
198
199 clientConnections_.Erase(i);
200 }
201 }
202
Connect(const String & address,unsigned short port,Scene * scene,const VariantMap & identity)203 bool Network::Connect(const String& address, unsigned short port, Scene* scene, const VariantMap& identity)
204 {
205 URHO3D_PROFILE(Connect);
206
207 // If a previous connection already exists, disconnect it and wait for some time for the connection to terminate
208 if (serverConnection_)
209 {
210 serverConnection_->Disconnect(100);
211 OnServerDisconnected();
212 }
213
214 kNet::SharedPtr<kNet::MessageConnection> connection = network_->Connect(address.CString(), port, kNet::SocketOverUDP, this);
215 if (connection)
216 {
217 serverConnection_ = new Connection(context_, false, connection);
218 serverConnection_->SetScene(scene);
219 serverConnection_->SetIdentity(identity);
220 serverConnection_->SetConnectPending(true);
221 serverConnection_->ConfigureNetworkSimulator(simulatedLatency_, simulatedPacketLoss_);
222
223 URHO3D_LOGINFO("Connecting to server " + serverConnection_->ToString());
224 return true;
225 }
226 else
227 {
228 URHO3D_LOGERROR("Failed to connect to server " + address + ":" + String(port));
229 SendEvent(E_CONNECTFAILED);
230 return false;
231 }
232 }
233
Disconnect(int waitMSec)234 void Network::Disconnect(int waitMSec)
235 {
236 if (!serverConnection_)
237 return;
238
239 URHO3D_PROFILE(Disconnect);
240 serverConnection_->Disconnect(waitMSec);
241 }
242
StartServer(unsigned short port)243 bool Network::StartServer(unsigned short port)
244 {
245 if (IsServerRunning())
246 return true;
247
248 URHO3D_PROFILE(StartServer);
249
250 if (network_->StartServer(port, kNet::SocketOverUDP, this, true) != 0)
251 {
252 URHO3D_LOGINFO("Started server on port " + String(port));
253 return true;
254 }
255 else
256 {
257 URHO3D_LOGERROR("Failed to start server on port " + String(port));
258 return false;
259 }
260 }
261
StopServer()262 void Network::StopServer()
263 {
264 if (!IsServerRunning())
265 return;
266
267 URHO3D_PROFILE(StopServer);
268
269 clientConnections_.Clear();
270 network_->StopServer();
271 URHO3D_LOGINFO("Stopped server");
272 }
273
BroadcastMessage(int msgID,bool reliable,bool inOrder,const VectorBuffer & msg,unsigned contentID)274 void Network::BroadcastMessage(int msgID, bool reliable, bool inOrder, const VectorBuffer& msg, unsigned contentID)
275 {
276 BroadcastMessage(msgID, reliable, inOrder, msg.GetData(), msg.GetSize(), contentID);
277 }
278
BroadcastMessage(int msgID,bool reliable,bool inOrder,const unsigned char * data,unsigned numBytes,unsigned contentID)279 void Network::BroadcastMessage(int msgID, bool reliable, bool inOrder, const unsigned char* data, unsigned numBytes,
280 unsigned contentID)
281 {
282 // Make sure not to use kNet internal message ID's
283 if (msgID <= 0x4 || msgID >= 0x3ffffffe)
284 {
285 URHO3D_LOGERROR("Can not send message with reserved ID");
286 return;
287 }
288
289 kNet::NetworkServer* server = network_->GetServer();
290 if (server)
291 server->BroadcastMessage((unsigned long)msgID, reliable, inOrder, 0, contentID, (const char*)data, numBytes);
292 else
293 URHO3D_LOGERROR("Server not running, can not broadcast messages");
294 }
295
BroadcastRemoteEvent(StringHash eventType,bool inOrder,const VariantMap & eventData)296 void Network::BroadcastRemoteEvent(StringHash eventType, bool inOrder, const VariantMap& eventData)
297 {
298 for (HashMap<kNet::MessageConnection*, SharedPtr<Connection> >::Iterator i = clientConnections_.Begin();
299 i != clientConnections_.End(); ++i)
300 i->second_->SendRemoteEvent(eventType, inOrder, eventData);
301 }
302
BroadcastRemoteEvent(Scene * scene,StringHash eventType,bool inOrder,const VariantMap & eventData)303 void Network::BroadcastRemoteEvent(Scene* scene, StringHash eventType, bool inOrder, const VariantMap& eventData)
304 {
305 for (HashMap<kNet::MessageConnection*, SharedPtr<Connection> >::Iterator i = clientConnections_.Begin();
306 i != clientConnections_.End(); ++i)
307 {
308 if (i->second_->GetScene() == scene)
309 i->second_->SendRemoteEvent(eventType, inOrder, eventData);
310 }
311 }
312
BroadcastRemoteEvent(Node * node,StringHash eventType,bool inOrder,const VariantMap & eventData)313 void Network::BroadcastRemoteEvent(Node* node, StringHash eventType, bool inOrder, const VariantMap& eventData)
314 {
315 if (!node)
316 {
317 URHO3D_LOGERROR("Null sender node for remote node event");
318 return;
319 }
320 if (node->GetID() >= FIRST_LOCAL_ID)
321 {
322 URHO3D_LOGERROR("Sender node has a local ID, can not send remote node event");
323 return;
324 }
325
326 Scene* scene = node->GetScene();
327 for (HashMap<kNet::MessageConnection*, SharedPtr<Connection> >::Iterator i = clientConnections_.Begin();
328 i != clientConnections_.End(); ++i)
329 {
330 if (i->second_->GetScene() == scene)
331 i->second_->SendRemoteEvent(node, eventType, inOrder, eventData);
332 }
333 }
334
SetUpdateFps(int fps)335 void Network::SetUpdateFps(int fps)
336 {
337 updateFps_ = Max(fps, 1);
338 updateInterval_ = 1.0f / (float)updateFps_;
339 updateAcc_ = 0.0f;
340 }
341
SetSimulatedLatency(int ms)342 void Network::SetSimulatedLatency(int ms)
343 {
344 simulatedLatency_ = Max(ms, 0);
345 ConfigureNetworkSimulator();
346 }
347
SetSimulatedPacketLoss(float loss)348 void Network::SetSimulatedPacketLoss(float loss)
349 {
350 simulatedPacketLoss_ = Clamp(loss, 0.0f, 1.0f);
351 ConfigureNetworkSimulator();
352 }
353
RegisterRemoteEvent(StringHash eventType)354 void Network::RegisterRemoteEvent(StringHash eventType)
355 {
356 if (blacklistedRemoteEvents_.Find(eventType) != blacklistedRemoteEvents_.End())
357 {
358 URHO3D_LOGERROR("Attempted to register blacklisted remote event type " + String(eventType));
359 return;
360 }
361
362 allowedRemoteEvents_.Insert(eventType);
363 }
364
UnregisterRemoteEvent(StringHash eventType)365 void Network::UnregisterRemoteEvent(StringHash eventType)
366 {
367 allowedRemoteEvents_.Erase(eventType);
368 }
369
UnregisterAllRemoteEvents()370 void Network::UnregisterAllRemoteEvents()
371 {
372 allowedRemoteEvents_.Clear();
373 }
374
SetPackageCacheDir(const String & path)375 void Network::SetPackageCacheDir(const String& path)
376 {
377 packageCacheDir_ = AddTrailingSlash(path);
378 }
379
SendPackageToClients(Scene * scene,PackageFile * package)380 void Network::SendPackageToClients(Scene* scene, PackageFile* package)
381 {
382 if (!scene)
383 {
384 URHO3D_LOGERROR("Null scene specified for SendPackageToClients");
385 return;
386 }
387 if (!package)
388 {
389 URHO3D_LOGERROR("Null package specified for SendPackageToClients");
390 return;
391 }
392
393 for (HashMap<kNet::MessageConnection*, SharedPtr<Connection> >::Iterator i = clientConnections_.Begin();
394 i != clientConnections_.End(); ++i)
395 {
396 if (i->second_->GetScene() == scene)
397 i->second_->SendPackageToClient(package);
398 }
399 }
400
MakeHttpRequest(const String & url,const String & verb,const Vector<String> & headers,const String & postData)401 SharedPtr<HttpRequest> Network::MakeHttpRequest(const String& url, const String& verb, const Vector<String>& headers,
402 const String& postData)
403 {
404 URHO3D_PROFILE(MakeHttpRequest);
405
406 // The initialization of the request will take time, can not know at this point if it has an error or not
407 SharedPtr<HttpRequest> request(new HttpRequest(url, verb, headers, postData));
408 return request;
409 }
410
GetConnection(kNet::MessageConnection * connection) const411 Connection* Network::GetConnection(kNet::MessageConnection* connection) const
412 {
413 if (serverConnection_ && serverConnection_->GetMessageConnection() == connection)
414 return serverConnection_;
415 else
416 {
417 HashMap<kNet::MessageConnection*, SharedPtr<Connection> >::ConstIterator i = clientConnections_.Find(connection);
418 if (i != clientConnections_.End())
419 return i->second_;
420 else
421 return 0;
422 }
423 }
424
GetServerConnection() const425 Connection* Network::GetServerConnection() const
426 {
427 return serverConnection_;
428 }
429
GetClientConnections() const430 Vector<SharedPtr<Connection> > Network::GetClientConnections() const
431 {
432 Vector<SharedPtr<Connection> > ret;
433 for (HashMap<kNet::MessageConnection*, SharedPtr<Connection> >::ConstIterator i = clientConnections_.Begin();
434 i != clientConnections_.End(); ++i)
435 ret.Push(i->second_);
436
437 return ret;
438 }
439
IsServerRunning() const440 bool Network::IsServerRunning() const
441 {
442 return network_->GetServer();
443 }
444
CheckRemoteEvent(StringHash eventType) const445 bool Network::CheckRemoteEvent(StringHash eventType) const
446 {
447 return allowedRemoteEvents_.Contains(eventType);
448 }
449
Update(float timeStep)450 void Network::Update(float timeStep)
451 {
452 URHO3D_PROFILE(UpdateNetwork);
453
454 // Process server connection if it exists
455 if (serverConnection_)
456 {
457 kNet::MessageConnection* connection = serverConnection_->GetMessageConnection();
458
459 // Receive new messages
460 connection->Process();
461
462 // Process latest data messages waiting for the correct nodes or components to be created
463 serverConnection_->ProcessPendingLatestData();
464
465 // Check for state transitions
466 kNet::ConnectionState state = connection->GetConnectionState();
467 if (serverConnection_->IsConnectPending() && state == kNet::ConnectionOK)
468 OnServerConnected();
469 else if (state == kNet::ConnectionPeerClosed)
470 serverConnection_->Disconnect();
471 else if (state == kNet::ConnectionClosed)
472 OnServerDisconnected();
473 }
474
475 // Process the network server if started
476 kNet::SharedPtr<kNet::NetworkServer> server = network_->GetServer();
477 if (server)
478 server->Process();
479 }
480
PostUpdate(float timeStep)481 void Network::PostUpdate(float timeStep)
482 {
483 URHO3D_PROFILE(PostUpdateNetwork);
484
485 // Check if periodic update should happen now
486 updateAcc_ += timeStep;
487 bool updateNow = updateAcc_ >= updateInterval_;
488
489 if (updateNow)
490 {
491 // Notify of the impending update to allow for example updated client controls to be set
492 SendEvent(E_NETWORKUPDATE);
493 updateAcc_ = fmodf(updateAcc_, updateInterval_);
494
495 if (IsServerRunning())
496 {
497 // Collect and prepare all networked scenes
498 {
499 URHO3D_PROFILE(PrepareServerUpdate);
500
501 networkScenes_.Clear();
502 for (HashMap<kNet::MessageConnection*, SharedPtr<Connection> >::Iterator i = clientConnections_.Begin();
503 i != clientConnections_.End(); ++i)
504 {
505 Scene* scene = i->second_->GetScene();
506 if (scene)
507 networkScenes_.Insert(scene);
508 }
509
510 for (HashSet<Scene*>::ConstIterator i = networkScenes_.Begin(); i != networkScenes_.End(); ++i)
511 (*i)->PrepareNetworkUpdate();
512 }
513
514 {
515 URHO3D_PROFILE(SendServerUpdate);
516
517 // Then send server updates for each client connection
518 for (HashMap<kNet::MessageConnection*, SharedPtr<Connection> >::Iterator i = clientConnections_.Begin();
519 i != clientConnections_.End(); ++i)
520 {
521 i->second_->SendServerUpdate();
522 i->second_->SendRemoteEvents();
523 i->second_->SendPackages();
524 }
525 }
526 }
527
528 if (serverConnection_)
529 {
530 // Send the client update
531 serverConnection_->SendClientUpdate();
532 serverConnection_->SendRemoteEvents();
533 }
534
535 // Notify that the update was sent
536 SendEvent(E_NETWORKUPDATESENT);
537 }
538 }
539
HandleBeginFrame(StringHash eventType,VariantMap & eventData)540 void Network::HandleBeginFrame(StringHash eventType, VariantMap& eventData)
541 {
542 using namespace BeginFrame;
543
544 Update(eventData[P_TIMESTEP].GetFloat());
545 }
546
HandleRenderUpdate(StringHash eventType,VariantMap & eventData)547 void Network::HandleRenderUpdate(StringHash eventType, VariantMap& eventData)
548 {
549 using namespace RenderUpdate;
550
551 PostUpdate(eventData[P_TIMESTEP].GetFloat());
552 }
553
OnServerConnected()554 void Network::OnServerConnected()
555 {
556 serverConnection_->SetConnectPending(false);
557
558 URHO3D_LOGINFO("Connected to server");
559
560 // Send the identity map now
561 VectorBuffer msg;
562 msg.WriteVariantMap(serverConnection_->GetIdentity());
563 serverConnection_->SendMessage(MSG_IDENTITY, true, true, msg);
564
565 SendEvent(E_SERVERCONNECTED);
566 }
567
OnServerDisconnected()568 void Network::OnServerDisconnected()
569 {
570 // Differentiate between failed connection, and disconnection
571 bool failedConnect = serverConnection_ && serverConnection_->IsConnectPending();
572 serverConnection_.Reset();
573
574 if (!failedConnect)
575 {
576 URHO3D_LOGINFO("Disconnected from server");
577 SendEvent(E_SERVERDISCONNECTED);
578 }
579 else
580 {
581 URHO3D_LOGERROR("Failed to connect to server");
582 SendEvent(E_CONNECTFAILED);
583 }
584 }
585
ConfigureNetworkSimulator()586 void Network::ConfigureNetworkSimulator()
587 {
588 if (serverConnection_)
589 serverConnection_->ConfigureNetworkSimulator(simulatedLatency_, simulatedPacketLoss_);
590
591 for (HashMap<kNet::MessageConnection*, SharedPtr<Connection> >::Iterator i = clientConnections_.Begin();
592 i != clientConnections_.End(); ++i)
593 i->second_->ConfigureNetworkSimulator(simulatedLatency_, simulatedPacketLoss_);
594 }
595
RegisterNetworkLibrary(Context * context)596 void RegisterNetworkLibrary(Context* context)
597 {
598 NetworkPriority::RegisterObject(context);
599 }
600
601 }
602