RigsofRods
Soft-body Physics Simulation
Network.cpp
Go to the documentation of this file.
1 /*
2  This source file is part of Rigs of Rods
3  Copyright 2005-2012 Pierre-Michel Ricordel
4  Copyright 2007-2012 Thomas Fischer
5  Copyright 2013-2016 Petr Ohlidal
6 
7  For more information, see http://www.rigsofrods.org/
8 
9  Rigs of Rods is free software: you can redistribute it and/or modify
10  it under the terms of the GNU General Public License version 3, as
11  published by the Free Software Foundation.
12 
13  Rigs of Rods is distributed in the hope that it will be useful,
14  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  GNU General Public License for more details.
17 
18  You should have received a copy of the GNU General Public License
19  along with Rigs of Rods. If not, see <http://www.gnu.org/licenses/>.
20 */
21 
22 #ifdef USE_SOCKETW
23 
24 #include "Network.h"
25 
26 #include "Application.h"
27 #include "ChatSystem.h"
28 #include "Console.h"
29 #include "ErrorUtils.h"
30 #include "GameContext.h"
31 #include "GUIManager.h"
32 #include "GUI_TopMenubar.h"
33 #include "Language.h"
34 #include "RoRVersion.h"
35 #include "ScriptEngine.h"
36 #include "Utils.h"
37 
38 #include <Ogre.h>
39 #include <SocketW.h>
40 
41 #include <algorithm>
42 #include <chrono>
43 #include <cstring>
44 
45 using namespace RoR;
46 
47 static Ogre::ColourValue MP_COLORS[] = // Classic RoR multiplayer colors
48 {
49  Ogre::ColourValue(0.0, 0.8, 0.0),
50  Ogre::ColourValue(0.0, 0.4, 0.701960784314),
51  Ogre::ColourValue(1.0, 0.501960784314, 0.0),
52  Ogre::ColourValue(1.0, 0.8, 0.0),
53  //Ogre::ColourValue(0.2, 0.0, 0.6),
54  //Ogre::ColourValue(0.6, 0.0, 0.6),
55  Ogre::ColourValue(0.8, 1.0, 0.0),
56  Ogre::ColourValue(1.0, 0.0, 0.0),
57  Ogre::ColourValue(0.501960784314, 0.501960784314, 0.501960784314),
58  Ogre::ColourValue(0.0, 0.560784313725, 0.0),
59  //Ogre::ColourValue(0.0, 0.282352941176, 0.490196078431),
60  Ogre::ColourValue(0.701960784314, 0.352941176471, 0.0),
61  Ogre::ColourValue(0.701960784314, 0.560784313725, 0.0),
62  //Ogre::ColourValue(0.419607843137, 0.0, 0.419607843137),
63  Ogre::ColourValue(0.560784313725, 0.701960784314, 0.0),
64  Ogre::ColourValue(0.701960784314, 0.0, 0.0),
65  Ogre::ColourValue(0.745098039216, 0.745098039216, 0.745098039216),
66  Ogre::ColourValue(0.501960784314, 1.0, 0.501960784314),
67  Ogre::ColourValue(0.501960784314, 0.788235294118, 1.0),
68  Ogre::ColourValue(1.0, 0.752941176471, 0.501960784314),
69  Ogre::ColourValue(1.0, 0.901960784314, 0.501960784314),
70  Ogre::ColourValue(0.666666666667, 0.501960784314, 1.0),
71  Ogre::ColourValue(0.933333333333, 0.0, 0.8),
72  Ogre::ColourValue(1.0, 0.501960784314, 0.501960784314),
73  Ogre::ColourValue(0.4, 0.4, 0.0),
74  Ogre::ColourValue(1.0, 0.749019607843, 1.0),
75  Ogre::ColourValue(0.0, 1.0, 0.8),
76  Ogre::ColourValue(0.8, 0.4, 0.6),
77  Ogre::ColourValue(0.6, 0.6, 0.0),
78 };
79 
80 using namespace RoRnet;
81 
82 static const unsigned int m_packet_buffer_size = 20;
83 
84 #define LOG_THREAD(_MSG_) { std::stringstream s; s << _MSG_ << " (Thread ID: " << std::this_thread::get_id() << ")"; LOG(s.str()); }
85 #define LOGSTREAM Ogre::LogManager().getSingleton().stream()
86 
87 void Network::PushNetMessage(MsgType type, std::string const & message)
88 {
89  Message m(type);
90  m.description = message;
92 }
93 
94 Ogre::ColourValue Network::GetPlayerColor(int color_num)
95 {
96  int numColours = sizeof(MP_COLORS) / sizeof(Ogre::ColourValue);
97  if (color_num < 0 || color_num >= numColours)
98  return Ogre::ColourValue::White;
99 
100  return MP_COLORS[color_num];
101 }
102 
103 // Internal helper
104 void DebugPacket(const char *name, RoRnet::Header *header, char *buffer)
105 {
106  std::stringstream msg;
107  msg << "++ " << name << ": " << header->source << ", " << header->streamid
108  << ", "<< header->command << ", " << header->size << ", hash: " << HashData(buffer, header->size);
109  LOG(msg.str());
110 }
111 
112 void Network::SetNetQuality(int quality)
113 {
114  m_net_quality = quality;
115 }
116 
118 {
119  return m_net_quality;
120 }
121 
123 {
124  return m_uid;
125 }
126 
127 bool Network::SendMessageRaw(char *buffer, int msgsize)
128 {
129  SWBaseSocket::SWBaseError error;
130 
131  if (m_socket.fsend(buffer, msgsize, &error) < msgsize)
132  {
133  LOG("NET send error: " + error.get_error());
134  return false;
135  }
136 
137  return true;
138 }
139 
140 bool Network::SendNetMessage(int type, unsigned int streamid, int len, char* content)
141 {
142  RoRnet::Header head;
143  memset(&head, 0, sizeof(RoRnet::Header));
144  head.command = type;
145  head.source = m_uid;
146  head.size = len;
147  head.streamid = streamid;
148 
149  const int msgsize = sizeof(RoRnet::Header) + len;
150 
151  if (msgsize >= RORNET_MAX_MESSAGE_LENGTH)
152  {
153  return false;
154  }
155 
156  char buffer[RORNET_MAX_MESSAGE_LENGTH] = {0};
157  memcpy(buffer, (char *)&head, sizeof(RoRnet::Header));
158  memcpy(buffer + sizeof(RoRnet::Header), content, len);
159 
160  return SendMessageRaw(buffer, msgsize);
161 }
162 
163 void Network::QueueStreamData(RoRnet::Header &header, char *buffer, size_t buffer_len)
164 {
165  NetRecvPacket packet;
166  packet.header = header;
167  memcpy(packet.buffer, buffer, std::min(buffer_len, size_t(RORNET_MAX_MESSAGE_LENGTH)));
168 
169  std::lock_guard<std::mutex> lock(m_recv_packetqueue_mutex);
170  m_recv_packet_buffer.push_back(packet);
171 }
172 
173 int Network::ReceiveMessage(RoRnet::Header *head, char* content, int bufferlen)
174 {
175  SWBaseSocket::SWBaseError error;
176 
177 #ifdef DEBUG
178  LOG_THREAD("[RoR|Networking] ReceiveMessage() waiting...");
179 #endif //DEBUG
180 
181  if (m_socket.frecv((char*)head, sizeof(RoRnet::Header), &error) < sizeof(RoRnet::Header))
182  {
183  LOG("NET receive error 1: " + error.get_error());
184  return -1;
185  }
186 
187 #ifdef DEBUG
188  LOG_THREAD("[RoR|Networking] ReceiveMessage() header received");
189 #endif //DEBUG
190 
191  if (head->size > uint32_t(bufferlen))
192  {
193  return -3;
194  }
195 
196  if (head->size > 0)
197  {
198  // Read the packet content
199  std::memset(content, 0, bufferlen);
200  if (m_socket.frecv(content, head->size, &error) < static_cast<int>(head->size))
201  {
202  LOG_THREAD("NET receive error 2: "+ error.get_error());
203  return -1;
204  }
205  }
206 
207 #ifdef DEBUG
208  LOG_THREAD("[RoR|Networking] ReceiveMessage() body received");
209 #endif //DEBUG
210 
211  return 0;
212 }
213 
215 {
216  LOG("[RoR|Networking] SendThread started");
217  while (!m_shutdown)
218  {
219  NetSendPacket packet;
220  {
221  std::unique_lock<std::mutex> queue_lock(m_send_packetqueue_mutex);
222  while (m_send_packet_buffer.empty() && !m_shutdown)
223  {
224  m_send_packet_available_cv.wait(queue_lock);
225  }
226  if (m_shutdown)
227  {
228  break;
229  }
230  packet = m_send_packet_buffer.front();
231  m_send_packet_buffer.pop_front();
232  }
233  SendMessageRaw(packet.buffer, packet.size);
234  }
235  LOG("[RoR|Networking] SendThread stopped");
236 }
237 
239 {
240  LOG_THREAD("[RoR|Networking] RecvThread starting...");
241 
242  RoRnet::Header header;
243 
244  char buffer[RORNET_MAX_MESSAGE_LENGTH] = {0};
245 
246  while (!m_shutdown)
247  {
248  int err = ReceiveMessage(&header, buffer, RORNET_MAX_MESSAGE_LENGTH);
249  //LOG("Received data: " + TOSTRING(header.command) + ", source: " + TOSTRING(header.source) + ":" + TOSTRING(header.streamid) + ", size: " + TOSTRING(header.size));
250  if (err != 0)
251  {
252  LOG_THREAD("[RoR|Networking] RecvThread: Error while receiving data: " + TOSTRING(err));
253  m_shutdown = true; // Atomic; instruct sender thread to stop
254  PushNetMessage(MSG_NET_RECV_ERROR, _LC("Network", "Error receiving data from network"));
255  continue; // Stop receiving data
256  }
257 
258  if (header.command == MSG2_STREAM_REGISTER)
259  {
260  if (header.source == m_uid)
261  continue;
262 
264 
265  LOG(" * received stream registration: " + TOSTRING(header.source) + ": " + TOSTRING(header.streamid) + ", type: " + TOSTRING(reg->type));
266  }
267  else if (header.command == MSG2_STREAM_REGISTER_RESULT)
268  {
270  LOG(" * received stream registration result: " + TOSTRING(header.source) + ": " + TOSTRING(header.streamid) + ", status: " + TOSTRING(reg->status));
271  }
272  else if (header.command == MSG2_STREAM_UNREGISTER)
273  {
274  LOG(" * received stream deregistration: " + TOSTRING(header.source) + ": " + TOSTRING(header.streamid));
275  }
276  else if (header.command == MSG2_UTF8_CHAT || header.command == MSG2_UTF8_PRIVCHAT)
277  {
278  // Chat message
279  }
280  else if (header.command == MSG2_NETQUALITY && header.source == -1)
281  {
282  if (header.size != sizeof(int))
283  {
284  continue;
285  }
286  int quality = *(int *)buffer;
287  SetNetQuality(quality);
288  continue;
289  }
290  else if (header.command == MSG2_USER_LEAVE)
291  {
292  if (header.source == m_uid)
293  {
294  m_shutdown = true; // Atomic; stop sending/receiving data - server closes the connection quickly after kick.
295 
296  std::stringstream msg;
297  msg << _L("disconnected: remote side closed the connection");
298  msg << " ** ";
299  msg << buffer;
300 
301  bool was_kick = (std::strstr(buffer, "disconnected on request") == nullptr); // FIXME: Add a reason code to MSG2_USER_LEAVE, this is ugly!
302  PushNetMessage((was_kick) ? MSG_NET_SERVER_KICK : MSG_NET_USER_DISCONNECT, msg.str());
304 
306  }
307  else
308  {
309  std::lock_guard<std::mutex> lock(m_users_mutex);
310  auto user = std::find_if(m_users.begin(), m_users.end(), [header](const RoRnet::UserInfo& u) { return static_cast<int>(u.uniqueid) == header.source; });
311  if (user != m_users.end())
312  {
313  // Console is now threadsafe, no need to send fake chatmessages to ourselves
314  Str<300> text;
315  text << _L("left the game");
318  LOG_THREAD(text);
319 
320  // Erase matching peer options
321  int peeropt_offset = (int)std::distance(m_users.begin(), user);
322  auto peeropt_itor = m_users_peeropts.begin() + peeropt_offset;
323  m_users_peeropts.erase(peeropt_itor);
324 
325  m_disconnected_users.push_back(*user); // Copy
326  m_users.erase(user);
327  }
328  }
329  }
330  else if (header.command == MSG2_USER_INFO || header.command == MSG2_USER_JOIN)
331  {
332  if (header.source == m_uid)
333  {
334  std::lock_guard<std::mutex> lock(m_userdata_mutex);
335  memcpy(&m_userdata, buffer, sizeof(RoRnet::UserInfo));
336  m_authlevel = m_userdata.authstatus;
337  m_username = Ogre::UTFString(m_userdata.username);
339  // TODO: Update the global variable 'mp_player_name' in a threadsafe way.
340  }
341  else
342  {
343  RoRnet::UserInfo user_info;
344  if (!GetUserInfo(header.source, user_info)) // Check that user doesn't exist yet.
345  {
346  memcpy(&user_info, buffer, sizeof(RoRnet::UserInfo));
347  Str<300> text;
348  if (user_info.authstatus != 0) // Show nothing for guests (no special authorization)
349  {
350  text << "(" << UserAuthToStringShort(user_info) << ") ";
351  }
352  text << _L("joined the game");
353 
354  // NB: Console is threadsafe
356  user_info.uniqueid, Console::CONSOLE_SYSTEM_NOTICE, text.ToCStr());
358  // Lock and update userlist
359  std::lock_guard<std::mutex> lock(m_users_mutex);
360  m_users.push_back(user_info);
361  m_users_peeropts.push_back(BitMask_t(0));
362  } // End of lock scope
363  }
364  continue;
365  }
366  else if (header.command == MSG2_GAME_CMD)
367  {
368 #ifdef USE_ANGELSCRIPT
369  App::GetScriptEngine()->queueStringForExecution(Ogre::String(buffer));
370 #endif // USE_ANGELSCRIPT
371  continue;
372  }
373  //DebugPacket("recv", &header, buffer);
374 
375  QueueStreamData(header, buffer, RORNET_MAX_MESSAGE_LENGTH);
376  }
377 
378  LOG_THREAD("[RoR|Networking] RecvThread stopped");
379 }
380 
381 
382 void Network::CouldNotConnect(std::string const & msg, bool close_socket /*= true*/)
383 {
384  RoR::LogFormat("[RoR|Networking] Failed to connect to server [%s:%d], message: %s", m_net_host.c_str(), m_net_port, msg.c_str());
385  PushNetMessage(MSG_NET_CONNECT_FAILURE, msg);
386 
387  if (close_socket)
388  {
389  m_socket.set_timeout(1, 0);
390  m_socket.disconnect();
391  }
392 }
393 
395 {
396  // Shadow vars for threaded access
397  m_username = App::mp_player_name->getStr();
398  m_token = App::mp_player_token->getStr();
399  m_net_host = App::mp_server_host->getStr();
400  m_net_port = App::mp_server_port->getInt();
401  m_password = App::mp_server_password->getStr();
402 
403  try
404  {
405  m_connect_thread = std::thread(&Network::ConnectThread, this);
406  App::mp_state->setVal((int)MpState::CONNECTING); // Mark connect thread as started
407  PushNetMessage(MSG_NET_CONNECT_STARTED, _LC("Network", "Starting..."));
408  return true;
409  }
410  catch (std::exception& e)
411  {
413  PushNetMessage(MSG_NET_CONNECT_FAILURE, _L("Failed to launch connection thread"));
414  RoR::LogFormat("[RoR|Networking] Failed to launch connection thread, message: %s", e.what());
415  return false;
416  }
417 }
418 
420 {
422 
423  if (m_connect_thread.joinable())
424  m_connect_thread.join(); // Clean up
425 }
426 
428 {
429  RoR::LogFormat("[RoR|Networking] Trying to join server '%s' on port '%d' ...", m_net_host.c_str(), m_net_port);
430 
431  SWBaseSocket::SWBaseError error;
432 
433  PushNetMessage(MSG_NET_CONNECT_PROGRESS, _LC("Network", "Estabilishing connection..."));
434  m_socket = SWInetSocket();
435  m_socket.set_timeout(10, 0);
436  m_socket.connect(m_net_port, m_net_host, &error);
437  if (error != SWBaseSocket::ok)
438  {
439  CouldNotConnect(_L("Could not create connection"), false);
440  return false;
441  }
442 
443  PushNetMessage(MSG_NET_CONNECT_PROGRESS, _LC("Network", "Getting server info..."));
444  if (!SendNetMessage(MSG2_HELLO, 0, (int)strlen(RORNET_VERSION), (char *)RORNET_VERSION))
445  {
446  CouldNotConnect(_L("Establishing network session: error sending hello"));
447  return false;
448  }
449 
450  RoRnet::Header header;
451  char buffer[RORNET_MAX_MESSAGE_LENGTH] = {0};
452 
453  // Receive server (rornet protocol) version
454  if (ReceiveMessage(&header, buffer, RORNET_MAX_MESSAGE_LENGTH))
455  {
456  CouldNotConnect(_L("Establishing network session: error getting server version"));
457  return false;
458  }
459  if (header.command == MSG2_WRONG_VER_LEGACY) // Pre-RoRnet_2.40 server
460  {
462  memcpy(&info, buffer, sizeof(RoRnet::LegacyServerInfo));
463  Ogre::UTFString format_wstr = _L("Establishing network session: wrong server version, you are using version '%s' and the server is using '%s'");
464  const char* server_ver = (info.protocolversion[0] != 0) ? info.protocolversion : "~ RoRnet_2.38 or earlier (not detected) ~";
465  char msg_buf[500];
466  snprintf(msg_buf, 500, format_wstr.asUTF8_c_str(), RORNET_VERSION, server_ver);
467  CouldNotConnect(msg_buf);
468  return false;
469  }
470  if (header.command == MSG2_WRONG_VER)
471  {
472  CouldNotConnect(_L("server uses a different protocol version"));
473  return false;
474  }
475  if (header.command != MSG2_HELLO)
476  {
477  CouldNotConnect(_L("Establishing network session: error getting server hello"));
478  return false;
479  }
480 
481  // Save server settings
482  memcpy(&m_server_settings, buffer, sizeof(RoRnet::ServerInfo));
483 
484  if (strncmp(m_server_settings.protocolversion, RORNET_VERSION, strlen(RORNET_VERSION)))
485  {
486  wchar_t tmp[512] = L"";
487  Ogre::UTFString tmp2 = _L("Establishing network session: wrong server version, you are using version '%s' and the server is using '%s'");
488  swprintf(tmp, 512, tmp2.asWStr_c_str(), RORNET_VERSION, m_server_settings.protocolversion);
489  CouldNotConnect(MyGUI::UString(tmp).asUTF8_c_str());
490  return false;
491  }
492 
493  PushNetMessage(MSG_NET_CONNECT_PROGRESS, _LC("Network", "Authorizing..."));
494 
495  // First handshake done, increase the timeout, important!
496  m_socket.set_timeout(0, 0);
497 
498  // Construct user credentials
499  // Beware of the wchar_t converted to UTF8 for networking
501  memset(&c, 0, sizeof(RoRnet::UserInfo));
502  // Cut off the UTF string on the highest level, otherwise you will break UTF info
503  strncpy((char *)c.username, m_username.substr(0, RORNET_MAX_USERNAME_LEN * 0.5f).asUTF8_c_str(), RORNET_MAX_USERNAME_LEN);
504  strncpy(c.serverpassword, Sha1Hash(m_password).c_str(), size_t(40));
505  strncpy(c.usertoken, Sha1Hash(m_token).c_str(), size_t(40));
507  strncpy(c.clientname, "RoR", 10);
508  std::string language = App::app_language->getStr().substr(0, 2);
509  std::string country = App::app_country->getStr().substr(0, 2);
510  strncpy(c.language, (language + std::string("_") + country).c_str(), 5);
511  strcpy(c.sessiontype, "normal");
512  if (!SendNetMessage(MSG2_USER_INFO, 0, sizeof(RoRnet::UserInfo), (char*)&c))
513  {
514  CouldNotConnect(_L("Establishing network session: error sending user info"));
515  return false;
516  }
517 
518  // Getting authorization
519  if (ReceiveMessage(&header, buffer, RORNET_MAX_MESSAGE_LENGTH))
520  {
521  CouldNotConnect(_L("Establishing network session: error getting server authorization"));
522  return false;
523  }
524 
525  if (header.command==MSG2_FULL)
526  {
527  CouldNotConnect(_L("Establishing network session: sorry, server has too many players"));
528  return false;
529  }
530  else if (header.command==MSG2_BANNED)
531  {
532  // Do NOT `disconnect()` the m_socket in this case - causes SocketW to terminate RoR.
533  CouldNotConnect(_L("Establishing network session: sorry, you are banned!"), /*close_socket=*/false);
534  return false;
535  }
536  else if (header.command==MSG2_WRONG_PW)
537  {
538  CouldNotConnect(_L("Establishing network session: sorry, wrong password!"));
539  return false;
540  }
541  else if (header.command==MSG2_WRONG_VER)
542  {
543  CouldNotConnect(_L("Establishing network session: sorry, wrong protocol version!"));
544  return false;
545  }
546 
547  if (header.command!=MSG2_WELCOME)
548  {
549  CouldNotConnect(_L("Establishing network session: sorry, unknown server response"));
550  return false;
551  }
552 
553  PushNetMessage(MSG_NET_CONNECT_PROGRESS, _LC("Network", "Finishing..."));
554 
555  m_uid = header.source;
556 
557  // we get our userdata back
558  memcpy(&m_userdata, buffer, std::min<int>(sizeof(RoRnet::UserInfo), header.size));
559 
560  m_shutdown = false;
561 
562  LOG("[RoR|Networking] Connect(): Creating Send/Recv threads");
563  m_send_thread = std::thread(&Network::SendThread, this);
564  m_recv_thread = std::thread(&Network::RecvThread, this);
565  PushNetMessage(MSG_NET_CONNECT_SUCCESS, "");
566 
567  return true;
568 }
569 
571 {
572  LOG("[RoR|Networking] Disconnect() disconnecting...");
573  bool is_clean_disconnect = !m_shutdown; // Hacky detection of invalid network state
574 
575  m_shutdown = true; // Instruct Send/Recv threads to shut down.
576 
577  m_send_packet_available_cv.notify_one();
578 
579  m_send_thread.join();
580  LOG("[RoR|Networking] Disconnect() sender thread cleaned up");
581 
582  m_socket.set_timeout(1, 0);
583 
584  if (is_clean_disconnect)
585  {
586  SendNetMessage(MSG2_USER_LEAVE, 0, 0, 0);
587  }
588 
589  m_recv_thread.join();
590  LOG("[RoR|Networking] Disconnect() receiver thread cleaned up");
591 
592  if (is_clean_disconnect)
593  {
594  m_socket.disconnect();
595  }
596  else
597  {
598  m_socket.close_fd();
599  }
600 
601  SetNetQuality(0);
602  m_users.clear();
603  m_disconnected_users.clear();
604  m_recv_packet_buffer.clear();
605  m_send_packet_buffer.clear();
606  App::GetConsole()->doCommand("clear net");
607 
608  m_shutdown = false;
610 
611  LOG("[RoR|Networking] Disconnect() done");
612 }
613 
614 void Network::AddPacket(int streamid, int type, int len, const char *content)
615 {
616  const auto max_len = RORNET_MAX_MESSAGE_LENGTH - sizeof(RoRnet::Header);
617  if (len > max_len)
618  {
619  LOGSTREAM << "[RoR|Networking] Discarding network packet (StreamID: "
620  <<streamid<<", Type: "<<type<<"), length is " << len << ", max is " << max_len;
621  return;
622  }
623 
624  NetSendPacket packet;
625  memset(&packet, 0, sizeof(NetSendPacket));
626 
627  char *buffer = (char*)(packet.buffer);
628 
629  RoRnet::Header *head = (RoRnet::Header *)buffer;
630  head->command = type;
631  head->source = m_uid;
632  head->size = len;
633  head->streamid = streamid;
634 
635  // then copy the contents
636  char *bufferContent = (char *)(buffer + sizeof(RoRnet::Header));
637  memcpy(bufferContent, content, len);
638 
639  // record the packet size
640  packet.size = len + sizeof(RoRnet::Header);
641 
642  { // Lock scope
643  std::lock_guard<std::mutex> lock(m_send_packetqueue_mutex);
644  if (type == MSG2_STREAM_DATA_DISCARDABLE)
645  {
646  if (m_send_packet_buffer.size() > m_packet_buffer_size)
647  {
648  // buffer full, discard unimportant data packets
649  return;
650  }
651  auto search = std::find_if(m_send_packet_buffer.begin(), m_send_packet_buffer.end(),
652  [&](const NetSendPacket& p) { return !memcmp(packet.buffer, p.buffer, sizeof(RoRnet::Header)); });
653  if (search != m_send_packet_buffer.end())
654  {
655  // Found outdated discardable streamdata -> replace it
656  (*search) = packet;
657  m_send_packet_available_cv.notify_one();
658  return;
659  }
660  }
661  //DebugPacket("send", head, buffer);
662  m_send_packet_buffer.push_back(packet);
663  }
664 
665  m_send_packet_available_cv.notify_one();
666 }
667 
669 {
670  reg->origin_sourceid = m_uid;
671  reg->origin_streamid = m_stream_id;
672  reg->status = 0;
673 
674  AddPacket(m_stream_id, MSG2_STREAM_REGISTER, size, (char*)reg);
675  LOG("adding local stream: " + TOSTRING(m_uid) + ":"+ TOSTRING(m_stream_id) + ", type: " + TOSTRING(reg->type));
676 
677  m_stream_id++;
678 }
679 
680 std::vector<NetRecvPacket> Network::GetIncomingStreamData()
681 {
682  std::lock_guard<std::mutex> lock(m_recv_packetqueue_mutex);
683  std::vector<NetRecvPacket> buf_copy = m_recv_packet_buffer;
684  m_recv_packet_buffer.clear();
685  return buf_copy;
686 }
687 
689 {
690  return m_server_settings.terrain;
691 }
692 
694 {
695  std::lock_guard<std::mutex> lock(m_userdata_mutex);
696  return m_userdata.colournum;
697 }
698 
699 Ogre::UTFString Network::GetUsername()
700 {
701  std::lock_guard<std::mutex> lock(m_userdata_mutex);
702  return m_username;
703 }
704 
706 {
707  std::lock_guard<std::mutex> lock(m_userdata_mutex);
708  return m_userdata;
709 }
710 
711 std::vector<RoRnet::UserInfo> Network::GetUserInfos()
712 {
713  std::lock_guard<std::mutex> lock(m_users_mutex);
714  return m_users;
715 }
716 
717 std::vector<BitMask_t> Network::GetAllUsersPeerOpts()
718 {
719  std::lock_guard<std::mutex> lock(m_users_mutex);
720  return m_users_peeropts;
721 }
722 
724 {
725  std::lock_guard<std::mutex> lock(m_users_mutex);
726  for (RoRnet::UserInfo user : m_users)
727  {
728  if ((int)user.uniqueid == uid)
729  {
730  result = user;
731  return true;
732  }
733  }
734  return false;
735 }
736 
737 bool Network::GetUserPeerOpts(int uid, BitMask_t& result)
738 {
739  std::lock_guard<std::mutex> lock(m_users_mutex);
740  for (size_t i = 0; i < m_users.size(); i++)
741  {
742  if (static_cast<int>(m_users[i].uniqueid) == uid)
743  {
744  result = m_users_peeropts[i];
745  return true;
746  }
747  }
748  return false;
749 }
750 
752 {
753  std::lock_guard<std::mutex> lock(m_users_mutex);
754  for (RoRnet::UserInfo user : m_disconnected_users)
755  {
756  if ((int)user.uniqueid == uid)
757  {
758  result = user;
759  return true;
760  }
761  }
762  return false;
763 }
764 
766 {
767  RoRnet::UserInfo tmp;
768 
769  // Try remote users
770  if (GetUserInfo(uid, tmp))
771  {
772  result = tmp;
773  return true;
774  }
775 
776  // Try local user
777  tmp = GetLocalUserData();
778  if (tmp.uniqueid == uid)
779  {
780  result = tmp;
781  return true;
782  }
783 
784  return false;
785 }
786 
787 bool Network::FindUserInfo(std::string const& username, RoRnet::UserInfo &result)
788 {
789  std::lock_guard<std::mutex> lock(m_users_mutex);
790  for (RoRnet::UserInfo user : m_users)
791  {
792  if (user.username == username)
793  {
794  result = user;
795  return true;
796  }
797  }
798  return false;
799 }
800 
801 
803 {
804  std::lock_guard<std::mutex> lock(m_users_mutex);
805 
806  const bool peeropts_sane = m_users.size() == m_users_peeropts.size();
807  ROR_ASSERT(peeropts_sane);
808  if (!peeropts_sane) return;
809 
810  for (size_t i = 0; i < m_users.size(); i++)
811  {
812  if (static_cast<int>(m_users[i].uniqueid) == rq->por_uid)
813  {
814  BITMASK_SET_1(m_users_peeropts[i], rq->por_peeropts);
815  }
816  }
817 }
818 
820 {
821  std::lock_guard<std::mutex> lock(m_users_mutex);
822 
823  const bool peeropts_sane = m_users.size() == m_users_peeropts.size();
824  ROR_ASSERT(peeropts_sane);
825  if (!peeropts_sane) return;
826 
827  for (size_t i = 0; i < m_users.size(); i++)
828  {
829  if (static_cast<int>(m_users[i].uniqueid) == rq->por_uid)
830  {
831  BITMASK_SET_0(m_users_peeropts[i], rq->por_peeropts);
832  }
833  }
834 }
835 
836 void Network::BroadcastChatMsg(const char* msg)
837 {
838  AddPacket(m_stream_id, RoRnet::MSG2_UTF8_CHAT, (int)std::strlen(msg), msg);
839 }
840 
841 void Network::WhisperChatMsg(RoRnet::UserInfo const& user, const char* msg)
842 {
843  // Prepare buffer
844  char payload[RORNET_MAX_MESSAGE_LENGTH - sizeof(RoRnet::Header)];
845  size_t payload_len = 0;
846 
847  // Write client ID
848  std::memcpy(payload, &user.uniqueid, sizeof(user.uniqueid));
849  payload_len += sizeof(user.uniqueid);
850 
851  // Write text
852  std::strncpy(payload + payload_len, msg, sizeof(payload) - payload_len);
853  payload_len += std::strlen(msg);
854 
855  // Queue packet
856  AddPacket(m_stream_id, RoRnet::MSG2_UTF8_PRIVCHAT, (int)payload_len, msg);
857 }
858 
860 {
861  if (user.authstatus & AUTH_ADMIN) { return _LC("NetUserAuth", "Admin"); }
862  else if (user.authstatus & AUTH_MOD) { return _LC("NetUserAuth", "Mod"); }
863  else if (user.authstatus & AUTH_BOT) { return _LC("NetUserAuth", "Bot"); }
864  else if (user.authstatus & AUTH_RANKED) { return _LC("NetUserAuth", "Ranked"); }
865  else if (user.authstatus & AUTH_BANNED) { return _LC("NetUserAuth", "Banned"); }
866  else { return _LC("NetUserAuth", "Guest"); }
867 }
868 
870 {
871  if (user.authstatus & AUTH_ADMIN) { return _LC("NetUserAuth","Server Administrator"); }
872  else if (user.authstatus & AUTH_MOD) { return _LC("NetUserAuth","Server Moderator"); }
873  else if (user.authstatus & AUTH_BOT) { return _LC("NetUserAuth","Bot"); }
874  else if (user.authstatus & AUTH_RANKED) { return _LC("NetUserAuth","Ranked user"); }
875  else if (user.authstatus & AUTH_BANNED) { return _LC("NetUserAuth","Banned user"); }
876  else { return _LC("NetUserAuth","Guest"); }
877 }
878 
879 #endif // USE_SOCKETW
ROR_ASSERT
#define ROR_ASSERT(_EXPR)
Definition: Application.h:40
GameContext.h
Game state manager and message-queue provider.
RoR::Network::Disconnect
void Disconnect()
Definition: Network.cpp:570
RoRnet::MSG2_STREAM_UNREGISTER
@ MSG2_STREAM_UNREGISTER
remove stream
Definition: RoRnet.h:65
RoR::Network::GetUserInfos
std::vector< RoRnet::UserInfo > GetUserInfos()
Definition: Network.cpp:711
RoR::Network::PushNetMessage
void PushNetMessage(MsgType type, std::string const &message)
Definition: Network.cpp:87
RoR::Network::StartConnecting
bool StartConnecting()
Launches connecting on background.
Definition: Network.cpp:394
RoR::Network::ConnectThread
bool ConnectThread()
Definition: Network.cpp:427
RoR::Network::UserAuthToStringShort
std::string UserAuthToStringShort(RoRnet::UserInfo const &user)
Definition: Network.cpp:859
RoRnet::LegacyServerInfo
Definition: RoRnet.h:219
RoR::MSG_NET_USER_DISCONNECT
@ MSG_NET_USER_DISCONNECT
Definition: Application.h:104
RoR::Network::GetUserColor
int GetUserColor()
Definition: Network.cpp:693
m_packet_buffer_size
static const unsigned int m_packet_buffer_size
Definition: Network.cpp:82
RoRnet::UserInfo
Definition: RoRnet.h:178
RoR::Network::GetUID
int GetUID()
Definition: Network.cpp:122
RoRnet::Header::source
int32_t source
source of this command: 0 = server
Definition: RoRnet.h:143
RoRnet::MSG2_WRONG_PW
@ MSG2_WRONG_PW
server send that on wrong pw
Definition: RoRnet.h:43
DebugPacket
void DebugPacket(const char *name, RoRnet::Header *header, char *buffer)
Definition: Network.cpp:104
RoR::App::mp_player_name
CVar * mp_player_name
Definition: Application.cpp:124
RORNET_MAX_USERNAME_LEN
#define RORNET_MAX_USERNAME_LEN
bytes.
Definition: RoRnet.h:33
RoR::App::mp_player_token
CVar * mp_player_token
Definition: Application.cpp:125
RORNET_VERSION
#define RORNET_VERSION
Definition: RoRnet.h:35
GUI_TopMenubar.h
RoR::Network::QueueStreamData
void QueueStreamData(RoRnet::Header &header, char *buffer, size_t buffer_len)
Definition: Network.cpp:163
RoR::App::app_language
CVar * app_language
Definition: Application.cpp:80
RoR::Network::GetIncomingStreamData
std::vector< NetRecvPacket > GetIncomingStreamData()
Definition: Network.cpp:680
RoR::MSG_NET_CONNECT_STARTED
@ MSG_NET_CONNECT_STARTED
Definition: Application.h:98
RoR::PeerOptionsRequest::por_uid
int por_uid
RoRnet unique user ID.
Definition: Network.h:107
RoRnet::Header::command
uint32_t command
the command of this packet: MSG2_*
Definition: RoRnet.h:142
RoR::LogFormat
void LogFormat(const char *format,...)
Improved logging utility. Uses fixed 2Kb buffer.
Definition: Application.cpp:428
RoRnet::StreamRegister::origin_sourceid
int32_t origin_sourceid
origin sourceid
Definition: RoRnet.h:152
RoR::HashData
Ogre::String HashData(const char *key, int len)
Definition: Utils.cpp:51
RoRnet::AUTH_ADMIN
@ AUTH_ADMIN
admin on the server
Definition: RoRnet.h:76
Console.h
RoR::MSG_NET_RECV_ERROR
@ MSG_NET_RECV_ERROR
Definition: Application.h:105
RoRnet::ServerInfo
Definition: RoRnet.h:210
RoR::App::app_country
CVar * app_country
Definition: Application.cpp:81
RoR::ScriptEngine::queueStringForExecution
void queueStringForExecution(const Ogre::String command)
Queues a string for execution.
Definition: ScriptEngine.cpp:497
Utils.h
RoRnet
Definition: ForwardDeclarations.h:263
RoR::Network::GetAnyUserInfo
bool GetAnyUserInfo(int uid, RoRnet::UserInfo &result)
Also considers local client.
Definition: Network.cpp:765
RoR::Network::GetUserPeerOpts
bool GetUserPeerOpts(int uid, BitMask_t &result)
Definition: Network.cpp:737
RoRnet::AUTH_BANNED
@ AUTH_BANNED
banned
Definition: RoRnet.h:80
RoRnet::MSG2_FULL
@ MSG2_FULL
no more slots for us
Definition: RoRnet.h:42
RoRnet::MSG2_BANNED
@ MSG2_BANNED
client not allowed to join
Definition: RoRnet.h:45
Language.h
RoR::NetRecvPacket::header
RoRnet::Header header
Definition: Network.h:96
RoRnet::MSG2_USER_JOIN
@ MSG2_USER_JOIN
new user joined
Definition: RoRnet.h:57
RoRnet::MSG2_WRONG_VER_LEGACY
@ MSG2_WRONG_VER_LEGACY
Wrong version.
Definition: RoRnet.h:70
RoR::MSG_NET_SERVER_KICK
@ MSG_NET_SERVER_KICK
Definition: Application.h:102
GUIManager.h
RoR::Network::GetUserInfo
bool GetUserInfo(int uid, RoRnet::UserInfo &result)
Definition: Network.cpp:723
RoR::App::GetScriptEngine
ScriptEngine * GetScriptEngine()
Definition: Application.cpp:283
RoR::Console::CONSOLE_SYSTEM_NOTICE
@ CONSOLE_SYSTEM_NOTICE
Definition: Console.h:51
RoRnet::MSG2_UTF8_CHAT
@ MSG2_UTF8_CHAT
broadcast chat line in UTF8 encoding; Payload: const char*(text)
Definition: RoRnet.h:59
RoR::NetSendPacket::size
int size
Definition: Network.h:91
RoR::NetSendPacket::buffer
char buffer[RORNET_MAX_MESSAGE_LENGTH]
Definition: Network.h:90
RoR::App::mp_state
CVar * mp_state
Definition: Application.cpp:115
BITMASK_SET_0
#define BITMASK_SET_0(VAR, FLAGS)
Definition: BitFlags.h:16
TOSTRING
#define TOSTRING(x)
Definition: Application.h:56
RoR::Network::GetUsername
Ogre::UTFString GetUsername()
Definition: Network.cpp:699
RoRnet::MSG2_GAME_CMD
@ MSG2_GAME_CMD
Script message. Can be sent in both directions.
Definition: RoRnet.h:56
RoRnet::UserInfo::authstatus
int32_t authstatus
auth status set by server: AUTH_*
Definition: RoRnet.h:181
RoRnet::MSG2_UTF8_PRIVCHAT
@ MSG2_UTF8_PRIVCHAT
private chat line in UTF8 encoding; Payload: uint32_t(uniqueid), const char*(text)
Definition: RoRnet.h:60
RoR::Network::GetTerrainName
Ogre::String GetTerrainName()
Definition: Network.cpp:688
RoR::CVar::getStr
std::string const & getStr() const
Definition: CVar.h:95
RoR::Str
Wrapper for classic c-string (local buffer) Refresher: strlen() excludes '\0' terminator; strncat() A...
Definition: Str.h:35
RoR::Network::GetAllUsersPeerOpts
std::vector< BitMask_t > GetAllUsersPeerOpts()
Definition: Network.cpp:717
RoRnet::UserInfo::serverpassword
char serverpassword[40]
server password
Definition: RoRnet.h:187
ErrorUtils.h
RoRnet::LegacyServerInfo::protocolversion
char protocolversion[20]
protocol version being used
Definition: RoRnet.h:221
RoRnet::AUTH_MOD
@ AUTH_MOD
moderator status
Definition: RoRnet.h:78
RoR::MSG_GUI_MP_CLIENTS_REFRESH
@ MSG_GUI_MP_CLIENTS_REFRESH
Definition: Application.h:141
RoRnet::MSG2_STREAM_REGISTER
@ MSG2_STREAM_REGISTER
create new stream
Definition: RoRnet.h:63
ScriptEngine.h
BITMASK_SET_1
#define BITMASK_SET_1(VAR, FLAGS)
Definition: BitFlags.h:17
RoR::GameContext::PushMessage
void PushMessage(Message m)
Doesn't guarantee order! Use ChainMessage() if order matters.
Definition: GameContext.cpp:66
strnlen
#define strnlen(str, len)
Definition: InputEngine.cpp:400
RoR::Network::BroadcastChatMsg
void BroadcastChatMsg(const char *msg)
Definition: Network.cpp:836
RoR::App::mp_server_password
CVar * mp_server_password
Definition: Application.cpp:123
RoR::Network::GetDisconnectedUserInfo
bool GetDisconnectedUserInfo(int uid, RoRnet::UserInfo &result)
Definition: Network.cpp:751
RoR::Network::GetNetQuality
int GetNetQuality()
Definition: Network.cpp:117
ChatSystem.h
RoR::Str::ToCStr
const char * ToCStr() const
Definition: Str.h:46
RORNET_MAX_MESSAGE_LENGTH
#define RORNET_MAX_MESSAGE_LENGTH
maximum size of a RoR message. 8192 bytes = 8 kibibytes
Definition: RoRnet.h:31
RoR::Network::SendNetMessage
bool SendNetMessage(int type, unsigned int streamid, int len, char *content)
Definition: Network.cpp:140
RoR::PeerOptionsRequest::por_peeropts
BitMask_t por_peeropts
See RoRnet::PeerOptions.
Definition: Network.h:108
RoR::Network::CouldNotConnect
void CouldNotConnect(std::string const &msg, bool close_socket=true)
Definition: Network.cpp:382
RoR::Network::AddPeerOptions
void AddPeerOptions(PeerOptionsRequest *rq)
Definition: Network.cpp:802
RoR::MsgType
MsgType
Global gameplay message loop, see struct Message in GameContext.h.
Definition: Application.h:74
Application.h
Central state/object manager and communications hub.
RoR::App::GetConsole
Console * GetConsole()
Definition: Application.cpp:274
RoRnet::Header
< Common header for every packet
Definition: RoRnet.h:140
RoRnet::MSG2_STREAM_REGISTER_RESULT
@ MSG2_STREAM_REGISTER_RESULT
result of a stream creation
Definition: RoRnet.h:64
RoR::Network::GetLocalUserData
RoRnet::UserInfo GetLocalUserData()
Definition: Network.cpp:705
RoR::App::GetGameContext
GameContext * GetGameContext()
Definition: Application.cpp:284
RoRnet::Header::streamid
uint32_t streamid
streamid for this command
Definition: RoRnet.h:144
RoR::Console::doCommand
void doCommand(std::string msg)
Identify and execute any console line.
Definition: ConsoleCmd.cpp:678
RoR::Network::ReceiveMessage
int ReceiveMessage(RoRnet::Header *head, char *content, int bufferlen)
Definition: Network.cpp:173
RoRnet::UserInfo::sessiontype
char sessiontype[10]
the requested session type. For example "normal", "bot", "rcon"
Definition: RoRnet.h:192
RoRVersion.h
RoRnet::UserInfo::clientversion
char clientversion[25]
a version number of the client. For example 1 for RoR 0.35
Definition: RoRnet.h:190
RoRnet::AUTH_RANKED
@ AUTH_RANKED
ranked status
Definition: RoRnet.h:77
ROR_VERSION_STRING
const char *const ROR_VERSION_STRING
RoR::Message::description
std::string description
Definition: GameContext.h:58
RoRnet::UserInfo::clientname
char clientname[10]
the name and version of the client. For exmaple: "ror" or "gamebot"
Definition: RoRnet.h:189
_LC
#define _LC(ctx, str)
Definition: Language.h:42
RoR::Network::SendMessageRaw
bool SendMessageRaw(char *buffer, int msgsize)
Definition: Network.cpp:127
RoRnet::StreamRegister::status
int32_t status
initial stream status
Definition: RoRnet.h:151
RoR::Network::GetPlayerColor
Ogre::ColourValue GetPlayerColor(int color_num)
Definition: Network.cpp:94
RoR::MSG_NET_CONNECT_SUCCESS
@ MSG_NET_CONNECT_SUCCESS
Definition: Application.h:100
RoR::NetSendPacket
Definition: Network.h:88
RoRnet::MSG2_WRONG_VER
@ MSG2_WRONG_VER
wrong version
Definition: RoRnet.h:44
RoRnet::StreamRegister::type
int32_t type
0 = Actor, 1 = Character, 3 = ChatSystem
Definition: RoRnet.h:150
RoR::Sha1Hash
std::string Sha1Hash(std::string const &data)
Definition: Utils.cpp:138
RoR::Network::FindUserInfo
bool FindUserInfo(std::string const &username, RoRnet::UserInfo &result)
Definition: Network.cpp:787
RoR::Network::RecvThread
void RecvThread()
Definition: Network.cpp:238
RoRnet::UserInfo::username
char username[RORNET_MAX_USERNAME_LEN]
the nickname of the user (UTF-8)
Definition: RoRnet.h:185
RoRnet::UserInfo::uniqueid
uint32_t uniqueid
user unique id
Definition: RoRnet.h:180
RoR::Network::RemovePeerOptions
void RemovePeerOptions(PeerOptionsRequest *rq)
Definition: Network.cpp:819
RoR::Network::SetNetQuality
void SetNetQuality(int quality)
Definition: Network.cpp:112
RoR::App::mp_server_port
CVar * mp_server_port
Definition: Application.cpp:122
RoR::PeerOptionsRequest
Payload of MSG_NET_{ADD/REMOVE}_PEEROPTIONS_REQUESTED.
Definition: Network.h:105
RoR::Message
Unified game event system - all requests and state changes are reported using a message.
Definition: GameContext.h:51
RoR::App::mp_server_host
CVar * mp_server_host
Definition: Application.cpp:121
RoRnet::MSG2_STREAM_DATA_DISCARDABLE
@ MSG2_STREAM_DATA_DISCARDABLE
stream data that is allowed to be discarded
Definition: RoRnet.h:67
RoR::CVar::setVal
void setVal(T val)
Definition: CVar.h:72
_L
#define _L
Definition: ErrorUtils.cpp:34
RoR::Console::putNetMessage
void putNetMessage(int user_id, MessageType type, const char *text)
Definition: Console.cpp:108
RoR::MpState::DISABLED
@ DISABLED
Not connected for whatever reason.
RoRnet::AUTH_BOT
@ AUTH_BOT
bot status
Definition: RoRnet.h:79
RoRnet::StreamRegister::origin_streamid
int32_t origin_streamid
origin streamid
Definition: RoRnet.h:153
MP_COLORS
static Ogre::ColourValue MP_COLORS[]
Definition: Network.cpp:47
RoR::MSG_NET_CONNECT_FAILURE
@ MSG_NET_CONNECT_FAILURE
Definition: Application.h:101
RoR::NetRecvPacket::buffer
char buffer[RORNET_MAX_MESSAGE_LENGTH]
Definition: Network.h:97
BitMask_t
uint32_t BitMask_t
Definition: BitFlags.h:7
RoRnet::MSG2_NETQUALITY
@ MSG2_NETQUALITY
network quality information
Definition: RoRnet.h:53
RoR::Network::UserAuthToStringLong
std::string UserAuthToStringLong(RoRnet::UserInfo const &user)
Definition: Network.cpp:869
RoRnet::MSG2_HELLO
@ MSG2_HELLO
client sends its version as first message
Definition: RoRnet.h:39
RoR::CVar::getInt
int getInt() const
Definition: CVar.h:97
RoR::MSG_NET_CONNECT_PROGRESS
@ MSG_NET_CONNECT_PROGRESS
Definition: Application.h:99
RoRnet::MSG2_USER_INFO
@ MSG2_USER_INFO
user data that is sent from the server to the clients
Definition: RoRnet.h:51
RoR::Network::AddLocalStream
void AddLocalStream(RoRnet::StreamRegister *reg, int size)
Definition: Network.cpp:668
RoR::NetRecvPacket
Definition: Network.h:94
LOGSTREAM
#define LOGSTREAM
Definition: Network.cpp:85
RoRnet::MSG2_WELCOME
@ MSG2_WELCOME
we can proceed
Definition: RoRnet.h:46
RoR::Network::AddPacket
void AddPacket(int streamid, int type, int len, const char *content)
Definition: Network.cpp:614
RoR
Definition: AppContext.h:36
Network.h
RoRnet::UserInfo::language
char language[10]
user's language. For example "de-DE" or "en-US"
Definition: RoRnet.h:188
RoRnet::MSG2_USER_LEAVE
@ MSG2_USER_LEAVE
user leaves
Definition: RoRnet.h:58
RoRnet::UserInfo::usertoken
char usertoken[40]
user token
Definition: RoRnet.h:186
RoR::Network::SendThread
void SendThread()
Definition: Network.cpp:214
RoRnet::Header::size
uint32_t size
size of the attached data block
Definition: RoRnet.h:145
RoR::MpState::CONNECTING
@ CONNECTING
RoR::Network::WhisperChatMsg
void WhisperChatMsg(RoRnet::UserInfo const &user, const char *msg)
Definition: Network.cpp:841
RoRnet::StreamRegister
< Sent from the client to server and vice versa, to broadcast a new stream
Definition: RoRnet.h:148
LOG_THREAD
#define LOG_THREAD(_MSG_)
Definition: Network.cpp:84
RoR::Network::StopConnecting
void StopConnecting()
Definition: Network.cpp:419