OpenClonk
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros
C4Network2IO.cpp
Go to the documentation of this file.
1 /*
2  * OpenClonk, http://www.openclonk.org
3  *
4  * Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de/
5  * Copyright (c) 2009-2016, The OpenClonk Team and contributors
6  *
7  * Distributed under the terms of the ISC license; see accompanying file
8  * "COPYING" for details.
9  *
10  * "Clonk" is a registered trademark of Matthes Bender, used with permission.
11  * See accompanying file "TRADEMARK" for details.
12  *
13  * To redistribute this file separately, substitute the full license texts
14  * for the above references.
15  */
16 #include "C4Include.h"
17 #include "network/C4Network2IO.h"
19 
21 #include "game/C4Application.h"
22 #include "lib/C4Log.h"
23 #include "game/C4Game.h"
24 #include "control/C4GameControl.h"
25 
26 #include "network/C4Network2UPnP.h"
27 
28 #ifndef HAVE_WINSOCK
29 #include <sys/socket.h>
30 #include <netinet/in.h>
31 #include <arpa/inet.h>
32 #endif
33 
34 // internal structures
36 {
39 };
40 
41 // compile options
42 #define C4NET2IO_DUMP_LEVEL 1
43 
44 // *** C4Network2IO
45 
47  : pNetIO_TCP(nullptr), pNetIO_UDP(nullptr),
48  pNetIODiscover(nullptr), pRefServer(nullptr),
49  UPnPMgr(nullptr),
50  pConnList(nullptr),
51  iNextConnID(0),
52  fAllowConnect(false),
53  pAutoAcceptList(nullptr),
54  fExclusiveConn(false),
55  tLastExecute(0), tLastPing(0), tLastStatistic(0),
56  iTCPIRate(0), iTCPORate(0), iTCPBCRate(0),
57  iUDPIRate(0), iUDPORate(0), iUDPBCRate(0)
58 {
59 }
60 
62 {
63  Clear();
64 }
65 
66 bool C4Network2IO::Init(int16_t iPortTCP, int16_t iPortUDP, int16_t iPortDiscover, int16_t iPortRefServer, bool fBroadcast, bool enable_upnp) // by main thread
67 {
68  // Already initialized? Clear first
69  if (pNetIO_TCP || pNetIO_UDP) Clear();
70 
71  // init members
75 
76  // init event callback
78  Thread.SetCallback(Ev_Net_Conn, this);
79  Thread.SetCallback(Ev_Net_Disconn, this);
80  Thread.SetCallback(Ev_Net_Packet, this);
81 
82  // initialize UPnP manager
83  if (enable_upnp && (iPortTCP > 0 || iPortUDP > 0))
84  {
85  assert(!UPnPMgr);
86  UPnPMgr = new C4Network2UPnP;
87  }
88 
89  // initialize net i/o classes: TCP first
90  if (iPortTCP > 0)
91  {
92  // create
93  pNetIO_TCP = new C4NetIOTCP();
94  // init
95  if (!pNetIO_TCP->Init(iPortTCP))
96  {
97  LogF("Network: could not init TCP i/o (%s)", pNetIO_TCP->GetError() ? pNetIO_TCP->GetError() : "");
98  delete pNetIO_TCP; pNetIO_TCP = nullptr;
99  }
100  else
101  LogSilentF("Network: TCP initialized on port %d", iPortTCP);
102 
103  // add to thread, set callback
104  if (pNetIO_TCP)
105  {
106  Thread.AddProc(pNetIO_TCP);
107  pNetIO_TCP->SetCallback(this);
108  if (UPnPMgr) UPnPMgr->AddMapping(P_TCP, iPortTCP, iPortTCP);
109  }
110 
111  }
112  // then UDP
113  if (iPortUDP > 0)
114  {
115  // create
116  pNetIO_UDP = new C4NetIOUDP();
117  // init
118  if (!pNetIO_UDP->Init(iPortUDP))
119  {
120  LogF("Network: could not init UDP i/o (%s)", pNetIO_UDP->GetError() ? pNetIO_UDP->GetError() : "");
121  delete pNetIO_UDP; pNetIO_UDP = nullptr;
122  }
123  else
124  LogSilentF("Network: UDP initialized on port %d", iPortUDP);
125 
126  // add to thread, set callback
127  if (pNetIO_UDP)
128  {
129  Thread.AddProc(pNetIO_UDP);
130  pNetIO_UDP->SetCallback(this);
131  if (UPnPMgr) UPnPMgr->AddMapping(P_UDP, iPortUDP, iPortUDP);
132  }
133  }
134 
135  // no protocols?
136  if (!pNetIO_TCP && !pNetIO_UDP)
137  {
138  LogFatal("Network: fatal - no protocols available!");
139  Thread.ClearCallback(Ev_Net_Conn, this);
140  Thread.ClearCallback(Ev_Net_Disconn, this);
141  Thread.ClearCallback(Ev_Net_Packet, this);
142  return false;
143  }
144 
145  // discovery last
146  if (iPortDiscover > 0)
147  {
148  // create
149  pNetIODiscover = new C4Network2IODiscover(iPortRefServer);
151  // init
152  if (!pNetIODiscover->Init(iPortDiscover))
153  {
154  LogF("Network: could not init discovery (%s)", pNetIODiscover->GetError() ? pNetIODiscover->GetError() : "");
155  delete pNetIODiscover; pNetIODiscover = nullptr;
156  }
157  else
158  LogSilentF("Network: discovery initialized on port %d", iPortDiscover);
159  // add to thread
160  if (pNetIODiscover)
161  Thread.AddProc(pNetIODiscover);
162  }
163 
164  // plus reference server
165  if (iPortRefServer > 0)
166  {
167  // create
169  // init
170  if (!pRefServer->Init(iPortRefServer))
171  {
172  LogF("Network: could not init reference server (%s)", pNetIO_UDP->GetError() ? pNetIO_UDP->GetError() : "");
173  delete pRefServer; pRefServer = nullptr;
174  }
175  else
176  LogSilentF("Network: reference server initialized on port %d", iPortRefServer);
177  // add to thread
178  if (pRefServer)
179  Thread.AddProc(pRefServer);
180  }
181 
182  // own timer
184  Thread.AddProc(this);
185 
186  // ok
187  return true;
188 }
189 
190 void C4Network2IO::Clear() // by main thread
191 {
192  // process remaining events
194  Thread.ProcessEvents();
195  // clear event callbacks
196  Thread.ClearCallback(Ev_Net_Conn, this);
197  Thread.ClearCallback(Ev_Net_Disconn, this);
198  Thread.ClearCallback(Ev_Net_Packet, this);
199  // close all connections
200  CStdLock ConnListLock(&ConnListCSec);
201  for (C4Network2IOConnection *pConn = pConnList, *pNext; pConn; pConn = pNext)
202  {
203  pNext = pConn->pNext;
204  // close
205  pConn->Close();
206  RemoveConnection(pConn);
207  }
208  // reset list
209  pConnList = nullptr;
210  ConnListLock.Clear();
211  // close net i/o classes
212  Thread.RemoveProc(this);
213  if (pNetIODiscover) { Thread.RemoveProc(pNetIODiscover); delete pNetIODiscover; pNetIODiscover = nullptr; }
214  if (pNetIO_TCP) { Thread.RemoveProc(pNetIO_TCP); delete pNetIO_TCP; pNetIO_TCP = nullptr; }
215  if (pNetIO_UDP) { Thread.RemoveProc(pNetIO_UDP); delete pNetIO_UDP; pNetIO_UDP = nullptr; }
216  if (pRefServer) { Thread.RemoveProc(pRefServer); delete pRefServer; pRefServer = nullptr; }
217  if (UPnPMgr) { delete UPnPMgr; UPnPMgr = nullptr; }
218  // remove auto-accepts
219  ClearAutoAccept();
220  // reset flags
221  fAllowConnect = fExclusiveConn = false;
222  // reset connection ID
223  iNextConnID = 0;
224 }
225 
227 {
228  CStdLock LCCoreLock(&LCCoreCSec);
229  LCCore = nCCore;
230 }
231 
233 {
234  if (pNetIO_UDP) return pNetIO_UDP;
235  if (pNetIO_TCP) return pNetIO_TCP;
236  return nullptr;
237 }
238 
240 {
241  if (pNetIO_TCP) return pNetIO_TCP;
242  if (pNetIO_UDP) return pNetIO_UDP;
243  return nullptr;
244 }
245 
246 bool C4Network2IO::Connect(const C4NetIO::addr_t &addr, C4Network2IOProtocol eProt, const C4ClientCore &nCCore, const char *szPassword) // by main thread
247 {
248  // get network class
249  C4NetIO *pNetIO = getNetIO(eProt);
250  if (!pNetIO) return false;
251  // already connected/connecting?
252  if (GetConnectionByConnAddr(addr, pNetIO)) return true;
253  // assign new connection ID, peer address isn't known yet
254  uint32_t iConnID = iNextConnID++;
255  C4NetIO::addr_t paddr;
256  // create connection object and add to list
258  pConn->Set(pNetIO, eProt, paddr, addr, CS_Connect, szPassword, iConnID);
259  pConn->SetCCore(nCCore);
260  AddConnection(pConn);
261  // connect
262  if (!pConn->Connect())
263  {
264  // show error
265  LogSilentF("Network: could not connect to %s using %s: %s", addr.ToString().getData(),
266  getNetIOName(pNetIO), pNetIO->GetError() ? pNetIO->GetError() : "");
267  pNetIO->ResetError();
268  // remove class
269  RemoveConnection(pConn);
270  return false;
271  }
272  // ok, wait for connection
273  return true;
274 }
275 
276 void C4Network2IO::SetAcceptMode(bool fnAllowConnect) // by main thread
277 {
278  fAllowConnect = fnAllowConnect;
279  // Allow connect? Allow discovery of this host
280  if (fAllowConnect)
281  {
282  if (pNetIODiscover)
283  {
286  }
287  }
288 }
289 
290 void C4Network2IO::SetExclusiveConnMode(bool fnExclusiveConn) // by main thread
291 {
292  if (fExclusiveConn == fnExclusiveConn)
293  return;
294  // Set flag
295  fExclusiveConn = fnExclusiveConn;
296  // Allowed? Send all pending welcome packets
297  if (!fExclusiveConn)
298  SendConnPackets();
299 }
300 
301 int C4Network2IO::getConnectionCount() // by main thread
302 {
303  int iCount = 0;
304  CStdLock ConnListLock(&ConnListCSec);
305  for (C4Network2IOConnection *pConn = pConnList; pConn; pConn = pConn->pNext)
306  if (!pConn->isClosed())
307  iCount++;
308  return iCount;
309 }
310 
311 void C4Network2IO::ClearAutoAccept() // by main thread
312 {
313  CStdLock AALock(&AutoAcceptCSec);
314  // delete
315  while (pAutoAcceptList)
316  {
317  // remove
318  AutoAccept *pAcc = pAutoAcceptList;
319  pAutoAcceptList = pAcc->Next;
320  // delete
321  delete pAcc;
322  }
323 }
324 
325 void C4Network2IO::AddAutoAccept(const C4ClientCore &CCore) // by main thread
326 {
327  CStdLock AALock(&AutoAcceptCSec);
328  // create
329  AutoAccept *pAcc = new AutoAccept();
330  pAcc->CCore = CCore;
331  // add
332  pAcc->Next = pAutoAcceptList;
333  pAutoAcceptList = pAcc;
334 }
335 
336 void C4Network2IO::RemoveAutoAccept(const C4ClientCore &CCore) // by main thread
337 {
338  CStdLock AALock(&AutoAcceptCSec);
339  // find & remove
340  AutoAccept *pAcc = pAutoAcceptList, *pLast = nullptr;
341  while (pAcc)
342  if (pAcc->CCore.getDiffLevel(CCore) <= C4ClientCoreDL_IDMatch)
343  {
344  // unlink
345  AutoAccept *pDelete = pAcc;
346  pAcc = pAcc->Next;
347  (pLast ? pLast->Next : pAutoAcceptList) = pAcc;
348  // delete
349  delete pDelete;
350  }
351  else
352  {
353  // next peer
354  pLast = pAcc;
355  pAcc = pAcc->Next;
356  }
357 }
358 
360 {
361  CStdLock ConnListLock(&ConnListCSec);
362  C4Network2IOConnection *pRes = nullptr;
363  for (C4Network2IOConnection *pConn = pConnList; pConn; pConn = pConn->pNext)
364  if (pConn->isAccepted())
365  if (pConn->getClientID() == iClientID)
366  if (pConn->getProtocol() == P_UDP || !pRes)
367  pRes = pConn;
368  // add reference
369  if (pRes) pRes->AddRef();
370  return pRes;
371 }
372 
374 {
375  CStdLock ConnListLock(&ConnListCSec);
376  C4Network2IOConnection *pRes = nullptr;
377  for (C4Network2IOConnection *pConn = pConnList; pConn; pConn = pConn->pNext)
378  if (pConn->isAccepted())
379  if (pConn->getClientID() == iClientID)
380  if (pConn->getProtocol() == P_TCP || !pRes)
381  pRes = pConn;
382  // add reference
383  if (pRes) pRes->AddRef();
384  return pRes;
385 }
386 
387 void C4Network2IO::BeginBroadcast(bool fSelectAll)
388 {
389  // lock
391  // reset all broadcast flags
392  CStdLock ConnListLock(&ConnListCSec);
393  for (C4Network2IOConnection *pConn = pConnList; pConn; pConn = pConn->pNext)
394  if (pConn->isOpen())
395  pConn->SetBroadcastTarget(fSelectAll);
396 }
397 
399 {
400  // unlock
402 }
403 
405 {
406  bool fSuccess = true;
407  // There is no broadcasting atm, emulate it
408  CStdLock ConnListLock(&ConnListCSec);
409  for (C4Network2IOConnection *pConn = pConnList; pConn; pConn = pConn->pNext)
410  if (pConn->isOpen() && pConn->isBroadcastTarget())
411  fSuccess &= pConn->Send(rPkt);
412  if(!fSuccess)
413  Log("Network: Warning! Broadcast failed.");
414  return fSuccess;
415 }
416 
417 bool C4Network2IO::SendMsgToClient(C4NetIOPacket &rPkt, int iClient) // by both
418 {
419  // find msg connection
420  C4Network2IOConnection *pConn = GetMsgConnection(iClient);
421  if (!pConn) return false;
422  // send
423  bool fSuccess = pConn->Send(rPkt);
424  pConn->DelRef();
425  return fSuccess;
426 }
427 
428 bool C4Network2IO::BroadcastMsg(const C4NetIOPacket &rPkt) // by both
429 {
430  // TODO: ugly algorithm. do better
431 
432  // begin broadcast
433  BeginBroadcast(false);
434  // select one connection per reachable client
435  CStdLock ConnListLock(&ConnListCSec);
436  for (C4Network2IOConnection *pConn = pConnList; pConn; pConn = pConn->pNext)
437  if (pConn->isAccepted())
438  {
439  if (pConn->getProtocol() == P_UDP)
440  pConn->SetBroadcastTarget(true);
441  else if (pConn->getProtocol() == P_TCP)
442  {
443  C4Network2IOConnection *pConn2 = GetMsgConnection(pConn->getClientID());
444  if (pConn == pConn2)
445  pConn->SetBroadcastTarget(true);
446  pConn2->DelRef();
447  }
448  }
449  // send
450  bool fSuccess = Broadcast(rPkt);
451  // end broadcast
452  EndBroadcast();
453  // return
454  return fSuccess;
455 }
456 
458 {
459  // UDP must be initialized
460  if (!pNetIO_UDP)
461  return false;
462  // save address
463  switch (nPuncherAddr.GetFamily())
464  {
466  PuncherAddrIPv4 = nPuncherAddr;
467  break;
469  PuncherAddrIPv6 = nPuncherAddr;
470  break;
472  assert(!"Unexpected address family");
473  }
474  // let's punch
475  return pNetIO_UDP->Connect(nPuncherAddr);
476 }
477 
478 void C4Network2IO::Punch(const C4NetIO::addr_t &punchee_addr)
479 {
480  if (!pNetIO_UDP)
481  return;
482  C4PacketPing PktPeng;
483  dynamic_cast<C4NetIOUDP*>(pNetIO_UDP)->SendDirect(MkC4NetIOPacket(PID_Pong, PktPeng, punchee_addr));
484 }
485 
487 {
488  if (!pNetIO_UDP) return;
491  else if (family == C4NetIO::HostAddress::IPv6 && !PuncherAddrIPv6.IsNull())
493 }
494 
496 {
497  return (!PuncherAddrIPv4.IsNull() && PuncherAddrIPv4 == addr)
498  || (!PuncherAddrIPv6.IsNull() && PuncherAddrIPv6 == addr);
499 }
500 
501 // C4NetIO interface
502 bool C4Network2IO::OnConn(const C4NetIO::addr_t &PeerAddr, const C4NetIO::addr_t &ConnectAddr, const C4NetIO::addr_t *pOwnAddr, C4NetIO *pNetIO)
503 {
504  // puncher answer?
505  if (pNetIO == pNetIO_UDP && IsPuncherAddr(ConnectAddr))
506  {
507  // got an address?
508  if (pOwnAddr)
509  ::Network.OnPuncherConnect(*pOwnAddr);
510  return true;
511  }
512 
513 #if(C4NET2IO_DUMP_LEVEL > 1)
514  Application.InteractiveThread.ThreadLogS("OnConn: %s %s",
515  C4TimeMilliseconds::Now().AsString().getData(),
516  getNetIOName(pNetIO));
517 #endif
518  // search connection
519  C4Network2IOConnection *pConn = nullptr;
520  if (!ConnectAddr.IsNull())
521  pConn = GetConnectionByConnAddr(ConnectAddr, pNetIO);
522  // not found?
523  if (!pConn)
524  {
525  // allow connect?
526  if (!fAllowConnect) return false;
527  // create new connection object
528  uint32_t iConnID = iNextConnID++;
529  pConn = new C4Network2IOConnection();
530  pConn->Set(pNetIO, getNetIOProt(pNetIO), PeerAddr, ConnectAddr, CS_Connected, nullptr, iConnID);
531  // add to list
532  AddConnection(pConn);
533  }
534  else
535  {
536  // already closed this connection (attempt)?
537  if (pConn->isClosed())
538  return false;
539  if (!pConn->isOpen())
540  {
541  // change status
542  pConn->SetStatus(CS_Connected);
543  pConn->SetPeerAddr(PeerAddr);
544  }
545  }
546  // send welcome packet, if appropriate
547  SendConnPackets();
548 #if(C4NET2IO_DUMP_LEVEL > 0)
549  // log
550  Application.InteractiveThread.ThreadLogS("Network: got %s connection from %s", getNetIOName(pNetIO), PeerAddr.ToString().getData());
551 #endif
552  // do event (disabled - unused)
553  // pConn->AddRef(); PushNetEv(NE_Conn, pConn);
554  // ok
555  return true;
556 }
557 
558 void C4Network2IO::OnDisconn(const C4NetIO::addr_t &addr, C4NetIO *pNetIO, const char *szReason)
559 {
560  // punch?
561  if (pNetIO == pNetIO_UDP && IsPuncherAddr(addr))
562  {
563  if (PuncherAddrIPv4 == addr)
565  else
567  return;
568  }
569 #if(C4NET2IO_DUMP_LEVEL > 1)
570  Application.InteractiveThread.ThreadLogS("OnDisconn: %s %s",
571  C4TimeMilliseconds::Now().AsString().getData(),
572  getNetIOName(pNetIO));
573 #endif
574  // find connection
575  C4Network2IOConnection *pConn = GetConnection(addr, pNetIO);
576  if (!pConn) pConn = GetConnectionByConnAddr(addr, pNetIO);
577  if (!pConn) return;
578 #if(C4NET2IO_DUMP_LEVEL > 0)
579  // log
580  Application.InteractiveThread.ThreadLogS("Network: %s connection to %s %s (%s)",
581  getNetIOName(pNetIO), addr.ToString().getData(), pConn->isConnecting() ? "failed" : "closed" , szReason);
582 #endif
583  // already closed? ignore
584  if (!pConn->isClosed())
585  // not accepted yet? count as connection failure
586  pConn->SetStatus(pConn->isHalfAccepted() ? CS_Closed : CS_ConnectFail);
587  // keep connection for main thread message
588  pConn->AddRef();
589  // check for pending welcome packets
590  SendConnPackets();
591  // signal to main thread
593  // don't remove connection from list - wait for postmortem or timeout
594 }
595 
596 void C4Network2IO::OnPacket(const class C4NetIOPacket &rPacket, C4NetIO *pNetIO)
597 {
598 #if C4NET2IO_DUMP_LEVEL > 0
599  auto tTime = C4TimeMilliseconds::Now();
600 #endif
601 #if(C4NET2IO_DUMP_LEVEL > 1)
602  Application.InteractiveThread.ThreadLogS("OnPacket: %s status %02x %s",
603  C4TimeMilliseconds::Now().AsString().getData(),
604  rPacket.getStatus(), getNetIOName(pNetIO));
605 #endif
606  if (pNetIO == pNetIO_UDP && IsPuncherAddr(rPacket.getAddr()))
607  {
608  HandlePuncherPacket(rPacket);
609  return;
610  }
611  if (!rPacket.getSize()) return;
612  // find connection
613  C4Network2IOConnection *pConn = GetConnection(rPacket.getAddr(), pNetIO);
614  if (!pConn)
615  {
616  Application.InteractiveThread.ThreadLog("Network: could not find connection for %s packet (status %02x) from %s!", getNetIOName(pNetIO), rPacket.getStatus(), rPacket.getAddr().ToString().getData());
617  return;
618  }
619 #if(C4NET2IO_DUMP_LEVEL > 2)
620  uint32_t iFindConnectionBlocked = C4TimeMilliseconds::Now() - tTime;
621  if (iFindConnectionBlocked > 100)
622  Application.InteractiveThread.ThreadLogS("OnPacket: ... blocked %d ms for finding the connection!", iFindConnectionBlocked);
623 #endif
624  // notify
625  pConn->OnPacketReceived(rPacket.getStatus());
626  // handle packet
627  HandlePacket(rPacket, pConn, true);
628  // log time
629 #if(C4NET2IO_DUMP_LEVEL > 1)
630  uint32_t iHandlingBlocked = C4TimeMilliseconds::Now() - tTime;
631  if (iHandlingBlocked > 100)
632  Application.InteractiveThread.ThreadLogS("OnPacket: ... blocked %d ms for handling!", iHandlingBlocked);
633 #endif
634 }
635 
636 void C4Network2IO::OnError(const char *strError, C4NetIO *pNetIO)
637 {
638  // let's log it
639  Application.InteractiveThread.ThreadLog("Network: %s error: %s", getNetIOName(pNetIO), strError);
640 }
641 
642 bool C4Network2IO::Execute(int iTimeout, pollfd *)
643 {
645 
646  // check for timeout
647  CheckTimeout();
648 
649  // ping all open connections
651  {
652  Ping();
654  }
655 
656  // do statistics
658  {
661  }
662 
663  // resources
665 
666  // ok
667  return true;
668 }
669 
671 {
672  return tLastExecute + C4NetTimer;
673 }
674 
675 void C4Network2IO::OnThreadEvent(C4InteractiveEventType eEvent, void *pEventData) // by main thread
676 {
677  switch (eEvent)
678  {
679  case Ev_Net_Conn: // got a connection
680  {
681  C4Network2IOConnection *pConn = reinterpret_cast<C4Network2IOConnection *>(pEventData);
682  // do callback
683  ::Network.OnConn(pConn);
684  // remove reference
685  pConn->DelRef();
686  }
687  break;
688 
689  case Ev_Net_Disconn: // connection closed
690  {
691  C4Network2IOConnection *pConn = reinterpret_cast<C4Network2IOConnection *>(pEventData);
692  assert(pConn->isClosed());
693  // do callback
694  ::Network.OnDisconn(pConn);
695  // remove reference
696  pConn->DelRef();
697  }
698  break;
699 
700  case Ev_Net_Packet: // got packet
701  {
702  NetEvPacketData *pData = reinterpret_cast<NetEvPacketData *>(pEventData);
703  // handle
704  HandlePacket(pData->Packet, pData->Conn, false);
705  // clear up
706  pData->Conn->DelRef();
707  delete pData;
708  }
709  break;
710 
711  default:
712  // TODO
713  break;
714  }
715 }
716 
718 {
719  switch (eProt)
720  {
721  case P_UDP: return pNetIO_UDP;
722  case P_TCP: return pNetIO_TCP;
723  default: return nullptr;
724  }
725 }
726 
728 {
729  if (!pNetIO) return "nullptr";
730  if (pNetIO == pNetIO_TCP) return "TCP";
731  if (pNetIO == pNetIO_UDP) return "UDP";
732  return "UNKNOWN";
733 }
734 
736 {
737  if (!pNetIO) return P_NONE;
738  if (pNetIO == pNetIO_TCP) return P_TCP;
739  if (pNetIO == pNetIO_UDP) return P_UDP;
740  return P_NONE;
741 }
742 
744 {
745  CStdLock ConnListLock(&ConnListCSec);
746  // add reference
747  pConn->AddRef();
748  // add to list
749  pConn->pNext = pConnList; pConnList = pConn;
750 }
751 
753 {
754  CStdLock ConnListLock(&ConnListCSec);
755  // search & remove
756  if (pConnList == pConn)
757  pConnList = pConn->pNext;
758  else
759  {
761  for (pAct = pConnList; pAct; pAct = pAct->pNext)
762  if (pAct->pNext == pConn)
763  break;
764  if (pAct)
765  pAct->pNext = pConn->pNext;
766  else
767  return;
768  }
769  // remove reference
770  pConn->pNext = nullptr; pConn->DelRef();
771 }
772 
774 {
775  CStdLock ConnListLock(&ConnListCSec);
776  // search
777  for (C4Network2IOConnection *pConn = pConnList; pConn; pConn = pConn->pNext)
778  if (pConn->getNetClass() == pNetIO && pConn->getPeerAddr() == addr)
779  return pConn;
780  return nullptr;
781 }
782 
784 {
785  CStdLock ConnListLock(&ConnListCSec);
786  // search
787  for (C4Network2IOConnection *pConn = pConnList; pConn; pConn = pConn->pNext)
788  if (pConn->getNetClass() == pNetIO && pConn->getConnectAddr() == addr)
789  return pConn;
790  return nullptr;
791 }
792 
794 {
795  CStdLock ConnListLock(&ConnListCSec);
796  // search
797  for (C4Network2IOConnection *pConn = pConnList; pConn; pConn = pConn->pNext)
798  if (pConn->getID() == iConnID)
799  return pConn;
800  return nullptr;
801 }
802 
804 {
805  if (pRefServer)
806  pRefServer->SetReference(pReference);
807  else
808  delete pReference;
809 }
810 
812 {
813  return !!pRefServer;
814 }
815 
817 {
818  CStdLock AALock(&AutoAcceptCSec);
819  // check if connection with the given client should be allowed
820  for (AutoAccept *pAcc = pAutoAcceptList; pAcc; pAcc = pAcc->Next)
821  // core match?
822  if (CCore.getDiffLevel(pAcc->CCore) <= C4ClientCoreDL_IDMatch)
823  {
824  // check: already got another connection for this client? Peer IP must match, then.
825  for (C4Network2IOConnection *pConn = pConnList; pConn; pConn = pConn->pNext)
826  if (pConn->isAccepted() &&
827  pConn->getCCore().getDiffLevel(CCore) <= C4ClientCoreDL_IDMatch &&
828  pConn->getPeerAddr().GetHost() != Conn.getPeerAddr().GetHost())
829  return false;
830  // not found or IP matches? Let pass
831  return true;
832  }
833  return false;
834 }
835 
836 bool C4Network2IO::HandlePacket(const C4NetIOPacket &rPacket, C4Network2IOConnection *pConn, bool fThread)
837 {
838  // security: add connection reference
839  if (!pConn) return false; pConn->AddRef();
840 
841  // accept only PID_Conn and PID_Ping on non-accepted connections
842  if(!pConn->isHalfAccepted())
843  if(rPacket.getStatus() != PID_Conn && rPacket.getStatus() != PID_Ping && rPacket.getStatus() != PID_ConnRe)
844  return false;
845 
846  // unpack packet (yet another no-idea-why-it's-needed-cast)
847  C4IDPacket Pkt; C4PacketBase &PktB = Pkt;
848  try
849  {
850  PktB.unpack(rPacket);
851  }
852  catch (StdCompiler::Exception *pExc)
853  {
854  Application.InteractiveThread.ThreadLog("Network: error: Failed to unpack packet id %02x: %s", rPacket.getStatus(), pExc->Msg.getData());
855  delete pExc;
856 #ifndef _DEBUG
857  pConn->Close();
858 #endif
859  return false;
860  }
861 
862  // dump packet (network thread only)
863 #if(C4NET2IO_DUMP_LEVEL > 0)
864  if (Config.Network.PacketLogging && fThread && Pkt.getPktType() != PID_Ping && Pkt.getPktType() != PID_Pong && Pkt.getPktType() != PID_NetResData)
865  {
866  // StdStrBuf PacketDump = DecompileToBuf<StdCompilerINIWrite>(mkNamingAdaptrPacket);
867  StdStrBuf PacketHeader = FormatString("HandlePacket: %s by %s (%lu bytes, counter %d)",
868  C4TimeMilliseconds::Now().AsString().getData(),
869  pConn->getPeerAddr().ToString().getData(),
870  static_cast<unsigned long>(rPacket.getSize()), pConn->getInPacketCounter());
871  StdStrBuf Dump = DecompileToBuf<StdCompilerINIWrite>(mkNamingAdapt(Pkt, PacketHeader.getData()));
872  // Put it directly. The standard functions behind StdBuf.Format seem to choke when you pass them too much data.
874  }
875 #endif
876 
877  // search packet handling data
878  bool fSendToMainThread = false, fHandled = false;
879  for (const C4PktHandlingData *pHData = PktHandlingData; pHData->ID != PID_None; pHData++)
880  if (pHData->ID == rPacket.getStatus())
881  {
882  // correct thread?
883  if (!pHData->ProcByThread == !fThread)
884  {
885  // connection accepted?
886  if (pHData->AcceptedOnly || pConn->isAccepted() || pConn->isClosed())
887  {
888  fHandled = true;
889 #if(C4NET2IO_DUMP_LEVEL > 2)
891 #endif
892 
893  // call handler(s)
894  CallHandlers(pHData->HandlerID, &Pkt, pConn, fThread);
895 
896 #if(C4NET2IO_DUMP_LEVEL > 2)
897  uint32_t iBlockedTime = C4TimeMilliseconds::Now() - tStart;
898  if (fThread && iBlockedTime > 100)
899  {
900  Application.InteractiveThread.ThreadLogS("HandlePacket: ... blocked for %u ms!", iBlockedTime);
901  }
902 #endif
903 
904  }
905  }
906  // transfer to main thread?
907  else if (!pHData->ProcByThread && fThread)
908  {
909  fHandled = true;
910  fSendToMainThread = true;
911  }
912  }
913 
914  // send to main thread?
915  if (fSendToMainThread)
916  {
917  // create data
918  NetEvPacketData *pEvData = new NetEvPacketData;
919  pEvData->Packet.Take(rPacket.Duplicate());
920  pEvData->Conn = pConn; pConn->AddRef();
921  // trigger event
923  Application.InteractiveThread.ThreadLogS("...push event ");
924  }
925 
926  // unhandled?
927  if (!fHandled && !pConn->isClosed())
928  Application.InteractiveThread.ThreadLog("Network: Unhandled packet (status %02x)", rPacket.getStatus());
929 
930  // remove connection reference
931  pConn->DelRef();
932  return fHandled;
933 }
934 
935 void C4Network2IO::CallHandlers(int iHandlerID, const C4IDPacket *pPkt, C4Network2IOConnection *pConn, bool fThread)
936 {
937  // emulate old callbacks
938  char cStatus = pPkt->getPktType();
939  const C4PacketBase *pPacket = pPkt->getPkt();
940  // this class (network thread)
941  if (iHandlerID & PH_C4Network2IO)
942  {
943  assert(fThread);
944  HandlePacket(cStatus, pPacket, pConn);
945  }
946  // main network class (main thread)
947  if (iHandlerID & PH_C4Network2)
948  {
949  assert(!fThread);
950  ::Network.HandlePacket(cStatus, pPacket, pConn);
951  }
952  // fullscreen lobby
953  if (iHandlerID & PH_C4GUIMainDlg)
954  {
955  assert(!fThread);
956  ::Network.HandleLobbyPacket(cStatus, pPacket, pConn);
957  }
958  // client list class (main thread)
959  if (iHandlerID & PH_C4Network2ClientList)
960  {
961  assert(!fThread);
962  ::Network.Clients.HandlePacket(cStatus, pPacket, pConn);
963  }
964  // player list class (main thread)
965  if (iHandlerID & PH_C4Network2Players)
966  {
967  assert(!fThread);
968  ::Network.Players.HandlePacket(cStatus, pPacket, pConn);
969  }
970  // resource list class (network thread)
971  if (iHandlerID & PH_C4Network2ResList)
972  {
973  assert(fThread);
974  ::Network.ResList.HandlePacket(cStatus, pPacket, pConn);
975  }
976  // network control (mixed)
977  if (iHandlerID & PH_C4GameControlNetwork)
978  {
979  ::Control.Network.HandlePacket(cStatus, pPacket, pConn);
980  }
981 }
982 
983 void C4Network2IO::HandlePacket(char cStatus, const C4PacketBase *pPacket, C4Network2IOConnection *pConn)
984 {
985  // security
986  if (!pConn) return;
987 
988 #define GETPKT(type, name) \
989  assert(pPacket); const type &name = \
990  static_cast<const type &>(*pPacket);
991 
992  switch (cStatus)
993  {
994 
995  case PID_Conn: // connection request
996  {
997  if (!pConn->isOpen()) break;
998  // get packet
999  GETPKT(C4PacketConn, rPkt)
1000  // set connection ID
1001  pConn->SetRemoteID(rPkt.getConnID());
1002  // check auto-accept
1003  if (doAutoAccept(rPkt.getCCore(), *pConn))
1004  {
1005  // send answer back
1006  C4PacketConnRe pcr(true, false, "auto accept");
1007  if (!pConn->Send(MkC4NetIOPacket(PID_ConnRe, pcr)))
1008  pConn->Close();
1009  // accept
1010  pConn->SetStatus(CS_HalfAccepted);
1011  pConn->SetCCore(rPkt.getCCore());
1012  pConn->SetAutoAccepted();
1013  }
1014  // note that this packet will get processed by C4Network2, too (main thread)
1015  }
1016  break;
1017 
1018  case PID_ConnRe: // connection request reply
1019  {
1020  if (!pConn->isOpen()) break;
1021  // conn not sent? That's fishy.
1022  // FIXME: Note this happens if the peer has exclusive connection mode on.
1023  if (!pConn->isConnSent())
1024  {
1025  pConn->Close();
1026  break;
1027  }
1028  // get packet
1029  GETPKT(C4PacketConnRe, rPkt)
1030  // auto accept connection
1031  if (rPkt.isOK())
1032  {
1033  if (pConn->isHalfAccepted() && pConn->isAutoAccepted())
1034  pConn->SetAccepted();
1035  }
1036  }
1037  break;
1038 
1039  case PID_Ping:
1040  {
1041  if (!pConn->isOpen()) break;
1042  GETPKT(C4PacketPing, rPkt)
1043  // pong
1044  C4PacketPing PktPong = rPkt;
1045  pConn->Send(MkC4NetIOPacket(PID_Pong, PktPong));
1046  // remove received packets from log
1047  pConn->ClearPacketLog(rPkt.getPacketCounter());
1048  }
1049  break;
1050 
1051  case PID_Pong:
1052  {
1053  if (!pConn->isOpen()) break;
1054  GETPKT(C4PacketPing, rPkt);
1055  // save
1056  pConn->SetPingTime(rPkt.getTravelTime());
1057  }
1058  break;
1059 
1060  case PID_FwdReq:
1061  {
1062  GETPKT(C4PacketFwd, rPkt);
1063  HandleFwdReq(rPkt, pConn);
1064  }
1065  break;
1066 
1067  case PID_Fwd:
1068  {
1069  GETPKT(C4PacketFwd, rPkt);
1070  // only received accidently?
1071  if (!rPkt.DoFwdTo(LCCore.getID())) break;
1072  // handle
1073  C4NetIOPacket Packet(rPkt.getData(), pConn->getPeerAddr());
1074  HandlePacket(Packet, pConn, true);
1075  }
1076  break;
1077 
1078  case PID_PostMortem:
1079  {
1080  GETPKT(C4PacketPostMortem, rPkt);
1081  // Get connection
1082  C4Network2IOConnection *pConn = GetConnectionByID(rPkt.getConnID());
1083  if (!pConn) return;
1084  // Handle all packets
1085  uint32_t iCounter;
1086  for (iCounter = pConn->getInPacketCounter(); ; iCounter++)
1087  {
1088  // Get packet
1089  const C4NetIOPacket *pPkt = rPkt.getPacket(iCounter);
1090  if (!pPkt) break;
1091  // Handle it
1092  HandlePacket(*pPkt, pConn, true);
1093  }
1094  // Log
1095  if (iCounter > pConn->getInPacketCounter())
1096  Application.InteractiveThread.ThreadLogS("Network: Recovered %d packets", iCounter - pConn->getInPacketCounter());
1097  // Remove the connection from our list
1098  if (!pConn->isClosed())
1099  pConn->Close();
1100  RemoveConnection(pConn);
1101  }
1102  break;
1103 
1104  }
1105 
1106 #undef GETPKT
1107 }
1108 
1110 {
1111  CStdLock ConnListLock(&ConnListCSec);
1112  // init packet
1113  C4PacketFwd nFwd;
1114  nFwd.SetListType(false);
1115  // find all clients the message should be forwarded to
1116  int iClientID; C4Network2IOConnection *pConn;
1117  for (pConn = pConnList; pConn; pConn = pConn->pNext)
1118  if (pConn->isAccepted())
1119  if ((iClientID = pConn->getClientID()) >= 0)
1120  if (iClientID != pBy->getClientID())
1121  if (rFwd.DoFwdTo(iClientID) && !nFwd.DoFwdTo(iClientID))
1122  nFwd.AddClient(iClientID);
1123  // check count (hardcoded: broadcast for > 2 clients)
1124  if (nFwd.getClientCnt() <= 2)
1125  {
1126  C4NetIOPacket Pkt(rFwd.getData(), C4NetIO::addr_t());
1127  for (int i = 0; i < nFwd.getClientCnt(); i++)
1128  if ((pConn = GetMsgConnection(nFwd.getClient(i))))
1129  {
1130  pConn->Send(Pkt);
1131  pConn->DelRef();
1132  }
1133  }
1134  else
1135  {
1136  // Temporarily unlock connection list for getting broadcast lock
1137  // (might lead to deathlocks otherwise, as the lock is often taken
1138  // in the opposite order)
1139  ConnListLock.Clear();
1140 
1141  BeginBroadcast();
1142  nFwd.SetData(rFwd.getData());
1143  // add all clients
1144  CStdLock ConnListLock(&ConnListCSec);
1145  for (int i = 0; i < nFwd.getClientCnt(); i++)
1146  if ((pConn = GetMsgConnection(nFwd.getClient(i))))
1147  {
1148  pConn->SetBroadcastTarget(true);
1149  pConn->DelRef();
1150  }
1151  // broadcast
1153  EndBroadcast();
1154  }
1155  // doing a callback here; don't lock!
1156  ConnListLock.Clear();
1157  // forward to self?
1158  if (rFwd.DoFwdTo(LCCore.getID()))
1159  {
1160  C4NetIOPacket Packet(rFwd.getData(), pBy->getPeerAddr());
1161  HandlePacket(Packet, pBy, true);
1162  }
1163 }
1164 
1166 {
1167  auto pkt = C4NetpuncherPacket::Construct(rPacket);
1168  if (pkt && ::Network.HandlePuncherPacket(move(pkt), rPacket.getAddr().GetFamily()));
1169  else
1170  {
1171  assert(pNetIO_UDP);
1172  pNetIO_UDP->Close(rPacket.getAddr());
1173  }
1174 }
1175 
1177 {
1178  bool fSuccess = true;
1179  // ping all connections
1180  for (C4Network2IOConnection *pConn = pConnList; pConn; pConn = pConn->pNext)
1181  if (pConn->isOpen())
1182  {
1183  C4PacketPing Ping(pConn->getInPacketCounter(), pConn->getOutPacketCounter());
1184  fSuccess &= pConn->Send(MkC4NetIOPacket(PID_Ping, Ping));
1185  pConn->OnPing();
1186  }
1187  return fSuccess;
1188 }
1189 
1191 {
1192  // acquire lock
1193  CStdLock ConnListLock(&ConnListCSec);
1194  // check all connections for timeout (use deletion-safe iteration method just in case)
1195  for (C4Network2IOConnection *pConn = pConnList, *pNext; pConn; pConn = pNext)
1196  {
1197  pNext = pConn->pNext;
1198  // status timeout
1199  if (!pConn->isClosed() && !pConn->isAccepted())
1200  if (difftime(time(nullptr), pConn->getTimestamp()) > C4NetAcceptTimeout)
1201  {
1202  Application.InteractiveThread.ThreadLogS("Network: connection accept timeout to %s", pConn->getPeerAddr().ToString().getData());
1203  pConn->Close();
1204  }
1205  // ping timeout
1206  if (pConn->isAccepted())
1207  if ((pConn->getLag() != -1 ? pConn->getLag() : 1000 * difftime(time(nullptr), pConn->getTimestamp()))
1208  > C4NetPingTimeout)
1209  {
1210  Application.InteractiveThread.ThreadLogS("%d %d %d", (int)pConn->getLag(), (int)time(nullptr), (int)pConn->getTimestamp());
1211  Application.InteractiveThread.ThreadLogS("Network: ping timeout to %s", pConn->getPeerAddr().ToString().getData());
1212  pConn->Close();
1213  }
1214  // delayed connection removal
1215  if (pConn->isClosed())
1216  if (difftime(time(nullptr), pConn->getTimestamp()) > C4NetAcceptTimeout)
1217  RemoveConnection(pConn);
1218  }
1219 }
1220 
1222 {
1223  int iTCPIRateSum = 0, iTCPORateSum = 0, iUDPIRateSum = 0, iUDPORateSum = 0;
1224 
1225  // acquire lock, get connection statistics
1226  CStdLock ConnListLock(&ConnListCSec);
1227  for (C4Network2IOConnection *pConn = pConnList; pConn; pConn = pConn->pNext)
1228  if (pConn->isOpen())
1229  {
1230  bool fTCP = pConn->getNetClass() == pNetIO_TCP;
1231  pConn->DoStatistics(iInterval, fTCP ? &iTCPIRateSum : &iUDPIRateSum,
1232  fTCP ? &iTCPORateSum : &iUDPORateSum);
1233  }
1234  ConnListLock.Clear();
1235 
1236  // get broadcast statistics
1237  int inTCPBCRate = 0, inUDPBCRate = 0;
1238  if (pNetIO_TCP) pNetIO_TCP->GetStatistic(&inTCPBCRate);
1239  if (pNetIO_UDP) pNetIO_UDP->GetStatistic(&inUDPBCRate);
1240 
1241  // normalize everything
1242  iTCPIRateSum = iTCPIRateSum * 1000 / iInterval;
1243  iTCPORateSum = iTCPORateSum * 1000 / iInterval;
1244  iUDPIRateSum = iUDPIRateSum * 1000 / iInterval;
1245  iUDPORateSum = iUDPORateSum * 1000 / iInterval;
1246  inTCPBCRate = inTCPBCRate * 1000 / iInterval;
1247  inUDPBCRate = inUDPBCRate * 1000 / iInterval;
1248 
1249  // clear
1252 
1253  // save back
1254  iTCPIRate = iTCPIRateSum; iTCPORate = iTCPORateSum; iTCPBCRate = inTCPBCRate;
1255  iUDPIRate = iUDPIRateSum; iUDPORate = iUDPORateSum; iUDPBCRate = inUDPBCRate;
1256 }
1257 
1259 {
1260  CStdLock ConnListLock(&ConnListCSec);
1261 
1262  // exlusive conn?
1263  if (fExclusiveConn)
1264  // find a live connection
1265  for (C4Network2IOConnection *pConn = pConnList; pConn; pConn = pConn->pNext)
1266  if (pConn->isAccepted() || (!pConn->isClosed() && pConn->isConnSent()))
1267  // do not sent additional conn packets - no other connection should succeed
1268  return;
1269 
1270  // sent pending welcome packet(s)
1271  for (C4Network2IOConnection *pConn = pConnList; pConn; pConn = pConn->pNext)
1272  if (pConn->isOpen() && !pConn->isConnSent())
1273  {
1274  // make packet
1275  CStdLock LCCoreLock(&LCCoreCSec);
1276  C4NetIOPacket Pkt = MkC4NetIOPacket(PID_Conn, C4PacketConn(LCCore, pConn->getID(), pConn->getPassword()));
1277  LCCoreLock.Clear();
1278  // send
1279  if (!pConn->Send(Pkt))
1280  pConn->Close();
1281  else
1282  {
1283  // set flag
1284  pConn->SetConnSent();
1285  // only one conn packet at a time
1286  if (fExclusiveConn)
1287  return;
1288  }
1289  }
1290 
1291 }
1292 
1293 // *** C4Network2IOConnection
1294 
1296  : pNetClass(nullptr),
1297  iID(~0), iRemoteID(~0),
1298  fAutoAccept(false),
1299  fBroadcastTarget(false),
1300  iTimestamp(0),
1301  iPingTime(-1),
1302  tLastPing(C4TimeMilliseconds::NegativeInfinity),
1303  tLastPong(C4TimeMilliseconds::NegativeInfinity),
1304  fConnSent(false),
1305  fPostMortemSent(false),
1306  iOutPacketCounter(0), iInPacketCounter(0),
1307  pPacketLog(nullptr),
1308  pNext(nullptr),
1309  iRefCnt(0)
1310 {
1311 }
1312 
1314 {
1315  assert(!iRefCnt);
1316  // connection needs to be closed?
1317  if (pNetClass && !isClosed()) Close();
1318  // clear the packet log
1319  ClearPacketLog();
1320 }
1321 
1323 {
1324  if (iPingTime != -1)
1325  {
1326  // Last ping not answered yet?
1327  if(tLastPing > tLastPong)
1328  {
1329  int iPingLag = C4TimeMilliseconds::Now() - tLastPing;
1330  // Use it for lag measurement once it's larger then the last ping time
1331  // (the ping time won't be better than this anyway once the pong's here)
1332  return std::max(iPingLag, iPingTime);
1333  }
1334  }
1335  // Last ping result
1336  return iPingTime;
1337 }
1338 
1339 void C4Network2IOConnection::Set(C4NetIO *pnNetClass, C4Network2IOProtocol enProt, const C4NetIO::addr_t &nPeerAddr, const C4NetIO::addr_t &nConnectAddr, C4Network2IOConnStatus nStatus, const char *szPassword, uint32_t inID)
1340 {
1341  // save data
1342  pNetClass = pnNetClass; eProt = enProt;
1343  PeerAddr = nPeerAddr; ConnectAddr = nConnectAddr;
1344  Status = nStatus;
1345  Password = szPassword;
1346  iID = inID;
1347  // initialize
1348  fBroadcastTarget = false;
1349  iTimestamp = time(nullptr); iPingTime = -1;
1350 }
1351 
1352 void C4Network2IOConnection::SetRemoteID(uint32_t inRemoteID)
1353 {
1354  iRemoteID = inRemoteID;
1355 }
1356 
1358 {
1359  // just do it
1360  PeerAddr = nPeerAddr;
1361 }
1362 
1364 {
1365  // Still no pong for the last ping?
1366  if (tLastPong < tLastPing)
1367  return;
1368 
1369  // Save time
1371 }
1372 
1374 {
1375  // save it
1376  iPingTime = inPingTime;
1377  // pong received - save timestamp
1379 }
1380 
1382 {
1383  if (nStatus != Status)
1384  {
1385  // Connection can't return from these
1386  assert(!isClosed());
1387  // set status
1388  Status = nStatus;
1389  // reset timestamp for connect/accept/close
1391  iTimestamp = time(nullptr);
1392  }
1393 }
1394 
1396 {
1397  fAutoAccept = true;
1398 }
1399 
1401 {
1402  // Just count them
1403  if (iPacketType >= PID_PacketLogStart)
1404  iInPacketCounter++;
1405 }
1406 
1408 {
1409  // Search position of first packet to delete
1410  PacketLogEntry *pPos, *pPrev = nullptr;
1411  for (pPos = pPacketLog; pPos; pPrev = pPos, pPos = pPos->Next)
1412  if (pPos->Number < iUntilID)
1413  break;
1414  if (pPos)
1415  {
1416  // Remove packets from list
1417  (pPrev ? pPrev->Next : pPacketLog) = nullptr;
1418  // Delete everything
1419  while (pPos)
1420  {
1421  PacketLogEntry *pDelete = pPos;
1422  pPos = pPos->Next;
1423  delete pDelete;
1424  }
1425  }
1426 }
1427 
1429 {
1430  // Security
1431  if (!pPkt) return false;
1432  CStdLock PacketLogLock(&PacketLogCSec);
1433  // Nothing to do?
1434  if (!pPacketLog) return false;
1435  // Already created?
1436  if (fPostMortemSent) return false;
1437  // Set connection ID and packet counter
1438  pPkt->SetConnID(iRemoteID);
1440  // Add packets
1441  for (PacketLogEntry *pEntry = pPacketLog; pEntry; pEntry = pEntry->Next)
1442  pPkt->Add(pEntry->Pkt);
1443  // Okay
1444  fPostMortemSent = true;
1445  return true;
1446 }
1447 
1449 {
1450  CStdLock CCoreLock(&CCoreCSec);
1451  CCore = nCCore;
1452 }
1453 
1455 {
1456  if (!pNetClass) return false;
1457  // try connect
1458  return pNetClass->Connect(ConnectAddr);
1459 }
1460 
1462 {
1463  if (!pNetClass || isClosed()) return;
1464  // set status
1466  // close
1468 }
1469 
1471 {
1472  // some packets shouldn't go into the log
1473  if (rPkt.getStatus() < PID_PacketLogStart)
1474  {
1475  assert(isOpen());
1476  C4NetIOPacket Copy(rPkt);
1477  Copy.SetAddr(PeerAddr);
1478  return pNetClass->Send(Copy);
1479  }
1480  CStdLock PacketLogLock(&PacketLogCSec);
1481  // create log entry
1482  PacketLogEntry *pLogEntry = new PacketLogEntry();
1483  pLogEntry->Number = iOutPacketCounter++;
1484  pLogEntry->Pkt = rPkt;
1485  pLogEntry->Next = pPacketLog;
1486  pPacketLog = pLogEntry;
1487  // set address
1488  pLogEntry->Pkt.SetAddr(PeerAddr);
1489  // closed? No sweat, post mortem will reroute it later.
1490  if (!isOpen())
1491  {
1492  // post mortem already sent? This shouldn't happen
1493  if (fPostMortemSent) { assert(false); return false; }
1494  // okay then
1495  return true;
1496  }
1497  // send
1498  bool fSuccess = pNetClass->Send(pLogEntry->Pkt);
1499  if (fSuccess)
1500  assert(!fPostMortemSent);
1501  else {
1502  // Not being able to send a packet is actually a big deal,
1503  // as this means that we will have hole in the packet
1504  // order. Better close the connection - post mortem should
1505  // ideally sort everything out from here.
1506  LogF("Network: Fatal: Send failed (%s)", pNetClass->GetError());
1507  pNetClass->ResetError();
1508  Close();
1509  }
1510  return fSuccess;
1511 }
1512 
1514 {
1515  // Note that each thread will have to make sure that this flag won't be
1516  // changed until Broadcast() is called. See C4Network2IO::BroadcastCSec.
1518  fBroadcastTarget = fSet;
1519 }
1520 
1521 void C4Network2IOConnection::DoStatistics(int iInterval, int *pIRateSum, int *pORateSum)
1522 {
1523  // get C4NetIO statistics
1524  int inIRate, inORate, inLoss;
1525  if (!isOpen() || !pNetClass->GetConnStatistic(PeerAddr, &inIRate, &inORate, &inLoss))
1526  {
1527  iIRate = iORate = iPacketLoss = 0;
1528  return;
1529  }
1530  // normalize
1531  inIRate = inIRate * 1000 / iInterval;
1532  inORate = inORate * 1000 / iInterval;
1533  // set
1534  iIRate = inIRate; iORate = inORate; iPacketLoss = inLoss;
1535  // sum up
1536  if (pIRateSum) *pIRateSum += iIRate;
1537  if (pORateSum) *pORateSum += iORate;
1538 }
1539 
1541 {
1542  ++iRefCnt;
1543 }
1544 
1546 {
1547  if (--iRefCnt == 0)
1548  delete this;
1549 }
1550 
1551 
1552 // *** C4PacketPostMortem
1553 
1555  : iConnID(~0),
1556  iPacketCounter(~0),
1557  iPacketCount(0),
1558  pPackets(nullptr)
1559 {
1560 
1561 }
1562 
1564 {
1565  while (pPackets)
1566  {
1567  PacketLink *pDelete = pPackets;
1568  pPackets = pPackets->Next;
1569  delete pDelete;
1570  }
1571  iPacketCount = 0;
1572 }
1573 
1574 const C4NetIOPacket *C4PacketPostMortem::getPacket(uint32_t iNumber) const
1575 {
1576  // Security
1577  if (!Inside(iNumber, iPacketCounter - iPacketCount, iPacketCounter - 1))
1578  return nullptr;
1579  // Calculate position in list
1580  iNumber = iNumber + iPacketCount - iPacketCounter;
1581  // Search for the packet with the given number
1582  PacketLink *pLink = pPackets;
1583  for (; pLink && iNumber; iNumber--)
1584  pLink = pLink->Next;
1585  // Not found?
1586  return pLink ? &pLink->Pkt : nullptr;
1587 }
1588 
1589 void C4PacketPostMortem::SetPacketCounter(uint32_t inPacketCounter)
1590 {
1591  iPacketCounter = inPacketCounter;
1592 }
1593 
1595 {
1596  // Add to head of list (reverse order)
1597  PacketLink *pLink = new PacketLink();
1598  pLink->Pkt = rPkt;
1599  pLink->Next = pPackets;
1600  pPackets = pLink;
1601  iPacketCount++;
1602 }
1603 
1605 {
1606  bool deserializing = pComp->isDeserializer();
1607 
1608  // Connection ID, packet number and packet count
1609  pComp->Value(mkNamingAdapt(iConnID, "ConnID"));
1610  pComp->Value(mkNamingAdapt(iPacketCounter, "PacketCounter"));
1611  pComp->Value(mkNamingAdapt(iPacketCount, "PacketCount"));
1612 
1613  // Packets
1614  if (deserializing)
1615  {
1616  // Read packets
1617  for (uint32_t i = 0; i < iPacketCount; i++)
1618  {
1619  // Create list entry
1620  PacketLink *pLink = new PacketLink();
1621  pLink->Next = pPackets;
1622  pPackets = pLink;
1623  // Compile data
1624  pComp->Value(mkNamingAdapt(pLink->Pkt, "PacketData"));
1625  }
1626  // Reverse order
1627  PacketLink *pPackets2 = pPackets;
1628  pPackets = nullptr;
1629  while (pPackets2)
1630  {
1631  // Get link
1632  PacketLink *pLink = pPackets2;
1633  pPackets2 = pLink->Next;
1634  // Readd to list
1635  pLink->Next = pPackets;
1636  pPackets = pLink;
1637  }
1638  }
1639  else
1640  {
1641  // Write packets
1642  for (PacketLink *pLink = pPackets; pLink; pLink = pLink->Next)
1643  pComp->Value(mkNamingAdapt(pLink->Pkt, "PacketData"));
1644  }
1645 }
bool fExclusiveConn
Definition: C4Network2IO.h:90
const char * getData() const
Definition: StdBuf.h:450
C4NetIO * pNetIO_UDP
Definition: C4Network2IO.h:55
void ResetError()
Definition: C4NetIO.h:286
int getConnectionCount()
void SetReference(class C4Network2Reference *pReference)
CStdCSec BroadcastCSec
Definition: C4Network2IO.h:72
C4Network2IOConnection * GetConnection(const C4NetIO::addr_t &addr, C4NetIO *pNetIO)
C4NetIOPacket PackTo(const C4NetIO::addr_t &) const
C4Config Config
Definition: C4Config.cpp:837
void Take(void *pnData, size_t inSize)
Definition: StdBuf.h:132
void SetPacketCounter(uint32_t iPacketCounter)
void HandlePacket(char cStatus, const C4PacketBase *pPacket, C4Network2IOConnection *pConn)
bool HandlePacket(const C4NetIOPacket &rPacket, C4Network2IOConnection *pConn, bool fThread)
void OnDisconn(C4Network2IOConnection *pConn)
Definition: C4Network2.cpp:858
void SendPuncherPacket(const C4NetpuncherPacket &, C4NetIO::HostAddress::AddressFamily family)
const int C4NetAcceptTimeout
Definition: C4Network2IO.h:37
bool ThreadLogS(const char *szMessage,...) GNUC_FORMAT_ATTRIBUTE_O
void AddAutoAccept(const C4ClientCore &CCore)
void SetCCore(const C4ClientCore &nCCore)
int32_t PacketLogging
Definition: C4Config.h:169
CStdCSec ConnListCSec
Definition: C4Network2IO.h:72
C4Network2IOConnection * GetMsgConnection(int iClientID)
bool SendMsgToClient(C4NetIOPacket &rPkt, int iClient)
void OnPacketReceived(uint8_t iPacketType)
C4Network2IOProtocol eProt
Definition: C4Network2IO.h:224
void OnPuncherConnect(C4NetIO::addr_t addr)
StdStrBuf ToString(int flags=0) const
Definition: C4NetIO.cpp:605
bool Connect(const C4NetIO::addr_t &addr, C4Network2IOProtocol eProt, const C4ClientCore &nCCore, const char *szPassword=nullptr)
C4Network2IOConnection * GetConnectionByConnAddr(const C4NetIO::addr_t &addr, C4NetIO *pNetIO)
C4Network2IOConnection * Conn
void OnThreadEvent(C4InteractiveEventType eEvent, void *pEventData)
virtual const char * GetError() const
Definition: C4NetIO.h:285
bool Send(const C4NetIOPacket &rPkt)
C4Network2IOProtocol
Definition: C4Network2IO.h:29
virtual C4TimeMilliseconds GetNextTick(C4TimeMilliseconds tNow)
const int32_t C4ClientCoreDL_IDMatch
Definition: C4Client.h:30
uint32_t getInPacketCounter() const
Definition: C4Network2IO.h:281
bool PushEvent(C4InteractiveEventType eEventType, void *pData=nullptr)
static std::unique_ptr< C4NetpuncherPacket > Construct(const C4NetIOPacket &rpack)
void SetExclusiveConnMode(bool fExclusiveConn)
C4Network2IOConnection * pNext
Definition: C4Network2IO.h:256
bool IsReferenceNeeded()
virtual void SetCallback(CBClass *pnCallback)=0
const C4PktHandlingData PktHandlingData[]
Definition: C4Packet2.cpp:51
C4PacketPostMortem()
struct C4Network2IO::AutoAccept * pAutoAcceptList
const C4NetIO::addr_t & getPeerAddr() const
Definition: C4Network2IO.h:264
C4NetIO * MsgIO()
bool isHalfAccepted() const
Definition: C4Network2IO.h:286
void ClearCallback(C4InteractiveEventType eEvent, Callback *pnNetworkCallback)
size_t getSize() const
Definition: StdBuf.h:109
void Add(const C4NetIOPacket &rPkt)
class C4Network2RefServer * pRefServer
Definition: C4Network2IO.h:61
const int C4NetPingFreq
Definition: C4Network2IO.h:35
bool ThreadLog(const char *szMessage,...) GNUC_FORMAT_ATTRIBUTE_O
bool Init(uint16_t iPort=C4NetIO::addr_t::IPPORT_NONE)
C4PacketType ID
Definition: C4PacketBase.h:201
StdNamingAdapt< T > mkNamingAdapt(T &&rValue, const char *szName)
Definition: StdAdaptors.h:93
void HandlePacket(char cStatus, const C4PacketBase *pPacket, class C4Network2IOConnection *pConn)
C4NetIO::addr_t PeerAddr
Definition: C4Network2IO.h:225
void SetPingTime(int iPingTime)
void HandleLobbyPacket(char cStatus, const C4PacketBase *pBasePkt, C4Network2IOConnection *pConn)
Definition: C4Network2.cpp:985
CStdCSec LCCoreCSec
Definition: C4Network2IO.h:68
const C4NetIO::addr_t & getAddr() const
Definition: C4NetIO.h:316
void ClearAutoAccept()
void Punch(const C4NetIO::addr_t &)
C4TimeMilliseconds tLastExecute
Definition: C4Network2IO.h:93
class C4NetIO * pNetClass
Definition: C4Network2IO.h:223
C4ClientCore LCCore
Definition: C4Network2IO.h:67
bool LogSilentF(const char *strMessage,...)
Definition: C4Log.cpp:263
void AddClient(int32_t iClient)
Definition: C4Packet2.cpp:484
C4Network2 Network
Definition: C4Globals.cpp:53
~C4PacketPostMortem()
int32_t getClient(int32_t i) const
Definition: C4Network2IO.h:402
C4Network2IOProtocol getNetIOProt(C4NetIO *pNetIO)
std::atomic_long iRefCnt
Definition: C4Network2IO.h:259
bool isAccepted() const
Definition: C4Network2IO.h:287
void Clear()
Definition: StdSync.h:178
bool doAutoAccept(const C4ClientCore &CCore, const C4Network2IOConnection &Conn)
uint32_t iNextConnID
Definition: C4Network2IO.h:75
void AddConnection(C4Network2IOConnection *pConn)
C4GameControl Control
void SetRemoteID(uint32_t iRemoteID)
void SetDiscoverable(bool fnEnabled)
C4NetIOPacket MkC4NetIOPacket(char cStatus, const class C4PacketBase &Pkt, const C4NetIO::addr_t &addr=C4NetIO::addr_t())
Definition: C4PacketBase.h:40
int getClientID() const
Definition: C4Network2IO.h:271
virtual void OnDisconn(const C4NetIO::addr_t &addr, C4NetIO *pNetIO, const char *szReason)
virtual bool GetConnStatistic(const addr_t &addr, int *pIRate, int *pORate, int *pLoss)=0
C4Network2IOConnection * GetConnectionByID(uint32_t iConnID)
void RemoveConnection(C4Network2IOConnection *pConn)
HostAddress GetHost() const
Definition: C4NetIO.h:155
C4NetIO * getNetIO(C4Network2IOProtocol eProt)
C4TimeMilliseconds tLastPing
Definition: C4Network2IO.h:234
void SetBroadcastTarget(bool fSet)
void SetListType(bool fnNegativeList)
Definition: C4Packet2.cpp:479
bool Broadcast(const C4NetIOPacket &rPkt)
void Set(C4NetIO *pnNetClass, C4Network2IOProtocol eProt, const C4NetIO::addr_t &nPeerAddr, const C4NetIO::addr_t &nConnectAddr, C4Network2IOConnStatus nStatus, const char *szPassword, uint32_t iID)
C4ConfigNetwork Network
Definition: C4Config.h:256
CStdCSec AutoAcceptCSec
Definition: C4Network2IO.h:87
#define GETPKT(type, name)
void SetAcceptMode(bool fAcceptAll)
bool isConnecting() const
Definition: C4Network2IO.h:284
bool fAllowConnect
Definition: C4Network2IO.h:78
const int C4NetPingTimeout
Definition: C4Network2IO.h:38
virtual void ClearStatistic()=0
bool LogFatal(const char *szMessage)
Definition: C4Log.cpp:230
C4NetIO::addr_t PuncherAddrIPv4
Definition: C4Network2IO.h:102
C4InteractiveEventType
AddressFamily GetFamily() const
Definition: C4NetIO.cpp:526
int32_t getID() const
Definition: C4Client.h:57
void SendConnPackets()
void HandlePacket(char cStatus, const C4PacketBase *pPacket, C4Network2IOConnection *pConn)
void Value(const T &rStruct)
Definition: StdCompiler.h:171
EndpointAddress addr_t
Definition: C4NetIO.h:212
void OnConn(C4Network2IOConnection *pConn)
Definition: C4Network2.cpp:850
virtual bool Execute(int iTimeout, pollfd *)
bool BroadcastMsg(const C4NetIOPacket &rPkt)
C4Network2ResList ResList
Definition: C4Network2.h:113
bool AddProc(StdSchedulerProc *pProc)
int32_t getDiffLevel(const C4ClientCore &CCore2) const
Definition: C4Client.cpp:85
C4Network2IOConnection * pConnList
Definition: C4Network2IO.h:71
C4NetIO * DataIO()
void SetLocalCCore(const C4ClientCore &CCore)
void RemoveProc(StdSchedulerProc *pProc)
virtual bool Close()=0
virtual bool isDeserializer()
Definition: StdCompiler.h:63
virtual bool Send(const class C4NetIOPacket &rPacket)=0
C4NetIO * pNetIO_TCP
Definition: C4Network2IO.h:55
void BeginBroadcast(bool fSelectAll=false)
virtual void OnError(const char *strError, C4NetIO *pNetIO)
C4Network2ClientList Clients
Definition: C4Network2.h:116
virtual void Leave()
Definition: StdSync.h:151
void SetCallback(C4InteractiveEventType eEvent, Callback *pnNetworkCallback)
int32_t getClientCnt() const
Definition: C4Network2IO.h:403
C4NetIO::addr_t PuncherAddrIPv6
Definition: C4Network2IO.h:102
void EndBroadcast()
void AddMapping(enum C4Network2IOProtocol protocol, uint16_t intport, uint16_t extport)
virtual void Enter()
Definition: StdSync.h:150
void GenerateStatistics(int iInterval)
C4GameControlNetwork Network
Definition: C4GameControl.h:67
void RemoveAutoAccept(const C4ClientCore &CCore)
class C4Network2UPnP * UPnPMgr
Definition: C4Network2IO.h:64
StdCopyStrBuf Password
Definition: C4Network2IO.h:240
const C4NetIOPacket * getPacket(uint32_t iNumber) const
const int C4NetTimer
Definition: C4Network2IO.h:34
C4Network2IOConnStatus Status
Definition: C4Network2IO.h:228
PacketLogEntry * pPacketLog
Definition: C4Network2IO.h:252
uint8_t getStatus() const
Definition: C4NetIO.h:318
C4Network2IOConnection * GetDataConnection(int iClientID)
class C4Network2IODiscover * pNetIODiscover
Definition: C4Network2IO.h:58
virtual void OnPacket(const C4NetIOPacket &rPacket, C4NetIO *pNetIO)
C4Network2IOConnStatus
Definition: C4Network2IO.h:203
C4Network2Players Players
Definition: C4Network2.h:119
void HandlePacket(char cStatus, const C4PacketBase *pBasePkt, C4Network2IOConnection *pConn)
virtual void CompileFunc(StdCompiler *pComp)
void SetConnID(uint32_t inConnID)
Definition: C4Network2IO.h:434
bool Log(const char *szMessage)
Definition: C4Log.cpp:195
C4TimeMilliseconds tLastPing
Definition: C4Network2IO.h:94
virtual bool Init(uint16_t iPort=addr_t::IPPORT_NONE)=0
bool DoFwdTo(int32_t iClient) const
Definition: C4Packet2.cpp:466
virtual bool Init(uint16_t iPort=addr_t::IPPORT_NONE)
Definition: C4NetIO.cpp:824
Definition: C4Network2IO.h:246
void DoStatistics(int iInterval, int *pIRateSum, int *pORateSum)
bool IsNull() const
Definition: C4NetIO.cpp:509
bool isConnSent() const
Definition: C4Network2IO.h:279
virtual bool GetStatistic(int *pBroadcastRate)=0
C4NetIO::addr_t ConnectAddr
Definition: C4Network2IO.h:225
void SetStatus(C4Network2IOConnStatus nStatus)
C4TimeMilliseconds tLastStatistic
Definition: C4Network2IO.h:97
void CallHandlers(int iHandlers, const class C4IDPacket *pPacket, C4Network2IOConnection *pConn, bool fThread)
bool CreatePostMortem(class C4PacketPostMortem *pPkt)
bool Init(int16_t iPortTCP, int16_t iPortUDP, int16_t iPortDiscovery=-1, int16_t iPortRefServer=-1, bool fBroadcast=false, bool enable_upnp=true)
void HandleFwdReq(const class C4PacketFwd &rFwd, C4Network2IOConnection *pBy)
const StdCopyBuf & getData() const
Definition: C4Network2IO.h:400
void SetData(const StdBuf &Pkt)
Definition: C4Packet2.cpp:474
const int C4NetStatisticsFreq
Definition: C4Network2IO.h:36
bool HandlePuncherPacket(C4NetpuncherPacket::uptr, C4NetIO::HostAddress::AddressFamily family)
Definition: C4Network2.cpp:994
C4NetIOPacket Duplicate() const
Definition: C4NetIO.h:325
C4TimeMilliseconds tLastPong
Definition: C4Network2IO.h:235
void HandlePuncherPacket(const C4NetIOPacket &rPacket)
bool InitPuncher(C4NetIO::addr_t PuncherAddr)
bool isAutoAccepted() const
Definition: C4Network2IO.h:289
const char * getNetIOName(C4NetIO *pNetIO)
bool IsPuncherAddr(const C4NetIO::addr_t &addr) const
bool LogF(const char *strMessage,...)
Definition: C4Log.cpp:253
bool isClosed() const
Definition: C4Network2IO.h:288
void SetAddr(const C4NetIO::addr_t &naddr)
Definition: C4NetIO.h:327
virtual ~C4Network2IO()
bool Inside(T ival, U lbound, V rbound)
Definition: Standard.h:45
C4PacketBase * getPkt() const
Definition: C4PacketBase.h:255
virtual bool OnConn(const C4NetIO::addr_t &addr, const C4NetIO::addr_t &AddrConnect, const C4NetIO::addr_t *pOwnAddr, C4NetIO *pNetIO)
virtual bool SetBroadcast(const addr_t &addr, bool fSet=true)=0
C4PacketType getPktType() const
Definition: C4PacketBase.h:254
Definition: C4Network2IO.h:414
C4Application Application
Definition: C4Globals.cpp:44
C4InteractiveThread InteractiveThread
Definition: C4Application.h:45
void unpack(const C4NetIOPacket &Pkt, char *pStatus=nullptr)
Definition: C4Packet2.cpp:166
static C4TimeMilliseconds Now()
void ClearPacketLog(uint32_t iStartNumber=~0)
void HandlePacket(char cStatus, const C4PacketBase *pBasePkt, C4Network2IOConnection *pConn)
Definition: C4Network2.cpp:901
bool isOpen() const
Definition: C4Network2IO.h:285
void SetReference(C4Network2Reference *pReference)
StdStrBuf FormatString(const char *szFmt,...)
Definition: StdBuf.cpp:277
virtual bool Connect(const addr_t &addr)=0
void SetPeerAddr(const C4NetIO::addr_t &nPeerAddr)