diff --git a/include/Client.h b/include/Client.h index b8858d1..97e3bd7 100644 --- a/include/Client.h +++ b/include/Client.h @@ -87,6 +87,23 @@ public: void UpdatePingTime(); int SecondsSinceLastPing(); + // bytes received on UDP + std::atomic_size_t UdpReceived = 0; + // number of packets received on UDP + std::atomic_size_t UdpPacketsReceived = 0; + // bytes sent on UDP + std::atomic_size_t UdpSent = 0; + // number of packets sent on UDP + std::atomic_size_t UdpPacketsSent = 0; + + // bytes received on TCP + std::atomic_size_t TcpReceived = 0; + // bytes sent on TCP + std::atomic_size_t TcpSent = 0; + + std::chrono::high_resolution_clock::time_point ConnectionTime{}; + + private: void InsertVehicle(int ID, const std::string& Data); diff --git a/include/Common.h b/include/Common.h index ceb6e89..fdf529c 100644 --- a/include/Common.h +++ b/include/Common.h @@ -148,6 +148,14 @@ public: static std::string SettingToString(const SettingValue& Value); + // Keeps track of how many packets we dropped on UDP due to fundamentally being malformed + static inline std::atomic_size_t MalformedUdpPackets { 0 }; + // Keeps track of how many packets we dropped on UDP due to + // 1) not having a valid (known) player id + // 2) player disconnecting + // 3) packet failing to parse + static inline std::atomic_size_t InvalidUdpPackets { 0 }; + private: static void SetShutdown(bool Val); diff --git a/src/Client.cpp b/src/Client.cpp index 047c92c..44710e8 100644 --- a/src/Client.cpp +++ b/src/Client.cpp @@ -140,12 +140,14 @@ std::optional> GetClient(TServer& Server, int ID) { std::optional> MaybeClient { std::nullopt }; Server.ForEachClient([&](std::weak_ptr CPtr) -> bool { ReadLock Lock(Server.GetClientMutex()); - if (!CPtr.expired()) { + try { auto C = CPtr.lock(); if (C->GetID() == ID) { MaybeClient = CPtr; return false; } + } catch (const std::exception&) { + // ignore } return true; }); diff --git a/src/TNetwork.cpp b/src/TNetwork.cpp index ef87230..d9dc363 100644 --- a/src/TNetwork.cpp +++ b/src/TNetwork.cpp @@ -81,29 +81,39 @@ void TNetwork::UDPServerMain() { try { ip::udp::endpoint client {}; std::vector Data = UDPRcvFromClient(client); // Receives any data from Socket - auto Pos = std::find(Data.begin(), Data.end(), ':'); - if (Data.empty() || Pos > Data.begin() + 2) + // It has to be size>=2, because each UDP packet has a header of + // : + // where id+1 is one byte. + // We discard a UDP packet if it doesn't start like this. + if (Data.size() < 2 || Data.at(1) != ':') { + ++Application::MalformedUdpPackets; continue; + } + // We do -1 here, because the launcher does +1 + // because it uses (or used to use) null-terminated strings + // to represent packets. This would mean that player 0 would + // cause empty packets. uint8_t ID = uint8_t(Data.at(0)) - 1; - mServer.ForEachClient([&](std::weak_ptr ClientPtr) -> bool { - std::shared_ptr Client; - { - ReadLock Lock(mServer.GetClientMutex()); - if (!ClientPtr.expired()) { - Client = ClientPtr.lock(); - } else - return true; + auto MaybeClient = GetClient(mServer, ID); + if (MaybeClient) { + try { + auto Locked = MaybeClient.value().lock(); + if (Locked->IsConnected() && Locked->GetUDPAddr() != client) { + beammp_debugf("Client at {}:{} tried to send UDP for client {}", client.address().to_string(), client.port(), Locked->GetID()); + ++Application::InvalidUdpPackets; + continue; + } else { + Locked->SetUDPAddr(client); + Locked->SetIsConnected(true); + } + Locked->UdpReceived += Data.size(); + ++Locked->UdpPacketsReceived; + Data.erase(Data.begin() + 0, Data.begin() + 2); + TServer::GlobalParser(Locked, std::move(Data), mPPSMonitor, *this); + } catch (const std::exception&) { + ++Application::InvalidUdpPackets; } - - if (Client->GetID() == ID) { - Client->SetUDPAddr(client); - Client->SetIsConnected(true); - Data.erase(Data.begin(), Data.begin() + 2); - TServer::GlobalParser(ClientPtr, std::move(Data), mPPSMonitor, *this); - } - - return true; - }); + } } catch (const std::exception& e) { beammp_error(("fatal: ") + std::string(e.what())); } @@ -214,6 +224,7 @@ void TNetwork::HandleDownload(TConnection&& Conn) { std::shared_ptr TNetwork::Authentication(TConnection&& RawConnection) { auto Client = CreateClient(std::move(RawConnection.Socket)); + Client->ConnectionTime = std::chrono::high_resolution_clock::now(); Client->SetIdentifier("ip", RawConnection.SockAddr.address().to_string()); beammp_tracef("This thread is ip {}", RawConnection.SockAddr.address().to_string()); @@ -371,6 +382,7 @@ bool TNetwork::TCPSend(TClient& c, const std::vector& Data, bool IsSync return false; } c.UpdatePingTime(); + c.TcpSent += ToSend.size(); return true; } @@ -419,6 +431,7 @@ std::vector TNetwork::TCPRcv(TClient& c) { beammp_errorf("Expected to read {} bytes, instead got {}", Header, N); } + c.TcpReceived += N + HeaderData.size(); constexpr std::string_view ABG = "ABG:"; if (Data.size() >= ABG.size() && std::equal(Data.begin(), Data.begin() + ABG.size(), ABG.begin(), ABG.end())) { Data.erase(Data.begin(), Data.begin() + ABG.size()); @@ -787,6 +800,7 @@ bool TNetwork::TCPSendRaw(TClient& C, ip::tcp::socket& socket, const uint8_t* Da return false; } C.UpdatePingTime(); + C.TcpSent += Size; return true; } @@ -929,6 +943,8 @@ bool TNetwork::UDPSend(TClient& Client, std::vector Data) { Client.Disconnect("UDP send failed"); return false; } + Client.UdpSent += Data.size(); + ++Client.UdpPacketsSent; return true; }