From 71a84b4f1b865897ecb81edd5dc360a4daa6021a Mon Sep 17 00:00:00 2001 From: Anonymous275 Date: Fri, 16 Oct 2020 17:05:31 +0300 Subject: [PATCH] Server update 0.63.5 - async lua implementation - cleaner backend heartbeat - two way encryption on connect - async tcp buffer - disconnect handler - cleaned UDP implementation --- include/Buffer.h | 42 +++++++++++ include/Client.hpp | 2 + include/Lua/LuaSystem.hpp | 7 +- include/Network.h | 4 +- include/Security/Enc.h | 1 + src/Enc.cpp | 8 ++- src/Init/Heartbeat.cpp | 24 +++---- src/Init/Startup.cpp | 6 +- src/Lua/LuaSystem.cpp | 108 ++++++++++++++++++---------- src/Network/Auth.cpp | 48 ++++++++++--- src/Network/GParser.cpp | 53 ++++++++++---- src/Network/InitClient.cpp | 17 +++-- src/Network/TCPHandler.cpp | 24 ++++--- src/Network/VehicleData.cpp | 139 ++++++++++++++++++------------------ 14 files changed, 314 insertions(+), 169 deletions(-) create mode 100644 include/Buffer.h diff --git a/include/Buffer.h b/include/Buffer.h new file mode 100644 index 0000000..ac1a1c8 --- /dev/null +++ b/include/Buffer.h @@ -0,0 +1,42 @@ +/// +/// Created by Anonymous275 on 8/25/2020 +/// +#pragma once +#include +class Client; +void GParser(Client*c, const std::string&Packet); +class Buffer{ +public: + void Handle(Client*c,const std::string& Data){ + if(c == nullptr)return; + Buf += Data; + Manage(c); + } + void clear(){ + Buf.clear(); + } +private: + std::string Buf; + void Manage(Client*c){ + if(!Buf.empty()){ + std::string::size_type p; + if (Buf.at(0) == '\n'){ + p = Buf.find('\n',1); + if(p != -1){ + std::string R = Buf.substr(1,p-1); + std::string_view B(R.c_str(),R.find(char(0))); + GParser(c, B.data()); + Buf = Buf.substr(p+1); + Manage(c); + } + }else{ + p = Buf.find('\n'); + if(p == -1)Buf.clear(); + else{ + Buf = Buf.substr(p); + Manage(c); + } + } + } + } +}; diff --git a/include/Client.hpp b/include/Client.hpp index b59a250..310c320 100644 --- a/include/Client.hpp +++ b/include/Client.hpp @@ -4,6 +4,7 @@ #pragma once #include +#include "Buffer.h" #include #include #include @@ -48,6 +49,7 @@ public: int GetCarCount(); void ClearCars(); int GetStatus(); + Buffer Handler; int GetID(); }; struct ClientInterface{ diff --git a/include/Lua/LuaSystem.hpp b/include/Lua/LuaSystem.hpp index bc5729c..77c1954 100644 --- a/include/Lua/LuaSystem.hpp +++ b/include/Lua/LuaSystem.hpp @@ -8,6 +8,7 @@ #include "lua.hpp" #include #include +#include #include #include namespace fs = std::experimental::filesystem; @@ -50,17 +51,15 @@ public: void SetPluginName(const std::string&Name); void SetFileName(const std::string&Name); fs::file_time_type GetLastWrite(); - bool isThreadExecuting = false; std::string GetPluginName(); std::string GetFileName(); - bool isExecuting = false; bool StopThread = false; - bool HasThread = false; lua_State* GetState(); char* GetOrigin(); + std::mutex Lock; void Reload(); void Init(); }; int CallFunction(Lua*lua,const std::string& FuncName,LuaArg* args); -int TriggerLuaEvent(const std::string& Event,bool local,Lua*Caller,LuaArg* arg); +int TriggerLuaEvent(const std::string& Event,bool local,Lua*Caller,LuaArg* arg,bool Wait); extern std::set PluginEngine; \ No newline at end of file diff --git a/include/Network.h b/include/Network.h index b22e8f2..7ed9efd 100644 --- a/include/Network.h +++ b/include/Network.h @@ -12,8 +12,8 @@ void SyncResources(Client*c); [[noreturn]] void UDPServerMain(); void OnDisconnect(Client*c,bool kicked); void UDPSend(Client*c,std::string Data); -void TCPSend(Client*c,const std::string&Data); -void SendLarge(Client*c,const std::string&Data); +void TCPSend(Client*c,const std::string& Data); +void SendLarge(Client*c,std::string Data); void GParser(Client*c, const std::string&Packet); void Respond(Client*c, const std::string& MSG, bool Rel); void SendToAll(Client*c, const std::string& Data, bool Self, bool Rel); diff --git a/include/Security/Enc.h b/include/Security/Enc.h index 11a35cf..e418232 100644 --- a/include/Security/Enc.h +++ b/include/Security/Enc.h @@ -10,6 +10,7 @@ struct RSA{ int e = 0; int d = 0; }; +std::string RSA_E(const std::string& Data,int e, int n); std::string RSA_E(const std::string& Data, RSA*k); std::string RSA_D(const std::string& Data, RSA*k); int Handle(EXCEPTION_POINTERS *ep,char* Origin); diff --git a/src/Enc.cpp b/src/Enc.cpp index a01eb73..e2ec495 100644 --- a/src/Enc.cpp +++ b/src/Enc.cpp @@ -100,7 +100,13 @@ std::string RSA_E(const std::string& Data, RSA*k){ } return stream.str(); } - +std::string RSA_E(const std::string& Data,int e, int n){ + std::stringstream stream; + for(const char&c : Data){ + stream << std::hex << Enc(uint8_t(c),e,n) << "g"; + } + return stream.str(); +} std::string RSA_D(const std::string& Data, RSA*k){ std::stringstream ss(Data); std::string token,ret; diff --git a/src/Init/Heartbeat.cpp b/src/Init/Heartbeat.cpp index fcae4d5..bf10e16 100644 --- a/src/Init/Heartbeat.cpp +++ b/src/Init/Heartbeat.cpp @@ -19,28 +19,28 @@ std::string GetPlayers(){ return Return; } std::string GenerateCall(){ - std::string State = Private ? Sec("true") : Sec("false"); - std::string ret = Sec("uuid="); - ret += Key+Sec("&players=")+std::to_string(CI->Size())+Sec("&maxplayers=")+std::to_string(MaxPlayers)+Sec("&port=") - + std::to_string(Port) + Sec("&map=") + MapName + Sec("&private=")+State+Sec("&version=")+GetSVer()+ - Sec("&clientversion=")+GetCVer()+Sec("&name=")+ServerName+Sec("&pps=")+StatReport+Sec("&modlist=")+FileList+ - Sec("&modstotalsize=")+std::to_string(MaxModSize)+Sec("&modstotal=")+std::to_string(ModsLoaded) - +Sec("&playerslist=")+GetPlayers()+Sec("&desc=")+ServerDesc; + std::string State = Private ? "true" : "false"; + std::string ret = "uuid="; + ret += Key+"&players="+std::to_string(CI->Size())+"&maxplayers="+std::to_string(MaxPlayers)+"&port=" + + std::to_string(Port) + "&map=" + MapName + "&private="+State+"&version="+GetSVer()+ + "&clientversion="+GetCVer()+"&name="+ServerName+"&pps="+StatReport+"&modlist="+FileList+ + "&modstotalsize="+std::to_string(MaxModSize)+"&modstotal="+std::to_string(ModsLoaded) + +"&playerslist="+GetPlayers()+"&desc="+ServerDesc; return ret; } void Heartbeat(){ std::string R,T; while(true){ R = GenerateCall(); - if(!CustomIP.empty())R+=Sec("&ip=")+CustomIP; - //https://beamng-mp.com/heartbeatv2 - std::string link = Sec("https://beamng-mp.com/heartbeatv2"); + if(!CustomIP.empty())R+="&ip="+CustomIP; + std::string link = Sec("https://beammp.com/heartbeatv2"); T = PostHTTP(link,R); if(T.find_first_not_of(Sec("20")) != std::string::npos){ //Backend system refused server startup! std::this_thread::sleep_for(std::chrono::seconds(10)); - T = PostHTTP(link,R); - if(T.find_first_not_of(Sec("20")) != std::string::npos){ + std::string Backup = Sec("https://backup1.beammp.com/heartbeatv2"); + T = PostHTTP(Backup,R); + if(T.find_first_not_of(Sec("20")) != std::string::npos) { error(Sec("Backend system refused server! Check your AuthKey")); std::this_thread::sleep_for(std::chrono::seconds(3)); exit(-1); diff --git a/src/Init/Startup.cpp b/src/Init/Startup.cpp index 2c17116..8abaf6a 100644 --- a/src/Init/Startup.cpp +++ b/src/Init/Startup.cpp @@ -8,10 +8,12 @@ std::string CustomIP; std::string GetSVer(){ - return Sec("0.60"); + static std::string r = Sec("0.63.5"); + return r; } std::string GetCVer(){ - return Sec("1.60"); + static std::string r = Sec("1.63"); + return r; } void Args(int argc, char* argv[]){ info(Sec("BeamMP Server Running version ") + GetSVer()); diff --git a/src/Lua/LuaSystem.cpp b/src/Lua/LuaSystem.cpp index 467de44..6101341 100644 --- a/src/Lua/LuaSystem.cpp +++ b/src/Lua/LuaSystem.cpp @@ -9,6 +9,7 @@ #include "Logger.h" #include #include +#include LuaArg* CreateArg(lua_State *L,int T,int S){ if(S > T)return nullptr; @@ -26,6 +27,9 @@ LuaArg* CreateArg(lua_State *L,int T,int S){ } return temp; } +void ClearStack(lua_State *L){ + lua_settop(L,0); +} Lua* GetScript(lua_State *L){ for(Lua*Script : PluginEngine){ if (Script->GetState() == L)return Script; @@ -35,33 +39,41 @@ Lua* GetScript(lua_State *L){ void SendError(lua_State *L,const std::string&msg){ Lua* S = GetScript(L); std::string a = S->GetFileName().substr(S->GetFileName().find('\\')); - warn(a + Sec(" | Incorrect Call of ") +msg); + warn(a + Sec(" | Incorrect Call of ") + msg); } int Trigger(Lua*lua,const std::string& R, LuaArg*arg){ + std::lock_guard lockGuard(lua->Lock); std::packaged_task task([lua,R,arg]{return CallFunction(lua,R,arg);}); std::future f1 = task.get_future(); std::thread t(std::move(task)); t.detach(); - auto status = f1.wait_for(std::chrono::seconds(3)); + auto status = f1.wait_for(std::chrono::seconds(5)); if(status != std::future_status::timeout)return f1.get(); SendError(lua->GetState(),R + " took too long to respond"); return 0; } -int TriggerLuaEvent(const std::string& Event,bool local,Lua*Caller,LuaArg* arg){ +int FutureWait(Lua*lua,const std::string& R, LuaArg*arg,bool Wait){ + std::packaged_task task([lua,R,arg]{return Trigger(lua,R,arg);}); + std::future f1 = task.get_future(); + std::thread t(std::move(task)); + t.detach(); + int T = 0; + if(Wait)T = 6; + auto status = f1.wait_for(std::chrono::seconds(T)); + if(status != std::future_status::timeout)return f1.get(); + return 0; +} +int TriggerLuaEvent(const std::string& Event,bool local,Lua*Caller,LuaArg* arg,bool Wait){ int R = 0; for(Lua*Script : PluginEngine){ if(Script->IsRegistered(Event)){ if(local){ if (Script->GetPluginName() == Caller->GetPluginName()){ - R += Trigger(Script,Script->GetRegistered(Event),arg); + R += FutureWait(Script,Script->GetRegistered(Event),arg,Wait); } - }else R += Trigger(Script,Script->GetRegistered(Event), arg); + }else R += FutureWait(Script,Script->GetRegistered(Event), arg,Wait); } } - if(arg != nullptr){ - delete arg; - arg = nullptr; - } return R; } @@ -70,7 +82,7 @@ bool CheckLua(lua_State *L, int r){ std::string msg = lua_tostring(L, -1); Lua * S = GetScript(L); std::string a = S->GetFileName().substr(S->GetFileName().find('\\')); - warn(a + " | at line " + msg.substr(msg.find(':')+1)); + warn(a + " | " + msg); return false; } return true; @@ -89,7 +101,7 @@ int lua_TriggerEventL(lua_State *L){ Lua* Script = GetScript(L); if(Args > 0){ if(lua_isstring(L,1)){ - TriggerLuaEvent(lua_tostring(L, 1), true, Script, CreateArg(L,Args,2)); + TriggerLuaEvent(lua_tostring(L, 1), true, Script, CreateArg(L,Args,2),false); }else SendError(L,Sec("TriggerLocalEvent wrong argument [1] need string")); }else{ SendError(L,Sec("TriggerLocalEvent not enough arguments expected 1 got 0")); @@ -102,7 +114,7 @@ int lua_TriggerEventG(lua_State *L){ Lua* Script = GetScript(L); if(Args > 0){ if(lua_isstring(L,1)) { - TriggerLuaEvent(lua_tostring(L, 1), false, Script, CreateArg(L,Args,2)); + TriggerLuaEvent(lua_tostring(L, 1), false, Script, CreateArg(L,Args,2),false); }else SendError(L,Sec("TriggerGlobalEvent wrong argument [1] need string")); }else SendError(L,Sec("TriggerGlobalEvent not enough arguments")); return 0; @@ -110,24 +122,12 @@ int lua_TriggerEventG(lua_State *L){ char* ThreadOrigin(Lua*lua){ std::string T = "Thread in " + lua->GetFileName().substr(lua->GetFileName().find('\\')); - char* Data = new char[T.size()]; - ZeroMemory(Data,T.size()); + char* Data = new char[T.size()+1]; + ZeroMemory(Data,T.size()+1); memcpy_s(Data,T.size(),T.c_str(),T.size()); return Data; } - -void Lock(Lua* lua,bool thread){ - bool Lock; - do{ - if(thread){ - Lock = lua->isExecuting; - }else Lock = lua->isThreadExecuting; - std::this_thread::sleep_for(std::chrono::milliseconds(1)); - }while(Lock); -} -void ExecuteAsync(Lua* lua,const std::string& FuncName){ - Lock(lua,true); - lua->isThreadExecuting = true; +void SafeExecution(Lua* lua,const std::string& FuncName){ lua_State* luaState = lua->GetState(); lua_getglobal(luaState, FuncName.c_str()); if(lua_isfunction(luaState, -1)) { @@ -138,21 +138,20 @@ void ExecuteAsync(Lua* lua,const std::string& FuncName){ }__except(Handle(GetExceptionInformation(),Origin)){} delete [] Origin; } - lua->isThreadExecuting = false; + ClearStack(luaState); +} + +void ExecuteAsync(Lua* lua,const std::string& FuncName){ + std::lock_guard lockGuard(lua->Lock); + SafeExecution(lua,FuncName); } void CallAsync(Lua* lua,const std::string& Func,int U){ - if(lua->HasThread){ - SendError(lua->GetState(),Sec("CreateThread : There is already a thread running!")); - return; - } lua->StopThread = false; - lua->HasThread = true; int D = 1000 / U; while(!lua->StopThread){ ExecuteAsync(lua,Func); std::this_thread::sleep_for(std::chrono::milliseconds(D)); } - lua->HasThread = false; } int lua_StopThread(lua_State *L){ GetScript(L)->StopThread = true; @@ -329,10 +328,43 @@ int lua_HWID(lua_State *L){ lua_pushinteger(L, -1); return 1; } +int lua_RemoteEvent(lua_State *L){ + int Args = lua_gettop(L); + if(Args != 3){ + SendError(L,Sec("TriggerClientEvent invalid argument count expected 3 got ") + std::to_string(Args)); + return 0; + } + if(!lua_isnumber(L,1)){ + SendError(L,Sec("TriggerClientEvent invalid argument [1] expected number")); + return 0; + } + if(!lua_isstring(L,2)){ + SendError(L,Sec("TriggerClientEvent invalid argument [2] expected string")); + return 0; + } + if(!lua_isstring(L,3)){ + SendError(L,Sec("TriggerClientEvent invalid argument [3] expected string")); + return 0; + } + 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); + }else{ + Client *c = GetClient(ID); + if(c == nullptr){ + SendError(L,Sec("TriggerClientEvent invalid Player ID")); + return 0; + } + Respond(c,Packet,true); + } + return 0; +} void Lua::Init(){ luaL_openlibs(luaState); lua_register(luaState,"TriggerGlobalEvent",lua_TriggerEventG); lua_register(luaState,"TriggerLocalEvent",lua_TriggerEventL); + lua_register(luaState,"TriggerClientEvent",lua_RemoteEvent); lua_register(luaState,"GetPlayerCount",lua_GetPlayerCount); lua_register(luaState,"isPlayerConnected",lua_isConnected); lua_register(luaState,"RegisterEvent",lua_RegisterEvent); @@ -352,7 +384,7 @@ void Lua::Init(){ void Lua::Reload(){ if(CheckLua(luaState,luaL_dofile(luaState,FileName.c_str()))){ - CallFunction(this,Sec("onInit"),{}); + CallFunction(this,Sec("onInit"), nullptr); } } char* Lua::GetOrigin(){ @@ -363,8 +395,6 @@ char* Lua::GetOrigin(){ return Data; } int CallFunction(Lua*lua,const std::string& FuncName,LuaArg* Arg){ - Lock(lua,false); - lua->isExecuting = true; lua_State*luaState = lua->GetState(); lua_getglobal(luaState, FuncName.c_str()); if(lua_isfunction(luaState, -1)) { @@ -372,6 +402,8 @@ int CallFunction(Lua*lua,const std::string& FuncName,LuaArg* Arg){ if(Arg != nullptr){ Size = int(Arg->args.size()); Arg->PushArgs(luaState); + delete Arg; + Arg = nullptr; } int R = 0; char* Origin = lua->GetOrigin(); @@ -385,7 +417,7 @@ int CallFunction(Lua*lua,const std::string& FuncName,LuaArg* Arg){ }__except(Handle(GetExceptionInformation(),Origin)){} delete [] Origin; } - lua->isExecuting = false; + ClearStack(luaState); return 0; } void Lua::SetPluginName(const std::string&Name){ diff --git a/src/Network/Auth.cpp b/src/Network/Auth.cpp index 9bd4e5b..ed3f7bc 100644 --- a/src/Network/Auth.cpp +++ b/src/Network/Auth.cpp @@ -29,8 +29,10 @@ std::string Rcv(SOCKET TCPSock){ } std::string GetRole(const std::string &DID){ if(!DID.empty()){ - std::string a = HttpRequest(Sec("https://beamng-mp.com/entitlement?did=")+DID,443); - if(!a.empty()){ + std::string a = HttpRequest(Sec("https://beammp.com/entitlement?did=")+DID,443); + std::string b = HttpRequest(Sec("https://backup1.beammp.com/entitlement?did=")+DID,443); + if(!a.empty() || !b.empty()){ + if(a != b)a = b; auto pos = a.find('"'); if(pos != std::string::npos){ return a.substr(pos+1,a.find('"',pos+1)-2); @@ -63,26 +65,46 @@ void CreateClient(SOCKET TCPSock,const std::string &Name, const std::string &DID CI->AddClient(c); InitClient(c); } - +std::pair Parse(const std::string& msg){ + std::stringstream ss(msg); + std::string t; + std::pair a = {0,0}; //N then E + while (std::getline(ss, t, 'g')) { + if(t.find_first_not_of(Sec("0123456789abcdef")) != std::string::npos)return a; + if(a.first == 0){ + a.first = std::stoi(t, nullptr, 16); + }else if(a.second == 0){ + a.second = std::stoi(t, nullptr, 16); + }else return a; + } + return {0,0}; +} std::string GenerateM(RSA*key){ std::stringstream stream; stream << std::hex << key->n << "g" << key->e << "g" << RSA_E(Sec("IDC"),key); return stream.str(); } -void Identification(SOCKET TCPSock,Hold*S,RSA*key){ +void Identification(SOCKET TCPSock,Hold*S,RSA*Skey){ S->TCPSock = TCPSock; std::thread Timeout(Check,S); Timeout.detach(); std::string Name,DID,Role; - if(!Send(TCPSock,GenerateM(key))){ + if(!Send(TCPSock,GenerateM(Skey))){ closesocket(TCPSock); return; } + std::string msg = Rcv(TCPSock); + auto Keys = Parse(msg); + if(!Send(TCPSock,RSA_E("HC",Keys.second,Keys.first))){ + closesocket(TCPSock); + return; + } + std::string Res = Rcv(TCPSock); std::string Ver = Rcv(TCPSock); S->Done = true; - Ver = RSA_D(Ver,key); + Ver = RSA_D(Ver,Skey); if(Ver.size() > 3 && Ver.substr(0,2) == Sec("VC")){ Ver = Ver.substr(2); if(Ver.length() > 4 || Ver != GetCVer()){ @@ -93,7 +115,7 @@ void Identification(SOCKET TCPSock,Hold*S,RSA*key){ closesocket(TCPSock); return; } - Res = RSA_D(Res,key); + Res = RSA_D(Res,Skey); if(Res.size() < 3 || Res.substr(0,2) != Sec("NR")) { closesocket(TCPSock); return; @@ -125,11 +147,15 @@ void Identification(SOCKET TCPSock,Hold*S,RSA*key){ } void Identify(SOCKET TCPSock){ auto* S = new Hold; - RSA*key = GenKey(); + RSA*Skey = GenKey(); __try{ - Identification(TCPSock,S,key); - }__except(1){} - delete key; + Identification(TCPSock,S,Skey); + }__except(1){ + if(TCPSock != -1){ + closesocket(TCPSock); + } + } + delete Skey; delete S; } void TCPServerMain(){ diff --git a/src/Network/GParser.cpp b/src/Network/GParser.cpp index eba2f9a..60f40ad 100644 --- a/src/Network/GParser.cpp +++ b/src/Network/GParser.cpp @@ -7,6 +7,7 @@ #include "Settings.h" #include "Network.h" #include "Logger.h" +#include int FC(const std::string& s,const std::string& p,int n) { auto i = s.find(p); @@ -42,7 +43,7 @@ void VehicleParser(Client*c,const std::string& Pckt){ Packet = "Os:"+c->GetRole()+":"+c->GetName()+":"+std::to_string(c->GetID())+"-"+std::to_string(CarID)+Packet.substr(4); if(c->GetCarCount() >= MaxCars || TriggerLuaEvent(Sec("onVehicleSpawn"),false,nullptr, - new LuaArg{{c->GetID(),CarID,Packet.substr(3)}})){ + new LuaArg{{c->GetID(),CarID,Packet.substr(3)}},true)){ Respond(c,Packet,true); std::string Destroy = "Od:" + std::to_string(c->GetID())+"-"+std::to_string(CarID); Respond(c,Destroy,true); @@ -62,7 +63,7 @@ void VehicleParser(Client*c,const std::string& Pckt){ } if(PID != -1 && VID != -1 && PID == c->GetID()){ if(!TriggerLuaEvent(Sec("onVehicleEdited"),false,nullptr, - new LuaArg{{c->GetID(),VID,Packet.substr(3)}})) { + new LuaArg{{c->GetID(),VID,Packet.substr(3)}},true)) { SendToAll(c, Packet, false, true); Apply(c,VID,Packet); }else{ @@ -82,7 +83,7 @@ void VehicleParser(Client*c,const std::string& Pckt){ if(PID != -1 && VID != -1 && PID == c->GetID()){ SendToAll(nullptr,Packet,true,true); TriggerLuaEvent(Sec("onVehicleDeleted"),false,nullptr, - new LuaArg{{c->GetID(),VID}}); + new LuaArg{{c->GetID(),VID}},false); c->DeleteCar(VID); debug(c->GetName() + Sec(" deleted car with ID ") + std::to_string(VID)); } @@ -96,19 +97,23 @@ void VehicleParser(Client*c,const std::string& Pckt){ } void SyncClient(Client*c){ if(c->isSynced)return; + c->isSynced = true; + std::this_thread::sleep_for(std::chrono::seconds(1)); Respond(c,Sec("Sn")+c->GetName(),true); SendToAll(c,Sec("JWelcome ")+c->GetName()+"!",false,true); - TriggerLuaEvent(Sec("onPlayerJoin"),false,nullptr,new LuaArg{{c->GetID()}}); + TriggerLuaEvent(Sec("onPlayerJoin"),false,nullptr,new LuaArg{{c->GetID()}},false); for (Client*client : CI->Clients) { if(client != nullptr){ if (client != c) { for (VData *v : client->GetAllCars()) { - Respond(c, v->Data, true); + if(v != nullptr){ + Respond(c, v->Data, true); + std::this_thread::sleep_for(std::chrono::seconds(2)); + } } } } } - c->isSynced = true; info(c->GetName() + Sec(" is now synced!")); } void ParseVeh(Client*c, const std::string&Packet){ @@ -117,9 +122,30 @@ void ParseVeh(Client*c, const std::string&Packet){ }__except(Handle(GetExceptionInformation(),Sec("Vehicle Handler"))){} } -void GlobalParser(Client*c, const std::string& Packet){ +void HandleEvent(Client*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,new LuaArg{{c->GetID(),t}},false); + break; + default: + break; + } + if(a == 2)break; + a++; + } +} + +void GlobalParser(Client*c, const std::string& Pack){ static int lastRecv = 0; - if(Packet.empty() || c == nullptr)return; + if(Pack.empty() || c == nullptr)return; + std::string Packet = Pack.substr(0,Pack.find(char(0))); std::string pct; char Code = Packet.at(0); @@ -150,14 +176,13 @@ void GlobalParser(Client*c, const std::string& Packet){ return; case 'C': if(Packet.length() < 4 || Packet.find(':', 3) == -1)break; - pct = "C:" + c->GetName() + Packet.substr(Packet.find(':', 3)); - if (TriggerLuaEvent(Sec("onChatMessage"), false, nullptr, - new LuaArg{{c->GetID(), c->GetName(), pct.substr(pct.find(':', 3) + 1)}}))break; - SendToAll(nullptr, pct, true, true); - pct.clear(); + if (TriggerLuaEvent(Sec("onChatMessage"), false, nullptr,new LuaArg{ + {c->GetID(), c->GetName(), Packet.substr(Packet.find(':', 3) + 1)} + },true))break; + SendToAll(nullptr, Packet, true, true); return; case 'E': - SendToAll(nullptr,Packet,true,true); + HandleEvent(c,Packet); return; default: return; diff --git a/src/Network/InitClient.cpp b/src/Network/InitClient.cpp index b37c1fe..e194e15 100644 --- a/src/Network/InitClient.cpp +++ b/src/Network/InitClient.cpp @@ -25,7 +25,7 @@ int OpenID(){ } void Respond(Client*c, const std::string& MSG, bool Rel){ char C = MSG.at(0); - if(Rel){ + 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 UDPSend(c,MSG); @@ -35,9 +35,10 @@ void SendToAll(Client*c, const std::string& Data, bool Self, bool Rel){ for(Client*client : CI->Clients){ if(client != nullptr) { if (Self || client != c) { - if (client->isSynced) { - if (Rel) { - if (C == 'O' || C == 'T' || Data.length() > 1000)SendLarge(client, Data); + if (client->isSynced || (C == 'O' && Data.at(1) == 's')) { + if (Rel || C == 'W' || C == 'Y' || C == 'V' || C == 'E') { + if (C == 'O' || C == 'T' || + Data.length() > 1000)SendLarge(client, Data); else TCPSend(client, Data); } else UDPSend(client, Data); } @@ -54,6 +55,7 @@ void UpdatePlayers(){ SendToAll(nullptr, Packet,true,true); } void OnDisconnect(Client*c,bool kicked){ + info(c->GetName() + Sec(" Connection Terminated")); if(c == nullptr)return; std::string Packet; for(VData*v : c->GetAllCars()){ @@ -66,17 +68,18 @@ void OnDisconnect(Client*c,bool kicked){ Packet = Sec("L")+c->GetName()+Sec(" Left the server!"); SendToAll(c, Packet,false,true); Packet.clear(); - TriggerLuaEvent(Sec("onPlayerDisconnect"),false,nullptr,new LuaArg{{c->GetID()}}); + TriggerLuaEvent(Sec("onPlayerDisconnect"),false,nullptr,new LuaArg{{c->GetID()}},false); c->ClearCars(); CI->RemoveClient(c); ///Removes the Client from existence } void OnConnect(Client*c){ + info(Sec("Client connected")); c->SetID(OpenID()); info(Sec("Assigned ID ") + std::to_string(c->GetID()) + Sec(" to ") + c->GetName()); - TriggerLuaEvent(Sec("onPlayerConnecting"),false,nullptr,new LuaArg{{c->GetID()}}); + TriggerLuaEvent(Sec("onPlayerConnecting"),false,nullptr,new LuaArg{{c->GetID()}},false); SyncResources(c); if(c->GetStatus() < 0)return; Respond(c,"M"+MapName,true); //Send the Map on connect info(c->GetName() + Sec(" : Connected")); - TriggerLuaEvent(Sec("onPlayerJoining"),false,nullptr,new LuaArg{{c->GetID()}}); + TriggerLuaEvent(Sec("onPlayerJoining"),false,nullptr,new LuaArg{{c->GetID()}},false); } \ No newline at end of file diff --git a/src/Network/TCPHandler.cpp b/src/Network/TCPHandler.cpp index 6a9edcf..630c180 100644 --- a/src/Network/TCPHandler.cpp +++ b/src/Network/TCPHandler.cpp @@ -8,16 +8,24 @@ void TCPSend(Client*c,const std::string&Data){ if(c == nullptr)return; - int BytesSent = send(c->GetTCPSock(), Data.c_str(), int(Data.length())+1, 0); - if (BytesSent == 0){ + std::string Send = "\n" + Data.substr(0,Data.find(char(0))) + "\n"; + size_t Sent = send(c->GetTCPSock(), Send.c_str(), int(Send.size()), 0); + if (Sent == 0){ if(c->GetStatus() > -1)c->SetStatus(-1); - }else if (BytesSent < 0) { + }else if (Sent < 0) { if(c->GetStatus() > -1)c->SetStatus(-1); closesocket(c->GetTCPSock()); } } +void TCPHandle(Client*c,const std::string& data){ + __try{ + c->Handler.Handle(c,data); + }__except(1){ + c->Handler.clear(); + } +} void TCPRcv(Client*c){ - if(c == nullptr)return; + if(c == nullptr || c->GetStatus() < 0)return; char buf[4096]; int len = 4096; ZeroMemory(buf, len); @@ -33,18 +41,18 @@ void TCPRcv(Client*c){ return; } std::string Buf(buf,BytesRcv); - GParser(c, Buf); + TCPHandle(c,Buf); } void TCPClient(Client*c){ if(c->GetTCPSock() == -1){ CI->RemoveClient(c); return; } - info(Sec("Client connected")); OnConnect(c); while (c->GetStatus() > -1)TCPRcv(c); - info(c->GetName() + Sec(" Connection Terminated")); - OnDisconnect(c, c->GetStatus() == -2); + __try{ + OnDisconnect(c, c->GetStatus() == -2); + }__except(Handle(GetExceptionInformation(),Sec("OnDisconnect"))){} } void InitClient(Client*c){ std::thread NewClient(TCPClient,c); diff --git a/src/Network/VehicleData.cpp b/src/Network/VehicleData.cpp index a6c4823..4680642 100644 --- a/src/Network/VehicleData.cpp +++ b/src/Network/VehicleData.cpp @@ -8,9 +8,11 @@ #include "Settings.h" #include "Network.h" #include "Logger.h" +#include #include +#include #include - +int FC(const std::string& s,const std::string& p,int n); struct PacketData{ int ID; Client* Client; @@ -30,11 +32,11 @@ void UDPSend(Client*c,std::string Data){ if(c == nullptr || !c->isConnected || c->GetStatus() < 0)return; sockaddr_in Addr = c->GetUDPAddr(); int AddrSize = sizeof(c->GetUDPAddr()); + Data = Data.substr(0,Data.find(char(0))); if(Data.length() > 400){ std::string CMP(Comp(Data)); Data = "ABG:" + CMP; } - int sendOk = sendto(UDPSock, Data.c_str(), int(Data.size()), 0, (sockaddr *) &Addr, AddrSize); if (sendOk == SOCKET_ERROR) { debug(Sec("(UDP) Send Failed Code : ") + std::to_string(WSAGetLastError())); @@ -44,7 +46,7 @@ void UDPSend(Client*c,std::string Data){ void AckID(int ID){ for(PacketData* p : DataAcks){ - if(p->ID == ID){ + if(p != nullptr && p->ID == ID){ DataAcks.erase(p); break; } @@ -62,7 +64,8 @@ int SplitID(){ else SID++; return SID; } -void SendLarge(Client*c,const std::string&Data){ +void SendLarge(Client*c,std::string Data){ + Data = Data.substr(0,Data.find(char(0))); int ID = PacktID(); std::string Packet; if(Data.length() > 1000){ @@ -70,16 +73,16 @@ void SendLarge(Client*c,const std::string&Data){ int S = 1,Split = int(ceil(float(pckt.length()) / 1000)); int SID = SplitID(); while(pckt.length() > 1000){ - Packet = "SC"+std::to_string(S)+"/"+std::to_string(Split)+":"+std::to_string(ID)+"|"+ - std::to_string(SID)+":"+pckt.substr(0,1000); + Packet = "SC|"+std::to_string(S)+"|"+std::to_string(Split)+"|"+std::to_string(ID)+"|"+ + std::to_string(SID)+"|"+pckt.substr(0,1000); DataAcks.insert(new PacketData{ID,c,Packet,1}); UDPSend(c,Packet); pckt = pckt.substr(1000); S++; ID = PacktID(); } - Packet = "SC"+std::to_string(S)+"/"+std::to_string(Split)+":"+ - std::to_string(ID)+"|"+std::to_string(SID)+":"+pckt; + Packet = "SC|"+std::to_string(S)+"|"+std::to_string(Split)+"|"+ + std::to_string(ID)+"|"+std::to_string(SID)+"|"+pckt; DataAcks.insert(new PacketData{ID,c,Packet,1}); UDPSend(c,Packet); }else{ @@ -90,12 +93,12 @@ void SendLarge(Client*c,const std::string&Data){ } struct HandledC{ int Pos = 0; - Client *c{}; - std::array HandledIDs{}; + Client *c = nullptr; + std::array HandledIDs = {-1}; }; std::set HandledIDs; void ResetIDs(HandledC*H){ - for(int C = 0;C < 50;C++){ + for(int C = 0;C < 100;C++){ H->HandledIDs.at(C) = -1; } } @@ -114,7 +117,7 @@ bool Handled(Client*c,int ID){ for(int id : h->HandledIDs){ if(id == ID)return true; } - if(h->Pos > 49)h->Pos = 0; + if(h->Pos > 99)h->Pos = 0; h->HandledIDs.at(h->Pos) = ID; h->Pos++; handle = true; @@ -129,7 +132,7 @@ bool Handled(Client*c,int ID){ if(!handle){ HandledC *h = GetHandled(c); ResetIDs(h); - if (h->Pos > 49)h->Pos = 0; + if (h->Pos > 99)h->Pos = 0; h->HandledIDs.at(h->Pos) = ID; h->Pos++; h->c = c; @@ -138,17 +141,14 @@ bool Handled(Client*c,int ID){ return false; } std::string UDPRcvFromClient(sockaddr_in& client){ - char buf[10240]; int clientLength = sizeof(client); ZeroMemory(&client, clientLength); - ZeroMemory(buf, 10240); - int Rcv = recvfrom(UDPSock, buf, 10240, 0, (sockaddr*)&client, &clientLength); + std::string Ret(10240,0); + int Rcv = recvfrom(UDPSock, &Ret[0], 10240, 0, (sockaddr*)&client, &clientLength); if (Rcv == -1){ error(Sec("(UDP) Error receiving from Client! Code : ") + std::to_string(WSAGetLastError())); return ""; } - std::string Ret(Rcv,0); - memcpy_s(&Ret[0],Rcv,buf,Rcv); return Ret; } @@ -161,22 +161,27 @@ SplitData*GetSplit(int SplitID){ return SP; } void HandleChunk(Client*c,const std::string&Data){ - int pos1 = int(Data.find(':'))+1, - pos2 = int(Data.find(':',pos1)), - pos3 = int(Data.find('/')), - pos4 = int(Data.find('|')); - if(pos1 == std::string::npos)return; - int Max = stoi(Data.substr(pos3+1,pos1-pos3-2)); - int Current = stoi(Data.substr(2,pos3-2)); - int ID = stoi(Data.substr(pos1,pos4-pos1)); - int SplitID = stoi(Data.substr(pos4+1,pos2-pos4-1)); - std::string ack = Sec("TRG:") + Data.substr(pos1,pos4-pos1); + int pos = FC(Data,"|",5); + if(pos == -1)return; + std::stringstream ss(Data.substr(0,pos++)); + std::string t; + int I = -1; + //Current Max ID SID + std::vector Num(4,0); + while (std::getline(ss, t, '|')) { + if(I != -1)Num.at(I) = std::stoi(t); + I++; + } + std::string ack = "TRG:" + std::to_string(Num.at(2)); UDPSend(c,ack); - if(Handled(c,ID))return; - SplitData* SData = GetSplit(SplitID); - SData->Total = Max; - SData->ID = SplitID; - SData->Fragments.insert(std::make_pair(Current,Data.substr(pos2+1))); + if(Handled(c,Num.at(2))){ + return; + } + std::string Packet = Data.substr(pos); + SplitData* SData = GetSplit(Num.at(3)); + SData->Total = Num.at(1); + SData->ID = Num.at(3); + SData->Fragments.insert(std::make_pair(Num.at(0),Packet)); if(SData->Fragments.size() == SData->Total){ std::string ToHandle; for(const std::pair& a : SData->Fragments){ @@ -184,36 +189,56 @@ void HandleChunk(Client*c,const std::string&Data){ } GParser(c,ToHandle); SplitPackets.erase(SData); + delete SData; + SData = nullptr; } } void UDPParser(Client*c,std::string Packet){ - if(Packet.substr(0,4) == Sec("ABG:")){ + if(Packet.substr(0,4) == "ABG:"){ Packet = DeComp(Packet.substr(4)); } - if(Packet.substr(0,4) == Sec("TRG:")){ + if(Packet.substr(0,4) == "TRG:"){ std::string pkt = Packet.substr(4); if(Packet.find_first_not_of("0123456789") == -1){ AckID(stoi(Packet)); } return; - }else if(Packet.substr(0,3) == Sec("BD:")){ + }else if(Packet.substr(0,3) == "BD:"){ auto pos = Packet.find(':',4); int ID = stoi(Packet.substr(3,pos-3)); - std::string pkt = Sec("TRG:") + std::to_string(ID); + std::string pkt = "TRG:" + std::to_string(ID); UDPSend(c,pkt); if(!Handled(c,ID)) { pkt = Packet.substr(pos + 1); GParser(c, pkt); } return; - }else if(Packet.substr(0,2) == Sec("SC")){ + }else if(Packet.substr(0,2) == "SC"){ HandleChunk(c,Packet); return; } GParser(c,Packet); } -#include -void StartLoop(); +void LOOP(){ + while(UDPSock != -1) { + for (PacketData* p : DataAcks){ + if(p != nullptr) { + if (p->Client == nullptr || p->Client->GetTCPSock() == -1) { + DataAcks.erase(p); + break; + } + if (p->Tries < 15) { + UDPSend(p->Client, p->Data); + p->Tries++; + } else { + DataAcks.erase(p); + break; + } + } + } + std::this_thread::sleep_for(std::chrono::milliseconds(300)); + } +} [[noreturn]] void UDPServerMain(){ WSADATA data; if (WSAStartup(514, &data)){ @@ -237,7 +262,8 @@ void StartLoop(); } DataAcks.clear(); - StartLoop(); + std::thread Ack(LOOP); + Ack.detach(); info(Sec("Vehicle data network online on port ")+std::to_string(Port)+Sec(" with a Max of ")+std::to_string(MaxPlayers)+Sec(" Clients")); while (true){ @@ -253,38 +279,11 @@ void StartLoop(); if(c != nullptr && c->GetID() == ID){ c->SetUDPAddr(client); c->isConnected = true; - std::thread Parse(UDPParser,c,Data.substr(2)); - Parse.detach(); + UDPParser(c,Data.substr(2)); } } } /*closesocket(UDPSock); WSACleanup(); return;*/ -} -void LOOP(){ - while(UDPSock != -1) { - for (PacketData* p : DataAcks){ - if(p != nullptr) { - if (p->Client == nullptr || p->Client->GetTCPSock() == -1) { - DataAcks.erase(p); - break; - } - if (p->Tries < 20) { - UDPSend(p->Client, p->Data); - p->Tries++; - } else { - DataAcks.erase(p); - delete p; - p = nullptr; - break; - } - } - } - std::this_thread::sleep_for(std::chrono::milliseconds(200)); - } -} -void StartLoop(){ - std::thread Ack(LOOP); - Ack.detach(); -} +} \ No newline at end of file