mirror of
https://github.com/BeamMP/BeamMP-Server.git
synced 2025-07-01 23:35:41 +00:00
finish rewrite, builds fully
This commit is contained in:
parent
bf74b1ae32
commit
d360403c56
@ -44,7 +44,9 @@ add_executable(BeamMP-Server
|
||||
include/THeartbeatThread.h src/THeartbeatThread.cpp
|
||||
include/Http.h src/Http.cpp
|
||||
include/SocketIO.h src/SocketIO.cpp
|
||||
include/TPPSMonitor.h src/TPPSMonitor.cpp include/TUDPServer.h src/TUDPServer.cpp include/TTCPServer.h src/TTCPServer.cpp)
|
||||
include/TPPSMonitor.h src/TPPSMonitor.cpp
|
||||
include/TUDPServer.h src/TUDPServer.cpp
|
||||
include/TTCPServer.h src/TTCPServer.cpp)
|
||||
|
||||
target_include_directories(BeamMP-Server PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" "${CMAKE_CURRENT_SOURCE_DIR}/commandline")
|
||||
|
||||
@ -54,11 +56,11 @@ target_include_directories(BeamMP-Server PUBLIC ${Boost_INCLUDE_DIRS} ${LUA_INCL
|
||||
find_package(OpenSSL REQUIRED)
|
||||
|
||||
if (UNIX)
|
||||
target_link_libraries(BeamMP-Server z pthread stdc++fs ${Boost_LINK_DIRS} ${LUA_LIBRARIES} dl crypto ${OPENSSL_LIBRARIES} ${Boost_LIBRARIES} commandline)
|
||||
target_link_libraries(BeamMP-Server z pthread stdc++fs ${Boost_LINK_DIRS} ${LUA_LIBRARIES} dl crypto ${OPENSSL_LIBRARIES} ${Boost_LIBRARIES} commandline sioclient)
|
||||
elseif (WIN32)
|
||||
include(FindLua)
|
||||
find_package(ZLIB REQUIRED)
|
||||
find_package(RapidJSON CONFIG REQUIRED)
|
||||
target_include_directories(BeamMP-Server PRIVATE ${RAPIDJSON_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS})
|
||||
target_link_libraries(BeamMP-Server PRIVATE ws2_32 ZLIB::ZLIB ${LUA_LIBRARIES} ${Boost_LIBRARIES} ${OPENSSL_LIBRARIES} commandline)
|
||||
target_link_libraries(BeamMP-Server PRIVATE ws2_32 ZLIB::ZLIB ${LUA_LIBRARIES} ${Boost_LIBRARIES} ${OPENSSL_LIBRARIES} commandline sioclient)
|
||||
endif ()
|
||||
|
@ -41,6 +41,9 @@ public:
|
||||
bool IsConnected() const { return mIsConnected; }
|
||||
bool IsSynced() const { return mIsSynced; }
|
||||
bool IsGuest() const { return mIsGuest; }
|
||||
void SetIsGuest(bool NewIsGuest) { mIsGuest = NewIsGuest; }
|
||||
void SetIsSynced(bool NewIsSynced) { mIsSynced = NewIsSynced; }
|
||||
void SetIsConnected(bool NewIsConnected) { mIsConnected = NewIsConnected; }
|
||||
TServer& Server() const;
|
||||
|
||||
private:
|
||||
|
@ -4,7 +4,7 @@
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
#include <deque>
|
||||
|
||||
#include "TConsole.h"
|
||||
|
||||
@ -56,7 +56,7 @@ private:
|
||||
static inline std::string mPPS;
|
||||
static std::unique_ptr<TConsole> mConsole;
|
||||
static inline std::mutex mShutdownHandlersMutex {};
|
||||
static inline std::vector<TShutdownHandler> mShutdownHandlers {};
|
||||
static inline std::deque<TShutdownHandler> mShutdownHandlers {};
|
||||
};
|
||||
|
||||
static inline void warn(const std::string& str) {
|
||||
|
@ -11,7 +11,7 @@
|
||||
|
||||
class TLuaEngine : public IThreaded {
|
||||
public:
|
||||
explicit TLuaEngine(TServer& Server);
|
||||
explicit TLuaEngine(TServer& Server, TTCPServer& TCPServer, TUDPServer& UDPServer);
|
||||
|
||||
using TSetOfLuaFile = std::set<std::unique_ptr<TLuaFile>>;
|
||||
|
||||
@ -23,11 +23,16 @@ public:
|
||||
|
||||
std::optional<std::reference_wrapper<TLuaFile>> GetScript(lua_State* L);
|
||||
|
||||
TTCPServer& TCPServer() { return mTCPServer; }
|
||||
TUDPServer& UDPServer() { return mUDPServer; }
|
||||
|
||||
private:
|
||||
void FolderList(const std::string& Path, bool HotSwap);
|
||||
void RegisterFiles(const std::string& Path, bool HotSwap);
|
||||
bool NewFile(const std::string& Path);
|
||||
|
||||
TTCPServer& mTCPServer;
|
||||
TUDPServer& mUDPServer;
|
||||
TServer& mServer;
|
||||
TSetOfLuaFile mLuaFiles;
|
||||
};
|
||||
|
@ -56,4 +56,6 @@ private:
|
||||
bool mConsole = false;
|
||||
};
|
||||
|
||||
std::any TriggerLuaEvent(const std::string& Event, bool local, TLuaFile* Caller, std::shared_ptr<TLuaArg> arg, bool Wait);
|
||||
|
||||
#endif // TLUAFILE_H
|
||||
|
@ -1,12 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include "Common.h"
|
||||
#include "IThreaded.h"
|
||||
#include "TServer.h"
|
||||
|
||||
class TPPSMonitor : public IThreaded {
|
||||
public:
|
||||
TPPSMonitor(TServer& Server);
|
||||
explicit TPPSMonitor(TServer& Server);
|
||||
|
||||
void operator()() override;
|
||||
|
||||
|
@ -1,13 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include "IThreaded.h"
|
||||
#include "RWMutex.h"
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <unordered_set>
|
||||
|
||||
#include "RWMutex.h"
|
||||
|
||||
class TClient;
|
||||
class TUDPServer;
|
||||
class TTCPServer;
|
||||
class TPPSMonitor;
|
||||
|
||||
class TServer final {
|
||||
public:
|
||||
@ -17,10 +20,16 @@ public:
|
||||
|
||||
std::weak_ptr<TClient> InsertNewClient();
|
||||
void RemoveClient(std::weak_ptr<TClient>);
|
||||
// in Fn, return true to continue, return false to break
|
||||
void ForEachClient(const std::function<bool(std::weak_ptr<TClient>)>& Fn);
|
||||
size_t ClientCount() const;
|
||||
|
||||
static void GlobalParser(std::weak_ptr<TClient> Client, std::string Packet, TPPSMonitor& PPSMonitor, TUDPServer& UDPServer, TTCPServer& TCPServer);
|
||||
static void HandleEvent(TClient& c, const std::string& Data);
|
||||
|
||||
private:
|
||||
TClientSet mClients;
|
||||
mutable RWMutex mClientsMutex;
|
||||
static void ParseVehicle(TClient& c, const std::string& Pckt, TTCPServer& TCPServer, TUDPServer& UDPServer);
|
||||
static void Apply(TClient& c, int VID, const std::string& pckt);
|
||||
};
|
||||
|
@ -1,14 +1,51 @@
|
||||
#pragma once
|
||||
|
||||
#include "Client.h"
|
||||
#include "Common.h"
|
||||
#include "Compat.h"
|
||||
#include "IThreaded.h"
|
||||
#include "TServer.h"
|
||||
|
||||
class TResourceManager;
|
||||
|
||||
class TTCPServer : public IThreaded {
|
||||
public:
|
||||
explicit TTCPServer(TServer& Server);
|
||||
explicit TTCPServer(TServer& Server, TPPSMonitor& PPSMonitor, TResourceManager& ResourceManager);
|
||||
|
||||
void operator()() override;
|
||||
|
||||
bool TCPSend(TClient& c, const std::string& Data);
|
||||
void SendLarge(TClient& c, std::string Data);
|
||||
void Respond(TClient& c, const std::string& MSG, bool Rel);
|
||||
std::weak_ptr<TClient> CreateClient(SOCKET TCPSock);
|
||||
std::string TCPRcv(TClient& c);
|
||||
void ClientKick(TClient& c, const std::string& R);
|
||||
|
||||
void SetUDPServer(TUDPServer& UDPServer);
|
||||
|
||||
TUDPServer& UDPServer() { return mUDPServer->get(); }
|
||||
|
||||
void SyncClient(std::weak_ptr<TClient> c);
|
||||
void Identify(SOCKET TCPSock);
|
||||
void Authentication(SOCKET TCPSock);
|
||||
bool CheckBytes(TClient& c, int32_t BytesRcv);
|
||||
void SyncResources(TClient& c);
|
||||
|
||||
void UpdatePlayers();
|
||||
private:
|
||||
std::optional<std::reference_wrapper<TUDPServer>> mUDPServer { std::nullopt };
|
||||
TServer& mServer;
|
||||
TPPSMonitor& mPPSMonitor;
|
||||
TResourceManager& mResourceManager;
|
||||
bool mShutdown { false };
|
||||
|
||||
void HandleDownload(SOCKET TCPSock);
|
||||
void OnConnect(std::weak_ptr<TClient> c);
|
||||
void TCPClient(std::weak_ptr<TClient> c);
|
||||
int OpenID();
|
||||
void OnDisconnect(std::weak_ptr<TClient> ClientPtr, bool kicked);
|
||||
void Parse(TClient& c, const std::string& Packet);
|
||||
void SendFile(TClient& c, const std::string& Name);
|
||||
static bool TCPSendRaw(SOCKET C, char* Data, int32_t Size);
|
||||
static void SplitLoad(TClient& c, int64_t Sent, int64_t Size, bool D, const std::string& Name);
|
||||
};
|
@ -9,7 +9,7 @@
|
||||
|
||||
class TUDPServer : public IThreaded {
|
||||
public:
|
||||
explicit TUDPServer(TServer& Server, TPPSMonitor& PPSMonitor);
|
||||
explicit TUDPServer(TServer& Server, TPPSMonitor& PPSMonitor, TTCPServer& TCPServer);
|
||||
|
||||
void operator()() override;
|
||||
|
||||
@ -17,10 +17,10 @@ public:
|
||||
void SendToAll(TClient* c, const std::string& Data, bool Self, bool Rel);
|
||||
|
||||
private:
|
||||
void UDPParser(TClient& Client, std::string Packet);
|
||||
|
||||
TServer& mServer;
|
||||
TPPSMonitor& mPPSMonitor;
|
||||
SOCKET mUDPSock;
|
||||
TTCPServer& mTCPServer;
|
||||
SOCKET mUDPSock {};
|
||||
|
||||
std::string UDPRcvFromClient(sockaddr_in& client) const;
|
||||
};
|
@ -11,7 +11,7 @@ std::unique_ptr<TConsole> Application::mConsole = std::make_unique<TConsole>();
|
||||
void Application::RegisterShutdownHandler(const TShutdownHandler& Handler) {
|
||||
std::unique_lock Lock(mShutdownHandlersMutex);
|
||||
if (Handler) {
|
||||
mShutdownHandlers.push_back(Handler);
|
||||
mShutdownHandlers.push_front(Handler);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5,8 +5,10 @@
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
TLuaEngine::TLuaEngine(TServer& Server)
|
||||
: mServer(Server) {
|
||||
TLuaEngine::TLuaEngine(TServer& Server, TTCPServer& TCPServer, TUDPServer& UDPServer)
|
||||
: mTCPServer(TCPServer)
|
||||
, mUDPServer(UDPServer)
|
||||
, mServer(Server) {
|
||||
if (!fs::exists(Application::Settings.ResourceFolder)) {
|
||||
fs::create_directory(Application::Settings.ResourceFolder);
|
||||
}
|
||||
|
@ -4,6 +4,8 @@
|
||||
#include "CustomAssert.h"
|
||||
#include "TLuaEngine.h"
|
||||
#include "TServer.h"
|
||||
#include "TTCPServer.h"
|
||||
#include "TUDPServer.h"
|
||||
|
||||
#include <future>
|
||||
#include <thread>
|
||||
@ -68,11 +70,11 @@ std::any FutureWait(TLuaFile* lua, const std::string& R, std::shared_ptr<TLuaArg
|
||||
return f1.get();
|
||||
return 0;
|
||||
}
|
||||
std::any TriggerLuaEvent(TLuaEngine& Engine, const std::string& Event, bool local, TLuaFile* Caller, std::shared_ptr<TLuaArg> arg, bool Wait) {
|
||||
std::any TriggerLuaEvent(const std::string& Event, bool local, TLuaFile* Caller, std::shared_ptr<TLuaArg> arg, bool Wait) {
|
||||
std::any R;
|
||||
std::string Type;
|
||||
int Ret = 0;
|
||||
for (auto& Script : Engine.LuaFiles()) {
|
||||
for (auto& Script : Engine().LuaFiles()) {
|
||||
if (Script->IsRegistered(Event)) {
|
||||
if (local) {
|
||||
if (Script->GetPluginName() == Caller->GetPluginName()) {
|
||||
@ -139,7 +141,7 @@ int lua_TriggerEventL(lua_State* L) {
|
||||
TLuaFile& Script = MaybeScript.value();
|
||||
if (Args > 0) {
|
||||
if (lua_isstring(L, 1)) {
|
||||
TriggerLuaEvent(Engine(), lua_tostring(L, 1), true, &Script, CreateArg(L, Args, 2), false);
|
||||
TriggerLuaEvent(lua_tostring(L, 1), true, &Script, CreateArg(L, Args, 2), false);
|
||||
} else
|
||||
SendError(Engine(), L, ("TriggerLocalEvent wrong argument [1] need string"));
|
||||
} else {
|
||||
@ -155,7 +157,7 @@ int lua_TriggerEventG(lua_State* L) {
|
||||
TLuaFile& Script = MaybeScript.value();
|
||||
if (Args > 0) {
|
||||
if (lua_isstring(L, 1)) {
|
||||
TriggerLuaEvent(Engine(), lua_tostring(L, 1), false, &Script, CreateArg(L, Args, 2), false);
|
||||
TriggerLuaEvent(lua_tostring(L, 1), false, &Script, CreateArg(L, Args, 2), false);
|
||||
} else
|
||||
SendError(Engine(), L, ("TriggerGlobalEvent wrong argument [1] need string"));
|
||||
} else
|
||||
@ -356,7 +358,7 @@ int lua_dropPlayer(lua_State* L) {
|
||||
Reason = std::string((" Reason : ")) + lua_tostring(L, 2);
|
||||
}
|
||||
auto c = MaybeClient.value().lock();
|
||||
Respond(c, "C:Server:You have been Kicked from the server! " + Reason, true);
|
||||
Engine().TCPServer().Respond(*c, "C:Server:You have been Kicked from the server! " + Reason, true);
|
||||
c->SetStatus(-2);
|
||||
info(("Closing socket due to kick"));
|
||||
CloseSocketProper(c->GetTCPSock());
|
||||
@ -370,7 +372,7 @@ int lua_sendChat(lua_State* L) {
|
||||
int ID = int(lua_tointeger(L, 1));
|
||||
if (ID == -1) {
|
||||
std::string Packet = "C:Server: " + std::string(lua_tostring(L, 2));
|
||||
SendToAll(nullptr, Packet, true, true);
|
||||
Engine().UDPServer().SendToAll(nullptr, Packet, true, true);
|
||||
} else {
|
||||
auto MaybeClient = GetClient(Engine().Server(), ID);
|
||||
if (MaybeClient && !MaybeClient.value().expired()) {
|
||||
@ -378,7 +380,7 @@ int lua_sendChat(lua_State* L) {
|
||||
if (!c->IsSynced())
|
||||
return 0;
|
||||
std::string Packet = "C:Server: " + std::string(lua_tostring(L, 2));
|
||||
Respond(c, Packet, true);
|
||||
Engine().TCPServer().Respond(*c, Packet, true);
|
||||
} else
|
||||
SendError(Engine(), L, ("SendChatMessage invalid argument [1] invalid ID"));
|
||||
}
|
||||
@ -405,7 +407,7 @@ int lua_RemoveVehicle(lua_State* L) {
|
||||
auto c = MaybeClient.value().lock();
|
||||
if (!c->GetCarData(VID).empty()) {
|
||||
std::string Destroy = "Od:" + std::to_string(PID) + "-" + std::to_string(VID);
|
||||
SendToAll(nullptr, Destroy, true, true);
|
||||
Engine().UDPServer().SendToAll(nullptr, Destroy, true, true);
|
||||
c->DeleteCar(VID);
|
||||
}
|
||||
} else
|
||||
@ -437,7 +439,7 @@ int lua_RemoteEvent(lua_State* L) {
|
||||
int ID = int(lua_tointeger(L, 1));
|
||||
std::string Packet = "E:" + std::string(lua_tostring(L, 2)) + ":" + std::string(lua_tostring(L, 3));
|
||||
if (ID == -1)
|
||||
SendToAll(nullptr, Packet, true, true);
|
||||
Engine().UDPServer().SendToAll(nullptr, Packet, true, true);
|
||||
else {
|
||||
auto MaybeClient = GetClient(Engine().Server(), ID);
|
||||
if (!MaybeClient || MaybeClient.value().expired()) {
|
||||
@ -445,7 +447,7 @@ int lua_RemoteEvent(lua_State* L) {
|
||||
return 0;
|
||||
}
|
||||
auto c = MaybeClient.value().lock();
|
||||
Respond(c, Packet, true);
|
||||
Engine().TCPServer().Respond(*c, Packet, true);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
222
src/TServer.cpp
222
src/TServer.cpp
@ -1,6 +1,17 @@
|
||||
#include "TServer.h"
|
||||
#include "Client.h"
|
||||
#include "Common.h"
|
||||
#include "TPPSMonitor.h"
|
||||
#include "TTCPServer.h"
|
||||
#include "TUDPServer.h"
|
||||
#include <TLuaFile.h>
|
||||
#include <any>
|
||||
#include <sstream>
|
||||
|
||||
#include "rapidjson/document.h"
|
||||
#include "rapidjson/stringbuffer.h"
|
||||
#include "rapidjson/writer.h"
|
||||
namespace json = rapidjson;
|
||||
|
||||
TServer::TServer(int argc, char** argv) {
|
||||
info("BeamMP Server running version " + Application::ServerVersion());
|
||||
@ -47,3 +58,214 @@ size_t TServer::ClientCount() const {
|
||||
ReadLock Lock(mClientsMutex);
|
||||
return mClients.size();
|
||||
}
|
||||
|
||||
void TServer::GlobalParser(std::weak_ptr<TClient> Client, std::string Packet, TPPSMonitor& PPSMonitor, TUDPServer& UDPServer, TTCPServer& TCPServer) {
|
||||
if (Packet.find("Zp") != std::string::npos && Packet.size() > 500) {
|
||||
abort();
|
||||
}
|
||||
if (Packet.substr(0, 4) == "ABG:") {
|
||||
Packet = DeComp(Packet.substr(4));
|
||||
}
|
||||
if (Packet.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (Client.expired()) {
|
||||
return;
|
||||
}
|
||||
auto LockedClient = Client.lock();
|
||||
|
||||
std::any Res;
|
||||
char Code = Packet.at(0);
|
||||
|
||||
//V to Z
|
||||
if (Code <= 90 && Code >= 86) {
|
||||
PPSMonitor.IncrementInternalPPS();
|
||||
UDPServer.SendToAll(LockedClient.get(), Packet, false, false);
|
||||
return;
|
||||
}
|
||||
switch (Code) {
|
||||
case 'H': // initial connection
|
||||
#ifdef DEBUG
|
||||
debug(std::string("got 'H' packet: '") + Packet + "' (" + std::to_string(Packet.size()) + ")");
|
||||
#endif
|
||||
TCPServer.SyncClient(Client);
|
||||
return;
|
||||
case 'p':
|
||||
TCPServer.Respond(*LockedClient, ("p"), false);
|
||||
TCPServer.UpdatePlayers();
|
||||
return;
|
||||
case 'O':
|
||||
if (Packet.length() > 1000) {
|
||||
debug(("Received data from: ") + LockedClient->GetName() + (" Size: ") + std::to_string(Packet.length()));
|
||||
}
|
||||
ParseVehicle(*LockedClient, Packet, TCPServer, UDPServer);
|
||||
return;
|
||||
case 'J':
|
||||
#ifdef DEBUG
|
||||
debug(std::string(("got 'J' packet: '")) + Packet + ("' (") + std::to_string(Packet.size()) + (")"));
|
||||
#endif
|
||||
UDPServer.SendToAll(LockedClient.get(), Packet, false, true);
|
||||
return;
|
||||
case 'C':
|
||||
#ifdef DEBUG
|
||||
debug(std::string(("got 'C' packet: '")) + Packet + ("' (") + std::to_string(Packet.size()) + (")"));
|
||||
#endif
|
||||
if (Packet.length() < 4 || Packet.find(':', 3) == std::string::npos)
|
||||
break;
|
||||
Res = TriggerLuaEvent("onChatMessage", false, nullptr, std::make_unique<TLuaArg>(TLuaArg { { LockedClient->GetID(), LockedClient->GetName(), Packet.substr(Packet.find(':', 3) + 1) } }), true);
|
||||
if (std::any_cast<int>(Res))
|
||||
break;
|
||||
UDPServer.SendToAll(nullptr, Packet, true, true);
|
||||
return;
|
||||
case 'E':
|
||||
#ifdef DEBUG
|
||||
debug(std::string(("got 'E' packet: '")) + Packet + ("' (") + std::to_string(Packet.size()) + (")"));
|
||||
#endif
|
||||
HandleEvent(*LockedClient, Packet);
|
||||
return;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void TServer::HandleEvent(TClient& c, const std::string& Data) {
|
||||
std::stringstream ss(Data);
|
||||
std::string t, Name;
|
||||
int a = 0;
|
||||
while (std::getline(ss, t, ':')) {
|
||||
switch (a) {
|
||||
case 1:
|
||||
Name = t;
|
||||
break;
|
||||
case 2:
|
||||
TriggerLuaEvent(Name, false, nullptr, std::make_unique<TLuaArg>(TLuaArg { { c.GetID(), t } }), false);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (a == 2)
|
||||
break;
|
||||
a++;
|
||||
}
|
||||
}
|
||||
|
||||
void TServer::ParseVehicle(TClient& c, const std::string& Pckt, TTCPServer& TCPServer, TUDPServer& UDPServer) {
|
||||
if (Pckt.length() < 4)
|
||||
return;
|
||||
std::string Packet = Pckt;
|
||||
char Code = Packet.at(1);
|
||||
int PID = -1;
|
||||
int VID = -1;
|
||||
std::string Data = Packet.substr(3), pid, vid;
|
||||
switch (Code) { //Spawned Destroyed Switched/Moved NotFound Reset
|
||||
case 's':
|
||||
#ifdef DEBUG
|
||||
debug(std::string(("got 'Os' packet: '")) + Packet + ("' (") + std::to_string(Packet.size()) + (")"));
|
||||
#endif
|
||||
if (Data.at(0) == '0') {
|
||||
int CarID = c.GetOpenCarID();
|
||||
debug(c.GetName() + (" created a car with ID ") + std::to_string(CarID));
|
||||
Packet = "Os:" + c.GetRoles() + ":" + c.GetName() + ":" + std::to_string(c.GetID()) + "-" + std::to_string(CarID) + Packet.substr(4);
|
||||
auto Res = TriggerLuaEvent(("onVehicleSpawn"), false, nullptr, std::make_unique<TLuaArg>(TLuaArg { { c.GetID(), CarID, Packet.substr(3) } }), true);
|
||||
if (c.GetCarCount() >= Application::Settings.MaxCars || std::any_cast<int>(Res)) {
|
||||
TCPServer.Respond(c, Packet, true);
|
||||
std::string Destroy = "Od:" + std::to_string(c.GetID()) + "-" + std::to_string(CarID);
|
||||
TCPServer.Respond(c, Destroy, true);
|
||||
debug(c.GetName() + (" (force : car limit/lua) removed ID ") + std::to_string(CarID));
|
||||
} else {
|
||||
c.AddNewCar(CarID, Packet);
|
||||
UDPServer.SendToAll(nullptr, Packet, true, true);
|
||||
}
|
||||
}
|
||||
return;
|
||||
case 'c':
|
||||
#ifdef DEBUG
|
||||
debug(std::string(("got 'Oc' packet: '")) + Packet + ("' (") + std::to_string(Packet.size()) + (")"));
|
||||
#endif
|
||||
pid = Data.substr(0, Data.find('-'));
|
||||
vid = Data.substr(Data.find('-') + 1, Data.find(':', 1) - Data.find('-') - 1);
|
||||
if (pid.find_first_not_of("0123456789") == std::string::npos && vid.find_first_not_of("0123456789") == std::string::npos) {
|
||||
PID = stoi(pid);
|
||||
VID = stoi(vid);
|
||||
}
|
||||
if (PID != -1 && VID != -1 && PID == c.GetID()) {
|
||||
auto Res = TriggerLuaEvent(("onVehicleEdited"), false, nullptr,
|
||||
std::make_unique<TLuaArg>(TLuaArg { { c.GetID(), VID, Packet.substr(3) } }),
|
||||
true);
|
||||
if (!std::any_cast<int>(Res)) {
|
||||
UDPServer.SendToAll(&c, Packet, false, true);
|
||||
Apply(c, VID, Packet);
|
||||
} else {
|
||||
std::string Destroy = "Od:" + std::to_string(c.GetID()) + "-" + std::to_string(VID);
|
||||
TCPServer.Respond(c, Destroy, true);
|
||||
c.DeleteCar(VID);
|
||||
}
|
||||
}
|
||||
return;
|
||||
case 'd':
|
||||
#ifdef DEBUG
|
||||
debug(std::string(("got 'Od' packet: '")) + Packet + ("' (") + std::to_string(Packet.size()) + (")"));
|
||||
#endif
|
||||
pid = Data.substr(0, Data.find('-'));
|
||||
vid = Data.substr(Data.find('-') + 1);
|
||||
if (pid.find_first_not_of("0123456789") == std::string::npos && vid.find_first_not_of("0123456789") == std::string::npos) {
|
||||
PID = stoi(pid);
|
||||
VID = stoi(vid);
|
||||
}
|
||||
if (PID != -1 && VID != -1 && PID == c.GetID()) {
|
||||
UDPServer.SendToAll(nullptr, Packet, true, true);
|
||||
TriggerLuaEvent(("onVehicleDeleted"), false, nullptr,
|
||||
std::make_unique<TLuaArg>(TLuaArg { { c.GetID(), VID } }), false);
|
||||
c.DeleteCar(VID);
|
||||
debug(c.GetName() + (" deleted car with ID ") + std::to_string(VID));
|
||||
}
|
||||
return;
|
||||
case 'r':
|
||||
#ifdef DEBUG
|
||||
debug(std::string(("got 'Or' packet: '")) + Packet + ("' (") + std::to_string(Packet.size()) + (")"));
|
||||
#endif
|
||||
UDPServer.SendToAll(&c, Packet, false, true);
|
||||
return;
|
||||
case 't':
|
||||
#ifdef DEBUG
|
||||
debug(std::string(("got 'Ot' packet: '")) + Packet + ("' (") + std::to_string(Packet.size()) + (")"));
|
||||
#endif
|
||||
UDPServer.SendToAll(&c, Packet, false, true);
|
||||
return;
|
||||
default:
|
||||
#ifdef DEBUG
|
||||
warn(std::string(("possibly not implemented: '") + Packet + ("' (") + std::to_string(Packet.size()) + (")")));
|
||||
#endif // DEBUG
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void TServer::Apply(TClient& c, int VID, const std::string& pckt) {
|
||||
std::string Packet = pckt.substr(pckt.find('{')), VD = c.GetCarData(VID);
|
||||
std::string Header = VD.substr(0, VD.find('{'));
|
||||
VD = VD.substr(VD.find('{'));
|
||||
rapidjson::Document Veh, Pack;
|
||||
Veh.Parse(VD.c_str());
|
||||
if (Veh.HasParseError()) {
|
||||
error("Could not get vehicle config!");
|
||||
return;
|
||||
}
|
||||
Pack.Parse(Packet.c_str());
|
||||
if (Pack.HasParseError() || Pack.IsNull()) {
|
||||
error("Could not get active vehicle config!");
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto& M : Pack.GetObject()) {
|
||||
if (Veh[M.name].IsNull()) {
|
||||
Veh.AddMember(M.name, M.value, Veh.GetAllocator());
|
||||
} else {
|
||||
Veh[M.name] = Pack[M.name];
|
||||
}
|
||||
}
|
||||
rapidjson::StringBuffer Buffer;
|
||||
rapidjson::Writer<rapidjson::StringBuffer> writer(Buffer);
|
||||
Veh.Accept(writer);
|
||||
c.SetCarData(VID, Header + Buffer.GetString());
|
||||
}
|
||||
|
@ -1,5 +1,644 @@
|
||||
#include "TTCPServer.h"
|
||||
#include "TLuaEngine.h"
|
||||
#include "TLuaFile.h"
|
||||
#include "TResourceManager.h"
|
||||
#include "TUDPServer.h"
|
||||
#include <CustomAssert.h>
|
||||
#include <Http.h>
|
||||
#include <cstring>
|
||||
|
||||
TTCPServer::TTCPServer(TServer& Server)
|
||||
: mServer(Server) {
|
||||
#include "rapidjson/document.h"
|
||||
#include "rapidjson/stringbuffer.h"
|
||||
#include "rapidjson/writer.h"
|
||||
namespace json = rapidjson;
|
||||
|
||||
bool TCPSend(std::weak_ptr<TClient> c, const std::string& Data);
|
||||
bool TCPSend(TClient& c, const std::string& Data);
|
||||
|
||||
TTCPServer::TTCPServer(TServer& Server, TPPSMonitor& PPSMonitor, TResourceManager& ResourceManager)
|
||||
: mServer(Server)
|
||||
, mPPSMonitor(PPSMonitor)
|
||||
, mResourceManager(ResourceManager) {
|
||||
Application::RegisterShutdownHandler([this] { mShutdown = true; });
|
||||
}
|
||||
|
||||
void TTCPServer::Identify(SOCKET TCPSock) {
|
||||
char Code;
|
||||
if (recv(TCPSock, &Code, 1, 0) != 1) {
|
||||
CloseSocketProper(TCPSock);
|
||||
return;
|
||||
}
|
||||
if (Code == 'C') {
|
||||
Authentication(TCPSock);
|
||||
} else if (Code == 'D') {
|
||||
HandleDownload(TCPSock);
|
||||
} else {
|
||||
CloseSocketProper(TCPSock);
|
||||
}
|
||||
}
|
||||
|
||||
void TTCPServer::HandleDownload(SOCKET TCPSock) {
|
||||
char D;
|
||||
if (recv(TCPSock, &D, 1, 0) != 1) {
|
||||
CloseSocketProper(TCPSock);
|
||||
return;
|
||||
}
|
||||
auto ID = uint8_t(D);
|
||||
mServer.ForEachClient([&](std::weak_ptr<TClient> ClientPtr) -> bool {
|
||||
if (!ClientPtr.expired()) {
|
||||
auto c = ClientPtr.lock();
|
||||
if (c->GetID() == ID) {
|
||||
c->SetDownSock(TCPSock);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
void TTCPServer::Authentication(SOCKET TCPSock) {
|
||||
auto c = CreateClient(TCPSock);
|
||||
|
||||
std::string Rc;
|
||||
info("Identifying new client...");
|
||||
|
||||
Assert(!c.expired());
|
||||
|
||||
auto LockedClient = c.lock();
|
||||
Rc = TCPRcv(*LockedClient);
|
||||
|
||||
if (Rc.size() > 3 && Rc.substr(0, 2) == "VC") {
|
||||
Rc = Rc.substr(2);
|
||||
if (Rc.length() > 4 || Rc != Application::ClientVersion()) {
|
||||
ClientKick(*LockedClient, "Outdated Version!");
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
ClientKick(*LockedClient, "Invalid version header!");
|
||||
return;
|
||||
}
|
||||
TCPSend(*LockedClient, "S");
|
||||
|
||||
Rc = TCPRcv(*LockedClient);
|
||||
|
||||
if (Rc.size() > 50) {
|
||||
ClientKick(*LockedClient, "Invalid Key!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Rc.empty()) {
|
||||
Rc = Http::POST("auth.beammp.com", "/pkToUser", {}, R"({"key":")" + Rc + "\"}", true);
|
||||
}
|
||||
|
||||
json::Document AuthResponse;
|
||||
AuthResponse.Parse(Rc.c_str());
|
||||
if (Rc == "-1" || AuthResponse.HasParseError()) {
|
||||
ClientKick(*LockedClient, "Invalid key! Please restart your game.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (AuthResponse["username"].IsString() && AuthResponse["roles"].IsString() && AuthResponse["guest"].IsBool()) {
|
||||
LockedClient->SetName(AuthResponse["username"].GetString());
|
||||
LockedClient->SetRoles(AuthResponse["roles"].GetString());
|
||||
LockedClient->SetIsGuest(AuthResponse["guest"].GetBool());
|
||||
} else {
|
||||
ClientKick(*LockedClient, "Invalid authentication data!");
|
||||
return;
|
||||
}
|
||||
|
||||
debug("Name -> " + LockedClient->GetName() + ", Guest -> " + std::to_string(LockedClient->IsGuest()) + ", Roles -> " + LockedClient->GetRoles());
|
||||
mServer.ForEachClient([&](std::weak_ptr<TClient> ClientPtr) -> bool {
|
||||
if (!ClientPtr.expired()) {
|
||||
auto Cl = ClientPtr.lock();
|
||||
if (Cl->GetName() == LockedClient->GetName() && Cl->IsGuest() == LockedClient->IsGuest()) {
|
||||
info("Old client (" + Cl->GetName() + ") kicked: Reconnecting");
|
||||
CloseSocketProper(Cl->GetTCPSock());
|
||||
Cl->SetStatus(-2);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
auto arg = std::make_unique<TLuaArg>(TLuaArg { { LockedClient->GetName(), LockedClient->GetRoles(), LockedClient->IsGuest() } });
|
||||
std::any Res = TriggerLuaEvent("onPlayerAuth", false, nullptr, std::move(arg), true);
|
||||
std::string Type = Res.type().name();
|
||||
if (Type.find("int") != std::string::npos && std::any_cast<int>(Res)) {
|
||||
ClientKick(*LockedClient, "you are not allowed on the server!");
|
||||
return;
|
||||
} else if (Type.find("string") != std::string::npos) {
|
||||
ClientKick(*LockedClient, std::any_cast<std::string>(Res));
|
||||
return;
|
||||
}
|
||||
|
||||
if (mServer.ClientCount() < size_t(Application::Settings.MaxPlayers)) {
|
||||
info("Identification success");
|
||||
TCPClient(c);
|
||||
} else
|
||||
ClientKick(*LockedClient, "Server full!");
|
||||
}
|
||||
|
||||
std::weak_ptr<TClient> TTCPServer::CreateClient(SOCKET TCPSock) {
|
||||
auto c = mServer.InsertNewClient();
|
||||
c.lock()->SetTCPSock(TCPSock);
|
||||
return c;
|
||||
}
|
||||
|
||||
bool TTCPServer::TCPSend(TClient& c, const std::string& Data) {
|
||||
int32_t Size, Sent;
|
||||
std::string Send(4, 0);
|
||||
Size = int32_t(Data.size());
|
||||
memcpy(&Send[0], &Size, sizeof(Size));
|
||||
Send += Data;
|
||||
Sent = 0;
|
||||
Size += 4;
|
||||
do {
|
||||
int32_t Temp = send(c.GetTCPSock(), &Send[Sent], Size - Sent, 0);
|
||||
if (Temp == 0) {
|
||||
if (c.GetStatus() > -1)
|
||||
c.SetStatus(-1);
|
||||
return false;
|
||||
} else if (Temp < 0) {
|
||||
if (c.GetStatus() > -1)
|
||||
c.SetStatus(-1);
|
||||
CloseSocketProper(c.GetTCPSock());
|
||||
return false;
|
||||
}
|
||||
Sent += Temp;
|
||||
} while (Sent < Size);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TTCPServer::CheckBytes(TClient& c, int32_t BytesRcv) {
|
||||
if (BytesRcv == 0) {
|
||||
debug("(TCP) Connection closing...");
|
||||
if (c.GetStatus() > -1)
|
||||
c.SetStatus(-1);
|
||||
return false;
|
||||
} else if (BytesRcv < 0) {
|
||||
#ifdef WIN32
|
||||
debug(("(TCP) recv failed with error: ") + std::to_string(WSAGetLastError()));
|
||||
#else // unix
|
||||
debug(("(TCP) recv failed with error: ") + std::string(strerror(errno)));
|
||||
#endif // WIN32
|
||||
if (c.GetStatus() > -1)
|
||||
c.SetStatus(-1);
|
||||
info(("Closing socket in CheckBytes, BytesRcv < 0"));
|
||||
CloseSocketProper(c.GetTCPSock());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string TTCPServer::TCPRcv(TClient& c) {
|
||||
int32_t Header, BytesRcv = 0, Temp;
|
||||
if (c.GetStatus() < 0)
|
||||
return "";
|
||||
|
||||
std::vector<char> Data(sizeof(Header));
|
||||
do {
|
||||
Temp = recv(c.GetTCPSock(), &Data[BytesRcv], 4 - BytesRcv, 0);
|
||||
if (!CheckBytes(c, Temp)) {
|
||||
#ifdef DEBUG
|
||||
error(std::string(__func__) + (": failed on CheckBytes in while(BytesRcv < 4)"));
|
||||
#endif // DEBUG
|
||||
return "";
|
||||
}
|
||||
BytesRcv += Temp;
|
||||
} while (size_t(BytesRcv) < sizeof(Header));
|
||||
memcpy(&Header, &Data[0], sizeof(Header));
|
||||
|
||||
#ifdef DEBUG
|
||||
//debug(std::string(__func__) + (": expecting ") + std::to_string(Header) + (" bytes."));
|
||||
#endif // DEBUG
|
||||
if (!CheckBytes(c, BytesRcv)) {
|
||||
#ifdef DEBUG
|
||||
error(std::string(__func__) + (": failed on CheckBytes"));
|
||||
#endif // DEBUG
|
||||
return "";
|
||||
}
|
||||
Data.resize(Header);
|
||||
BytesRcv = 0;
|
||||
do {
|
||||
Temp = recv(c.GetTCPSock(), &Data[BytesRcv], Header - BytesRcv, 0);
|
||||
if (!CheckBytes(c, Temp)) {
|
||||
#ifdef DEBUG
|
||||
error(std::string(__func__) + (": failed on CheckBytes in while(BytesRcv < Header)"));
|
||||
#endif // DEBUG
|
||||
|
||||
return "";
|
||||
}
|
||||
#ifdef DEBUG
|
||||
//debug(std::string(__func__) + (": Temp: ") + std::to_string(Temp) + (", BytesRcv: ") + std::to_string(BytesRcv));
|
||||
#endif // DEBUG
|
||||
BytesRcv += Temp;
|
||||
} while (BytesRcv < Header);
|
||||
#ifdef DEBUG
|
||||
//debug(std::string(__func__) + (": finished recv with Temp: ") + std::to_string(Temp) + (", BytesRcv: ") + std::to_string(BytesRcv));
|
||||
#endif // DEBUG
|
||||
std::string Ret(Data.data(), Header);
|
||||
|
||||
if (Ret.substr(0, 4) == "ABG:") {
|
||||
Ret = DeComp(Ret.substr(4));
|
||||
}
|
||||
#ifdef DEBUG
|
||||
//debug("Parsing from " + c->GetName() + " -> " +std::to_string(Ret.size()));
|
||||
#endif
|
||||
|
||||
return Ret;
|
||||
}
|
||||
|
||||
void TTCPServer::ClientKick(TClient& c, const std::string& R) {
|
||||
info("Client kicked: " + R);
|
||||
TCPSend(c, "E" + R);
|
||||
CloseSocketProper(c.GetTCPSock());
|
||||
}
|
||||
|
||||
void TTCPServer::TCPClient(std::weak_ptr<TClient> c) {
|
||||
// TODO: the c.expired() might cause issues here, remove if you end up here with your debugger
|
||||
if (c.expired() || c.lock()->GetTCPSock() == -1) {
|
||||
mServer.RemoveClient(c);
|
||||
return;
|
||||
}
|
||||
OnConnect(c);
|
||||
while (true) {
|
||||
if (c.expired())
|
||||
break;
|
||||
auto Client = c.lock();
|
||||
if (Client->GetStatus() <= -1) {
|
||||
break;
|
||||
}
|
||||
TServer::GlobalParser(c, TCPRcv(*Client), mPPSMonitor, UDPServer(), *this);
|
||||
}
|
||||
if (!c.expired()) {
|
||||
auto Client = c.lock();
|
||||
OnDisconnect(c, Client->GetStatus() == -2);
|
||||
} else {
|
||||
warn("client expired in TCPClient, should never happen");
|
||||
}
|
||||
}
|
||||
|
||||
void TTCPServer::UpdatePlayers() {
|
||||
std::string Packet = ("Ss") + std::to_string(mServer.ClientCount()) + "/" + std::to_string(Application::Settings.MaxPlayers) + ":";
|
||||
mServer.ForEachClient([&](std::weak_ptr<TClient> ClientPtr) -> bool {
|
||||
if (!ClientPtr.expired()) {
|
||||
auto c = ClientPtr.lock();
|
||||
Packet += c->GetName() + ",";
|
||||
}
|
||||
});
|
||||
Packet = Packet.substr(0, Packet.length() - 1);
|
||||
UDPServer().SendToAll(nullptr, Packet, true, true);
|
||||
}
|
||||
|
||||
void TTCPServer::OnDisconnect(std::weak_ptr<TClient> ClientPtr, bool kicked) {
|
||||
Assert(!ClientPtr.expired());
|
||||
auto LockedClientPtr = ClientPtr.lock();
|
||||
TClient& c = *LockedClientPtr;
|
||||
info(c.GetName() + (" Connection Terminated"));
|
||||
std::string Packet;
|
||||
for (auto& v : c.GetAllCars()) {
|
||||
if (v != nullptr) {
|
||||
Packet = "Od:" + std::to_string(c.GetID()) + "-" + std::to_string(v->ID());
|
||||
UDPServer().SendToAll(&c, Packet, false, true);
|
||||
}
|
||||
}
|
||||
if (kicked)
|
||||
Packet = ("L") + c.GetName() + (" was kicked!");
|
||||
else
|
||||
Packet = ("L") + c.GetName() + (" left the server!");
|
||||
UDPServer().SendToAll(&c, Packet, false, true);
|
||||
Packet.clear();
|
||||
TriggerLuaEvent(("onPlayerDisconnect"), false, nullptr, std::make_unique<TLuaArg>(TLuaArg { { c.GetID() } }), false);
|
||||
if (c.GetTCPSock())
|
||||
CloseSocketProper(c.GetTCPSock());
|
||||
if (c.GetDownSock())
|
||||
CloseSocketProper(c.GetDownSock());
|
||||
mServer.RemoveClient(ClientPtr);
|
||||
}
|
||||
|
||||
int TTCPServer::OpenID() {
|
||||
int ID = 0;
|
||||
bool found;
|
||||
do {
|
||||
found = true;
|
||||
mServer.ForEachClient([&](std::weak_ptr<TClient> ClientPtr) -> bool {
|
||||
if (!ClientPtr.expired()) {
|
||||
auto c = ClientPtr.lock();
|
||||
if (c->GetID() == ID) {
|
||||
found = false;
|
||||
ID++;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
} while (!found);
|
||||
return ID;
|
||||
}
|
||||
|
||||
void TTCPServer::OnConnect(std::weak_ptr<TClient> c) {
|
||||
Assert(!c.expired());
|
||||
info("Client connected");
|
||||
auto LockedClient = c.lock();
|
||||
LockedClient->SetID(OpenID());
|
||||
info("Assigned ID " + std::to_string(LockedClient->GetID()) + " to " + LockedClient->GetName());
|
||||
TriggerLuaEvent("onPlayerConnecting", false, nullptr, std::make_unique<TLuaArg>(TLuaArg { { LockedClient->GetID() } }), false);
|
||||
SyncResources(*LockedClient);
|
||||
if (LockedClient->GetStatus() < 0)
|
||||
return;
|
||||
Respond(*LockedClient, "M" + Application::Settings.MapName, true); //Send the Map on connect
|
||||
info(LockedClient->GetName() + " : Connected");
|
||||
TriggerLuaEvent("onPlayerJoining", false, nullptr, std::make_unique<TLuaArg>(TLuaArg { { LockedClient->GetID() } }), false);
|
||||
}
|
||||
|
||||
void TTCPServer::SyncResources(TClient& c) {
|
||||
#ifndef DEBUG
|
||||
try {
|
||||
#endif
|
||||
TCPSend(c, "P" + std::to_string(c.GetID()));
|
||||
std::string Data;
|
||||
while (c.GetStatus() > -1) {
|
||||
Data = TCPRcv(c);
|
||||
if (Data == "Done")
|
||||
break;
|
||||
Parse(c, Data);
|
||||
}
|
||||
#ifndef DEBUG
|
||||
} catch (std::exception& e) {
|
||||
except("Exception! : " + std::string(e.what()));
|
||||
c->SetStatus(-1);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void TTCPServer::Parse(TClient& c, const std::string& Packet) {
|
||||
if (Packet.empty())
|
||||
return;
|
||||
char Code = Packet.at(0), SubCode = 0;
|
||||
if (Packet.length() > 1)
|
||||
SubCode = Packet.at(1);
|
||||
switch (Code) {
|
||||
case 'f':
|
||||
SendFile(c, Packet.substr(1));
|
||||
return;
|
||||
case 'S':
|
||||
if (SubCode == 'R') {
|
||||
debug("Sending Mod Info");
|
||||
std::string ToSend = mResourceManager.FileList() + mResourceManager.FileSizes();
|
||||
if (ToSend.empty())
|
||||
ToSend = "-";
|
||||
TCPSend(c, ToSend);
|
||||
}
|
||||
return;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void TTCPServer::SendFile(TClient& c, const std::string& Name) {
|
||||
info(c.GetName() + " requesting : " + Name.substr(Name.find_last_of('/')));
|
||||
|
||||
if (!std::filesystem::exists(Name)) {
|
||||
TCPSend(c, "CO");
|
||||
warn("File " + Name + " could not be accessed!");
|
||||
return;
|
||||
} else
|
||||
TCPSend(c, "AG");
|
||||
|
||||
///Wait for connections
|
||||
int T = 0;
|
||||
while (c.GetDownSock() < 1 && T < 50) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
T++;
|
||||
}
|
||||
|
||||
if (c.GetDownSock() < 1) {
|
||||
error("Client doesn't have a download socket!");
|
||||
if (c.GetStatus() > -1)
|
||||
c.SetStatus(-1);
|
||||
return;
|
||||
}
|
||||
|
||||
int64_t Size = std::filesystem::file_size(Name), MSize = Size / 2;
|
||||
|
||||
std::thread SplitThreads[2] {
|
||||
std::thread([&] {
|
||||
SplitLoad(c, 0, MSize, false, Name);
|
||||
}),
|
||||
std::thread([&] {
|
||||
SplitLoad(c, MSize, Size, true, Name);
|
||||
})
|
||||
};
|
||||
|
||||
for (auto& SplitThread : SplitThreads) {
|
||||
if (SplitThread.joinable()) {
|
||||
SplitThread.join();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TTCPServer::SplitLoad(TClient& c, int64_t Sent, int64_t Size, bool D, const std::string& Name) {
|
||||
std::ifstream f(Name.c_str(), std::ios::binary);
|
||||
int32_t Split = 0x7735940; //125MB
|
||||
char* Data;
|
||||
if (Size > Split)
|
||||
Data = new char[Split];
|
||||
else
|
||||
Data = new char[Size];
|
||||
SOCKET TCPSock;
|
||||
if (D)
|
||||
TCPSock = c.GetDownSock();
|
||||
else
|
||||
TCPSock = c.GetTCPSock();
|
||||
info("Split load Socket " + std::to_string(TCPSock));
|
||||
while (c.GetStatus() > -1 && Sent < Size) {
|
||||
int64_t Diff = Size - Sent;
|
||||
if (Diff > Split) {
|
||||
f.seekg(Sent, std::ios_base::beg);
|
||||
f.read(Data, Split);
|
||||
if (!TCPSendRaw(TCPSock, Data, Split)) {
|
||||
if (c.GetStatus() > -1)
|
||||
c.SetStatus(-1);
|
||||
break;
|
||||
}
|
||||
Sent += Split;
|
||||
} else {
|
||||
f.seekg(Sent, std::ios_base::beg);
|
||||
f.read(Data, Diff);
|
||||
if (!TCPSendRaw(TCPSock, Data, int32_t(Diff))) {
|
||||
if (c.GetStatus() > -1)
|
||||
c.SetStatus(-1);
|
||||
break;
|
||||
}
|
||||
Sent += Diff;
|
||||
}
|
||||
}
|
||||
delete[] Data;
|
||||
f.close();
|
||||
}
|
||||
|
||||
bool TTCPServer::TCPSendRaw(SOCKET C, char* Data, int32_t Size) {
|
||||
int64_t Sent = 0;
|
||||
do {
|
||||
int64_t Temp = send(C, &Data[Sent], int(Size - Sent), 0);
|
||||
if (Temp < 1) {
|
||||
info("Socket Closed! " + std::to_string(C));
|
||||
CloseSocketProper(C);
|
||||
return false;
|
||||
}
|
||||
Sent += Temp;
|
||||
} while (Sent < Size);
|
||||
return true;
|
||||
}
|
||||
|
||||
void TTCPServer::SendLarge(TClient& c, std::string Data) {
|
||||
if (Data.length() > 400) {
|
||||
std::string CMP(Comp(Data));
|
||||
Data = "ABG:" + CMP;
|
||||
}
|
||||
TCPSend(c, Data);
|
||||
}
|
||||
|
||||
void TTCPServer::Respond(TClient& c, const std::string& MSG, bool Rel) {
|
||||
char C = MSG.at(0);
|
||||
if (Rel || C == 'W' || C == 'Y' || C == 'V' || C == 'E') {
|
||||
if (C == 'O' || C == 'T' || MSG.length() > 1000) {
|
||||
SendLarge(c, MSG);
|
||||
} else {
|
||||
TCPSend(c, MSG);
|
||||
}
|
||||
} else {
|
||||
UDPServer().UDPSend(c, MSG);
|
||||
}
|
||||
}
|
||||
|
||||
void TTCPServer::SyncClient(std::weak_ptr<TClient> c) {
|
||||
if (c.expired()) {
|
||||
return;
|
||||
}
|
||||
auto LockedClient = c.lock();
|
||||
if (LockedClient->IsSynced())
|
||||
return;
|
||||
LockedClient->SetIsSynced(true);
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
Respond(*LockedClient, ("Sn") + LockedClient->GetName(), true);
|
||||
UDPServer().SendToAll(LockedClient.get(), ("JWelcome ") + LockedClient->GetName() + "!", false, true);
|
||||
TriggerLuaEvent(("onPlayerJoin"), false, nullptr, std::make_unique<TLuaArg>(TLuaArg { { LockedClient->GetID() } }), false);
|
||||
bool Return = false;
|
||||
mServer.ForEachClient([&](std::weak_ptr<TClient> ClientPtr) -> bool {
|
||||
if (!ClientPtr.expired()) {
|
||||
auto client = ClientPtr.lock();
|
||||
if (client != LockedClient) {
|
||||
for (auto& v : LockedClient->GetAllCars()) {
|
||||
if (v != nullptr) {
|
||||
if (LockedClient->GetStatus() < 0) {
|
||||
Return = true;
|
||||
return false;
|
||||
}
|
||||
Respond(*LockedClient, v->Data(), true);
|
||||
std::this_thread::sleep_for(std::chrono::seconds(2));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
if (Return) {
|
||||
return;
|
||||
}
|
||||
info(LockedClient->GetName() + (" is now synced!"));
|
||||
}
|
||||
|
||||
void TTCPServer::SetUDPServer(TUDPServer& UDPServer) {
|
||||
mUDPServer = std::ref(UDPServer);
|
||||
}
|
||||
|
||||
void TTCPServer::operator()() {
|
||||
while (!mUDPServer.has_value()) {
|
||||
// hard spin
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
}
|
||||
#ifdef WIN32
|
||||
WSADATA wsaData;
|
||||
if (WSAStartup(514, &wsaData)) {
|
||||
error("Can't start Winsock!");
|
||||
return;
|
||||
}
|
||||
SOCKET client, Listener = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||
sockaddr_in addr {};
|
||||
addr.sin_addr.S_un.S_addr = ADDR_ANY;
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons(Application::Settings.Port);
|
||||
if (bind(Listener, (sockaddr*)&addr, sizeof(addr)) == SOCKET_ERROR) {
|
||||
error("Can't bind socket! " + std::to_string(WSAGetLastError()));
|
||||
std::this_thread::sleep_for(std::chrono::seconds(5));
|
||||
exit(-1);
|
||||
}
|
||||
if (Listener == -1) {
|
||||
error("Invalid listening socket");
|
||||
return;
|
||||
}
|
||||
if (listen(Listener, SOMAXCONN)) {
|
||||
error("listener failed " + std::to_string(GetLastError()));
|
||||
return;
|
||||
}
|
||||
info("Vehicle event network online");
|
||||
do {
|
||||
try {
|
||||
client = accept(Listener, nullptr, nullptr);
|
||||
if (client == -1) {
|
||||
warn("Got an invalid client socket on connect! Skipping...");
|
||||
continue;
|
||||
}
|
||||
std::thread ID(&TTCPServer::Identify, this, client);
|
||||
ID.detach();
|
||||
} catch (const std::exception& e) {
|
||||
error("fatal: " + std::string(e.what()));
|
||||
}
|
||||
} while (client);
|
||||
|
||||
CloseSocketProper(client);
|
||||
WSACleanup();
|
||||
#else // unix
|
||||
// wondering why we need slightly different implementations of this?
|
||||
// ask ms.
|
||||
SOCKET client = -1;
|
||||
SOCKET Listener = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||
int optval = 1;
|
||||
setsockopt(Listener, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof(optval));
|
||||
// TODO: check optval or return value idk
|
||||
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, (sockaddr*)&addr, sizeof(addr)) != 0) {
|
||||
error(("Can't bind socket! ") + std::string(strerror(errno)));
|
||||
std::this_thread::sleep_for(std::chrono::seconds(5));
|
||||
exit(-1);
|
||||
}
|
||||
if (Listener == -1) {
|
||||
error(("Invalid listening socket"));
|
||||
return;
|
||||
}
|
||||
if (listen(Listener, SOMAXCONN)) {
|
||||
error(("listener failed ") + std::string(strerror(errno)));
|
||||
return;
|
||||
}
|
||||
info(("Vehicle event network online"));
|
||||
do {
|
||||
try {
|
||||
client = accept(Listener, nullptr, nullptr);
|
||||
if (client == -1) {
|
||||
warn(("Got an invalid client socket on connect! Skipping..."));
|
||||
continue;
|
||||
}
|
||||
std::thread ID(&TTCPServer::Identify, this, client);
|
||||
ID.detach();
|
||||
} catch (const std::exception& e) {
|
||||
error(("fatal: ") + std::string(e.what()));
|
||||
}
|
||||
} while (client && !mShutdown);
|
||||
|
||||
debug("all ok, arrived at " + std::string(__func__) + ":" + std::to_string(__LINE__));
|
||||
|
||||
CloseSocketProper(client);
|
||||
#endif
|
||||
}
|
||||
|
@ -1,11 +1,14 @@
|
||||
#include "TUDPServer.h"
|
||||
#include "CustomAssert.h"
|
||||
#include "TTCPServer.h"
|
||||
#include <any>
|
||||
#include <cstring>
|
||||
#include <utility>
|
||||
|
||||
TUDPServer::TUDPServer(TServer& Server, TPPSMonitor& PPSMonitor)
|
||||
TUDPServer::TUDPServer(TServer& Server, TPPSMonitor& PPSMonitor, TTCPServer& TCPServer)
|
||||
: mServer(Server)
|
||||
, mPPSMonitor(PPSMonitor) {
|
||||
, mPPSMonitor(PPSMonitor)
|
||||
, mTCPServer(TCPServer) {
|
||||
}
|
||||
|
||||
void TUDPServer::operator()() {
|
||||
@ -65,8 +68,8 @@ void TUDPServer::operator()() {
|
||||
auto Client = ClientPtr.lock();
|
||||
if (Client->GetID() == ID) {
|
||||
Client->SetUDPAddr(client);
|
||||
Client->SetConnected(true);
|
||||
UDPParser(*Client, Data.substr(2));
|
||||
Client->SetIsConnected(true);
|
||||
TServer::GlobalParser(ClientPtr, Data.substr(2), mPPSMonitor, *this, mTCPServer);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
@ -76,69 +79,6 @@ void TUDPServer::operator()() {
|
||||
}
|
||||
}
|
||||
}
|
||||
void TUDPServer::UDPParser(TClient& Client, std::string Packet) {
|
||||
if (Packet.find("Zp") != std::string::npos && Packet.size() > 500) {
|
||||
abort();
|
||||
}
|
||||
if (Packet.substr(0, 4) == "ABG:") {
|
||||
Packet = DeComp(Packet.substr(4));
|
||||
}
|
||||
if (Packet.empty()) {
|
||||
return;
|
||||
}
|
||||
std::any Res;
|
||||
char Code = Packet.at(0);
|
||||
|
||||
//V to Z
|
||||
if (Code <= 90 && Code >= 86) {
|
||||
mPPSMonitor.IncrementInternalPPS();
|
||||
SendToAll(&Client, Packet, false, false);
|
||||
return;
|
||||
}
|
||||
switch (Code) {
|
||||
case 'H': // initial connection
|
||||
#ifdef DEBUG
|
||||
debug(std::string("got 'H' packet: '") + Packet + "' (" + std::to_string(Packet.size()) + ")");
|
||||
#endif
|
||||
SyncClient(Client);
|
||||
return;
|
||||
case 'p':
|
||||
Respond(Client, ("p"), false);
|
||||
UpdatePlayers();
|
||||
return;
|
||||
case 'O':
|
||||
if (Packet.length() > 1000) {
|
||||
debug(("Received data from: ") + Client->GetName() + (" Size: ") + std::to_string(Packet.length()));
|
||||
}
|
||||
ParseVeh(Client, Packet);
|
||||
return;
|
||||
case 'J':
|
||||
#ifdef DEBUG
|
||||
debug(std::string(("got 'J' packet: '")) + Packet + ("' (") + std::to_string(Packet.size()) + (")"));
|
||||
#endif
|
||||
SendToAll(Client, Packet, false, true);
|
||||
return;
|
||||
case 'C':
|
||||
#ifdef DEBUG
|
||||
debug(std::string(("got 'C' packet: '")) + Packet + ("' (") + std::to_string(Packet.size()) + (")"));
|
||||
#endif
|
||||
if (Packet.length() < 4 || Packet.find(':', 3) == std::string::npos)
|
||||
break;
|
||||
Res = TriggerLuaEvent("onChatMessage", false, nullptr, std::make_unique<LuaArg>(LuaArg { { Client->GetID(), Client->GetName(), Packet.substr(Packet.find(':', 3) + 1) } }), true);
|
||||
if (std::any_cast<int>(Res))
|
||||
break;
|
||||
SendToAll(nullptr, Packet, true, true);
|
||||
return;
|
||||
case 'E':
|
||||
#ifdef DEBUG
|
||||
debug(std::string(("got 'E' packet: '")) + Packet + ("' (") + std::to_string(Packet.size()) + (")"));
|
||||
#endif
|
||||
HandleEvent(Client, Packet);
|
||||
return;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void TUDPServer::SendToAll(TClient* c, const std::string& Data, bool Self, bool Rel) {
|
||||
if (!Self)
|
||||
@ -151,9 +91,9 @@ void TUDPServer::SendToAll(TClient* c, const std::string& Data, bool Self, bool
|
||||
if (Client->IsSynced()) {
|
||||
if (Rel || C == 'W' || C == 'Y' || C == 'V' || C == 'E') {
|
||||
if (C == 'O' || C == 'T' || Data.length() > 1000)
|
||||
SendLarge(*Client, Data);
|
||||
mTCPServer.SendLarge(*Client, Data);
|
||||
else
|
||||
TCPSend(*Client, Data);
|
||||
mTCPServer.TCPSend(*Client, Data);
|
||||
} else
|
||||
UDPSend(*Client, Data);
|
||||
}
|
||||
|
22
src/main.cpp
22
src/main.cpp
@ -1,20 +1,16 @@
|
||||
#include "Client.h"
|
||||
#include "Common.h"
|
||||
#include "IThreaded.h"
|
||||
#include "TConfig.h"
|
||||
#include "TConsole.h"
|
||||
#include "THeartbeatThread.h"
|
||||
#include "TLuaEngine.h"
|
||||
#include "TPPSMonitor.h"
|
||||
#include "TResourceManager.h"
|
||||
#include "TServer.h"
|
||||
#include "TPPSMonitor.h"
|
||||
#include "TUDPServer.h"
|
||||
#include <atomic>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
|
||||
#ifdef __unix
|
||||
#include <TTCPServer.h>
|
||||
#include <csignal>
|
||||
|
||||
void UnixSignalHandler(int sig) {
|
||||
@ -45,18 +41,20 @@ int main(int argc, char** argv) {
|
||||
signal(SIGINT, UnixSignalHandler);
|
||||
#endif // __unix
|
||||
|
||||
bool Shutdown = false;
|
||||
Application::RegisterShutdownHandler([&Shutdown] { Shutdown = true; });
|
||||
|
||||
TServer Server(argc, argv);
|
||||
[[maybe_unused]] TConfig Config("Server.cfg");
|
||||
TResourceManager ResourceManager;
|
||||
[[maybe_unused]] TPPSMonitor PPSMonitor(Server);
|
||||
TPPSMonitor PPSMonitor(Server);
|
||||
THeartbeatThread Heartbeat(ResourceManager, Server);
|
||||
TTCPServer TCPServer(Server);
|
||||
TUDPServer UDPServer(Server, PPSMonitor);
|
||||
TLuaEngine LuaEngine(Server);
|
||||
TTCPServer TCPServer(Server, PPSMonitor, ResourceManager);
|
||||
TUDPServer UDPServer(Server, PPSMonitor, TCPServer);
|
||||
TCPServer.SetUDPServer(UDPServer);
|
||||
TLuaEngine LuaEngine(Server, TCPServer, UDPServer);
|
||||
|
||||
// TODO: replace
|
||||
bool Shutdown = false;
|
||||
Application::RegisterShutdownHandler([&Shutdown] { Shutdown = true; });
|
||||
while (!Shutdown) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user