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