mirror of
https://github.com/BeamMP/BeamMP-Server.git
synced 2025-07-03 00:05:34 +00:00
replace tcp networking with boost::asio tcp networking
This commit is contained in:
parent
7446526a19
commit
7d2e4d4581
@ -20,9 +20,8 @@ class TServer;
|
|||||||
#endif // WINDOWS
|
#endif // WINDOWS
|
||||||
|
|
||||||
struct TConnection final {
|
struct TConnection final {
|
||||||
SOCKET Socket;
|
ip::tcp::socket Socket;
|
||||||
struct sockaddr SockAddr;
|
ip::tcp::endpoint SockAddr;
|
||||||
socklen_t SockAddrLen;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class TClient final {
|
class TClient final {
|
||||||
@ -34,8 +33,9 @@ public:
|
|||||||
std::unique_lock<std::mutex> Lock;
|
std::unique_lock<std::mutex> Lock;
|
||||||
};
|
};
|
||||||
|
|
||||||
explicit TClient(TServer& Server);
|
TClient(TServer& Server, ip::tcp::socket&& Socket);
|
||||||
TClient(const TClient&) = delete;
|
TClient(const TClient&) = delete;
|
||||||
|
~TClient();
|
||||||
TClient& operator=(const TClient&) = delete;
|
TClient& operator=(const TClient&) = delete;
|
||||||
|
|
||||||
void AddNewCar(int Ident, const std::string& Data);
|
void AddNewCar(int Ident, const std::string& Data);
|
||||||
@ -48,16 +48,19 @@ public:
|
|||||||
std::string GetCarData(int Ident);
|
std::string GetCarData(int Ident);
|
||||||
std::string GetCarPositionRaw(int Ident);
|
std::string GetCarPositionRaw(int Ident);
|
||||||
void SetUDPAddr(const ip::udp::endpoint& Addr) { mUDPAddress = Addr; }
|
void SetUDPAddr(const ip::udp::endpoint& Addr) { mUDPAddress = Addr; }
|
||||||
void SetDownSock(SOCKET CSock) { mSocket[1] = CSock; }
|
void SetDownSock(ip::tcp::socket&& CSock) { mDownSocket = std::move(CSock); }
|
||||||
void SetTCPSock(SOCKET CSock) { mSocket[0] = CSock; }
|
void SetTCPSock(ip::tcp::socket&& CSock) { mSocket = std::move(CSock); }
|
||||||
void SetStatus(int Status) { mStatus = Status; }
|
void Disconnect(std::string_view Reason);
|
||||||
|
bool IsDisconnected() const { return !mSocket.is_open(); }
|
||||||
// locks
|
// locks
|
||||||
void DeleteCar(int Ident);
|
void DeleteCar(int Ident);
|
||||||
[[nodiscard]] const std::unordered_map<std::string, std::string>& GetIdentifiers() const { return mIdentifiers; }
|
[[nodiscard]] const std::unordered_map<std::string, std::string>& GetIdentifiers() const { return mIdentifiers; }
|
||||||
[[nodiscard]] const ip::udp::endpoint& GetUDPAddr() const { return mUDPAddress; }
|
[[nodiscard]] const ip::udp::endpoint& GetUDPAddr() const { return mUDPAddress; }
|
||||||
[[nodiscard]] ip::udp::endpoint& GetUDPAddr() { return mUDPAddress; }
|
[[nodiscard]] ip::udp::endpoint& GetUDPAddr() { return mUDPAddress; }
|
||||||
[[nodiscard]] SOCKET GetDownSock() const { return mSocket[1]; }
|
[[nodiscard]] ip::tcp::socket& GetDownSock() { return mDownSocket; }
|
||||||
[[nodiscard]] SOCKET GetTCPSock() const { return mSocket[0]; }
|
[[nodiscard]] const ip::tcp::socket& GetDownSock() const { return mDownSocket; }
|
||||||
|
[[nodiscard]] ip::tcp::socket& GetTCPSock() { return mSocket; }
|
||||||
|
[[nodiscard]] const ip::tcp::socket& GetTCPSock() const { return mSocket; }
|
||||||
[[nodiscard]] std::string GetRoles() const { return mRole; }
|
[[nodiscard]] std::string GetRoles() const { return mRole; }
|
||||||
[[nodiscard]] std::string GetName() const { return mName; }
|
[[nodiscard]] std::string GetName() const { return mName; }
|
||||||
void SetUnicycleID(int ID) { mUnicycleID = ID; }
|
void SetUnicycleID(int ID) { mUnicycleID = ID; }
|
||||||
@ -65,7 +68,6 @@ public:
|
|||||||
[[nodiscard]] int GetOpenCarID() const;
|
[[nodiscard]] int GetOpenCarID() const;
|
||||||
[[nodiscard]] int GetCarCount() const;
|
[[nodiscard]] int GetCarCount() const;
|
||||||
void ClearCars();
|
void ClearCars();
|
||||||
[[nodiscard]] int GetStatus() const { return mStatus; }
|
|
||||||
[[nodiscard]] int GetID() const { return mID; }
|
[[nodiscard]] int GetID() const { return mID; }
|
||||||
[[nodiscard]] int GetUnicycleID() const { return mUnicycleID; }
|
[[nodiscard]] int GetUnicycleID() const { return mUnicycleID; }
|
||||||
[[nodiscard]] bool IsConnected() const { return mIsConnected; }
|
[[nodiscard]] bool IsConnected() const { return mIsConnected; }
|
||||||
@ -75,9 +77,9 @@ public:
|
|||||||
void SetIsGuest(bool NewIsGuest) { mIsGuest = NewIsGuest; }
|
void SetIsGuest(bool NewIsGuest) { mIsGuest = NewIsGuest; }
|
||||||
void SetIsSynced(bool NewIsSynced) { mIsSynced = NewIsSynced; }
|
void SetIsSynced(bool NewIsSynced) { mIsSynced = NewIsSynced; }
|
||||||
void SetIsSyncing(bool NewIsSyncing) { mIsSyncing = NewIsSyncing; }
|
void SetIsSyncing(bool NewIsSyncing) { mIsSyncing = NewIsSyncing; }
|
||||||
void EnqueuePacket(const std::string& Packet);
|
void EnqueuePacket(const std::vector<uint8_t>& Packet);
|
||||||
[[nodiscard]] std::queue<std::string>& MissedPacketQueue() { return mPacketsSync; }
|
[[nodiscard]] std::queue<std::vector<uint8_t>>& MissedPacketQueue() { return mPacketsSync; }
|
||||||
[[nodiscard]] const std::queue<std::string>& MissedPacketQueue() const { return mPacketsSync; }
|
[[nodiscard]] const std::queue<std::vector<uint8_t>>& MissedPacketQueue() const { return mPacketsSync; }
|
||||||
[[nodiscard]] size_t MissedPacketQueueSize() const { return mPacketsSync.size(); }
|
[[nodiscard]] size_t MissedPacketQueueSize() const { return mPacketsSync.size(); }
|
||||||
[[nodiscard]] std::mutex& MissedPacketQueueMutex() const { return mMissedPacketsMutex; }
|
[[nodiscard]] std::mutex& MissedPacketQueueMutex() const { return mMissedPacketsMutex; }
|
||||||
void SetIsConnected(bool NewIsConnected) { mIsConnected = NewIsConnected; }
|
void SetIsConnected(bool NewIsConnected) { mIsConnected = NewIsConnected; }
|
||||||
@ -93,7 +95,7 @@ private:
|
|||||||
bool mIsSynced = false;
|
bool mIsSynced = false;
|
||||||
bool mIsSyncing = false;
|
bool mIsSyncing = false;
|
||||||
mutable std::mutex mMissedPacketsMutex;
|
mutable std::mutex mMissedPacketsMutex;
|
||||||
std::queue<std::string> mPacketsSync;
|
std::queue<std::vector<uint8_t>> mPacketsSync;
|
||||||
std::unordered_map<std::string, std::string> mIdentifiers;
|
std::unordered_map<std::string, std::string> mIdentifiers;
|
||||||
bool mIsGuest = false;
|
bool mIsGuest = false;
|
||||||
mutable std::mutex mVehicleDataMutex;
|
mutable std::mutex mVehicleDataMutex;
|
||||||
@ -101,12 +103,12 @@ private:
|
|||||||
TSetOfVehicleData mVehicleData;
|
TSetOfVehicleData mVehicleData;
|
||||||
SparseArray<std::string> mVehiclePosition;
|
SparseArray<std::string> mVehiclePosition;
|
||||||
std::string mName = "Unknown Client";
|
std::string mName = "Unknown Client";
|
||||||
SOCKET mSocket[2] { SOCKET(0), SOCKET(0) };
|
ip::tcp::socket mSocket;
|
||||||
|
ip::tcp::socket mDownSocket;
|
||||||
ip::udp::endpoint mUDPAddress {};
|
ip::udp::endpoint mUDPAddress {};
|
||||||
int mUnicycleID = -1;
|
int mUnicycleID = -1;
|
||||||
std::string mRole;
|
std::string mRole;
|
||||||
std::string mDID;
|
std::string mDID;
|
||||||
int mStatus = 0;
|
|
||||||
int mID = -1;
|
int mID = -1;
|
||||||
std::chrono::time_point<std::chrono::high_resolution_clock> mLastPingTime;
|
std::chrono::time_point<std::chrono::high_resolution_clock> mLastPingTime;
|
||||||
};
|
};
|
||||||
|
@ -80,7 +80,7 @@ public:
|
|||||||
static TConsole& Console() { return *mConsole; }
|
static TConsole& Console() { return *mConsole; }
|
||||||
static std::string ServerVersionString();
|
static std::string ServerVersionString();
|
||||||
static const Version& ServerVersion() { return mVersion; }
|
static const Version& ServerVersion() { return mVersion; }
|
||||||
static std::string ClientVersionString() { return "2.0"; }
|
static uint8_t ClientMajorVersion() { return 2; }
|
||||||
static std::string PPS() { return mPPS; }
|
static std::string PPS() { return mPPS; }
|
||||||
static void SetPPS(const std::string& NewPPS) { mPPS = NewPPS; }
|
static void SetPPS(const std::string& NewPPS) { mPPS = NewPPS; }
|
||||||
|
|
||||||
|
@ -13,19 +13,18 @@ class TNetwork {
|
|||||||
public:
|
public:
|
||||||
TNetwork(TServer& Server, TPPSMonitor& PPSMonitor, TResourceManager& ResourceManager);
|
TNetwork(TServer& Server, TPPSMonitor& PPSMonitor, TResourceManager& ResourceManager);
|
||||||
|
|
||||||
[[nodiscard]] bool TCPSend(TClient& c, const std::string& Data, bool IsSync = false);
|
[[nodiscard]] bool TCPSend(TClient& c, const std::vector<uint8_t>& Data, bool IsSync = false);
|
||||||
[[nodiscard]] bool SendLarge(TClient& c, std::string Data, bool isSync = false);
|
[[nodiscard]] bool SendLarge(TClient& c, std::vector<uint8_t> Data, bool isSync = false);
|
||||||
[[nodiscard]] bool Respond(TClient& c, const std::string& MSG, bool Rel, bool isSync = false);
|
[[nodiscard]] bool Respond(TClient& c, const std::vector<uint8_t>& MSG, bool Rel, bool isSync = false);
|
||||||
std::shared_ptr<TClient> CreateClient(SOCKET TCPSock);
|
std::shared_ptr<TClient> CreateClient(ip::tcp::socket&& TCPSock);
|
||||||
std::string TCPRcv(TClient& c);
|
std::vector<uint8_t> TCPRcv(TClient& c);
|
||||||
void ClientKick(TClient& c, const std::string& R);
|
void ClientKick(TClient& c, const std::string& R);
|
||||||
[[nodiscard]] bool SyncClient(const std::weak_ptr<TClient>& c);
|
[[nodiscard]] bool SyncClient(const std::weak_ptr<TClient>& c);
|
||||||
void Identify(const TConnection& client);
|
void Identify(TConnection&& client);
|
||||||
void Authentication(const TConnection& ClientConnection);
|
std::shared_ptr<TClient> Authentication(TConnection&& ClientConnection);
|
||||||
[[nodiscard]] bool CheckBytes(TClient& c, int32_t BytesRcv);
|
|
||||||
void SyncResources(TClient& c);
|
void SyncResources(TClient& c);
|
||||||
[[nodiscard]] bool UDPSend(TClient& Client, std::string Data);
|
[[nodiscard]] bool UDPSend(TClient& Client, std::vector<uint8_t> Data);
|
||||||
void SendToAll(TClient* c, const std::string& Data, bool Self, bool Rel);
|
void SendToAll(TClient* c, const std::vector<uint8_t>& Data, bool Self, bool Rel);
|
||||||
void UpdatePlayer(TClient& Client);
|
void UpdatePlayer(TClient& Client);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -34,22 +33,23 @@ private:
|
|||||||
|
|
||||||
TServer& mServer;
|
TServer& mServer;
|
||||||
TPPSMonitor& mPPSMonitor;
|
TPPSMonitor& mPPSMonitor;
|
||||||
io_context mIoCtx;
|
|
||||||
ip::udp::socket mUDPSock;
|
ip::udp::socket mUDPSock;
|
||||||
TResourceManager& mResourceManager;
|
TResourceManager& mResourceManager;
|
||||||
std::thread mUDPThread;
|
std::thread mUDPThread;
|
||||||
std::thread mTCPThread;
|
std::thread mTCPThread;
|
||||||
|
|
||||||
std::string UDPRcvFromClient(ip::udp::endpoint& ClientEndpoint);
|
std::vector<uint8_t> UDPRcvFromClient(ip::udp::endpoint& ClientEndpoint);
|
||||||
void HandleDownload(SOCKET TCPSock);
|
void HandleDownload(TConnection&& TCPSock);
|
||||||
void OnConnect(const std::weak_ptr<TClient>& c);
|
void OnConnect(const std::weak_ptr<TClient>& c);
|
||||||
void TCPClient(const std::weak_ptr<TClient>& c);
|
void TCPClient(const std::weak_ptr<TClient>& c);
|
||||||
void Looper(const std::weak_ptr<TClient>& c);
|
void Looper(const std::weak_ptr<TClient>& c);
|
||||||
int OpenID();
|
int OpenID();
|
||||||
void OnDisconnect(const std::weak_ptr<TClient>& ClientPtr, bool kicked);
|
void OnDisconnect(const std::weak_ptr<TClient>& ClientPtr);
|
||||||
void Parse(TClient& c, const std::string& Packet);
|
void Parse(TClient& c, const std::vector<uint8_t>& Packet);
|
||||||
void SendFile(TClient& c, const std::string& Name);
|
void SendFile(TClient& c, const std::string& Name);
|
||||||
static bool TCPSendRaw(TClient& C, SOCKET socket, char* Data, int32_t Size);
|
static bool TCPSendRaw(TClient& C, ip::tcp::socket& socket, const uint8_t* Data, size_t Size);
|
||||||
static void SplitLoad(TClient& c, size_t Sent, size_t Size, bool D, const std::string& Name);
|
static void SplitLoad(TClient& c, size_t Sent, size_t Size, bool D, const std::string& Name);
|
||||||
static uint8_t* SendSplit(TClient& c, SOCKET Socket, uint8_t* DataPtr, size_t Size);
|
static const uint8_t* SendSplit(TClient& c, ip::tcp::socket& Socket, const uint8_t* DataPtr, size_t Size);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
std::vector<uint8_t> StringToVector(const std::string& Str);
|
||||||
|
@ -8,6 +8,8 @@
|
|||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
|
|
||||||
|
#include "BoostAliases.h"
|
||||||
|
|
||||||
class TClient;
|
class TClient;
|
||||||
class TNetwork;
|
class TNetwork;
|
||||||
class TPPSMonitor;
|
class TPPSMonitor;
|
||||||
@ -19,19 +21,22 @@ public:
|
|||||||
TServer(const std::vector<std::string_view>& Arguments);
|
TServer(const std::vector<std::string_view>& Arguments);
|
||||||
|
|
||||||
void InsertClient(const std::shared_ptr<TClient>& Ptr);
|
void InsertClient(const std::shared_ptr<TClient>& Ptr);
|
||||||
std::weak_ptr<TClient> InsertNewClient();
|
|
||||||
void RemoveClient(const std::weak_ptr<TClient>&);
|
void RemoveClient(const std::weak_ptr<TClient>&);
|
||||||
// in Fn, return true to continue, return false to break
|
// in Fn, return true to continue, return false to break
|
||||||
void ForEachClient(const std::function<bool(std::weak_ptr<TClient>)>& Fn);
|
void ForEachClient(const std::function<bool(std::weak_ptr<TClient>)>& Fn);
|
||||||
size_t ClientCount() const;
|
size_t ClientCount() const;
|
||||||
|
|
||||||
static void GlobalParser(const std::weak_ptr<TClient>& Client, std::string Packet, TPPSMonitor& PPSMonitor, TNetwork& Network);
|
static void GlobalParser(const std::weak_ptr<TClient>& Client, std::vector<uint8_t>&& Packet, TPPSMonitor& PPSMonitor, TNetwork& Network);
|
||||||
static void HandleEvent(TClient& c, const std::string& Data);
|
static void HandleEvent(TClient& c, const std::string& Data);
|
||||||
RWMutex& GetClientMutex() const { return mClientsMutex; }
|
RWMutex& GetClientMutex() const { return mClientsMutex; }
|
||||||
|
|
||||||
|
|
||||||
const TScopedTimer UptimeTimer;
|
const TScopedTimer UptimeTimer;
|
||||||
|
|
||||||
|
// asio io context
|
||||||
|
io_context& IoCtx() { return mIoCtx; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
io_context mIoCtx {};
|
||||||
TClientSet mClients;
|
TClientSet mClients;
|
||||||
mutable RWMutex mClientsMutex;
|
mutable RWMutex mClientsMutex;
|
||||||
static void ParseVehicle(TClient& c, const std::string& Pckt, TNetwork& Network);
|
static void ParseVehicle(TClient& c, const std::string& Pckt, TNetwork& Network);
|
||||||
@ -40,3 +45,11 @@ private:
|
|||||||
static void Apply(TClient& c, int VID, const std::string& pckt);
|
static void Apply(TClient& c, int VID, const std::string& pckt);
|
||||||
static void HandlePosition(TClient& c, const std::string& Packet);
|
static void HandlePosition(TClient& c, const std::string& Packet);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct BufferView {
|
||||||
|
uint8_t* Data { nullptr };
|
||||||
|
size_t Size { 0 };
|
||||||
|
const uint8_t* data() const { return Data; }
|
||||||
|
uint8_t* data() { return Data; }
|
||||||
|
size_t size() const { return Size; }
|
||||||
|
};
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include "CustomAssert.h"
|
#include "CustomAssert.h"
|
||||||
#include "TServer.h"
|
#include "TServer.h"
|
||||||
|
#include <boost/system/detail/error_code.hpp>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
|
||||||
@ -49,16 +50,27 @@ TClient::TVehicleDataLockPair TClient::GetAllCars() {
|
|||||||
|
|
||||||
std::string TClient::GetCarPositionRaw(int Ident) {
|
std::string TClient::GetCarPositionRaw(int Ident) {
|
||||||
std::unique_lock lock(mVehiclePositionMutex);
|
std::unique_lock lock(mVehiclePositionMutex);
|
||||||
try
|
try {
|
||||||
{
|
|
||||||
return mVehiclePosition.at(Ident);
|
return mVehiclePosition.at(Ident);
|
||||||
}
|
} catch (const std::out_of_range& oor) {
|
||||||
catch (const std::out_of_range& oor) {
|
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TClient::Disconnect(std::string_view Reason) {
|
||||||
|
beammp_debugf("Disconnecting client {} for reason: {}", GetID(), Reason);
|
||||||
|
boost::system::error_code ec;
|
||||||
|
mSocket.shutdown(socket_base::shutdown_both, ec);
|
||||||
|
if (ec) {
|
||||||
|
beammp_warnf("Failed to shutdown client socket: {}", ec.what());
|
||||||
|
}
|
||||||
|
mSocket.close(ec);
|
||||||
|
if (ec) {
|
||||||
|
beammp_warnf("Failed to close client socket: {}", ec.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void TClient::SetCarPosition(int Ident, const std::string& Data) {
|
void TClient::SetCarPosition(int Ident, const std::string& Data) {
|
||||||
std::unique_lock lock(mVehiclePositionMutex);
|
std::unique_lock lock(mVehiclePositionMutex);
|
||||||
mVehiclePosition[Ident] = Data;
|
mVehiclePosition[Ident] = Data;
|
||||||
@ -98,16 +110,22 @@ TServer& TClient::Server() const {
|
|||||||
return mServer;
|
return mServer;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TClient::EnqueuePacket(const std::string& Packet) {
|
void TClient::EnqueuePacket(const std::vector<uint8_t>& Packet) {
|
||||||
std::unique_lock Lock(mMissedPacketsMutex);
|
std::unique_lock Lock(mMissedPacketsMutex);
|
||||||
mPacketsSync.push(Packet);
|
mPacketsSync.push(Packet);
|
||||||
}
|
}
|
||||||
|
|
||||||
TClient::TClient(TServer& Server)
|
TClient::TClient(TServer& Server, ip::tcp::socket&& Socket)
|
||||||
: mServer(Server)
|
: mServer(Server)
|
||||||
|
, mSocket(std::move(Socket))
|
||||||
|
, mDownSocket(ip::tcp::socket(Server.IoCtx()))
|
||||||
, mLastPingTime(std::chrono::high_resolution_clock::now()) {
|
, mLastPingTime(std::chrono::high_resolution_clock::now()) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TClient::~TClient() {
|
||||||
|
beammp_debugf("client destroyed: {} ('{}')", this->GetID(), this->GetName());
|
||||||
|
}
|
||||||
|
|
||||||
void TClient::UpdatePingTime() {
|
void TClient::UpdatePingTime() {
|
||||||
mLastPingTime = std::chrono::high_resolution_clock::now();
|
mLastPingTime = std::chrono::high_resolution_clock::now();
|
||||||
}
|
}
|
||||||
|
@ -116,7 +116,7 @@ TEST_CASE("LuaAPI::MP::GetServerVersion") {
|
|||||||
static inline std::pair<bool, std::string> InternalTriggerClientEvent(int PlayerID, const std::string& EventName, const std::string& Data) {
|
static inline std::pair<bool, std::string> InternalTriggerClientEvent(int PlayerID, const std::string& EventName, const std::string& Data) {
|
||||||
std::string Packet = "E:" + EventName + ":" + Data;
|
std::string Packet = "E:" + EventName + ":" + Data;
|
||||||
if (PlayerID == -1) {
|
if (PlayerID == -1) {
|
||||||
LuaAPI::MP::Engine->Network().SendToAll(nullptr, Packet, true, true);
|
LuaAPI::MP::Engine->Network().SendToAll(nullptr, StringToVector(Packet), true, true);
|
||||||
return { true, "" };
|
return { true, "" };
|
||||||
} else {
|
} else {
|
||||||
auto MaybeClient = GetClient(LuaAPI::MP::Engine->Server(), PlayerID);
|
auto MaybeClient = GetClient(LuaAPI::MP::Engine->Server(), PlayerID);
|
||||||
@ -125,7 +125,7 @@ static inline std::pair<bool, std::string> InternalTriggerClientEvent(int Player
|
|||||||
return { false, "Invalid Player ID" };
|
return { false, "Invalid Player ID" };
|
||||||
}
|
}
|
||||||
auto c = MaybeClient.value().lock();
|
auto c = MaybeClient.value().lock();
|
||||||
if (!LuaAPI::MP::Engine->Network().Respond(*c, Packet, true)) {
|
if (!LuaAPI::MP::Engine->Network().Respond(*c, StringToVector(Packet), true)) {
|
||||||
beammp_lua_errorf("Respond failed, dropping client {}", PlayerID);
|
beammp_lua_errorf("Respond failed, dropping client {}", PlayerID);
|
||||||
LuaAPI::MP::Engine->Network().ClientKick(*c, "Disconnected after failing to receive packets");
|
LuaAPI::MP::Engine->Network().ClientKick(*c, "Disconnected after failing to receive packets");
|
||||||
return { false, "Respond failed, dropping client" };
|
return { false, "Respond failed, dropping client" };
|
||||||
@ -155,7 +155,7 @@ std::pair<bool, std::string> LuaAPI::MP::SendChatMessage(int ID, const std::stri
|
|||||||
std::string Packet = "C:Server: " + Message;
|
std::string Packet = "C:Server: " + Message;
|
||||||
if (ID == -1) {
|
if (ID == -1) {
|
||||||
LogChatMessage("<Server> (to everyone) ", -1, Message);
|
LogChatMessage("<Server> (to everyone) ", -1, Message);
|
||||||
Engine->Network().SendToAll(nullptr, Packet, true, true);
|
Engine->Network().SendToAll(nullptr, StringToVector(Packet), true, true);
|
||||||
Result.first = true;
|
Result.first = true;
|
||||||
} else {
|
} else {
|
||||||
auto MaybeClient = GetClient(Engine->Server(), ID);
|
auto MaybeClient = GetClient(Engine->Server(), ID);
|
||||||
@ -167,7 +167,7 @@ std::pair<bool, std::string> LuaAPI::MP::SendChatMessage(int ID, const std::stri
|
|||||||
return Result;
|
return Result;
|
||||||
}
|
}
|
||||||
LogChatMessage("<Server> (to \"" + c->GetName() + "\")", -1, Message);
|
LogChatMessage("<Server> (to \"" + c->GetName() + "\")", -1, Message);
|
||||||
if (!Engine->Network().Respond(*c, Packet, true)) {
|
if (!Engine->Network().Respond(*c, StringToVector(Packet), true)) {
|
||||||
beammp_errorf("Failed to send chat message back to sender (id {}) - did the sender disconnect?", ID);
|
beammp_errorf("Failed to send chat message back to sender (id {}) - did the sender disconnect?", ID);
|
||||||
// TODO: should we return an error here?
|
// TODO: should we return an error here?
|
||||||
}
|
}
|
||||||
@ -194,7 +194,7 @@ std::pair<bool, std::string> LuaAPI::MP::RemoveVehicle(int PID, int VID) {
|
|||||||
auto c = MaybeClient.value().lock();
|
auto c = MaybeClient.value().lock();
|
||||||
if (!c->GetCarData(VID).empty()) {
|
if (!c->GetCarData(VID).empty()) {
|
||||||
std::string Destroy = "Od:" + std::to_string(PID) + "-" + std::to_string(VID);
|
std::string Destroy = "Od:" + std::to_string(PID) + "-" + std::to_string(VID);
|
||||||
Engine->Network().SendToAll(nullptr, Destroy, true, true);
|
Engine->Network().SendToAll(nullptr, StringToVector(Destroy), true, true);
|
||||||
c->DeleteCar(VID);
|
c->DeleteCar(VID);
|
||||||
Result.first = true;
|
Result.first = true;
|
||||||
} else {
|
} else {
|
||||||
@ -526,7 +526,7 @@ static void JsonEncodeRecursive(nlohmann::json& json, const sol::object& left, c
|
|||||||
beammp_lua_error("json serialize will not go deeper than 100 nested tables, internal references assumed, aborted this path");
|
beammp_lua_error("json serialize will not go deeper than 100 nested tables, internal references assumed, aborted this path");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
std::string key{};
|
std::string key {};
|
||||||
switch (left.get_type()) {
|
switch (left.get_type()) {
|
||||||
case sol::type::lua_nil:
|
case sol::type::lua_nil:
|
||||||
case sol::type::none:
|
case sol::type::none:
|
||||||
|
@ -148,7 +148,7 @@ std::string THeartbeatThread::GenerateCall() {
|
|||||||
<< "&map=" << Application::Settings.MapName
|
<< "&map=" << Application::Settings.MapName
|
||||||
<< "&private=" << (Application::Settings.Private ? "true" : "false")
|
<< "&private=" << (Application::Settings.Private ? "true" : "false")
|
||||||
<< "&version=" << Application::ServerVersionString()
|
<< "&version=" << Application::ServerVersionString()
|
||||||
<< "&clientversion=" << Application::ClientVersionString()
|
<< "&clientversion=" << Application::ClientMajorVersion()
|
||||||
<< "&name=" << Application::Settings.ServerName
|
<< "&name=" << Application::Settings.ServerName
|
||||||
<< "&modlist=" << mResourceManager.TrimmedList()
|
<< "&modlist=" << mResourceManager.TrimmedList()
|
||||||
<< "&modstotalsize=" << mResourceManager.MaxModSize()
|
<< "&modstotalsize=" << mResourceManager.MaxModSize()
|
||||||
|
602
src/TNetwork.cpp
602
src/TNetwork.cpp
@ -1,7 +1,9 @@
|
|||||||
#include "TNetwork.h"
|
#include "TNetwork.h"
|
||||||
#include "Client.h"
|
#include "Client.h"
|
||||||
|
#include "Common.h"
|
||||||
#include "LuaAPI.h"
|
#include "LuaAPI.h"
|
||||||
#include "TLuaEngine.h"
|
#include "TLuaEngine.h"
|
||||||
|
#include "nlohmann/json.hpp"
|
||||||
#include <CustomAssert.h>
|
#include <CustomAssert.h>
|
||||||
#include <Http.h>
|
#include <Http.h>
|
||||||
#include <array>
|
#include <array>
|
||||||
@ -10,11 +12,23 @@
|
|||||||
#include <boost/system/detail/error_code.hpp>
|
#include <boost/system/detail/error_code.hpp>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
|
std::vector<uint8_t> StringToVector(const std::string& Str) {
|
||||||
|
return std::vector<uint8_t>(Str.data(), Str.data() + Str.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
static void CompressProperly(std::vector<uint8_t>& Data) {
|
||||||
|
constexpr std::string_view ABG = "ABG:";
|
||||||
|
auto CombinedData = std::vector<uint8_t>(ABG.begin(), ABG.end());
|
||||||
|
auto CompData = Comp(Data);
|
||||||
|
CombinedData.resize(ABG.size() + CompData.size());
|
||||||
|
std::copy(CompData.begin(), CompData.end(), CombinedData.begin() + ABG.size());
|
||||||
|
Data = CombinedData;
|
||||||
|
}
|
||||||
|
|
||||||
TNetwork::TNetwork(TServer& Server, TPPSMonitor& PPSMonitor, TResourceManager& ResourceManager)
|
TNetwork::TNetwork(TServer& Server, TPPSMonitor& PPSMonitor, TResourceManager& ResourceManager)
|
||||||
: mServer(Server)
|
: mServer(Server)
|
||||||
, mPPSMonitor(PPSMonitor)
|
, mPPSMonitor(PPSMonitor)
|
||||||
, mIoCtx {}
|
, mUDPSock(Server.IoCtx())
|
||||||
, mUDPSock(mIoCtx)
|
|
||||||
, mResourceManager(ResourceManager) {
|
, mResourceManager(ResourceManager) {
|
||||||
Application::SetSubsystemStatus("TCPNetwork", Application::Status::Starting);
|
Application::SetSubsystemStatus("TCPNetwork", Application::Status::Starting);
|
||||||
Application::SetSubsystemStatus("UDPNetwork", Application::Status::Starting);
|
Application::SetSubsystemStatus("UDPNetwork", Application::Status::Starting);
|
||||||
@ -67,9 +81,9 @@ void TNetwork::UDPServerMain() {
|
|||||||
while (!Application::IsShuttingDown()) {
|
while (!Application::IsShuttingDown()) {
|
||||||
try {
|
try {
|
||||||
ip::udp::endpoint client {};
|
ip::udp::endpoint client {};
|
||||||
std::string Data = UDPRcvFromClient(client); // Receives any data from Socket
|
std::vector<uint8_t> Data = UDPRcvFromClient(client); // Receives any data from Socket
|
||||||
size_t Pos = Data.find(':');
|
auto Pos = std::find(Data.begin(), Data.end(), ':');
|
||||||
if (Data.empty() || Pos > 2)
|
if (Data.empty() || Pos > Data.begin() + 2)
|
||||||
continue;
|
continue;
|
||||||
uint8_t ID = uint8_t(Data.at(0)) - 1;
|
uint8_t ID = uint8_t(Data.at(0)) - 1;
|
||||||
mServer.ForEachClient([&](std::weak_ptr<TClient> ClientPtr) -> bool {
|
mServer.ForEachClient([&](std::weak_ptr<TClient> ClientPtr) -> bool {
|
||||||
@ -85,7 +99,8 @@ void TNetwork::UDPServerMain() {
|
|||||||
if (Client->GetID() == ID) {
|
if (Client->GetID() == ID) {
|
||||||
Client->SetUDPAddr(client);
|
Client->SetUDPAddr(client);
|
||||||
Client->SetIsConnected(true);
|
Client->SetIsConnected(true);
|
||||||
TServer::GlobalParser(ClientPtr, Data.substr(2), mPPSMonitor, *this);
|
Data.erase(Data.begin(), Data.begin() + 2);
|
||||||
|
TServer::GlobalParser(ClientPtr, std::move(Data), mPPSMonitor, *this);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -98,47 +113,30 @@ void TNetwork::UDPServerMain() {
|
|||||||
|
|
||||||
void TNetwork::TCPServerMain() {
|
void TNetwork::TCPServerMain() {
|
||||||
RegisterThread("TCPServer");
|
RegisterThread("TCPServer");
|
||||||
#if defined(BEAMMP_WINDOWS)
|
|
||||||
WSADATA wsaData;
|
ip::tcp::endpoint ListenEp(ip::address::from_string("0.0.0.0"), Application::Settings.Port);
|
||||||
if (WSAStartup(514, &wsaData)) {
|
ip::tcp::socket Listener(mServer.IoCtx());
|
||||||
beammp_error("Can't start Winsock! Shutting down");
|
boost::system::error_code ec;
|
||||||
Application::GracefullyShutdown();
|
Listener.open(ListenEp.protocol(), ec);
|
||||||
|
if (ec) {
|
||||||
|
beammp_errorf("Failed to open socket: {}", ec.what());
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
#endif // WINDOWS
|
socket_base::linger LingerOpt {};
|
||||||
TConnection client {};
|
LingerOpt.enabled(false);
|
||||||
SOCKET Listener = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
Listener.set_option(LingerOpt, ec);
|
||||||
if (Listener == BEAMMP_INVALID_SOCKET) {
|
if (ec) {
|
||||||
beammp_error("Failed to create socket: " + GetPlatformAgnosticErrorString()
|
beammp_errorf("Failed to set up listening socket to not linger / reuse address. "
|
||||||
+ ". This is a fatal error, as a socket is needed for the server to operate. Shutting down.");
|
"This may cause the socket to refuse to bind(). Error: {}",
|
||||||
Application::GracefullyShutdown();
|
ec.what());
|
||||||
}
|
}
|
||||||
#if defined(BEAMMP_WINDOWS)
|
|
||||||
const char optval = 0;
|
ip::tcp::acceptor Acceptor(mServer.IoCtx(), ListenEp);
|
||||||
int ret = ::setsockopt(Listener, SOL_SOCKET, SO_DONTLINGER, &optval, sizeof(optval));
|
Acceptor.listen(socket_base::max_listen_connections, ec);
|
||||||
#elif defined(BEAMMP_LINUX) || defined(BEAMMP_APPLE)
|
if (ec) {
|
||||||
int optval = true;
|
beammp_errorf("listen() failed, which is needed for the server to operate. "
|
||||||
int ret = ::setsockopt(Listener, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<void*>(&optval), sizeof(optval));
|
"Shutting down. Error: {}",
|
||||||
#endif
|
ec.what());
|
||||||
// not a fatal error
|
|
||||||
if (ret < 0) {
|
|
||||||
beammp_error("Failed to set up listening socket to not linger / reuse address. "
|
|
||||||
"This may cause the socket to refuse to bind(). Error: "
|
|
||||||
+ GetPlatformAgnosticErrorString());
|
|
||||||
}
|
|
||||||
sockaddr_in addr {};
|
|
||||||
addr.sin_addr.s_addr = INADDR_ANY;
|
|
||||||
addr.sin_family = AF_INET;
|
|
||||||
addr.sin_port = htons(uint16_t(Application::Settings.Port));
|
|
||||||
if (bind(Listener, reinterpret_cast<struct sockaddr*>(&addr), sizeof(addr)) < 0) {
|
|
||||||
beammp_error("bind() failed, the server cannot operate and will shut down now. "
|
|
||||||
"Error: "
|
|
||||||
+ GetPlatformAgnosticErrorString());
|
|
||||||
Application::GracefullyShutdown();
|
|
||||||
}
|
|
||||||
if (listen(Listener, SOMAXCONN) < 0) {
|
|
||||||
beammp_error("listen() failed, which is needed for the server to operate. "
|
|
||||||
"Shutting down. Error: "
|
|
||||||
+ GetPlatformAgnosticErrorString());
|
|
||||||
Application::GracefullyShutdown();
|
Application::GracefullyShutdown();
|
||||||
}
|
}
|
||||||
Application::SetSubsystemStatus("TCPNetwork", Application::Status::Good);
|
Application::SetSubsystemStatus("TCPNetwork", Application::Status::Good);
|
||||||
@ -149,39 +147,22 @@ void TNetwork::TCPServerMain() {
|
|||||||
beammp_debug("shutdown during TCP wait for accept loop");
|
beammp_debug("shutdown during TCP wait for accept loop");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
client.SockAddrLen = sizeof(client.SockAddr);
|
ip::tcp::endpoint ClientEp;
|
||||||
client.Socket = accept(Listener, &client.SockAddr, &client.SockAddrLen);
|
ip::tcp::socket ClientSocket = Acceptor.accept(ClientEp, ec);
|
||||||
if (client.Socket == -1) {
|
if (ec) {
|
||||||
beammp_warn(("Got an invalid client socket on connect! Skipping..."));
|
beammp_errorf("failed to accept: {}", ec.what());
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
// set timeout
|
ClientSocket.set_option(boost::asio::detail::socket_option::integer<SOL_SOCKET, SO_SNDTIMEO> { 30 * 1000 }, ec);
|
||||||
size_t SendTimeoutMS = 30 * 1000;
|
if (!ec) {
|
||||||
#if defined(BEAMMP_WINDOWS)
|
beammp_errorf("failed to set send timeout on client socket: {}", ec.what());
|
||||||
int ret = ::setsockopt(client.Socket, SOL_SOCKET, SO_SNDTIMEO, reinterpret_cast<const char*>(&SendTimeoutMS), sizeof(SendTimeoutMS));
|
|
||||||
#else // POSIX
|
|
||||||
struct timeval optval;
|
|
||||||
optval.tv_sec = int(SendTimeoutMS / 1000);
|
|
||||||
optval.tv_usec = (SendTimeoutMS % 1000) * 1000;
|
|
||||||
ret = ::setsockopt(client.Socket, SOL_SOCKET, SO_SNDTIMEO, reinterpret_cast<void*>(&optval), sizeof(optval));
|
|
||||||
#endif
|
|
||||||
if (ret < 0) {
|
|
||||||
throw std::runtime_error("setsockopt recv timeout: " + GetPlatformAgnosticErrorString());
|
|
||||||
}
|
}
|
||||||
std::thread ID(&TNetwork::Identify, this, client);
|
TConnection Conn { std::move(ClientSocket), ClientEp };
|
||||||
|
std::thread ID(&TNetwork::Identify, this, std::move(Conn));
|
||||||
ID.detach(); // TODO: Add to a queue and attempt to join periodically
|
ID.detach(); // TODO: Add to a queue and attempt to join periodically
|
||||||
} catch (const std::exception& e) {
|
} catch (const std::exception& e) {
|
||||||
beammp_error("fatal: " + std::string(e.what()));
|
beammp_error("fatal: " + std::string(e.what()));
|
||||||
}
|
}
|
||||||
} while (client.Socket != BEAMMP_INVALID_SOCKET);
|
} while (!Application::IsShuttingDown());
|
||||||
|
|
||||||
beammp_debug("all ok, arrived at " + std::string(__func__) + ":" + std::to_string(__LINE__));
|
|
||||||
|
|
||||||
CloseSocketProper(client.Socket);
|
|
||||||
#ifdef BEAMMP_WINDOWS
|
|
||||||
CloseSocketProper(client.Socket);
|
|
||||||
WSACleanup();
|
|
||||||
#endif // WINDOWS
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#undef GetObject // Fixes Windows
|
#undef GetObject // Fixes Windows
|
||||||
@ -189,34 +170,38 @@ void TNetwork::TCPServerMain() {
|
|||||||
#include "Json.h"
|
#include "Json.h"
|
||||||
namespace json = rapidjson;
|
namespace json = rapidjson;
|
||||||
|
|
||||||
void TNetwork::Identify(const TConnection& client) {
|
void TNetwork::Identify(TConnection&& RawConnection) {
|
||||||
RegisterThreadAuto();
|
RegisterThreadAuto();
|
||||||
char Code;
|
char Code;
|
||||||
if (recv(client.Socket, &Code, 1, 0) != 1) {
|
|
||||||
CloseSocketProper(client.Socket);
|
boost::system::error_code ec;
|
||||||
|
read(RawConnection.Socket, buffer(&Code, 1), ec);
|
||||||
|
if (ec) {
|
||||||
|
// TODO: is this right?!
|
||||||
|
RawConnection.Socket.shutdown(socket_base::shutdown_both);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
std::shared_ptr<TClient> Client { nullptr };
|
||||||
if (Code == 'C') {
|
if (Code == 'C') {
|
||||||
Authentication(client);
|
Client = Authentication(std::move(RawConnection));
|
||||||
} else if (Code == 'D') {
|
} else if (Code == 'D') {
|
||||||
HandleDownload(client.Socket);
|
HandleDownload(std::move(RawConnection));
|
||||||
} else if (Code == 'P') {
|
} else if (Code == 'P') {
|
||||||
#if defined(BEAMMP_LINUX) || defined(BEAMMP_APPLE)
|
boost::system::error_code ec;
|
||||||
send(client.Socket, "P", 1, MSG_NOSIGNAL);
|
write(RawConnection.Socket, buffer("P"), ec);
|
||||||
#else
|
|
||||||
send(client.Socket, "P", 1, 0);
|
|
||||||
#endif
|
|
||||||
CloseSocketProper(client.Socket);
|
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
CloseSocketProper(client.Socket);
|
beammp_errorf("Invalid code got in Identify: '{}'", Code);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void TNetwork::HandleDownload(SOCKET TCPSock) {
|
void TNetwork::HandleDownload(TConnection&& Conn) {
|
||||||
char D;
|
char D;
|
||||||
if (recv(TCPSock, &D, 1, 0) != 1) {
|
boost::system::error_code ec;
|
||||||
CloseSocketProper(TCPSock);
|
read(Conn.Socket, buffer(&D, 1), ec);
|
||||||
|
if (ec) {
|
||||||
|
Conn.Socket.shutdown(socket_base::shutdown_both, ec);
|
||||||
|
// ignore ec
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
auto ID = uint8_t(D);
|
auto ID = uint8_t(D);
|
||||||
@ -225,110 +210,78 @@ void TNetwork::HandleDownload(SOCKET TCPSock) {
|
|||||||
if (!ClientPtr.expired()) {
|
if (!ClientPtr.expired()) {
|
||||||
auto c = ClientPtr.lock();
|
auto c = ClientPtr.lock();
|
||||||
if (c->GetID() == ID) {
|
if (c->GetID() == ID) {
|
||||||
c->SetDownSock(TCPSock);
|
c->SetDownSock(std::move(Conn.Socket));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
static int get_ip_str(const struct sockaddr* sa, char* strBuf, socklen_t strBufSize) {
|
std::shared_ptr<TClient> TNetwork::Authentication(TConnection&& RawConnection) {
|
||||||
switch (sa->sa_family) {
|
auto Client = CreateClient(std::move(RawConnection.Socket));
|
||||||
case AF_INET:
|
Client->SetIdentifier("ip", RawConnection.SockAddr.address().to_string());
|
||||||
inet_ntop(AF_INET, &reinterpret_cast<const struct sockaddr_in*>(sa)->sin_addr, strBuf, strBufSize);
|
beammp_tracef("This thread is ip {}", RawConnection.SockAddr.address().to_string());
|
||||||
break;
|
|
||||||
case AF_INET6:
|
|
||||||
inet_ntop(AF_INET6, &reinterpret_cast<const struct sockaddr_in6*>(sa)->sin6_addr, strBuf, strBufSize);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TNetwork::Authentication(const TConnection& ClientConnection) {
|
|
||||||
auto Client = CreateClient(ClientConnection.Socket);
|
|
||||||
char AddrBuf[INET6_ADDRSTRLEN];
|
|
||||||
get_ip_str(&ClientConnection.SockAddr, AddrBuf, sizeof(AddrBuf));
|
|
||||||
beammp_trace("This thread is ip " + std::string(AddrBuf));
|
|
||||||
Client->SetIdentifier("ip", AddrBuf);
|
|
||||||
|
|
||||||
std::string Rc; // TODO: figure out why this is not default constructed
|
|
||||||
beammp_info("Identifying new ClientConnection...");
|
beammp_info("Identifying new ClientConnection...");
|
||||||
|
|
||||||
Rc = TCPRcv(*Client);
|
auto Data = TCPRcv(*Client);
|
||||||
|
|
||||||
if (Rc.size() > 3 && Rc.substr(0, 2) == "VC") {
|
constexpr std::string_view VC = "VC";
|
||||||
Rc = Rc.substr(2);
|
if (Data.size() > 3 && std::equal(Data.begin(), Data.begin() + VC.size(), VC.begin(), VC.end())) {
|
||||||
if (Rc.length() > 4 || Rc != Application::ClientVersionString()) {
|
std::string ClientVersionStr(reinterpret_cast<const char*>(Data.data() + 2), Data.size() - 2);
|
||||||
|
Version ClientVersion = Application::VersionStrToInts(ClientVersionStr);
|
||||||
|
if (ClientVersion.major != Application::ClientMajorVersion()) {
|
||||||
|
beammp_errorf("Client tried to connect with version '{}', but only versions '{}.x.x' is allowed",
|
||||||
|
ClientVersion.AsString(), Application::ClientMajorVersion());
|
||||||
ClientKick(*Client, "Outdated Version!");
|
ClientKick(*Client, "Outdated Version!");
|
||||||
return;
|
return nullptr;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ClientKick(*Client, "Invalid version header!");
|
ClientKick(*Client, fmt::format("Invalid version header: '{}' ({})", std::string(reinterpret_cast<const char*>(Data.data()), Data.size()), Data.size()));
|
||||||
return;
|
return nullptr;
|
||||||
}
|
}
|
||||||
if (!TCPSend(*Client, "S")) {
|
if (!TCPSend(*Client, StringToVector("S"))) {
|
||||||
// TODO: handle
|
// TODO: handle
|
||||||
}
|
}
|
||||||
|
|
||||||
Rc = TCPRcv(*Client);
|
Data = TCPRcv(*Client);
|
||||||
|
|
||||||
if (Rc.size() > 50) {
|
if (Data.size() > 50) {
|
||||||
ClientKick(*Client, "Invalid Key!");
|
ClientKick(*Client, "Invalid Key (too long)!");
|
||||||
return;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto RequestString = R"({"key":")" + Rc + "\"}";
|
nlohmann::json AuthReq {
|
||||||
|
{ "key", std::string(reinterpret_cast<const char*>(Data.data()), Data.size()) }
|
||||||
|
};
|
||||||
auto Target = "/pkToUser";
|
auto Target = "/pkToUser";
|
||||||
unsigned int ResponseCode = 0;
|
unsigned int ResponseCode = 0;
|
||||||
if (!Rc.empty()) {
|
const auto AuthResStr = Http::POST(Application::GetBackendUrlForAuth(), 443, Target, AuthReq.dump(), "application/json", &ResponseCode);
|
||||||
Rc = Http::POST(Application::GetBackendUrlForAuth(), 443, Target, RequestString, "application/json", &ResponseCode);
|
|
||||||
}
|
|
||||||
|
|
||||||
json::Document AuthResponse;
|
try {
|
||||||
AuthResponse.Parse(Rc.c_str());
|
nlohmann::json AuthRes = nlohmann::json::parse(AuthResStr);
|
||||||
if (Rc == Http::ErrorString || AuthResponse.HasParseError()) {
|
|
||||||
ClientKick(*Client, "Invalid key! Please restart your game.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!AuthResponse.IsObject()) {
|
if (AuthRes["username"].is_string() && AuthRes["roles"].is_string()
|
||||||
if (Rc == "0") {
|
&& AuthRes["guest"].is_boolean() && AuthRes["identifiers"].is_array()) {
|
||||||
auto Lock = Sentry.CreateExclusiveContext();
|
|
||||||
Sentry.SetContext("auth",
|
|
||||||
{ { "response-body", Rc },
|
|
||||||
{ "key", RequestString } });
|
|
||||||
Sentry.SetTransaction(Application::GetBackendUrlForAuth() + Target);
|
|
||||||
Sentry.Log(SentryLevel::Info, "default", "backend returned 0 instead of json (" + std::to_string(ResponseCode) + ")");
|
|
||||||
} else { // Rc != "0"
|
|
||||||
ClientKick(*Client, "Backend returned invalid auth response format.");
|
|
||||||
beammp_error("Backend returned invalid auth response format. This should never happen.");
|
|
||||||
auto Lock = Sentry.CreateExclusiveContext();
|
|
||||||
Sentry.SetContext("auth",
|
|
||||||
{ { "response-body", Rc },
|
|
||||||
{ "key", RequestString } });
|
|
||||||
Sentry.SetTransaction(Application::GetBackendUrlForAuth() + Target);
|
|
||||||
Sentry.Log(SentryLevel::Error, "default", "unexpected backend response (" + std::to_string(ResponseCode) + ")");
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (AuthResponse["username"].IsString() && AuthResponse["roles"].IsString()
|
Client->SetName(AuthRes["username"]);
|
||||||
&& AuthResponse["guest"].IsBool() && AuthResponse["identifiers"].IsArray()) {
|
Client->SetRoles(AuthRes["roles"]);
|
||||||
|
Client->SetIsGuest(AuthRes["guest"]);
|
||||||
Client->SetName(AuthResponse["username"].GetString());
|
for (const auto& ID : AuthRes["identifier"]) {
|
||||||
Client->SetRoles(AuthResponse["roles"].GetString());
|
auto Raw = std::string(ID);
|
||||||
Client->SetIsGuest(AuthResponse["guest"].GetBool());
|
|
||||||
for (const auto& ID : AuthResponse["identifiers"].GetArray()) {
|
|
||||||
auto Raw = std::string(ID.GetString());
|
|
||||||
auto SepIndex = Raw.find(':');
|
auto SepIndex = Raw.find(':');
|
||||||
Client->SetIdentifier(Raw.substr(0, SepIndex), Raw.substr(SepIndex + 1));
|
Client->SetIdentifier(Raw.substr(0, SepIndex), Raw.substr(SepIndex + 1));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
beammp_error("Invalid authentication data received from authentication backend");
|
||||||
ClientKick(*Client, "Invalid authentication data!");
|
ClientKick(*Client, "Invalid authentication data!");
|
||||||
return;
|
return nullptr;
|
||||||
|
}
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
beammp_errorf("Client sent invalid key: {}", e.what());
|
||||||
|
// TODO: we should really clarify that this was a backend response or parsing error
|
||||||
|
ClientKick(*Client, "Invalid key! Please restart your game.");
|
||||||
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
beammp_debug("Name -> " + Client->GetName() + ", Guest -> " + std::to_string(Client->IsGuest()) + ", Roles -> " + Client->GetRoles());
|
beammp_debug("Name -> " + Client->GetName() + ", Guest -> " + std::to_string(Client->IsGuest()) + ", Roles -> " + Client->GetRoles());
|
||||||
@ -342,8 +295,7 @@ void TNetwork::Authentication(const TConnection& ClientConnection) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (Cl->GetName() == Client->GetName() && Cl->IsGuest() == Client->IsGuest()) {
|
if (Cl->GetName() == Client->GetName() && Cl->IsGuest() == Client->IsGuest()) {
|
||||||
CloseSocketProper(Cl->GetTCPSock());
|
Cl->Disconnect("Stale Client (not a real player)");
|
||||||
Cl->SetStatus(-2);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -368,27 +320,28 @@ void TNetwork::Authentication(const TConnection& ClientConnection) {
|
|||||||
|
|
||||||
if (NotAllowed) {
|
if (NotAllowed) {
|
||||||
ClientKick(*Client, "you are not allowed on the server!");
|
ClientKick(*Client, "you are not allowed on the server!");
|
||||||
return;
|
return {};
|
||||||
} else if (NotAllowedWithReason) {
|
} else if (NotAllowedWithReason) {
|
||||||
ClientKick(*Client, Reason);
|
ClientKick(*Client, Reason);
|
||||||
return;
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mServer.ClientCount() < size_t(Application::Settings.MaxPlayers)) {
|
if (mServer.ClientCount() < size_t(Application::Settings.MaxPlayers)) {
|
||||||
beammp_info("Identification success");
|
beammp_info("Identification success");
|
||||||
mServer.InsertClient(Client);
|
mServer.InsertClient(Client);
|
||||||
TCPClient(Client);
|
TCPClient(Client);
|
||||||
} else
|
} else {
|
||||||
ClientKick(*Client, "Server full!");
|
ClientKick(*Client, "Server full!");
|
||||||
|
}
|
||||||
|
return Client;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<TClient> TNetwork::CreateClient(SOCKET TCPSock) {
|
std::shared_ptr<TClient> TNetwork::CreateClient(ip::tcp::socket&& TCPSock) {
|
||||||
auto c = std::make_shared<TClient>(mServer);
|
auto c = std::make_shared<TClient>(mServer, std::move(TCPSock));
|
||||||
c->SetTCPSock(TCPSock);
|
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TNetwork::TCPSend(TClient& c, const std::string& Data, bool IsSync) {
|
bool TNetwork::TCPSend(TClient& c, const std::vector<uint8_t>& Data, bool IsSync) {
|
||||||
if (!IsSync) {
|
if (!IsSync) {
|
||||||
if (c.IsSyncing()) {
|
if (c.IsSyncing()) {
|
||||||
if (!Data.empty()) {
|
if (!Data.empty()) {
|
||||||
@ -400,120 +353,101 @@ bool TNetwork::TCPSend(TClient& c, const std::string& Data, bool IsSync) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t Size, Sent;
|
auto& Sock = c.GetTCPSock();
|
||||||
std::string Send(4, 0);
|
|
||||||
Size = int32_t(Data.size());
|
/*
|
||||||
memcpy(&Send[0], &Size, sizeof(Size));
|
* our TCP protocol sends a header of 4 bytes, followed by the data.
|
||||||
Send += Data;
|
*
|
||||||
Sent = 0;
|
* [][][][][][]...[]
|
||||||
Size += 4;
|
* ^------^^---...-^
|
||||||
do {
|
* size data
|
||||||
#if defined(BEAMMP_WINDOWS)
|
*/
|
||||||
int32_t Temp = send(c.GetTCPSock(), &Send[Sent], Size - Sent, 0);
|
|
||||||
#elif defined(BEAMMP_LINUX) || defined(BEAMMP_APPLE)
|
const auto Size = int32_t(Data.size());
|
||||||
int32_t Temp = send(c.GetTCPSock(), &Send[Sent], Size - Sent, MSG_NOSIGNAL);
|
std::vector<uint8_t> ToSend;
|
||||||
#endif
|
ToSend.resize(Data.size() + sizeof(Size));
|
||||||
if (Temp == 0) {
|
std::memcpy(ToSend.data(), &Size, sizeof(Size));
|
||||||
beammp_debug("send() == 0: " + GetPlatformAgnosticErrorString());
|
std::memcpy(ToSend.data() + sizeof(Size), Data.data(), Data.size());
|
||||||
if (c.GetStatus() > -1)
|
boost::system::error_code ec;
|
||||||
c.SetStatus(-1);
|
write(Sock, buffer(ToSend), ec);
|
||||||
return false;
|
if (ec) {
|
||||||
} else if (Temp < 0) {
|
beammp_debugf("write(): {}", ec.what());
|
||||||
beammp_debug("send() < 0: " + GetPlatformAgnosticErrorString()); // TODO fix it was spamming yet everyone stayed on the server
|
c.Disconnect("write() failed");
|
||||||
if (c.GetStatus() > -1)
|
|
||||||
c.SetStatus(-1);
|
|
||||||
CloseSocketProper(c.GetTCPSock());
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
Sent += Temp;
|
|
||||||
c.UpdatePingTime();
|
c.UpdatePingTime();
|
||||||
} while (Sent < Size);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TNetwork::CheckBytes(TClient& c, int32_t BytesRcv) {
|
std::vector<uint8_t> TNetwork::TCPRcv(TClient& c) {
|
||||||
if (BytesRcv == 0) {
|
if (c.IsDisconnected()) {
|
||||||
beammp_trace("(TCP) Connection closing...");
|
beammp_error("Client disconnected, cancelling TCPRcv");
|
||||||
if (c.GetStatus() > -1)
|
return {};
|
||||||
c.SetStatus(-1);
|
|
||||||
return false;
|
|
||||||
} else if (BytesRcv < 0) {
|
|
||||||
beammp_debug("(TCP) recv() failed: " + GetPlatformAgnosticErrorString());
|
|
||||||
if (c.GetStatus() > -1)
|
|
||||||
c.SetStatus(-1);
|
|
||||||
CloseSocketProper(c.GetTCPSock());
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string TNetwork::TCPRcv(TClient& c) {
|
int32_t Header {};
|
||||||
int32_t Header {}, BytesRcv = 0, Temp {};
|
auto& Sock = c.GetTCPSock();
|
||||||
if (c.GetStatus() < 0)
|
|
||||||
return "";
|
|
||||||
|
|
||||||
std::vector<char> Data(sizeof(Header));
|
boost::system::error_code ec;
|
||||||
do {
|
std::array<uint8_t, sizeof(Header)> HeaderData;
|
||||||
Temp = recv(c.GetTCPSock(), &Data[BytesRcv], 4 - BytesRcv, 0);
|
read(Sock, buffer(HeaderData), ec);
|
||||||
if (!CheckBytes(c, Temp)) {
|
if (ec) {
|
||||||
return "";
|
// TODO: handle this case (read failed)
|
||||||
|
beammp_debugf("TCPRcv: Reading header failed: {}", ec.what());
|
||||||
|
return {};
|
||||||
}
|
}
|
||||||
BytesRcv += Temp;
|
Header = *reinterpret_cast<int32_t*>(HeaderData.data());
|
||||||
} while (size_t(BytesRcv) < sizeof(Header));
|
beammp_tracef("Expecting to read {} bytes", Header);
|
||||||
memcpy(&Header, &Data[0], sizeof(Header));
|
|
||||||
|
|
||||||
if (!CheckBytes(c, BytesRcv)) {
|
std::vector<uint8_t> Data;
|
||||||
return "";
|
// TODO: This is arbitrary, this needs to be handled another way
|
||||||
}
|
|
||||||
if (Header < int32_t(100 * MB)) {
|
if (Header < int32_t(100 * MB)) {
|
||||||
Data.resize(Header);
|
Data.resize(Header);
|
||||||
} else {
|
} else {
|
||||||
ClientKick(c, "Header size limit exceeded");
|
ClientKick(c, "Header size limit exceeded");
|
||||||
beammp_warn("Client " + c.GetName() + " (" + std::to_string(c.GetID()) + ") sent header of >100MB - assuming malicious intent and disconnecting the client.");
|
beammp_warn("Client " + c.GetName() + " (" + std::to_string(c.GetID()) + ") sent header of >100MB - assuming malicious intent and disconnecting the client.");
|
||||||
return "";
|
return {};
|
||||||
}
|
}
|
||||||
BytesRcv = 0;
|
auto N = read(Sock, buffer(Data), ec);
|
||||||
do {
|
if (ec) {
|
||||||
Temp = recv(c.GetTCPSock(), &Data[BytesRcv], Header - BytesRcv, 0);
|
// TODO: handle this case properly
|
||||||
if (!CheckBytes(c, Temp)) {
|
beammp_debugf("TCPRcv: Reading data failed: {}", ec.what());
|
||||||
return "";
|
return {};
|
||||||
}
|
}
|
||||||
BytesRcv += Temp;
|
|
||||||
} while (BytesRcv < Header);
|
|
||||||
std::string Ret(Data.data(), Header);
|
|
||||||
|
|
||||||
if (Ret.substr(0, 4) == "ABG:") {
|
if (N != Header) {
|
||||||
Ret = DeComp(Ret.substr(4));
|
beammp_errorf("Expected to read {} bytes, instead got {}", Header, N);
|
||||||
|
}
|
||||||
|
|
||||||
|
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());
|
||||||
|
return DeComp(Data);
|
||||||
|
} else {
|
||||||
|
return Data;
|
||||||
}
|
}
|
||||||
return Ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TNetwork::ClientKick(TClient& c, const std::string& R) {
|
void TNetwork::ClientKick(TClient& c, const std::string& R) {
|
||||||
beammp_info("Client kicked: " + R);
|
beammp_info("Client kicked: " + R);
|
||||||
if (!TCPSend(c, "K" + R)) {
|
if (!TCPSend(c, StringToVector("K" + R))) {
|
||||||
beammp_warn("tried to kick player '" + c.GetName() + "' (id " + std::to_string(c.GetID()) + "), but was already disconnected");
|
beammp_warn("tried to kick player '" + c.GetName() + "' (id " + std::to_string(c.GetID()) + "), but was already disconnected");
|
||||||
}
|
}
|
||||||
c.SetStatus(-2);
|
c.Disconnect("Kicked");
|
||||||
|
|
||||||
if (c.GetTCPSock())
|
|
||||||
CloseSocketProper(c.GetTCPSock());
|
|
||||||
|
|
||||||
if (c.GetDownSock())
|
|
||||||
CloseSocketProper(c.GetDownSock());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TNetwork::Looper(const std::weak_ptr<TClient>& c) {
|
void TNetwork::Looper(const std::weak_ptr<TClient>& c) {
|
||||||
RegisterThreadAuto();
|
RegisterThreadAuto();
|
||||||
while (!c.expired()) {
|
while (!c.expired()) {
|
||||||
auto Client = c.lock();
|
auto Client = c.lock();
|
||||||
if (Client->GetStatus() < 0) {
|
if (Client->IsDisconnected()) {
|
||||||
beammp_debug("client status < 0, breaking client loop");
|
beammp_debug("client is disconnected, breaking client loop");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (!Client->IsSyncing() && Client->IsSynced() && Client->MissedPacketQueueSize() != 0) {
|
if (!Client->IsSyncing() && Client->IsSynced() && Client->MissedPacketQueueSize() != 0) {
|
||||||
// debug("sending " + std::to_string(Client->MissedPacketQueueSize()) + " queued packets");
|
// debug("sending " + std::to_string(Client->MissedPacketQueueSize()) + " queued packets");
|
||||||
while (Client->MissedPacketQueueSize() > 0) {
|
while (Client->MissedPacketQueueSize() > 0) {
|
||||||
std::string QData {};
|
std::vector<uint8_t> QData {};
|
||||||
{ // locked context
|
{ // locked context
|
||||||
std::unique_lock lock(Client->MissedPacketQueueMutex());
|
std::unique_lock lock(Client->MissedPacketQueueMutex());
|
||||||
if (Client->MissedPacketQueueSize() <= 0) {
|
if (Client->MissedPacketQueueSize() <= 0) {
|
||||||
@ -524,15 +458,15 @@ void TNetwork::Looper(const std::weak_ptr<TClient>& c) {
|
|||||||
} // end locked context
|
} // end locked context
|
||||||
// beammp_debug("sending a missed packet: " + QData);
|
// beammp_debug("sending a missed packet: " + QData);
|
||||||
if (!TCPSend(*Client, QData, true)) {
|
if (!TCPSend(*Client, QData, true)) {
|
||||||
if (Client->GetStatus() > -1)
|
if (!Client->IsDisconnected())
|
||||||
Client->SetStatus(-1);
|
Client->Disconnect("Failed to TCPSend while clearing the missed packet queue");
|
||||||
{
|
{
|
||||||
std::unique_lock lock(Client->MissedPacketQueueMutex());
|
std::unique_lock lock(Client->MissedPacketQueueMutex());
|
||||||
while (!Client->MissedPacketQueue().empty()) {
|
while (!Client->MissedPacketQueue().empty()) {
|
||||||
Client->MissedPacketQueue().pop();
|
Client->MissedPacketQueue().pop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
CloseSocketProper(Client->GetTCPSock());
|
Client->Disconnect("WHY THE FUCK NOT");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -544,7 +478,7 @@ void TNetwork::Looper(const std::weak_ptr<TClient>& c) {
|
|||||||
|
|
||||||
void TNetwork::TCPClient(const std::weak_ptr<TClient>& c) {
|
void TNetwork::TCPClient(const std::weak_ptr<TClient>& c) {
|
||||||
// TODO: the c.expired() might cause issues here, remove if you end up here with your debugger
|
// TODO: the c.expired() might cause issues here, remove if you end up here with your debugger
|
||||||
if (c.expired() || c.lock()->GetTCPSock() == -1) {
|
if (c.expired() || !c.lock()->GetTCPSock().is_open()) {
|
||||||
mServer.RemoveClient(c);
|
mServer.RemoveClient(c);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -557,24 +491,23 @@ void TNetwork::TCPClient(const std::weak_ptr<TClient>& c) {
|
|||||||
if (c.expired())
|
if (c.expired())
|
||||||
break;
|
break;
|
||||||
auto Client = c.lock();
|
auto Client = c.lock();
|
||||||
if (Client->GetStatus() < 0) {
|
if (Client->IsDisconnected()) {
|
||||||
beammp_debug("client status < 0, breaking client loop");
|
beammp_debug("client status < 0, breaking client loop");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto res = TCPRcv(*Client);
|
auto res = TCPRcv(*Client);
|
||||||
if (res == "") {
|
if (res.empty()) {
|
||||||
beammp_debug("TCPRcv error, break client loop");
|
beammp_debug("TCPRcv empty, ignoring");
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
TServer::GlobalParser(c, res, mPPSMonitor, *this);
|
TServer::GlobalParser(c, std::move(res), mPPSMonitor, *this);
|
||||||
}
|
}
|
||||||
if (QueueSync.joinable())
|
if (QueueSync.joinable())
|
||||||
QueueSync.join();
|
QueueSync.join();
|
||||||
|
|
||||||
if (!c.expired()) {
|
if (!c.expired()) {
|
||||||
auto Client = c.lock();
|
auto Client = c.lock();
|
||||||
OnDisconnect(c, Client->GetStatus() == -2);
|
OnDisconnect(c);
|
||||||
} else {
|
} else {
|
||||||
beammp_warn("client expired in TCPClient, should never happen");
|
beammp_warn("client expired in TCPClient, should never happen");
|
||||||
}
|
}
|
||||||
@ -591,11 +524,11 @@ void TNetwork::UpdatePlayer(TClient& Client) {
|
|||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
Packet = Packet.substr(0, Packet.length() - 1);
|
Packet = Packet.substr(0, Packet.length() - 1);
|
||||||
Client.EnqueuePacket(Packet);
|
Client.EnqueuePacket(StringToVector(Packet));
|
||||||
//(void)Respond(Client, Packet, true);
|
//(void)Respond(Client, Packet, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TNetwork::OnDisconnect(const std::weak_ptr<TClient>& ClientPtr, bool kicked) {
|
void TNetwork::OnDisconnect(const std::weak_ptr<TClient>& ClientPtr) {
|
||||||
beammp_assert(!ClientPtr.expired());
|
beammp_assert(!ClientPtr.expired());
|
||||||
auto LockedClientPtr = ClientPtr.lock();
|
auto LockedClientPtr = ClientPtr.lock();
|
||||||
TClient& c = *LockedClientPtr;
|
TClient& c = *LockedClientPtr;
|
||||||
@ -608,20 +541,14 @@ void TNetwork::OnDisconnect(const std::weak_ptr<TClient>& ClientPtr, bool kicked
|
|||||||
} // End Vehicle Data Lock Scope
|
} // End Vehicle Data Lock Scope
|
||||||
for (auto& v : VehicleData) {
|
for (auto& v : VehicleData) {
|
||||||
Packet = "Od:" + std::to_string(c.GetID()) + "-" + std::to_string(v.ID());
|
Packet = "Od:" + std::to_string(c.GetID()) + "-" + std::to_string(v.ID());
|
||||||
SendToAll(&c, Packet, false, true);
|
SendToAll(&c, StringToVector(Packet), false, true);
|
||||||
}
|
}
|
||||||
if (kicked)
|
|
||||||
Packet = ("L") + c.GetName() + (" was kicked!");
|
|
||||||
else
|
|
||||||
Packet = ("L") + c.GetName() + (" left the server!");
|
Packet = ("L") + c.GetName() + (" left the server!");
|
||||||
SendToAll(&c, Packet, false, true);
|
SendToAll(&c, StringToVector(Packet), false, true);
|
||||||
Packet.clear();
|
Packet.clear();
|
||||||
auto Futures = LuaAPI::MP::Engine->TriggerEvent("onPlayerDisconnect", "", c.GetID());
|
auto Futures = LuaAPI::MP::Engine->TriggerEvent("onPlayerDisconnect", "", c.GetID());
|
||||||
LuaAPI::MP::Engine->ReportErrors(Futures);
|
LuaAPI::MP::Engine->ReportErrors(Futures);
|
||||||
if (c.GetTCPSock())
|
c.Disconnect("Already Disconnected (OnDisconnect)");
|
||||||
CloseSocketProper(c.GetTCPSock());
|
|
||||||
if (c.GetDownSock())
|
|
||||||
CloseSocketProper(c.GetDownSock());
|
|
||||||
mServer.RemoveClient(ClientPtr);
|
mServer.RemoveClient(ClientPtr);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -653,44 +580,39 @@ void TNetwork::OnConnect(const std::weak_ptr<TClient>& c) {
|
|||||||
beammp_info("Assigned ID " + std::to_string(LockedClient->GetID()) + " to " + LockedClient->GetName());
|
beammp_info("Assigned ID " + std::to_string(LockedClient->GetID()) + " to " + LockedClient->GetName());
|
||||||
LuaAPI::MP::Engine->ReportErrors(LuaAPI::MP::Engine->TriggerEvent("onPlayerConnecting", "", LockedClient->GetID()));
|
LuaAPI::MP::Engine->ReportErrors(LuaAPI::MP::Engine->TriggerEvent("onPlayerConnecting", "", LockedClient->GetID()));
|
||||||
SyncResources(*LockedClient);
|
SyncResources(*LockedClient);
|
||||||
if (LockedClient->GetStatus() < 0)
|
if (LockedClient->IsDisconnected())
|
||||||
return;
|
return;
|
||||||
(void)Respond(*LockedClient, "M" + Application::Settings.MapName, true); // Send the Map on connect
|
(void)Respond(*LockedClient, StringToVector("M" + Application::Settings.MapName), true); // Send the Map on connect
|
||||||
beammp_info(LockedClient->GetName() + " : Connected");
|
beammp_info(LockedClient->GetName() + " : Connected");
|
||||||
LuaAPI::MP::Engine->ReportErrors(LuaAPI::MP::Engine->TriggerEvent("onPlayerJoining", "", LockedClient->GetID()));
|
LuaAPI::MP::Engine->ReportErrors(LuaAPI::MP::Engine->TriggerEvent("onPlayerJoining", "", LockedClient->GetID()));
|
||||||
}
|
}
|
||||||
|
|
||||||
void TNetwork::SyncResources(TClient& c) {
|
void TNetwork::SyncResources(TClient& c) {
|
||||||
#ifndef DEBUG
|
if (!TCPSend(c, StringToVector("P" + std::to_string(c.GetID())))) {
|
||||||
try {
|
|
||||||
#endif
|
|
||||||
if (!TCPSend(c, "P" + std::to_string(c.GetID()))) {
|
|
||||||
// TODO handle
|
// TODO handle
|
||||||
}
|
}
|
||||||
std::string Data;
|
std::vector<uint8_t> Data;
|
||||||
while (c.GetStatus() > -1) {
|
while (!c.IsDisconnected()) {
|
||||||
Data = TCPRcv(c);
|
Data = TCPRcv(c);
|
||||||
if (Data == "Done")
|
if (Data.empty()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
constexpr std::string_view Done = "Done";
|
||||||
|
if (std::equal(Data.begin(), Data.end(), Done.begin(), Done.end()))
|
||||||
break;
|
break;
|
||||||
Parse(c, Data);
|
Parse(c, Data);
|
||||||
}
|
}
|
||||||
#ifndef DEBUG
|
|
||||||
} catch (std::exception& e) {
|
|
||||||
beammp_error("Exception! : " + std::string(e.what()));
|
|
||||||
c.SetStatus(-1);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TNetwork::Parse(TClient& c, const std::string& Packet) {
|
void TNetwork::Parse(TClient& c, const std::vector<uint8_t>& Packet) {
|
||||||
if (Packet.empty())
|
if (Packet.empty())
|
||||||
return;
|
return;
|
||||||
char Code = Packet.at(0), SubCode = 0;
|
char Code = Packet.at(0), SubCode = 0;
|
||||||
if (Packet.length() > 1)
|
if (Packet.size() > 1)
|
||||||
SubCode = Packet.at(1);
|
SubCode = Packet.at(1);
|
||||||
switch (Code) {
|
switch (Code) {
|
||||||
case 'f':
|
case 'f':
|
||||||
SendFile(c, Packet.substr(1));
|
SendFile(c, std::string(reinterpret_cast<const char*>(Packet.data() + 1), Packet.size() - 1));
|
||||||
return;
|
return;
|
||||||
case 'S':
|
case 'S':
|
||||||
if (SubCode == 'R') {
|
if (SubCode == 'R') {
|
||||||
@ -698,7 +620,7 @@ void TNetwork::Parse(TClient& c, const std::string& Packet) {
|
|||||||
std::string ToSend = mResourceManager.FileList() + mResourceManager.FileSizes();
|
std::string ToSend = mResourceManager.FileList() + mResourceManager.FileSizes();
|
||||||
if (ToSend.empty())
|
if (ToSend.empty())
|
||||||
ToSend = "-";
|
ToSend = "-";
|
||||||
if (!TCPSend(c, ToSend)) {
|
if (!TCPSend(c, StringToVector(ToSend))) {
|
||||||
// TODO: error
|
// TODO: error
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -712,7 +634,7 @@ void TNetwork::SendFile(TClient& c, const std::string& UnsafeName) {
|
|||||||
beammp_info(c.GetName() + " requesting : " + UnsafeName.substr(UnsafeName.find_last_of('/')));
|
beammp_info(c.GetName() + " requesting : " + UnsafeName.substr(UnsafeName.find_last_of('/')));
|
||||||
|
|
||||||
if (!fs::path(UnsafeName).has_filename()) {
|
if (!fs::path(UnsafeName).has_filename()) {
|
||||||
if (!TCPSend(c, "CO")) {
|
if (!TCPSend(c, StringToVector("CO"))) {
|
||||||
// TODO: handle
|
// TODO: handle
|
||||||
}
|
}
|
||||||
beammp_warn("File " + UnsafeName + " is not a file!");
|
beammp_warn("File " + UnsafeName + " is not a file!");
|
||||||
@ -722,28 +644,28 @@ void TNetwork::SendFile(TClient& c, const std::string& UnsafeName) {
|
|||||||
FileName = Application::Settings.Resource + "/Client/" + FileName;
|
FileName = Application::Settings.Resource + "/Client/" + FileName;
|
||||||
|
|
||||||
if (!std::filesystem::exists(FileName)) {
|
if (!std::filesystem::exists(FileName)) {
|
||||||
if (!TCPSend(c, "CO")) {
|
if (!TCPSend(c, StringToVector("CO"))) {
|
||||||
// TODO: handle
|
// TODO: handle
|
||||||
}
|
}
|
||||||
beammp_warn("File " + UnsafeName + " could not be accessed!");
|
beammp_warn("File " + UnsafeName + " could not be accessed!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!TCPSend(c, "AG")) {
|
if (!TCPSend(c, StringToVector("AG"))) {
|
||||||
// TODO: handle
|
// TODO: handle
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Wait for connections
|
/// Wait for connections
|
||||||
int T = 0;
|
int T = 0;
|
||||||
while (c.GetDownSock() < 1 && T < 50) {
|
while (!c.GetDownSock().is_open() && T < 50) {
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||||
T++;
|
T++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (c.GetDownSock() < 1) {
|
if (!c.GetDownSock().is_open()) {
|
||||||
beammp_error("Client doesn't have a download socket!");
|
beammp_error("Client doesn't have a download socket!");
|
||||||
if (c.GetStatus() > -1)
|
if (!c.IsDisconnected())
|
||||||
c.SetStatus(-1);
|
c.Disconnect("Missing download socket");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -807,8 +729,8 @@ TEST_CASE("SplitIntoChunks") {
|
|||||||
CHECK((Count * ChunkSize) + LastSize == FullSize);
|
CHECK((Count * ChunkSize) + LastSize == FullSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t* /* end ptr */ TNetwork::SendSplit(TClient& c, SOCKET Socket, uint8_t* DataPtr, size_t Size) {
|
const uint8_t* /* end ptr */ TNetwork::SendSplit(TClient& c, ip::tcp::socket& Socket, const uint8_t* DataPtr, size_t Size) {
|
||||||
if (TCPSendRaw(c, Socket, reinterpret_cast<char*>(DataPtr), Size)) {
|
if (TCPSendRaw(c, Socket, DataPtr, Size)) {
|
||||||
return DataPtr + Size;
|
return DataPtr + Size;
|
||||||
} else {
|
} else {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
@ -823,29 +745,28 @@ void TNetwork::SplitLoad(TClient& c, size_t Sent, size_t Size, bool D, const std
|
|||||||
Data.resize(Split);
|
Data.resize(Split);
|
||||||
else
|
else
|
||||||
Data.resize(Size);
|
Data.resize(Size);
|
||||||
SOCKET TCPSock;
|
ip::tcp::socket* TCPSock { nullptr };
|
||||||
if (D)
|
if (D)
|
||||||
TCPSock = c.GetDownSock();
|
TCPSock = &c.GetDownSock();
|
||||||
else
|
else
|
||||||
TCPSock = c.GetTCPSock();
|
TCPSock = &c.GetTCPSock();
|
||||||
beammp_debug("Split load Socket " + std::to_string(TCPSock));
|
while (!c.IsDisconnected() && Sent < Size) {
|
||||||
while (c.GetStatus() > -1 && Sent < Size) {
|
|
||||||
size_t Diff = Size - Sent;
|
size_t Diff = Size - Sent;
|
||||||
if (Diff > Split) {
|
if (Diff > Split) {
|
||||||
f.seekg(Sent, std::ios_base::beg);
|
f.seekg(Sent, std::ios_base::beg);
|
||||||
f.read(reinterpret_cast<char*>(Data.data()), Split);
|
f.read(reinterpret_cast<char*>(Data.data()), Split);
|
||||||
if (!TCPSendRaw(c, TCPSock, reinterpret_cast<char*>(Data.data()), Split)) {
|
if (!TCPSendRaw(c, *TCPSock, Data.data(), Split)) {
|
||||||
if (c.GetStatus() > -1)
|
if (!c.IsDisconnected())
|
||||||
c.SetStatus(-1);
|
c.Disconnect("TCPSendRaw failed in mod download (1)");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
Sent += Split;
|
Sent += Split;
|
||||||
} else {
|
} else {
|
||||||
f.seekg(Sent, std::ios_base::beg);
|
f.seekg(Sent, std::ios_base::beg);
|
||||||
f.read(reinterpret_cast<char*>(Data.data()), Diff);
|
f.read(reinterpret_cast<char*>(Data.data()), Diff);
|
||||||
if (!TCPSendRaw(c, TCPSock, reinterpret_cast<char*>(Data.data()), int32_t(Diff))) {
|
if (!TCPSendRaw(c, *TCPSock, Data.data(), int32_t(Diff))) {
|
||||||
if (c.GetStatus() > -1)
|
if (!c.IsDisconnected())
|
||||||
c.SetStatus(-1);
|
c.Disconnect("TCPSendRaw failed in mod download (2)");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
Sent += Diff;
|
Sent += Diff;
|
||||||
@ -853,37 +774,28 @@ void TNetwork::SplitLoad(TClient& c, size_t Sent, size_t Size, bool D, const std
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TNetwork::TCPSendRaw(TClient& C, SOCKET socket, char* Data, int32_t Size) {
|
bool TNetwork::TCPSendRaw(TClient& C, ip::tcp::socket& socket, const uint8_t* Data, size_t Size) {
|
||||||
intmax_t Sent = 0;
|
boost::system::error_code ec;
|
||||||
do {
|
write(socket, buffer(Data, Size), ec);
|
||||||
#if defined(BEAMMP_LINUX) || defined(BEAMMP_APPLE)
|
if (ec) {
|
||||||
intmax_t Temp = send(socket, &Data[Sent], int(Size - Sent), MSG_NOSIGNAL);
|
beammp_errorf("Failed to send raw data to client: {}", ec.what());
|
||||||
#else
|
|
||||||
intmax_t Temp = send(socket, &Data[Sent], int(Size - Sent), 0);
|
|
||||||
#endif
|
|
||||||
if (Temp < 1) {
|
|
||||||
beammp_info("Socket Closed! " + std::to_string(socket));
|
|
||||||
CloseSocketProper(socket);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
Sent += Temp;
|
|
||||||
C.UpdatePingTime();
|
C.UpdatePingTime();
|
||||||
} while (Sent < Size);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TNetwork::SendLarge(TClient& c, std::string Data, bool isSync) {
|
bool TNetwork::SendLarge(TClient& c, std::vector<uint8_t> Data, bool isSync) {
|
||||||
if (Data.length() > 400) {
|
if (Data.size() > 400) {
|
||||||
std::string CMP(Comp(Data));
|
CompressProperly(Data);
|
||||||
Data = "ABG:" + CMP;
|
|
||||||
}
|
}
|
||||||
return TCPSend(c, Data, isSync);
|
return TCPSend(c, Data, isSync);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TNetwork::Respond(TClient& c, const std::string& MSG, bool Rel, bool isSync) {
|
bool TNetwork::Respond(TClient& c, const std::vector<uint8_t>& MSG, bool Rel, bool isSync) {
|
||||||
char C = MSG.at(0);
|
char C = MSG.at(0);
|
||||||
if (Rel || C == 'W' || C == 'Y' || C == 'V' || C == 'E') {
|
if (Rel || C == 'W' || C == 'Y' || C == 'V' || C == 'E') {
|
||||||
if (C == 'O' || C == 'T' || MSG.length() > 1000) {
|
if (C == 'O' || C == 'T' || MSG.size() > 1000) {
|
||||||
return SendLarge(c, MSG, isSync);
|
return SendLarge(c, MSG, isSync);
|
||||||
} else {
|
} else {
|
||||||
return TCPSend(c, MSG, isSync);
|
return TCPSend(c, MSG, isSync);
|
||||||
@ -902,11 +814,11 @@ bool TNetwork::SyncClient(const std::weak_ptr<TClient>& c) {
|
|||||||
return true;
|
return true;
|
||||||
// Syncing, later set isSynced
|
// Syncing, later set isSynced
|
||||||
// after syncing is done, we apply all packets they missed
|
// after syncing is done, we apply all packets they missed
|
||||||
if (!Respond(*LockedClient, ("Sn") + LockedClient->GetName(), true)) {
|
if (!Respond(*LockedClient, StringToVector("Sn" + LockedClient->GetName()), true)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// ignore error
|
// ignore error
|
||||||
(void)SendToAll(LockedClient.get(), ("JWelcome ") + LockedClient->GetName() + "!", false, true);
|
(void)SendToAll(LockedClient.get(), StringToVector("JWelcome " + LockedClient->GetName() + "!"), false, true);
|
||||||
|
|
||||||
LuaAPI::MP::Engine->ReportErrors(LuaAPI::MP::Engine->TriggerEvent("onPlayerJoin", "", LockedClient->GetID()));
|
LuaAPI::MP::Engine->ReportErrors(LuaAPI::MP::Engine->TriggerEvent("onPlayerJoin", "", LockedClient->GetID()));
|
||||||
LockedClient->SetIsSyncing(true);
|
LockedClient->SetIsSyncing(true);
|
||||||
@ -928,12 +840,12 @@ bool TNetwork::SyncClient(const std::weak_ptr<TClient>& c) {
|
|||||||
} // End Vehicle Data Lock Scope
|
} // End Vehicle Data Lock Scope
|
||||||
if (client != LockedClient) {
|
if (client != LockedClient) {
|
||||||
for (auto& v : VehicleData) {
|
for (auto& v : VehicleData) {
|
||||||
if (LockedClient->GetStatus() < 0) {
|
if (LockedClient->IsDisconnected()) {
|
||||||
Return = true;
|
Return = true;
|
||||||
res = false;
|
res = false;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
res = Respond(*LockedClient, v.Data(), true, true);
|
res = Respond(*LockedClient, StringToVector(v.Data()), true, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -948,7 +860,7 @@ bool TNetwork::SyncClient(const std::weak_ptr<TClient>& c) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TNetwork::SendToAll(TClient* c, const std::string& Data, bool Self, bool Rel) {
|
void TNetwork::SendToAll(TClient* c, const std::vector<uint8_t>& Data, bool Self, bool Rel) {
|
||||||
if (!Self)
|
if (!Self)
|
||||||
beammp_assert(c);
|
beammp_assert(c);
|
||||||
char C = Data.at(0);
|
char C = Data.at(0);
|
||||||
@ -965,10 +877,11 @@ void TNetwork::SendToAll(TClient* c, const std::string& Data, bool Self, bool Re
|
|||||||
if (Self || Client.get() != c) {
|
if (Self || Client.get() != c) {
|
||||||
if (Client->IsSynced() || Client->IsSyncing()) {
|
if (Client->IsSynced() || Client->IsSyncing()) {
|
||||||
if (Rel || C == 'W' || C == 'Y' || C == 'V' || C == 'E') {
|
if (Rel || C == 'W' || C == 'Y' || C == 'V' || C == 'E') {
|
||||||
if (C == 'O' || C == 'T' || Data.length() > 1000) {
|
if (C == 'O' || C == 'T' || Data.size() > 1000) {
|
||||||
if (Data.length() > 400) {
|
if (Data.size() > 400) {
|
||||||
std::string CMP(Comp(Data));
|
auto CompressedData = Data;
|
||||||
Client->EnqueuePacket("ABG:" + CMP);
|
CompressProperly(CompressedData);
|
||||||
|
Client->EnqueuePacket(CompressedData);
|
||||||
} else {
|
} else {
|
||||||
Client->EnqueuePacket(Data);
|
Client->EnqueuePacket(Data);
|
||||||
}
|
}
|
||||||
@ -990,8 +903,8 @@ void TNetwork::SendToAll(TClient* c, const std::string& Data, bool Self, bool Re
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TNetwork::UDPSend(TClient& Client, std::string Data) {
|
bool TNetwork::UDPSend(TClient& Client, std::vector<uint8_t> Data) {
|
||||||
if (!Client.IsConnected() || Client.GetStatus() < 0) {
|
if (!Client.IsConnected() || Client.IsDisconnected()) {
|
||||||
// this can happen if we try to send a packet to a client that is either
|
// this can happen if we try to send a packet to a client that is either
|
||||||
// 1. not yet fully connected, or
|
// 1. not yet fully connected, or
|
||||||
// 2. disconnected and not yet fully removed
|
// 2. disconnected and not yet fully removed
|
||||||
@ -999,22 +912,21 @@ bool TNetwork::UDPSend(TClient& Client, std::string Data) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
const auto Addr = Client.GetUDPAddr();
|
const auto Addr = Client.GetUDPAddr();
|
||||||
if (Data.length() > 400) {
|
if (Data.size() > 400) {
|
||||||
std::string CMP(Comp(Data));
|
CompressProperly(Data);
|
||||||
Data = "ABG:" + CMP;
|
|
||||||
}
|
}
|
||||||
boost::system::error_code ec;
|
boost::system::error_code ec;
|
||||||
mUDPSock.send_to(buffer(Data), Addr, 0, ec);
|
mUDPSock.send_to(buffer(Data), Addr, 0, ec);
|
||||||
if (ec) {
|
if (ec) {
|
||||||
beammp_debugf("UDP sendto() failed: {}", ec.what());
|
beammp_debugf("UDP sendto() failed: {}", ec.what());
|
||||||
if (Client.GetStatus() > -1)
|
if (!Client.IsDisconnected())
|
||||||
Client.SetStatus(-1);
|
Client.Disconnect("UDP send failed");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string TNetwork::UDPRcvFromClient(ip::udp::endpoint& ClientEndpoint) {
|
std::vector<uint8_t> TNetwork::UDPRcvFromClient(ip::udp::endpoint& ClientEndpoint) {
|
||||||
std::array<char, 1024> Ret {};
|
std::array<char, 1024> Ret {};
|
||||||
boost::system::error_code ec;
|
boost::system::error_code ec;
|
||||||
beammp_debugf("receiving data from {}:{}", ClientEndpoint.address().to_string(), ClientEndpoint.port());
|
beammp_debugf("receiving data from {}:{}", ClientEndpoint.address().to_string(), ClientEndpoint.port());
|
||||||
@ -1022,8 +934,8 @@ std::string TNetwork::UDPRcvFromClient(ip::udp::endpoint& ClientEndpoint) {
|
|||||||
beammp_debugf("received {} bytes from {}:{}", Rcv, ClientEndpoint.address().to_string(), ClientEndpoint.port());
|
beammp_debugf("received {} bytes from {}:{}", Rcv, ClientEndpoint.address().to_string(), ClientEndpoint.port());
|
||||||
if (ec) {
|
if (ec) {
|
||||||
beammp_errorf("UDP recvfrom() failed: {}", ec.what());
|
beammp_errorf("UDP recvfrom() failed: {}", ec.what());
|
||||||
return "";
|
return {};
|
||||||
}
|
}
|
||||||
// FIXME: This breaks binary data due to \0.
|
// FIXME: This breaks binary data due to \0.
|
||||||
return std::string(Ret.begin(), Ret.begin() + Rcv);
|
return std::vector<uint8_t>(Ret.begin(), Ret.end());
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
#include "TNetwork.h"
|
#include "TNetwork.h"
|
||||||
#include "TPPSMonitor.h"
|
#include "TPPSMonitor.h"
|
||||||
#include <TLuaPlugin.h>
|
#include <TLuaPlugin.h>
|
||||||
|
#include <algorithm>
|
||||||
#include <any>
|
#include <any>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
@ -102,13 +103,6 @@ void TServer::RemoveClient(const std::weak_ptr<TClient>& WeakClientPtr) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::weak_ptr<TClient> TServer::InsertNewClient() {
|
|
||||||
beammp_debug("inserting new client (" + std::to_string(ClientCount()) + ")");
|
|
||||||
WriteLock Lock(mClientsMutex);
|
|
||||||
auto [Iter, Replaced] = mClients.insert(std::make_shared<TClient>(*this));
|
|
||||||
return *Iter;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TServer::ForEachClient(const std::function<bool(std::weak_ptr<TClient>)>& Fn) {
|
void TServer::ForEachClient(const std::function<bool(std::weak_ptr<TClient>)>& Fn) {
|
||||||
decltype(mClients) Clients;
|
decltype(mClients) Clients;
|
||||||
{
|
{
|
||||||
@ -127,12 +121,11 @@ size_t TServer::ClientCount() const {
|
|||||||
return mClients.size();
|
return mClients.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
void TServer::GlobalParser(const std::weak_ptr<TClient>& Client, std::string Packet, TPPSMonitor& PPSMonitor, TNetwork& Network) {
|
void TServer::GlobalParser(const std::weak_ptr<TClient>& Client, std::vector<uint8_t>&& Packet, TPPSMonitor& PPSMonitor, TNetwork& Network) {
|
||||||
if (Packet.find("Zp") != std::string::npos && Packet.size() > 500) {
|
constexpr std::string_view ABG = "ABG:";
|
||||||
// abort();
|
if (Packet.size() >= ABG.size() && std::equal(Packet.begin(), Packet.begin() + ABG.size(), ABG.begin(), ABG.end())) {
|
||||||
}
|
Packet.erase(Packet.begin(), Packet.begin() + ABG.size());
|
||||||
if (Packet.substr(0, 4) == "ABG:") {
|
Packet = DeComp(Packet);
|
||||||
Packet = DeComp(Packet.substr(4));
|
|
||||||
}
|
}
|
||||||
if (Packet.empty()) {
|
if (Packet.empty()) {
|
||||||
return;
|
return;
|
||||||
@ -146,6 +139,8 @@ void TServer::GlobalParser(const std::weak_ptr<TClient>& Client, std::string Pac
|
|||||||
std::any Res;
|
std::any Res;
|
||||||
char Code = Packet.at(0);
|
char Code = Packet.at(0);
|
||||||
|
|
||||||
|
std::string StringPacket(reinterpret_cast<const char*>(Packet.data()), Packet.size());
|
||||||
|
|
||||||
// V to Y
|
// V to Y
|
||||||
if (Code <= 89 && Code >= 86) {
|
if (Code <= 89 && Code >= 86) {
|
||||||
PPSMonitor.IncrementInternalPPS();
|
PPSMonitor.IncrementInternalPPS();
|
||||||
@ -154,38 +149,34 @@ void TServer::GlobalParser(const std::weak_ptr<TClient>& Client, std::string Pac
|
|||||||
}
|
}
|
||||||
switch (Code) {
|
switch (Code) {
|
||||||
case 'H': // initial connection
|
case 'H': // initial connection
|
||||||
beammp_trace(std::string("got 'H' packet: '") + Packet + "' (" + std::to_string(Packet.size()) + ")");
|
|
||||||
if (!Network.SyncClient(Client)) {
|
if (!Network.SyncClient(Client)) {
|
||||||
// TODO handle
|
// TODO handle
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
case 'p':
|
case 'p':
|
||||||
if (!Network.Respond(*LockedClient, ("p"), false)) {
|
if (!Network.Respond(*LockedClient, StringToVector("p"), false)) {
|
||||||
// failed to send
|
// failed to send
|
||||||
if (LockedClient->GetStatus() > -1) {
|
LockedClient->Disconnect("Failed to send ping");
|
||||||
LockedClient->SetStatus(-1);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
Network.UpdatePlayer(*LockedClient);
|
Network.UpdatePlayer(*LockedClient);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
case 'O':
|
case 'O':
|
||||||
if (Packet.length() > 1000) {
|
if (Packet.size() > 1000) {
|
||||||
beammp_debug(("Received data from: ") + LockedClient->GetName() + (" Size: ") + std::to_string(Packet.length()));
|
beammp_debug(("Received data from: ") + LockedClient->GetName() + (" Size: ") + std::to_string(Packet.size()));
|
||||||
}
|
}
|
||||||
ParseVehicle(*LockedClient, Packet, Network);
|
ParseVehicle(*LockedClient, StringPacket, Network);
|
||||||
return;
|
return;
|
||||||
case 'J':
|
case 'J':
|
||||||
beammp_trace(std::string(("got 'J' packet: '")) + Packet + ("' (") + std::to_string(Packet.size()) + (")"));
|
|
||||||
Network.SendToAll(LockedClient.get(), Packet, false, true);
|
Network.SendToAll(LockedClient.get(), Packet, false, true);
|
||||||
return;
|
return;
|
||||||
case 'C': {
|
case 'C': {
|
||||||
beammp_trace(std::string(("got 'C' packet: '")) + Packet + ("' (") + std::to_string(Packet.size()) + (")"));
|
if (Packet.size() < 4 || std::find(Packet.begin() + 3, Packet.end(), ':') == Packet.end())
|
||||||
if (Packet.length() < 4 || Packet.find(':', 3) == std::string::npos)
|
|
||||||
break;
|
break;
|
||||||
auto Futures = LuaAPI::MP::Engine->TriggerEvent("onChatMessage", "", LockedClient->GetID(), LockedClient->GetName(), Packet.substr(Packet.find(':', 3) + 2));
|
const auto PacketAsString = std::string(reinterpret_cast<const char*>(Packet.data()), Packet.size());
|
||||||
|
auto Futures = LuaAPI::MP::Engine->TriggerEvent("onChatMessage", "", LockedClient->GetID(), LockedClient->GetName(), PacketAsString.substr(PacketAsString.find(':', 3) + 2));
|
||||||
TLuaEngine::WaitForAll(Futures);
|
TLuaEngine::WaitForAll(Futures);
|
||||||
LogChatMessage(LockedClient->GetName(), LockedClient->GetID(), Packet.substr(Packet.find(':', 3) + 1));
|
LogChatMessage(LockedClient->GetName(), LockedClient->GetID(), PacketAsString.substr(PacketAsString.find(':', 3) + 1));
|
||||||
if (std::any_of(Futures.begin(), Futures.end(),
|
if (std::any_of(Futures.begin(), Futures.end(),
|
||||||
[](const std::shared_ptr<TLuaResult>& Elem) {
|
[](const std::shared_ptr<TLuaResult>& Elem) {
|
||||||
return !Elem->Error
|
return !Elem->Error
|
||||||
@ -198,8 +189,7 @@ void TServer::GlobalParser(const std::weak_ptr<TClient>& Client, std::string Pac
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
case 'E':
|
case 'E':
|
||||||
beammp_trace(std::string(("got 'E' packet: '")) + Packet + ("' (") + std::to_string(Packet.size()) + (")"));
|
HandleEvent(*LockedClient, StringPacket);
|
||||||
HandleEvent(*LockedClient, Packet);
|
|
||||||
return;
|
return;
|
||||||
case 'N':
|
case 'N':
|
||||||
beammp_trace("got 'N' packet (" + std::to_string(Packet.size()) + ")");
|
beammp_trace("got 'N' packet (" + std::to_string(Packet.size()) + ")");
|
||||||
@ -209,7 +199,7 @@ void TServer::GlobalParser(const std::weak_ptr<TClient>& Client, std::string Pac
|
|||||||
PPSMonitor.IncrementInternalPPS();
|
PPSMonitor.IncrementInternalPPS();
|
||||||
Network.SendToAll(LockedClient.get(), Packet, false, false);
|
Network.SendToAll(LockedClient.get(), Packet, false, false);
|
||||||
|
|
||||||
HandlePosition(*LockedClient, Packet);
|
HandlePosition(*LockedClient, StringPacket);
|
||||||
default:
|
default:
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -275,13 +265,13 @@ void TServer::ParseVehicle(TClient& c, const std::string& Pckt, TNetwork& Networ
|
|||||||
|
|
||||||
if (ShouldSpawn(c, CarJson, CarID) && !ShouldntSpawn) {
|
if (ShouldSpawn(c, CarJson, CarID) && !ShouldntSpawn) {
|
||||||
c.AddNewCar(CarID, Packet);
|
c.AddNewCar(CarID, Packet);
|
||||||
Network.SendToAll(nullptr, Packet, true, true);
|
Network.SendToAll(nullptr, StringToVector(Packet), true, true);
|
||||||
} else {
|
} else {
|
||||||
if (!Network.Respond(c, Packet, true)) {
|
if (!Network.Respond(c, StringToVector(Packet), true)) {
|
||||||
// TODO: handle
|
// TODO: handle
|
||||||
}
|
}
|
||||||
std::string Destroy = "Od:" + std::to_string(c.GetID()) + "-" + std::to_string(CarID);
|
std::string Destroy = "Od:" + std::to_string(c.GetID()) + "-" + std::to_string(CarID);
|
||||||
if (!Network.Respond(c, Destroy, true)) {
|
if (!Network.Respond(c, StringToVector(Destroy), true)) {
|
||||||
// TODO: handle
|
// TODO: handle
|
||||||
}
|
}
|
||||||
beammp_debugf("{} (force : car limit/lua) removed ID {}", c.GetName(), CarID);
|
beammp_debugf("{} (force : car limit/lua) removed ID {}", c.GetName(), CarID);
|
||||||
@ -306,14 +296,14 @@ void TServer::ParseVehicle(TClient& c, const std::string& Pckt, TNetwork& Networ
|
|||||||
FoundPos = FoundPos == std::string::npos ? 0 : FoundPos; // attempt at sanitizing this
|
FoundPos = FoundPos == std::string::npos ? 0 : FoundPos; // attempt at sanitizing this
|
||||||
if ((c.GetUnicycleID() != VID || IsUnicycle(c, Packet.substr(FoundPos)))
|
if ((c.GetUnicycleID() != VID || IsUnicycle(c, Packet.substr(FoundPos)))
|
||||||
&& !ShouldntAllow) {
|
&& !ShouldntAllow) {
|
||||||
Network.SendToAll(&c, Packet, false, true);
|
Network.SendToAll(&c, StringToVector(Packet), false, true);
|
||||||
Apply(c, VID, Packet);
|
Apply(c, VID, Packet);
|
||||||
} else {
|
} else {
|
||||||
if (c.GetUnicycleID() == VID) {
|
if (c.GetUnicycleID() == VID) {
|
||||||
c.SetUnicycleID(-1);
|
c.SetUnicycleID(-1);
|
||||||
}
|
}
|
||||||
std::string Destroy = "Od:" + std::to_string(c.GetID()) + "-" + std::to_string(VID);
|
std::string Destroy = "Od:" + std::to_string(c.GetID()) + "-" + std::to_string(VID);
|
||||||
Network.SendToAll(nullptr, Destroy, true, true);
|
Network.SendToAll(nullptr, StringToVector(Destroy), true, true);
|
||||||
c.DeleteCar(VID);
|
c.DeleteCar(VID);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -329,7 +319,7 @@ void TServer::ParseVehicle(TClient& c, const std::string& Pckt, TNetwork& Networ
|
|||||||
if (c.GetUnicycleID() == VID) {
|
if (c.GetUnicycleID() == VID) {
|
||||||
c.SetUnicycleID(-1);
|
c.SetUnicycleID(-1);
|
||||||
}
|
}
|
||||||
Network.SendToAll(nullptr, Packet, true, true);
|
Network.SendToAll(nullptr, StringToVector(Packet), true, true);
|
||||||
// TODO: should this trigger on all vehicle deletions?
|
// TODO: should this trigger on all vehicle deletions?
|
||||||
LuaAPI::MP::Engine->ReportErrors(LuaAPI::MP::Engine->TriggerEvent("onVehicleDeleted", "", c.GetID(), VID));
|
LuaAPI::MP::Engine->ReportErrors(LuaAPI::MP::Engine->TriggerEvent("onVehicleDeleted", "", c.GetID(), VID));
|
||||||
c.DeleteCar(VID);
|
c.DeleteCar(VID);
|
||||||
@ -347,16 +337,16 @@ void TServer::ParseVehicle(TClient& c, const std::string& Pckt, TNetwork& Networ
|
|||||||
if (PID != -1 && VID != -1 && PID == c.GetID()) {
|
if (PID != -1 && VID != -1 && PID == c.GetID()) {
|
||||||
Data = Data.substr(Data.find('{'));
|
Data = Data.substr(Data.find('{'));
|
||||||
LuaAPI::MP::Engine->ReportErrors(LuaAPI::MP::Engine->TriggerEvent("onVehicleReset", "", c.GetID(), VID, Data));
|
LuaAPI::MP::Engine->ReportErrors(LuaAPI::MP::Engine->TriggerEvent("onVehicleReset", "", c.GetID(), VID, Data));
|
||||||
Network.SendToAll(&c, Packet, false, true);
|
Network.SendToAll(&c, StringToVector(Packet), false, true);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
case 't':
|
case 't':
|
||||||
beammp_trace(std::string(("got 'Ot' packet: '")) + Packet + ("' (") + std::to_string(Packet.size()) + (")"));
|
beammp_trace(std::string(("got 'Ot' packet: '")) + Packet + ("' (") + std::to_string(Packet.size()) + (")"));
|
||||||
Network.SendToAll(&c, Packet, false, true);
|
Network.SendToAll(&c, StringToVector(Packet), false, true);
|
||||||
return;
|
return;
|
||||||
case 'm':
|
case 'm':
|
||||||
Network.SendToAll(&c, Packet, true, true);
|
Network.SendToAll(&c, StringToVector(Packet), true, true);
|
||||||
return;
|
return;
|
||||||
default:
|
default:
|
||||||
beammp_trace(std::string(("possibly not implemented: '") + Packet + ("' (") + std::to_string(Packet.size()) + (")")));
|
beammp_trace(std::string(("possibly not implemented: '") + Packet + ("' (") + std::to_string(Packet.size()) + (")")));
|
||||||
|
Loading…
x
Reference in New Issue
Block a user