Network.cpp 36 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045
  1. //
  2. // Copyright (c) 2008-2019 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. #include "../Precompiled.h"
  23. #include "../Core/Context.h"
  24. #include "../Core/CoreEvents.h"
  25. #include "../Core/Profiler.h"
  26. #include "../Engine/EngineEvents.h"
  27. #include "../IO/FileSystem.h"
  28. #include "../Input/InputEvents.h"
  29. #include "../IO/IOEvents.h"
  30. #include "../IO/Log.h"
  31. #include "../IO/MemoryBuffer.h"
  32. #include "../Network/HttpRequest.h"
  33. #include "../Network/Network.h"
  34. #include "../Network/NetworkEvents.h"
  35. #include "../Network/NetworkPriority.h"
  36. #include "../Network/Protocol.h"
  37. #include "../Scene/Scene.h"
  38. #include <SLikeNet/MessageIdentifiers.h>
  39. #include <SLikeNet/NatPunchthroughClient.h>
  40. #include <SLikeNet/peerinterface.h>
  41. #include <SLikeNet/statistics.h>
  42. #ifdef SendMessage
  43. #undef SendMessage
  44. #endif
  45. #include "../DebugNew.h"
  46. namespace Urho3D
  47. {
  48. static const char* RAKNET_MESSAGEID_STRINGS[] = {
  49. "ID_CONNECTED_PING",
  50. "ID_UNCONNECTED_PING",
  51. "ID_UNCONNECTED_PING_OPEN_CONNECTIONS",
  52. "ID_CONNECTED_PONG",
  53. "ID_DETECT_LOST_CONNECTIONS",
  54. "ID_OPEN_CONNECTION_REQUEST_1",
  55. "ID_OPEN_CONNECTION_REPLY_1",
  56. "ID_OPEN_CONNECTION_REQUEST_2",
  57. "ID_OPEN_CONNECTION_REPLY_2",
  58. "ID_CONNECTION_REQUEST",
  59. "ID_REMOTE_SYSTEM_REQUIRES_PUBLIC_KEY",
  60. "ID_OUR_SYSTEM_REQUIRES_SECURITY",
  61. "ID_PUBLIC_KEY_MISMATCH",
  62. "ID_OUT_OF_BAND_INTERNAL",
  63. "ID_SND_RECEIPT_ACKED",
  64. "ID_SND_RECEIPT_LOSS",
  65. "ID_CONNECTION_REQUEST_ACCEPTED",
  66. "ID_CONNECTION_ATTEMPT_FAILED",
  67. "ID_ALREADY_CONNECTED",
  68. "ID_NEW_INCOMING_CONNECTION",
  69. "ID_NO_FREE_INCOMING_CONNECTIONS",
  70. "ID_DISCONNECTION_NOTIFICATION",
  71. "ID_CONNECTION_LOST",
  72. "ID_CONNECTION_BANNED",
  73. "ID_INVALID_PASSWORD",
  74. "ID_INCOMPATIBLE_PROTOCOL_VERSION",
  75. "ID_IP_RECENTLY_CONNECTED",
  76. "ID_TIMESTAMP",
  77. "ID_UNCONNECTED_PONG",
  78. "ID_ADVERTISE_SYSTEM",
  79. "ID_DOWNLOAD_PROGRESS",
  80. "ID_REMOTE_DISCONNECTION_NOTIFICATION",
  81. "ID_REMOTE_CONNECTION_LOST",
  82. "ID_REMOTE_NEW_INCOMING_CONNECTION",
  83. "ID_FILE_LIST_TRANSFER_HEADER",
  84. "ID_FILE_LIST_TRANSFER_FILE",
  85. "ID_FILE_LIST_REFERENCE_PUSH_ACK",
  86. "ID_DDT_DOWNLOAD_REQUEST",
  87. "ID_TRANSPORT_STRING",
  88. "ID_REPLICA_MANAGER_CONSTRUCTION",
  89. "ID_REPLICA_MANAGER_SCOPE_CHANGE",
  90. "ID_REPLICA_MANAGER_SERIALIZE",
  91. "ID_REPLICA_MANAGER_DOWNLOAD_STARTED",
  92. "ID_REPLICA_MANAGER_DOWNLOAD_COMPLETE",
  93. "ID_RAKVOICE_OPEN_CHANNEL_REQUEST",
  94. "ID_RAKVOICE_OPEN_CHANNEL_REPLY",
  95. "ID_RAKVOICE_CLOSE_CHANNEL",
  96. "ID_RAKVOICE_DATA",
  97. "ID_AUTOPATCHER_GET_CHANGELIST_SINCE_DATE",
  98. "ID_AUTOPATCHER_CREATION_LIST",
  99. "ID_AUTOPATCHER_DELETION_LIST",
  100. "ID_AUTOPATCHER_GET_PATCH",
  101. "ID_AUTOPATCHER_PATCH_LIST",
  102. "ID_AUTOPATCHER_REPOSITORY_FATAL_ERROR",
  103. "ID_AUTOPATCHER_CANNOT_DOWNLOAD_ORIGINAL_UNMODIFIED_FILES",
  104. "ID_AUTOPATCHER_FINISHED_INTERNAL",
  105. "ID_AUTOPATCHER_FINISHED",
  106. "ID_AUTOPATCHER_RESTART_APPLICATION",
  107. "ID_NAT_PUNCHTHROUGH_REQUEST",
  108. "ID_NAT_CONNECT_AT_TIME",
  109. "ID_NAT_GET_MOST_RECENT_PORT",
  110. "ID_NAT_CLIENT_READY",
  111. "ID_NAT_TARGET_NOT_CONNECTED",
  112. "ID_NAT_TARGET_UNRESPONSIVE",
  113. "ID_NAT_CONNECTION_TO_TARGET_LOST",
  114. "ID_NAT_ALREADY_IN_PROGRESS",
  115. "ID_NAT_PUNCHTHROUGH_FAILED",
  116. "ID_NAT_PUNCHTHROUGH_SUCCEEDED",
  117. "ID_READY_EVENT_SET",
  118. "ID_READY_EVENT_UNSET",
  119. "ID_READY_EVENT_ALL_SET",
  120. "ID_READY_EVENT_QUERY",
  121. "ID_LOBBY_GENERAL",
  122. "ID_RPC_REMOTE_ERROR",
  123. "ID_RPC_PLUGIN",
  124. "ID_FILE_LIST_REFERENCE_PUSH",
  125. "ID_READY_EVENT_FORCE_ALL_SET",
  126. "ID_ROOMS_EXECUTE_FUNC",
  127. "ID_ROOMS_LOGON_STATUS",
  128. "ID_ROOMS_HANDLE_CHANGE",
  129. "ID_LOBBY2_SEND_MESSAGE",
  130. "ID_LOBBY2_SERVER_ERROR",
  131. "ID_FCM2_NEW_HOST",
  132. "ID_FCM2_REQUEST_FCMGUID",
  133. "ID_FCM2_RESPOND_CONNECTION_COUNT",
  134. "ID_FCM2_INFORM_FCMGUID",
  135. "ID_FCM2_UPDATE_MIN_TOTAL_CONNECTION_COUNT",
  136. "ID_FCM2_VERIFIED_JOIN_START",
  137. "ID_FCM2_VERIFIED_JOIN_CAPABLE",
  138. "ID_FCM2_VERIFIED_JOIN_FAILED",
  139. "ID_FCM2_VERIFIED_JOIN_ACCEPTED",
  140. "ID_FCM2_VERIFIED_JOIN_REJECTED",
  141. "ID_UDP_PROXY_GENERAL",
  142. "ID_SQLite3_EXEC",
  143. "ID_SQLite3_UNKNOWN_DB",
  144. "ID_SQLLITE_LOGGER",
  145. "ID_NAT_TYPE_DETECTION_REQUEST",
  146. "ID_NAT_TYPE_DETECTION_RESULT",
  147. "ID_ROUTER_2_INTERNAL",
  148. "ID_ROUTER_2_FORWARDING_NO_PATH",
  149. "ID_ROUTER_2_FORWARDING_ESTABLISHED",
  150. "ID_ROUTER_2_REROUTED",
  151. "ID_TEAM_BALANCER_INTERNAL",
  152. "ID_TEAM_BALANCER_REQUESTED_TEAM_FULL",
  153. "ID_TEAM_BALANCER_REQUESTED_TEAM_LOCKED",
  154. "ID_TEAM_BALANCER_TEAM_REQUESTED_CANCELLED",
  155. "ID_TEAM_BALANCER_TEAM_ASSIGNED",
  156. "ID_LIGHTSPEED_INTEGRATION",
  157. "ID_XBOX_LOBBY",
  158. "ID_TWO_WAY_AUTHENTICATION_INCOMING_CHALLENGE_SUCCESS",
  159. "ID_TWO_WAY_AUTHENTICATION_OUTGOING_CHALLENGE_SUCCESS",
  160. "ID_TWO_WAY_AUTHENTICATION_INCOMING_CHALLENGE_FAILURE",
  161. "ID_TWO_WAY_AUTHENTICATION_OUTGOING_CHALLENGE_FAILURE",
  162. "ID_TWO_WAY_AUTHENTICATION_OUTGOING_CHALLENGE_TIMEOUT",
  163. "ID_TWO_WAY_AUTHENTICATION_NEGOTIATION",
  164. "ID_CLOUD_POST_REQUEST",
  165. "ID_CLOUD_RELEASE_REQUEST",
  166. "ID_CLOUD_GET_REQUEST",
  167. "ID_CLOUD_GET_RESPONSE",
  168. "ID_CLOUD_UNSUBSCRIBE_REQUEST",
  169. "ID_CLOUD_SERVER_TO_SERVER_COMMAND",
  170. "ID_CLOUD_SUBSCRIPTION_NOTIFICATION",
  171. "ID_LIB_VOICE",
  172. "ID_RELAY_PLUGIN",
  173. "ID_NAT_REQUEST_BOUND_ADDRESSES",
  174. "ID_NAT_RESPOND_BOUND_ADDRESSES",
  175. "ID_FCM2_UPDATE_USER_CONTEXT",
  176. "ID_RESERVED_3",
  177. "ID_RESERVED_4",
  178. "ID_RESERVED_5",
  179. "ID_RESERVED_6",
  180. "ID_RESERVED_7",
  181. "ID_RESERVED_8",
  182. "ID_RESERVED_9",
  183. "ID_USER_PACKET_ENUM"
  184. };
  185. static const int DEFAULT_UPDATE_FPS = 30;
  186. static const int SERVER_TIMEOUT_TIME = 10000;
  187. Network::Network(Context* context) :
  188. Object(context),
  189. updateFps_(DEFAULT_UPDATE_FPS),
  190. simulatedLatency_(0),
  191. simulatedPacketLoss_(0.0f),
  192. updateInterval_(1.0f / (float)DEFAULT_UPDATE_FPS),
  193. updateAcc_(0.0f),
  194. isServer_(false),
  195. scene_(nullptr),
  196. natPunchServerAddress_(nullptr),
  197. remoteGUID_(nullptr)
  198. {
  199. rakPeer_ = SLNet::RakPeerInterface::GetInstance();
  200. rakPeerClient_ = SLNet::RakPeerInterface::GetInstance();
  201. rakPeer_->SetTimeoutTime(SERVER_TIMEOUT_TIME, SLNet::UNASSIGNED_SYSTEM_ADDRESS);
  202. SetPassword("");
  203. SetDiscoveryBeacon(VariantMap());
  204. natPunchthroughClient_ = new SLNet::NatPunchthroughClient;
  205. natPunchthroughServerClient_ = new SLNet::NatPunchthroughClient;
  206. SetNATServerInfo("127.0.0.1", 61111);
  207. // Register Network library object factories
  208. RegisterNetworkLibrary(context_);
  209. SubscribeToEvent(E_BEGINFRAME, URHO3D_HANDLER(Network, HandleBeginFrame));
  210. SubscribeToEvent(E_RENDERUPDATE, URHO3D_HANDLER(Network, HandleRenderUpdate));
  211. // Blacklist remote events which are not to be allowed to be registered in any case
  212. blacklistedRemoteEvents_.Insert(E_CONSOLECOMMAND);
  213. blacklistedRemoteEvents_.Insert(E_LOGMESSAGE);
  214. blacklistedRemoteEvents_.Insert(E_BEGINFRAME);
  215. blacklistedRemoteEvents_.Insert(E_UPDATE);
  216. blacklistedRemoteEvents_.Insert(E_POSTUPDATE);
  217. blacklistedRemoteEvents_.Insert(E_RENDERUPDATE);
  218. blacklistedRemoteEvents_.Insert(E_ENDFRAME);
  219. blacklistedRemoteEvents_.Insert(E_MOUSEBUTTONDOWN);
  220. blacklistedRemoteEvents_.Insert(E_MOUSEBUTTONUP);
  221. blacklistedRemoteEvents_.Insert(E_MOUSEMOVE);
  222. blacklistedRemoteEvents_.Insert(E_MOUSEWHEEL);
  223. blacklistedRemoteEvents_.Insert(E_KEYDOWN);
  224. blacklistedRemoteEvents_.Insert(E_KEYUP);
  225. blacklistedRemoteEvents_.Insert(E_TEXTINPUT);
  226. blacklistedRemoteEvents_.Insert(E_JOYSTICKCONNECTED);
  227. blacklistedRemoteEvents_.Insert(E_JOYSTICKDISCONNECTED);
  228. blacklistedRemoteEvents_.Insert(E_JOYSTICKBUTTONDOWN);
  229. blacklistedRemoteEvents_.Insert(E_JOYSTICKBUTTONUP);
  230. blacklistedRemoteEvents_.Insert(E_JOYSTICKAXISMOVE);
  231. blacklistedRemoteEvents_.Insert(E_JOYSTICKHATMOVE);
  232. blacklistedRemoteEvents_.Insert(E_TOUCHBEGIN);
  233. blacklistedRemoteEvents_.Insert(E_TOUCHEND);
  234. blacklistedRemoteEvents_.Insert(E_TOUCHMOVE);
  235. blacklistedRemoteEvents_.Insert(E_GESTURERECORDED);
  236. blacklistedRemoteEvents_.Insert(E_GESTUREINPUT);
  237. blacklistedRemoteEvents_.Insert(E_MULTIGESTURE);
  238. blacklistedRemoteEvents_.Insert(E_DROPFILE);
  239. blacklistedRemoteEvents_.Insert(E_INPUTFOCUS);
  240. blacklistedRemoteEvents_.Insert(E_MOUSEVISIBLECHANGED);
  241. blacklistedRemoteEvents_.Insert(E_EXITREQUESTED);
  242. blacklistedRemoteEvents_.Insert(E_SERVERCONNECTED);
  243. blacklistedRemoteEvents_.Insert(E_SERVERDISCONNECTED);
  244. blacklistedRemoteEvents_.Insert(E_CONNECTFAILED);
  245. blacklistedRemoteEvents_.Insert(E_CLIENTCONNECTED);
  246. blacklistedRemoteEvents_.Insert(E_CLIENTDISCONNECTED);
  247. blacklistedRemoteEvents_.Insert(E_CLIENTIDENTITY);
  248. blacklistedRemoteEvents_.Insert(E_CLIENTSCENELOADED);
  249. blacklistedRemoteEvents_.Insert(E_NETWORKMESSAGE);
  250. blacklistedRemoteEvents_.Insert(E_NETWORKUPDATE);
  251. blacklistedRemoteEvents_.Insert(E_NETWORKUPDATESENT);
  252. blacklistedRemoteEvents_.Insert(E_NETWORKSCENELOADFAILED);
  253. }
  254. Network::~Network()
  255. {
  256. rakPeer_->DetachPlugin(natPunchthroughServerClient_);
  257. rakPeerClient_->DetachPlugin(natPunchthroughClient_);
  258. // If server connection exists, disconnect, but do not send an event because we are shutting down
  259. Disconnect(100);
  260. serverConnection_.Reset();
  261. clientConnections_.Clear();
  262. delete natPunchthroughServerClient_;
  263. natPunchthroughServerClient_ = nullptr;
  264. delete natPunchthroughClient_;
  265. natPunchthroughClient_ = nullptr;
  266. delete remoteGUID_;
  267. remoteGUID_ = nullptr;
  268. delete natPunchServerAddress_;
  269. natPunchServerAddress_ = nullptr;
  270. SLNet::RakPeerInterface::DestroyInstance(rakPeer_);
  271. SLNet::RakPeerInterface::DestroyInstance(rakPeerClient_);
  272. rakPeer_ = nullptr;
  273. rakPeerClient_ = nullptr;
  274. }
  275. void Network::HandleMessage(const SLNet::AddressOrGUID& source, int packetID, int msgID, const char* data, size_t numBytes)
  276. {
  277. // Only process messages from known sources
  278. Connection* connection = GetConnection(source);
  279. if (connection)
  280. {
  281. MemoryBuffer msg(data, (unsigned)numBytes);
  282. if (connection->ProcessMessage((int)msgID, msg))
  283. return;
  284. // If message was not handled internally, forward as an event
  285. using namespace NetworkMessage;
  286. VariantMap& eventData = GetEventDataMap();
  287. eventData[P_CONNECTION] = connection;
  288. eventData[P_MESSAGEID] = (int)msgID;
  289. eventData[P_DATA].SetBuffer(msg.GetData(), msg.GetSize());
  290. connection->SendEvent(E_NETWORKMESSAGE, eventData);
  291. }
  292. else
  293. URHO3D_LOGWARNING("Discarding message from unknown MessageConnection " + String(source.ToString()));
  294. }
  295. void Network::NewConnectionEstablished(const SLNet::AddressOrGUID& connection)
  296. {
  297. // Create a new client connection corresponding to this MessageConnection
  298. SharedPtr<Connection> newConnection(new Connection(context_, true, connection, rakPeer_));
  299. newConnection->ConfigureNetworkSimulator(simulatedLatency_, simulatedPacketLoss_);
  300. clientConnections_[connection] = newConnection;
  301. URHO3D_LOGINFO("Client " + newConnection->ToString() + " connected");
  302. using namespace ClientConnected;
  303. VariantMap& eventData = GetEventDataMap();
  304. eventData[P_CONNECTION] = newConnection;
  305. newConnection->SendEvent(E_CLIENTCONNECTED, eventData);
  306. }
  307. void Network::ClientDisconnected(const SLNet::AddressOrGUID& connection)
  308. {
  309. // Remove the client connection that corresponds to this MessageConnection
  310. HashMap<SLNet::AddressOrGUID, SharedPtr<Connection> >::Iterator i = clientConnections_.Find(connection);
  311. if (i != clientConnections_.End())
  312. {
  313. Connection* connection = i->second_;
  314. URHO3D_LOGINFO("Client " + connection->ToString() + " disconnected");
  315. using namespace ClientDisconnected;
  316. VariantMap& eventData = GetEventDataMap();
  317. eventData[P_CONNECTION] = connection;
  318. connection->SendEvent(E_CLIENTDISCONNECTED, eventData);
  319. clientConnections_.Erase(i);
  320. }
  321. }
  322. void Network::SetDiscoveryBeacon(const VariantMap& data)
  323. {
  324. VectorBuffer buffer;
  325. buffer.WriteVariantMap(data);
  326. if (buffer.GetSize() > 400)
  327. URHO3D_LOGERROR("Discovery beacon of size: " + String(buffer.GetSize()) + " bytes is too large, modify MAX_OFFLINE_DATA_LENGTH in RakNet or reduce size");
  328. rakPeer_->SetOfflinePingResponse((const char*)buffer.GetData(), buffer.GetSize());
  329. }
  330. void Network::DiscoverHosts(unsigned port)
  331. {
  332. // JSandusky: Contrary to the manual, we actually do have to perform Startup first before we can Ping
  333. if (!rakPeerClient_->IsActive())
  334. {
  335. SLNet::SocketDescriptor socket;
  336. // Startup local connection with max 1 incoming connection(first param) and 1 socket description (third param)
  337. rakPeerClient_->Startup(1, &socket, 1);
  338. }
  339. rakPeerClient_->Ping("255.255.255.255", port, false);
  340. }
  341. void Network::SetPassword(const String& password)
  342. {
  343. rakPeer_->SetIncomingPassword(password.CString(), password.Length());
  344. password_ = password;
  345. }
  346. bool Network::Connect(const String& address, unsigned short port, Scene* scene, const VariantMap& identity)
  347. {
  348. URHO3D_PROFILE(Connect);
  349. if (!rakPeerClient_->IsActive())
  350. {
  351. URHO3D_LOGINFO("Initializing client connection...");
  352. SLNet::SocketDescriptor socket;
  353. // Startup local connection with max 2 incoming connections(first param) and 1 socket description (third param)
  354. rakPeerClient_->Startup(2, &socket, 1);
  355. }
  356. //isServer_ = false;
  357. SLNet::ConnectionAttemptResult connectResult = rakPeerClient_->Connect(address.CString(), port, password_.CString(), password_.Length());
  358. if (connectResult == SLNet::CONNECTION_ATTEMPT_STARTED)
  359. {
  360. serverConnection_ = new Connection(context_, false, rakPeerClient_->GetMyBoundAddress(), rakPeerClient_);
  361. serverConnection_->SetScene(scene);
  362. serverConnection_->SetIdentity(identity);
  363. serverConnection_->SetConnectPending(true);
  364. serverConnection_->ConfigureNetworkSimulator(simulatedLatency_, simulatedPacketLoss_);
  365. URHO3D_LOGINFO("Connecting to server " + address + ":" + String(port) + ", Client: " + serverConnection_->ToString());
  366. return true;
  367. }
  368. else if (connectResult == SLNet::ALREADY_CONNECTED_TO_ENDPOINT) {
  369. URHO3D_LOGWARNING("Already connected to server!");
  370. SendEvent(E_CONNECTIONINPROGRESS);
  371. return false;
  372. }
  373. else if (connectResult == SLNet::CONNECTION_ATTEMPT_ALREADY_IN_PROGRESS) {
  374. URHO3D_LOGWARNING("Connection attempt already in progress!");
  375. SendEvent(E_CONNECTIONINPROGRESS);
  376. return false;
  377. }
  378. else
  379. {
  380. URHO3D_LOGERROR("Failed to connect to server " + address + ":" + String(port) + ", error code: " + String((int)connectResult));
  381. SendEvent(E_CONNECTFAILED);
  382. return false;
  383. }
  384. }
  385. void Network::Disconnect(int waitMSec)
  386. {
  387. if (!serverConnection_)
  388. return;
  389. URHO3D_PROFILE(Disconnect);
  390. serverConnection_->Disconnect(waitMSec);
  391. }
  392. bool Network::StartServer(unsigned short port)
  393. {
  394. if (IsServerRunning())
  395. return true;
  396. URHO3D_PROFILE(StartServer);
  397. SLNet::SocketDescriptor socket;//(port, AF_INET);
  398. socket.port = port;
  399. socket.socketFamily = AF_INET;
  400. // Startup local connection with max 128 incoming connection(first param) and 1 socket description (third param)
  401. SLNet::StartupResult startResult = rakPeer_->Startup(128, &socket, 1);
  402. if (startResult == SLNet::RAKNET_STARTED)
  403. {
  404. URHO3D_LOGINFO("Started server on port " + String(port));
  405. rakPeer_->SetMaximumIncomingConnections(128);
  406. isServer_ = true;
  407. rakPeer_->SetOccasionalPing(true);
  408. rakPeer_->SetUnreliableTimeout(1000);
  409. //rakPeer_->SetIncomingPassword("Parole", (int)strlen("Parole"));
  410. return true;
  411. }
  412. else
  413. {
  414. URHO3D_LOGINFO("Failed to start server on port " + String(port) + ", error code: " + String((int)startResult));
  415. return false;
  416. }
  417. }
  418. void Network::StopServer()
  419. {
  420. clientConnections_.Clear();
  421. if (!rakPeer_)
  422. return;
  423. if (!IsServerRunning())
  424. return;
  425. // Provide 300 ms to notify
  426. rakPeer_->Shutdown(300);
  427. URHO3D_PROFILE(StopServer);
  428. URHO3D_LOGINFO("Stopped server");
  429. }
  430. void Network::SetNATServerInfo(const String& address, unsigned short port)
  431. {
  432. if (!natPunchServerAddress_)
  433. natPunchServerAddress_ = new SLNet::SystemAddress;
  434. natPunchServerAddress_->FromStringExplicitPort(address.CString(), port);
  435. }
  436. void Network::StartNATClient()
  437. {
  438. if (!rakPeer_) {
  439. URHO3D_LOGERROR("Unable to start NAT client, client not initialized!");
  440. return;
  441. }
  442. if (natPunchServerAddress_->GetPort() == 0) {
  443. URHO3D_LOGERROR("NAT master server address incorrect!");
  444. return;
  445. }
  446. rakPeer_->AttachPlugin(natPunchthroughServerClient_);
  447. guid_ = String(rakPeer_->GetGuidFromSystemAddress(SLNet::UNASSIGNED_SYSTEM_ADDRESS).ToString());
  448. URHO3D_LOGINFO("GUID: " + guid_);
  449. rakPeer_->Connect(natPunchServerAddress_->ToString(false), natPunchServerAddress_->GetPort(), nullptr, 0);
  450. }
  451. void Network::AttemptNATPunchtrough(const String& guid, Scene* scene, const VariantMap& identity)
  452. {
  453. scene_ = scene;
  454. identity_ = identity;
  455. if (!remoteGUID_)
  456. remoteGUID_ = new SLNet::RakNetGUID;
  457. remoteGUID_->FromString(guid.CString());
  458. rakPeerClient_->AttachPlugin(natPunchthroughClient_);
  459. if (rakPeerClient_->IsActive()) {
  460. natPunchthroughClient_->OpenNAT(*remoteGUID_, *natPunchServerAddress_);
  461. }
  462. else {
  463. SLNet::SocketDescriptor socket;
  464. // Startup local connection with max 2 incoming connections(first param) and 1 socket description (third param)
  465. rakPeerClient_->Startup(2, &socket, 1);
  466. }
  467. rakPeerClient_->Connect(natPunchServerAddress_->ToString(false), natPunchServerAddress_->GetPort(), nullptr, 0);
  468. }
  469. void Network::BroadcastMessage(int msgID, bool reliable, bool inOrder, const VectorBuffer& msg, unsigned contentID)
  470. {
  471. BroadcastMessage(msgID, reliable, inOrder, msg.GetData(), msg.GetSize(), contentID);
  472. }
  473. void Network::BroadcastMessage(int msgID, bool reliable, bool inOrder, const unsigned char* data, unsigned numBytes,
  474. unsigned contentID)
  475. {
  476. if (!rakPeer_)
  477. return;
  478. /* Make sure not to use SLikeNet(RakNet) internal message ID's
  479. and since RakNet uses 1 byte message ID's, they cannot exceed 255 limit */
  480. if (msgID < ID_USER_PACKET_ENUM || msgID >= 255)
  481. {
  482. URHO3D_LOGERROR("Can not send message with reserved ID");
  483. return;
  484. }
  485. VectorBuffer msgData;
  486. msgData.WriteUByte((unsigned char)msgID);
  487. msgData.Write(data, numBytes);
  488. if (isServer_)
  489. rakPeer_->Send((const char*)msgData.GetData(), (int)msgData.GetSize(), HIGH_PRIORITY, RELIABLE, (char)0, SLNet::UNASSIGNED_RAKNET_GUID, true);
  490. else
  491. URHO3D_LOGERROR("Server not running, can not broadcast messages");
  492. }
  493. void Network::BroadcastRemoteEvent(StringHash eventType, bool inOrder, const VariantMap& eventData)
  494. {
  495. for (HashMap<SLNet::AddressOrGUID, SharedPtr<Connection> >::Iterator i = clientConnections_.Begin(); i != clientConnections_.End(); ++i)
  496. i->second_->SendRemoteEvent(eventType, inOrder, eventData);
  497. }
  498. void Network::BroadcastRemoteEvent(Scene* scene, StringHash eventType, bool inOrder, const VariantMap& eventData)
  499. {
  500. for (HashMap<SLNet::AddressOrGUID, SharedPtr<Connection> >::Iterator i = clientConnections_.Begin();
  501. i != clientConnections_.End(); ++i)
  502. {
  503. if (i->second_->GetScene() == scene)
  504. i->second_->SendRemoteEvent(eventType, inOrder, eventData);
  505. }
  506. }
  507. void Network::BroadcastRemoteEvent(Node* node, StringHash eventType, bool inOrder, const VariantMap& eventData)
  508. {
  509. if (!node)
  510. {
  511. URHO3D_LOGERROR("Null sender node for remote node event");
  512. return;
  513. }
  514. if (!node->IsReplicated())
  515. {
  516. URHO3D_LOGERROR("Sender node has a local ID, can not send remote node event");
  517. return;
  518. }
  519. Scene* scene = node->GetScene();
  520. for (HashMap<SLNet::AddressOrGUID, SharedPtr<Connection> >::Iterator i = clientConnections_.Begin();
  521. i != clientConnections_.End(); ++i)
  522. {
  523. if (i->second_->GetScene() == scene)
  524. i->second_->SendRemoteEvent(node, eventType, inOrder, eventData);
  525. }
  526. }
  527. void Network::SetUpdateFps(int fps)
  528. {
  529. updateFps_ = Max(fps, 1);
  530. updateInterval_ = 1.0f / (float)updateFps_;
  531. updateAcc_ = 0.0f;
  532. }
  533. void Network::SetSimulatedLatency(int ms)
  534. {
  535. simulatedLatency_ = Max(ms, 0);
  536. ConfigureNetworkSimulator();
  537. }
  538. void Network::SetSimulatedPacketLoss(float probability)
  539. {
  540. simulatedPacketLoss_ = Clamp(probability, 0.0f, 1.0f);
  541. ConfigureNetworkSimulator();
  542. }
  543. void Network::RegisterRemoteEvent(StringHash eventType)
  544. {
  545. if (blacklistedRemoteEvents_.Find(eventType) != blacklistedRemoteEvents_.End())
  546. {
  547. URHO3D_LOGERROR("Attempted to register blacklisted remote event type " + String(eventType));
  548. return;
  549. }
  550. allowedRemoteEvents_.Insert(eventType);
  551. }
  552. void Network::UnregisterRemoteEvent(StringHash eventType)
  553. {
  554. allowedRemoteEvents_.Erase(eventType);
  555. }
  556. void Network::UnregisterAllRemoteEvents()
  557. {
  558. allowedRemoteEvents_.Clear();
  559. }
  560. void Network::SetPackageCacheDir(const String& path)
  561. {
  562. packageCacheDir_ = AddTrailingSlash(path);
  563. }
  564. void Network::SendPackageToClients(Scene* scene, PackageFile* package)
  565. {
  566. if (!scene)
  567. {
  568. URHO3D_LOGERROR("Null scene specified for SendPackageToClients");
  569. return;
  570. }
  571. if (!package)
  572. {
  573. URHO3D_LOGERROR("Null package specified for SendPackageToClients");
  574. return;
  575. }
  576. for (HashMap<SLNet::AddressOrGUID, SharedPtr<Connection> >::Iterator i = clientConnections_.Begin();
  577. i != clientConnections_.End(); ++i)
  578. {
  579. if (i->second_->GetScene() == scene)
  580. i->second_->SendPackageToClient(package);
  581. }
  582. }
  583. SharedPtr<HttpRequest> Network::MakeHttpRequest(const String& url, const String& verb, const Vector<String>& headers,
  584. const String& postData)
  585. {
  586. URHO3D_PROFILE(MakeHttpRequest);
  587. // The initialization of the request will take time, can not know at this point if it has an error or not
  588. SharedPtr<HttpRequest> request(new HttpRequest(url, verb, headers, postData));
  589. return request;
  590. }
  591. void Network::BanAddress(const String& address)
  592. {
  593. rakPeer_->AddToBanList(address.CString(), 0);
  594. }
  595. Connection* Network::GetConnection(const SLNet::AddressOrGUID& connection) const
  596. {
  597. if (serverConnection_ && serverConnection_->GetAddressOrGUID() == connection)
  598. return serverConnection_;
  599. else
  600. {
  601. HashMap<SLNet::AddressOrGUID, SharedPtr<Connection> >::ConstIterator i = clientConnections_.Find(connection);
  602. if (i != clientConnections_.End())
  603. return i->second_;
  604. else
  605. return nullptr;
  606. }
  607. }
  608. Connection* Network::GetServerConnection() const
  609. {
  610. return serverConnection_;
  611. }
  612. Vector<SharedPtr<Connection> > Network::GetClientConnections() const
  613. {
  614. Vector<SharedPtr<Connection> > ret;
  615. for (HashMap<SLNet::AddressOrGUID, SharedPtr<Connection> >::ConstIterator i = clientConnections_.Begin();
  616. i != clientConnections_.End(); ++i)
  617. ret.Push(i->second_);
  618. return ret;
  619. }
  620. bool Network::IsServerRunning() const
  621. {
  622. if (!rakPeer_)
  623. return false;
  624. return rakPeer_->IsActive() && isServer_;
  625. }
  626. bool Network::CheckRemoteEvent(StringHash eventType) const
  627. {
  628. return allowedRemoteEvents_.Contains(eventType);
  629. }
  630. void Network::HandleIncomingPacket(SLNet::Packet* packet, bool isServer)
  631. {
  632. unsigned char packetID = packet->data[0];
  633. bool packetHandled = false;
  634. // Deal with timestamped backents
  635. unsigned dataStart = sizeof(char);
  636. if (packetID == ID_TIMESTAMP)
  637. {
  638. dataStart += sizeof(SLNet::Time);
  639. packetID = packet->data[dataStart];
  640. dataStart += sizeof(char);
  641. }
  642. if (packetID == ID_NEW_INCOMING_CONNECTION)
  643. {
  644. if (isServer)
  645. {
  646. NewConnectionEstablished(packet->systemAddress);
  647. packetHandled = true;
  648. }
  649. }
  650. else if (packetID == ID_ALREADY_CONNECTED)
  651. {
  652. if (natPunchServerAddress_ && packet->systemAddress == *natPunchServerAddress_) {
  653. URHO3D_LOGINFO("Already connected to NAT server! ");
  654. if (!isServer)
  655. {
  656. natPunchthroughClient_->OpenNAT(*remoteGUID_, *natPunchServerAddress_);
  657. }
  658. }
  659. packetHandled = true;
  660. }
  661. else if (packetID == ID_CONNECTION_REQUEST_ACCEPTED) // We're a client, our connection as been accepted
  662. {
  663. if(natPunchServerAddress_ && packet->systemAddress == *natPunchServerAddress_) {
  664. URHO3D_LOGINFO("Succesfully connected to NAT punchtrough server! ");
  665. SendEvent(E_NATMASTERCONNECTIONSUCCEEDED);
  666. if (!isServer)
  667. {
  668. natPunchthroughClient_->OpenNAT(*remoteGUID_, *natPunchServerAddress_);
  669. }
  670. } else {
  671. if (!isServer)
  672. {
  673. OnServerConnected(packet->systemAddress);
  674. }
  675. }
  676. packetHandled = true;
  677. }
  678. else if (packetID == ID_NAT_TARGET_NOT_CONNECTED)
  679. {
  680. URHO3D_LOGERROR("Target server not connected to NAT master server!");
  681. packetHandled = true;
  682. }
  683. else if (packetID == ID_CONNECTION_LOST) // We've lost connectivity with the packet source
  684. {
  685. if (isServer)
  686. {
  687. ClientDisconnected(packet->systemAddress);
  688. }
  689. else
  690. {
  691. OnServerDisconnected();
  692. }
  693. packetHandled = true;
  694. }
  695. else if (packetID == ID_DISCONNECTION_NOTIFICATION) // We've lost connection with the other side
  696. {
  697. if (isServer)
  698. {
  699. ClientDisconnected(packet->systemAddress);
  700. }
  701. else
  702. {
  703. OnServerDisconnected();
  704. }
  705. packetHandled = true;
  706. }
  707. else if (packetID == ID_CONNECTION_ATTEMPT_FAILED) // We've failed to connect to the server/peer
  708. {
  709. if (natPunchServerAddress_ && packet->systemAddress == *natPunchServerAddress_) {
  710. URHO3D_LOGERROR("Connection to NAT punchtrough server failed!");
  711. SendEvent(E_NATMASTERCONNECTIONFAILED);
  712. } else {
  713. if (!isServer)
  714. {
  715. OnServerDisconnected();
  716. }
  717. }
  718. packetHandled = true;
  719. }
  720. else if (packetID == ID_NAT_PUNCHTHROUGH_SUCCEEDED)
  721. {
  722. SLNet::SystemAddress remotePeer = packet->systemAddress;
  723. URHO3D_LOGINFO("NAT punchtrough succeeded! Remote peer: " + String(remotePeer.ToString()));
  724. if (!isServer)
  725. {
  726. using namespace NetworkNatPunchtroughSucceeded;
  727. VariantMap eventMap;
  728. eventMap[P_ADDRESS] = remotePeer.ToString(false);
  729. eventMap[P_PORT] = remotePeer.GetPort();
  730. SendEvent(E_NETWORKNATPUNCHTROUGHSUCCEEDED, eventMap);
  731. URHO3D_LOGINFO("Connecting to server behind NAT: " + String(remotePeer.ToString()));
  732. Connect(String(remotePeer.ToString(false)), remotePeer.GetPort(), scene_, identity_);
  733. }
  734. packetHandled = true;
  735. }
  736. else if (packetID == ID_NAT_PUNCHTHROUGH_FAILED)
  737. {
  738. URHO3D_LOGERROR("NAT punchtrough failed!");
  739. SLNet::SystemAddress remotePeer = packet->systemAddress;
  740. using namespace NetworkNatPunchtroughFailed;
  741. VariantMap eventMap;
  742. eventMap[P_ADDRESS] = remotePeer.ToString(false);
  743. eventMap[P_PORT] = remotePeer.GetPort();
  744. SendEvent(E_NETWORKNATPUNCHTROUGHFAILED, eventMap);
  745. packetHandled = true;
  746. }
  747. else if (packetID == ID_CONNECTION_BANNED) // We're a client and we're on the ban list
  748. {
  749. URHO3D_LOGERROR("Connection failed, you're banned!");
  750. SendEvent(E_NETWORKBANNED);
  751. packetHandled = true;
  752. }
  753. else if (packetID == ID_INVALID_PASSWORD) // We're a client, and we gave an invalid password
  754. {
  755. URHO3D_LOGERROR("Invalid password provided for connection!");
  756. SendEvent(E_NETWORKINVALIDPASSWORD);
  757. packetHandled = true;
  758. }
  759. else if (packetID == ID_DOWNLOAD_PROGRESS) // Part of a file transfer
  760. {
  761. //URHO3D_LOGINFO("101010");
  762. }
  763. else if (packetID == ID_UNCONNECTED_PING)
  764. {
  765. packetHandled = true;
  766. }
  767. else if (packetID == ID_UNCONNECTED_PONG) // Host discovery response
  768. {
  769. if (!isServer)
  770. {
  771. using namespace NetworkHostDiscovered;
  772. dataStart += sizeof(SLNet::TimeMS);
  773. VariantMap& eventMap = context_->GetEventDataMap();
  774. if (packet->length > packet->length - dataStart) {
  775. VectorBuffer buffer(packet->data + dataStart, packet->length - dataStart);
  776. VariantMap srcData = buffer.ReadVariantMap();
  777. eventMap[P_BEACON] = srcData;
  778. }
  779. else {
  780. eventMap[P_BEACON] = VariantMap();
  781. }
  782. eventMap[P_ADDRESS] = String(packet->systemAddress.ToString(false));
  783. eventMap[P_PORT] = (int)packet->systemAddress.GetPort();
  784. SendEvent(E_NETWORKHOSTDISCOVERED, eventMap);
  785. }
  786. packetHandled = true;
  787. }
  788. // Urho3D messages
  789. if (packetID >= ID_USER_PACKET_ENUM)
  790. {
  791. if (isServer)
  792. {
  793. HandleMessage(packet->systemAddress, 0, packetID, (const char*)(packet->data + dataStart), packet->length - dataStart);
  794. }
  795. else
  796. {
  797. MemoryBuffer buffer(packet->data + dataStart, packet->length - dataStart);
  798. bool processed = serverConnection_->ProcessMessage(packetID, buffer);
  799. if (!processed)
  800. {
  801. HandleMessage(packet->systemAddress, 0, packetID, (const char*)(packet->data + dataStart), packet->length - dataStart);
  802. }
  803. }
  804. packetHandled = true;
  805. }
  806. if (!packetHandled && packetID < sizeof(RAKNET_MESSAGEID_STRINGS))
  807. URHO3D_LOGERROR("Unhandled network packet: " + String(RAKNET_MESSAGEID_STRINGS[packetID]));
  808. else if (!packetHandled)
  809. URHO3D_LOGERRORF("Unhandled network packet: %i", packetID);
  810. }
  811. void Network::Update(float timeStep)
  812. {
  813. URHO3D_PROFILE(UpdateNetwork);
  814. //Process all incoming messages for the server
  815. if (rakPeer_->IsActive())
  816. {
  817. while (SLNet::Packet* packet = rakPeer_->Receive())
  818. {
  819. HandleIncomingPacket(packet, true);
  820. rakPeer_->DeallocatePacket(packet);
  821. }
  822. }
  823. // Process all incoming messages for the client
  824. if (rakPeerClient_->IsActive())
  825. {
  826. while (SLNet::Packet* packet = rakPeerClient_->Receive())
  827. {
  828. HandleIncomingPacket(packet, false);
  829. rakPeerClient_->DeallocatePacket(packet);
  830. }
  831. }
  832. }
  833. void Network::PostUpdate(float timeStep)
  834. {
  835. URHO3D_PROFILE(PostUpdateNetwork);
  836. // Check if periodic update should happen now
  837. updateAcc_ += timeStep;
  838. bool updateNow = updateAcc_ >= updateInterval_;
  839. if (updateNow)
  840. {
  841. // Notify of the impending update to allow for example updated client controls to be set
  842. SendEvent(E_NETWORKUPDATE);
  843. updateAcc_ = fmodf(updateAcc_, updateInterval_);
  844. if (IsServerRunning())
  845. {
  846. // Collect and prepare all networked scenes
  847. {
  848. URHO3D_PROFILE(PrepareServerUpdate);
  849. networkScenes_.Clear();
  850. for (HashMap<SLNet::AddressOrGUID, SharedPtr<Connection> >::Iterator i = clientConnections_.Begin();
  851. i != clientConnections_.End(); ++i)
  852. {
  853. Scene* scene = i->second_->GetScene();
  854. if (scene)
  855. networkScenes_.Insert(scene);
  856. }
  857. for (HashSet<Scene*>::ConstIterator i = networkScenes_.Begin(); i != networkScenes_.End(); ++i)
  858. (*i)->PrepareNetworkUpdate();
  859. }
  860. {
  861. URHO3D_PROFILE(SendServerUpdate);
  862. // Then send server updates for each client connection
  863. for (HashMap<SLNet::AddressOrGUID, SharedPtr<Connection> >::Iterator i = clientConnections_.Begin();
  864. i != clientConnections_.End(); ++i)
  865. {
  866. i->second_->SendServerUpdate();
  867. i->second_->SendRemoteEvents();
  868. i->second_->SendPackages();
  869. }
  870. }
  871. }
  872. if (serverConnection_)
  873. {
  874. // Send the client update
  875. serverConnection_->SendClientUpdate();
  876. serverConnection_->SendRemoteEvents();
  877. }
  878. // Notify that the update was sent
  879. SendEvent(E_NETWORKUPDATESENT);
  880. }
  881. }
  882. void Network::HandleBeginFrame(StringHash eventType, VariantMap& eventData)
  883. {
  884. using namespace BeginFrame;
  885. Update(eventData[P_TIMESTEP].GetFloat());
  886. }
  887. void Network::HandleRenderUpdate(StringHash eventType, VariantMap& eventData)
  888. {
  889. using namespace RenderUpdate;
  890. PostUpdate(eventData[P_TIMESTEP].GetFloat());
  891. }
  892. void Network::OnServerConnected(const SLNet::AddressOrGUID& address)
  893. {
  894. serverConnection_->SetConnectPending(false);
  895. serverConnection_->SetAddressOrGUID(address);
  896. URHO3D_LOGINFO("Connected to server!");
  897. // Send the identity map now
  898. VectorBuffer msg;
  899. msg.WriteVariantMap(serverConnection_->GetIdentity());
  900. serverConnection_->SendMessage(MSG_IDENTITY, true, true, msg);
  901. SendEvent(E_SERVERCONNECTED);
  902. }
  903. void Network::OnServerDisconnected()
  904. {
  905. // Differentiate between failed connection, and disconnection
  906. bool failedConnect = serverConnection_ && serverConnection_->IsConnectPending();
  907. serverConnection_.Reset();
  908. if (!failedConnect)
  909. {
  910. URHO3D_LOGINFO("Disconnected from server");
  911. SendEvent(E_SERVERDISCONNECTED);
  912. }
  913. else
  914. {
  915. URHO3D_LOGERROR("Failed to connect to server");
  916. SendEvent(E_CONNECTFAILED);
  917. }
  918. }
  919. void Network::ConfigureNetworkSimulator()
  920. {
  921. if (serverConnection_)
  922. serverConnection_->ConfigureNetworkSimulator(simulatedLatency_, simulatedPacketLoss_);
  923. for (HashMap<SLNet::AddressOrGUID, SharedPtr<Connection> >::Iterator i = clientConnections_.Begin();
  924. i != clientConnections_.End(); ++i)
  925. i->second_->ConfigureNetworkSimulator(simulatedLatency_, simulatedPacketLoss_);
  926. }
  927. void RegisterNetworkLibrary(Context* context)
  928. {
  929. NetworkPriority::RegisterObject(context);
  930. }
  931. }