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