From 6dfeba1e4997847bd261673726bcdba527b0a558 Mon Sep 17 00:00:00 2001 From: Anonymous275 <36374260+Anonymous-275@users.noreply.github.com> Date: Mon, 31 Jan 2022 23:39:42 +0200 Subject: [PATCH] Network, bug fixes, more patters, GELua and a lot more --- CMakeLists.txt | 7 +- include/Compressor.h | 13 ++ include/Launcher.h | 14 +- include/Memory/BeamNG.h | 17 +- include/Memory/Definitions.h | 12 +- include/Memory/GELua.h | 43 +++++ include/Memory/IPC.h | 7 +- include/Memory/Memory.h | 2 +- include/Memory/Patterns.h | 20 +++ include/Server.h | 72 ++++++++ src/Handler.cpp | 93 ++++++++++ src/Launcher.cpp | 41 ++++- src/Memory/BeamNG.cpp | 92 ++++++---- src/Memory/Definitions.cpp | 16 ++ src/Memory/GELua.cpp | 38 +++++ src/Memory/IPC.cpp | 18 +- src/Memory/Memory.cpp | 6 +- src/Network/Compressor.cpp | 52 ++++++ src/Network/Resources.cpp | 317 +++++++++++++++++++++++++++++++++++ src/Network/Server.cpp | 302 +++++++++++++++++++++++++++++++++ src/main.cpp | 2 +- 21 files changed, 1130 insertions(+), 54 deletions(-) create mode 100644 include/Compressor.h create mode 100644 include/Memory/GELua.h create mode 100644 include/Server.h create mode 100644 src/Handler.cpp create mode 100644 src/Memory/Definitions.cpp create mode 100644 src/Memory/GELua.cpp create mode 100644 src/Network/Compressor.cpp create mode 100644 src/Network/Resources.cpp create mode 100644 src/Network/Server.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 7848870..d2a7ae0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -31,16 +31,21 @@ set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DDEBUG") add_executable(${PROJECT_NAME} src/main.cpp include/easyloggingpp/src/easylogging++.cc - src/Launcher.cpp include/Launcher.h include/Memory/Definitions.h + src/Launcher.cpp include/Launcher.h + src/Memory/Definitions.cpp include/Memory/Definitions.h src/Memory/Memory.cpp include/Memory/Memory.h include/Memory/Patterns.h src/Memory/Detours.cpp include/Memory/Detours.h src/Memory/BeamNG.cpp include/Memory/BeamNG.h + src/Memory/GELua.cpp include/Memory/GELua.h src/Memory/IPC.cpp include/Memory/IPC.h src/Logger.cpp include/Logger.h src/gui/Gui.cpp include/Json.h src/gui/gifs.cpp src/gui/gifs.h src/Network/Http.cpp include/Http.h + src/Network/Server.cpp include/Server.h src/Network/Login.cpp src/Network/Update.cpp + src/Network/Compressor.cpp include/Compressor.h + src/Handler.cpp src/Network/Resources.cpp src/Discord.cpp src/Config.cpp ) diff --git a/include/Compressor.h b/include/Compressor.h new file mode 100644 index 0000000..03d2fc4 --- /dev/null +++ b/include/Compressor.h @@ -0,0 +1,13 @@ +/// +/// Created by Anonymous275 on 1/30/22 +/// Copyright (c) 2021-present Anonymous275 read the LICENSE file for more info. +/// + +#pragma once +#include + +class Zlib { +public: + static std::string DeComp(std::string Compressed); + static std::string Comp(std::string Data); +}; \ No newline at end of file diff --git a/include/Launcher.h b/include/Launcher.h index 4d122c2..d9e6032 100644 --- a/include/Launcher.h +++ b/include/Launcher.h @@ -4,10 +4,12 @@ /// #pragma once +#include "Memory/IPC.h" #include -#include +#include "Server.h" #include + namespace fs = std::filesystem; struct VersionParser { @@ -25,6 +27,7 @@ public: //constructors public: //available functions static void StaticAbort(Launcher* Instance = nullptr); std::string Login(const std::string& fields); + void SendIPC(const std::string& Data, bool core = true); void RunDiscordRPC(); void QueryRegistry(); void WaitForGame(); @@ -36,11 +39,15 @@ public: //Getters and Setters void setDiscordMessage(const std::string& message); static void setExit(bool exit) noexcept; const std::string& getFullVersion(); + const std::string& getMPUserPath(); static bool Terminated() noexcept; + const std::string& getPublicKey(); const std::string& getUserRole(); const std::string& getVersion(); static bool getExit() noexcept; + private: //functions + void HandleIPC(const std::string& Data); std::string GetLocalAppdata(); void UpdatePresence(); void AdminRelaunch(); @@ -50,6 +57,7 @@ private: //functions void ResetMods(); void EnableMP(); void Relaunch(); + void ListenIPC(); void Abort(); private: //variables uint32_t GamePID{0}; @@ -60,16 +68,20 @@ private: //variables std::string BeamRoot{}; std::string UserRole{}; std::string PublicKey{}; + std::thread IPCSystem{}; std::thread DiscordRPC{}; std::string MPUserPath{}; std::string BeamVersion{}; std::string BeamUserPath{}; std::string DiscordMessage{}; std::string Version{"3.0"}; + Server ServerHandler{this}; std::string TargetBuild{"default"}; static std::atomic Shutdown, Exit; std::string FullVersion{Version + ".0"}; VersionParser SupportedVersion{"0.24.1.1"}; + IPC IPCToGame{"BeamMP_OUT", "BeamMP_Sem1", "BeamMP_Sem2", 0x1900000}; + IPC IPCFromGame{"BeamMP_IN", "BeamMP_Sem3", "BeamMP_Sem4", 0x1900000}; }; class ShutdownException : public std::runtime_error { diff --git a/include/Memory/BeamNG.h b/include/Memory/BeamNG.h index aa4d831..cd28f74 100644 --- a/include/Memory/BeamNG.h +++ b/include/Memory/BeamNG.h @@ -5,26 +5,23 @@ #pragma once #include "Memory/Detours.h" -#include "Definitions.h" -#include +#include "Memory/GELua.h" +#include "Memory/IPC.h" #include +#include class BeamNG { public: static void EntryPoint(); + static void SendIPC(const std::string& Data); private: static std::unique_ptr TickCountDetour; static std::unique_ptr OpenJITDetour; + static std::unique_ptr IPCFromLauncher; + static std::unique_ptr IPCToLauncher; static int lua_open_jit_D(lua_State* State); + static void RegisterGEFunctions(); static uint32_t GetTickCount_D(); static uint64_t GameBaseAddr; static uint64_t DllBaseAddr; - static def::GetTickCount GetTickCount; - static def::lua_open_jit lua_open_jit; - static def::lua_push_fstring lua_push_fstring; - static def::lua_get_field lua_get_field; - static def::lua_p_call lua_p_call; - static const char* GameModule; - static const char* DllModule; - static lua_State* GEState; }; diff --git a/include/Memory/Definitions.h b/include/Memory/Definitions.h index 884eaf7..aaa91a4 100644 --- a/include/Memory/Definitions.h +++ b/include/Memory/Definitions.h @@ -4,12 +4,20 @@ /// #pragma once -typedef struct lua_State lua_State; +#include +typedef struct lua_State lua_State; +typedef int (*lua_CFunction)(lua_State*); +extern int lua_gettop(lua_State *L); namespace def { typedef unsigned long (*GetTickCount)(); typedef int (*lua_open_jit)(lua_State* L); typedef void (*lua_get_field)(lua_State* L, int idx, const char* k); typedef const char* (*lua_push_fstring)(lua_State* L, const char* fmt, ...); - typedef int(*lua_p_call)(lua_State* L, int arg, int res, int err); + typedef int (*lua_p_call)(lua_State* L, int arg, int res, int err); + typedef void (*lua_pushcclosure)(lua_State* L, lua_CFunction fn, int n); + typedef void (*lua_settable)(lua_State* L, int idx); + typedef void (*lua_createtable)(lua_State* L, int narray, int nrec); + typedef void (*lua_setfield)(lua_State* L, int idx, const char* k); + typedef const char* (*lua_tolstring)(lua_State* L, int idx, size_t* len); } diff --git a/include/Memory/GELua.h b/include/Memory/GELua.h new file mode 100644 index 0000000..b2c9b0d --- /dev/null +++ b/include/Memory/GELua.h @@ -0,0 +1,43 @@ +/// +/// Created by Anonymous275 on 1/30/22 +/// Copyright (c) 2021-present Anonymous275 read the LICENSE file for more info. +/// + +#pragma once +#include "Definitions.h" + +class GELua { +public: + static void FindAddresses(); + static def::GetTickCount GetTickCount; + static def::lua_open_jit lua_open_jit; + static def::lua_push_fstring lua_push_fstring; + static def::lua_get_field lua_get_field; + static def::lua_p_call lua_p_call; + static def::lua_createtable lua_createtable; + static def::lua_pushcclosure lua_pushcclosure; + static def::lua_setfield lua_setfield; + static def::lua_settable lua_settable; + static def::lua_tolstring lua_tolstring; + static lua_State* State; +}; + +namespace GELuaTable { + inline void Begin(lua_State* L) { + GELua::lua_createtable(L, 0, 0); + } + inline void End(lua_State* L, const char* name) { + GELua::lua_setfield(L, -10002, name); + } + inline void BeginEntry(lua_State* L, const char* name) { + GELua::lua_push_fstring(L, "%s", name); + } + inline void EndEntry(lua_State* L) { + GELua::lua_settable(L, -3); + } + inline void InsertFunction(lua_State* L, const char* name, lua_CFunction func) { + BeginEntry(L, name); + GELua::lua_pushcclosure(L, func, 0); + EndEntry(L); + } +} diff --git a/include/Memory/IPC.h b/include/Memory/IPC.h index d34d8cf..4cea822 100644 --- a/include/Memory/IPC.h +++ b/include/Memory/IPC.h @@ -14,15 +14,20 @@ public: [[nodiscard]] char* c_str() const noexcept; void send(const std::string& msg) noexcept; [[nodiscard]] void* raw() const noexcept; + [[nodiscard]] bool receive_timed_out() const noexcept; + [[nodiscard]] bool send_timed_out() const noexcept; const std::string& msg() noexcept; void confirm_receive() noexcept; - void receive(); + void try_receive() noexcept; + void receive() noexcept; ~IPC() noexcept; private: void* SemConfHandle_; void* MemoryHandle_; void* SemHandle_; std::string Msg_; + bool SendTimeout; + bool RcvTimeout; size_t Size_; char* Data_; }; diff --git a/include/Memory/Memory.h b/include/Memory/Memory.h index fbc70af..fa83c13 100644 --- a/include/Memory/Memory.h +++ b/include/Memory/Memory.h @@ -8,7 +8,7 @@ class Memory{ public: - static uint64_t FindPattern(const char* module, const char* Pattern, const char* Mask); + static uint64_t FindPattern(const char* module, const char* Pattern[]); static uint64_t GetModuleBase(const char* Name); static void Print(const std::string& msg); static void Inject(uint32_t PID); diff --git a/include/Memory/Patterns.h b/include/Memory/Patterns.h index bd89508..83b1be2 100644 --- a/include/Memory/Patterns.h +++ b/include/Memory/Patterns.h @@ -25,4 +25,24 @@ namespace Patterns { "\x48\x89\x5c\x24\x00\x48\x89\x74\x24\x00\x57\x48\x83\xec\x00\x48\x8b\x59\x00\x41\x8b\xf0\x4c\x63\xda", "xxxx?xxxx?xxxx?xxx?xxxxxx" }; + const char* lua_setfield[2] { + "\x48\x89\x5c\x24\x00\x57\x48\x83\xec\x00\x4d\x8b\xd0\x48\x8b\xd9\xe8\x00\x00\x00\x00\x48\x8b\xf8\x49\xc7\xc0\x00\x00\x00\x00\x90\x49\xff\xc0\x43\x80\x3c\x02\x00\x75\x00\x49\x8b\xd2\x48\x8b\xcb\xe8\x00\x00\x00\x00\x48\xb9\x00\x00\x00\x00\x00\x00\x00\x00\x4c\x8d\x44\x24\x00\x48\x0b\xc1\x48\x8b\xd7\x48\x8b\xcb\x48\x89\x44\x24\x00\xe8\x00\x00\x00\x00\x48\x8b\x53", + "xxxx?xxxx?xxxxxxx????xxxxxx????xxxxxxxx?x?xxxxxxx????xx????????xxxx?xxxxxxxxxxxxx?x????xxx" + }; + const char* lua_createtable[2] { + "\x48\x89\x5c\x24\x00\x48\x89\x74\x24\x00\x57\x48\x83\xec\x00\x4c\x8b\x49\x00\x41\x8b\xf8", + "xxxx?xxxx?xxxx?xxx?xxx" + }; + const char* lua_settable[2] { + "\x40\x53\x48\x83\xec\x00\x48\x8b\xd9\xe8\x00\x00\x00\x00\x4c\x8b\x43\x00\x48\x8b\xd0\x49\x83\xe8\x00\x48\x8b\xcb\xe8\x00\x00\x00\x00\x48\x8b\x53", + "xxxxx?xxxx????xxx?xxxxxx?xxxx????xxx" + }; + const char* lua_pushcclosure[2] { + "\x48\x89\x5c\x24\x00\x48\x89\x74\x24\x00\x57\x48\x83\xec\x00\x48\x8b\xd9\x49\x63\xf8\x48\x8b\x49\x00\x48\x8b\xf2", + "xxxx?xxxx?xxxx?xxxxxxxxx?xxx" + }; + const char* lua_tolstring[2] { + "\x48\x89\x5c\x24\x00\x48\x89\x74\x24\x00\x57\x48\x83\xec\x00\x49\x8b\xf8\x8b\xda\x48\x8b\xf1\xe8", + "xxxx?xxxx?xxxx?xxxxxxxxx" + }; } \ No newline at end of file diff --git a/include/Server.h b/include/Server.h new file mode 100644 index 0000000..1f544da --- /dev/null +++ b/include/Server.h @@ -0,0 +1,72 @@ +/// +/// Created by Anonymous275 on 1/30/22 +/// Copyright (c) 2021-present Anonymous275 read the LICENSE file for more info. +/// + +#pragma once +#include +#include +#include +#include + +struct sockaddr_in; +class Launcher; +class Server { +public: + Server() = delete; + explicit Server(Launcher* Instance); + ~Server(); +public: + void ServerSend(std::string Data, bool Rel); + void Connect(const std::string& Data); + const std::string& getModList(); + const std::string& getUIStatus(); + const std::string& getMap(); + void setModLoaded(); + bool Terminated(); + int getPing() const; + void Close(); +private: + std::chrono::time_point PingStart, PingEnd; + std::string MultiDownload(uint64_t DSock, uint64_t Size, const std::string& Name); + void AsyncUpdate(uint64_t& Rcv,uint64_t Size,const std::string& Name); + std::atomic Terminate{false}, ModLoaded{false}; + char* TCPRcvRaw(uint64_t Sock, uint64_t& GRcv, uint64_t Size); + std::string GetAddress(const std::string& Data); + void InvalidResource(const std::string& File); + void UpdateUl(bool D, const std::string& msg); + std::unique_ptr UDPSockAddress; + void ServerParser(const std::string& Data); + void TCPSend(const std::string& Data); + void UDPParser(std::string Packet); + void SendLarge(std::string Data); + void UDPSend(std::string Data); + void UUl(const std::string& R); + bool CheckBytes(int32_t Bytes); + int KillSocket(uint64_t Dead); + void MultiKill(uint64_t Sock); + Launcher* LauncherInstance; + std::thread TCPConnection; + std::thread UDPConnection; + std::thread AutoPing; + uint64_t TCPSocket = -1; + uint64_t UDPSocket = -1; + void WaitForConfirm(); + std::string UStatus{}; + std::string MStatus{}; + std::string ModList{}; + void TCPClientMain(); + void SyncResources(); + std::string TCPRcv(); + uint64_t InitDSock(); + std::string Auth(); + std::string IP{}; + void UDPClient(); + void PingLoop(); + int ClientID{0}; + void UDPMain(); + void UDPRcv(); + void Abort(); + int Port{0}; + int Ping{0}; +}; \ No newline at end of file diff --git a/src/Handler.cpp b/src/Handler.cpp new file mode 100644 index 0000000..f3e0429 --- /dev/null +++ b/src/Handler.cpp @@ -0,0 +1,93 @@ +/// +/// Created by Anonymous275 on 1/29/22 +/// Copyright (c) 2021-present Anonymous275 read the LICENSE file for more info. +/// + + +#include "Memory/Memory.h" +#include "Memory/BeamNG.h" +#include "Launcher.h" +#include "Logger.h" +#include "Http.h" + +void Launcher::HandleIPC(const std::string& Data) { + char Code = Data.at(0), SubCode = 0; + if(Data.length() > 1)SubCode = Data.at(1); + LOG(INFO) << Data; + switch (Code) { + case 'A': + SendIPC(Data.substr(0,1)); + break; + case 'B': + ServerHandler.Close(); + SendIPC(Code + HTTP::Post("https://backend.beammp.com/servers","")); + LOG(INFO) << "Sent Server List"; + break; + case 'C': + ServerHandler.Connect(Data); + while(ServerHandler.getModList().empty() && !ServerHandler.Terminated()){ + std::this_thread::sleep_for(std::chrono::seconds(1)); + } + if(ServerHandler.getModList() == "-")SendIPC("L"); + else SendIPC("L"+ServerHandler.getModList()); + break; + case 'U': + if(SubCode == 'l')SendIPC("Ul" + ServerHandler.getUIStatus()); + else if(SubCode == 'p') { + if(ServerHandler.getPing() > 800) { + SendIPC("Up-2"); + } else SendIPC("Up" + std::to_string(ServerHandler.getPing())); + } else if(!SubCode) { + std::string Ping; + if(ServerHandler.getPing() > 800)Ping = "-2"; + else Ping = std::to_string(ServerHandler.getPing()); + SendIPC("Ul" + ServerHandler.getUIStatus() + "\nUp" + Ping); + } + break; + case 'M': + SendIPC(ServerHandler.getMap()); + break; + case 'Q': + if(SubCode == 'S') { + ServerHandler.Close(); + } + if(SubCode == 'G')exit(2); + break; + case 'R': //will send mod name + ServerHandler.setModLoaded(); + break; + case 'Z': + SendIPC("Z" + Version); + break; + case 'N': + if(SubCode == 'c') { + SendIPC("N{\"Auth\":"+std::to_string(LoginAuth)+"}"); + } else { + SendIPC("N" + Login(Data.substr(Data.find(':') + 1))); + } + break; + default: + break; + } +} + +void Server::ServerParser(const std::string& Data) { + if(Data.empty())return; + LOG(INFO) << "IPC Parser: " << Data; + char Code = Data.at(0),SubCode = 0; + if(Data.length() > 1)SubCode = Data.at(1); + switch (Code) { + case 'p': + PingEnd = std::chrono::high_resolution_clock::now(); + if(PingStart > PingEnd)Ping = 0; + else Ping = int(std::chrono::duration_cast(PingEnd-PingStart).count()); + return; + case 'M': + MStatus = Data; + UStatus = "done"; + return; + default: + break; + } + LauncherInstance->SendIPC(Data, false); +} diff --git a/src/Launcher.cpp b/src/Launcher.cpp index 312d104..74f1749 100644 --- a/src/Launcher.cpp +++ b/src/Launcher.cpp @@ -13,6 +13,7 @@ #include #include #include +#include LONG WINAPI CrashHandler(EXCEPTION_POINTERS* p) { LOG(ERROR) << "CAUGHT EXCEPTION! Code " << p->ExceptionRecord->ExceptionCode; @@ -35,6 +36,9 @@ void Launcher::Abort() { if(DiscordRPC.joinable()) { DiscordRPC.join(); } + if(IPCSystem.joinable()) { + IPCSystem.join(); + } if(!MPUserPath.empty()) { ResetMods(); } @@ -114,8 +118,8 @@ void Launcher::WaitForGame() { throw ShutdownException("Fatal Error"); } LOG(INFO) << "Game found! PID " << GamePID; + IPCSystem = std::thread(&Launcher::ListenIPC, this); Memory::Inject(GamePID); - //TODO: start IPC setDiscordMessage("In menus"); while(!Shutdown.load() && Memory::GetBeamNGPID() != 0) { std::this_thread::sleep_for(std::chrono::seconds(2)); @@ -123,6 +127,31 @@ void Launcher::WaitForGame() { LOG(INFO) << "Game process was lost"; } +void Launcher::ListenIPC() { + while(!Shutdown.load()) { + IPCFromGame.receive(); + if(!IPCFromGame.receive_timed_out()) { + auto& MSG = IPCFromGame.msg(); + if(MSG[0] == 'C') { + HandleIPC(IPCFromGame.msg().substr(1)); + } else { + ServerHandler.ServerSend(IPCFromGame.msg().substr(1), false); + } + IPCFromGame.confirm_receive(); + } + } +} + +void Launcher::SendIPC(const std::string& Data, bool core) { + static std::mutex Lock; + std::scoped_lock Guard(Lock); + if(core) { + IPCToGame.send("C" + Data); + } else { + IPCToGame.send("G" + Data); + } +} + std::string QueryValue(HKEY& hKey, const char* Name) { DWORD keySize; BYTE buffer[16384]; @@ -194,7 +223,7 @@ const std::string& Launcher::getFullVersion() { return FullVersion; } -const std::string &Launcher::getVersion() { +const std::string& Launcher::getVersion() { return Version; } @@ -213,3 +242,11 @@ bool Launcher::getExit() noexcept { void Launcher::setExit(bool exit) noexcept { Exit.store(exit); } + +const std::string& Launcher::getMPUserPath() { + return MPUserPath; +} + +const std::string &Launcher::getPublicKey() { + return PublicKey; +} diff --git a/src/Memory/BeamNG.cpp b/src/Memory/BeamNG.cpp index e1554c1..9f57a12 100644 --- a/src/Memory/BeamNG.cpp +++ b/src/Memory/BeamNG.cpp @@ -3,55 +3,89 @@ /// Copyright (c) 2021-present Anonymous275 read the LICENSE file for more info. /// -#include "Memory/Patterns.h" #include "Memory/BeamNG.h" #include "Memory/Memory.h" uint32_t BeamNG::GetTickCount_D() { - if(GEState != nullptr){ - lua_get_field(GEState, -10002, "print"); - lua_push_fstring(GEState, "Helloooooo"); - lua_p_call(GEState, 1, 0, 0); - } - return Memory::GetTickCount(); + if(GELua::State != nullptr) { + IPCFromLauncher->try_receive(); + if(!IPCFromLauncher->receive_timed_out()) { + if(IPCFromLauncher->msg()[0] == 'C') { + GELua::lua_get_field(GELua::State, -10002, "handleCoreMsg"); + Memory::Print(std::string("Sending to handleCoreMsg -> ") + char(IPCFromLauncher->msg()[1]) + std::to_string(IPCFromLauncher->msg().size()) ); + } else { + GELua::lua_get_field(GELua::State, -10002, "handleGameMsg"); + Memory::Print(std::string("Sending to handleGameMsg -> ") + char(IPCFromLauncher->msg()[1]) + std::to_string(IPCFromLauncher->msg().size()) ); + } + GELua::lua_push_fstring(GELua::State, "%s", &IPCFromLauncher->c_str()[1]); + GELua::lua_p_call(GELua::State, 1, 0, 0); + IPCFromLauncher->confirm_receive(); + } + } + return Memory::GetTickCount(); } int BeamNG::lua_open_jit_D(lua_State* State) { Memory::Print("Got lua State"); - GEState = State; + GELua::State = State; + RegisterGEFunctions(); OpenJITDetour->Detach(); - int r = lua_open_jit(State); + int r = GELua::lua_open_jit(State); OpenJITDetour->Attach(); return r; } void BeamNG::EntryPoint() { Memory::Print("PID : " + std::to_string(Memory::GetPID())); - GameModule = "BeamNG.drive.x64.exe"; - DllModule = "libbeamng.x64.dll"; - GEState = nullptr; - GameBaseAddr = Memory::GetModuleBase(GameModule); - DllBaseAddr = Memory::GetModuleBase(DllModule); - GetTickCount = reinterpret_cast(Memory::FindPattern(GameModule, Patterns::GetTickCount[0],Patterns::GetTickCount[1])); - lua_open_jit = reinterpret_cast(Memory::FindPattern(GameModule, Patterns::open_jit[0], Patterns::open_jit[1])); - lua_push_fstring = reinterpret_cast(Memory::FindPattern(GameModule, Patterns::push_fstring[0], Patterns::push_fstring[1])); - lua_get_field = reinterpret_cast(Memory::FindPattern(GameModule, Patterns::get_field[0], Patterns::get_field[1])); - lua_p_call = reinterpret_cast(Memory::FindPattern(GameModule, Patterns::p_call[0], Patterns::p_call[1])); - TickCountDetour = std::make_unique((void*)GetTickCount, (void*)GetTickCount_D); + GELua::FindAddresses(); + /*GameBaseAddr = Memory::GetModuleBase(GameModule); + DllBaseAddr = Memory::GetModuleBase(DllModule);*/ + TickCountDetour = std::make_unique((void*)GELua::GetTickCount, (void*)GetTickCount_D); TickCountDetour->Attach(); - OpenJITDetour = std::make_unique((void*)lua_open_jit, (void*)lua_open_jit_D); + OpenJITDetour = std::make_unique((void*)GELua::lua_open_jit, (void*)lua_open_jit_D); OpenJITDetour->Attach(); + IPCToLauncher = std::make_unique("BeamMP_IN", "BeamMP_Sem3", "BeamMP_Sem4", 0x1900000); + IPCFromLauncher = std::make_unique("BeamMP_OUT", "BeamMP_Sem1", "BeamMP_Sem2", 0x1900000); +} + +int Core(lua_State* L) { + if(lua_gettop(L) == 1) { + size_t Size; + const char* Data = GELua::lua_tolstring(L, 1, &Size); + Memory::Print("Core -> " + std::string(Data) + " - " + std::to_string(Size)); + std::string msg(Data, Size); + BeamNG::SendIPC("C" + msg); + } + return 0; +} + +int Game(lua_State* L) { + if(lua_gettop(L) == 1) { + size_t Size; + const char* Data = GELua::lua_tolstring(L, 1, &Size); + Memory::Print("Game -> " + std::string(Data) + " - " + std::to_string(Size)); + std::string msg(Data, Size); + BeamNG::SendIPC("G" + msg); + } + return 0; +} + +void BeamNG::RegisterGEFunctions() { + Memory::Print("Registering GE Functions"); + GELuaTable::Begin(GELua::State); + GELuaTable::InsertFunction(GELua::State, "Core", Core); + GELuaTable::InsertFunction(GELua::State, "Game", Game); + GELuaTable::End(GELua::State, "MP"); + Memory::Print("Registered!"); +} + +void BeamNG::SendIPC(const std::string& Data) { + IPCToLauncher->send(Data); } std::unique_ptr BeamNG::TickCountDetour; std::unique_ptr BeamNG::OpenJITDetour; +std::unique_ptr BeamNG::IPCFromLauncher; +std::unique_ptr BeamNG::IPCToLauncher; uint64_t BeamNG::GameBaseAddr; uint64_t BeamNG::DllBaseAddr; -def::GetTickCount BeamNG::GetTickCount; -def::lua_open_jit BeamNG::lua_open_jit; -def::lua_push_fstring BeamNG::lua_push_fstring; -def::lua_get_field BeamNG::lua_get_field; -def::lua_p_call BeamNG::lua_p_call; -const char* BeamNG::GameModule; -const char* BeamNG::DllModule; -lua_State* BeamNG::GEState; diff --git a/src/Memory/Definitions.cpp b/src/Memory/Definitions.cpp new file mode 100644 index 0000000..868db88 --- /dev/null +++ b/src/Memory/Definitions.cpp @@ -0,0 +1,16 @@ +/// +/// Created by Anonymous275 on 1/30/22 +/// Copyright (c) 2021-present Anonymous275 read the LICENSE file for more info. +/// + +#include "Memory/Definitions.h" +#include "lua/lj_strscan.h" +#include "lua/lj_arch.h" +#include "lua/lj_obj.h" +#include "lua/lj_def.h" +#include "lua/lj_gc.h" +#include "lua/lj_bc.h" + +LUA_API int lua_gettop(lua_State *L) { + return (int)(L->top - L->base); +} diff --git a/src/Memory/GELua.cpp b/src/Memory/GELua.cpp new file mode 100644 index 0000000..74ff4c2 --- /dev/null +++ b/src/Memory/GELua.cpp @@ -0,0 +1,38 @@ +/// +/// Created by Anonymous275 on 1/30/22 +/// Copyright (c) 2021-present Anonymous275 read the LICENSE file for more info. +/// + +#include "Memory/Patterns.h" +#include "Memory/Memory.h" +#include "Memory/GELua.h" + +const char* GameModule = "BeamNG.drive.x64.exe"; +const char* DllModule = "libbeamng.x64.dll"; + +void GELua::FindAddresses() { + GELua::State = nullptr; + GetTickCount = reinterpret_cast(Memory::FindPattern(GameModule, Patterns::GetTickCount)); + lua_open_jit = reinterpret_cast(Memory::FindPattern(GameModule, Patterns::open_jit)); + lua_push_fstring = reinterpret_cast(Memory::FindPattern(GameModule, Patterns::push_fstring)); + lua_get_field = reinterpret_cast(Memory::FindPattern(GameModule, Patterns::get_field)); + lua_p_call = reinterpret_cast(Memory::FindPattern(GameModule, Patterns::p_call)); + lua_createtable = reinterpret_cast(Memory::FindPattern(GameModule, Patterns::lua_createtable)); + lua_pushcclosure = reinterpret_cast(Memory::FindPattern(GameModule, Patterns::lua_pushcclosure)); + lua_setfield = reinterpret_cast(Memory::FindPattern(GameModule, Patterns::lua_setfield)); + lua_settable = reinterpret_cast(Memory::FindPattern(GameModule, Patterns::lua_settable)); + lua_tolstring = reinterpret_cast(Memory::FindPattern(GameModule, Patterns::lua_tolstring)); +} + + +def::GetTickCount GELua::GetTickCount; +def::lua_open_jit GELua::lua_open_jit; +def::lua_push_fstring GELua::lua_push_fstring; +def::lua_get_field GELua::lua_get_field; +def::lua_p_call GELua::lua_p_call; +def::lua_createtable GELua::lua_createtable; +def::lua_pushcclosure GELua::lua_pushcclosure; +def::lua_setfield GELua::lua_setfield; +def::lua_settable GELua::lua_settable; +def::lua_tolstring GELua::lua_tolstring; +lua_State* GELua::State; diff --git a/src/Memory/IPC.cpp b/src/Memory/IPC.cpp index 85a5b7f..0bc2d4a 100644 --- a/src/Memory/IPC.cpp +++ b/src/Memory/IPC.cpp @@ -33,11 +33,15 @@ void IPC::send(const std::string& msg) noexcept { memcpy(Data_ + sizeof(size_t), msg.c_str(), Size); memset(Data_ + sizeof(size_t) + Size, 0, 3); ReleaseSemaphore(SemHandle_, 1, nullptr); - WaitForSingleObject(SemConfHandle_, 5000); + SendTimeout = WaitForSingleObject(SemConfHandle_, 5000) == WAIT_TIMEOUT; } -void IPC::receive() { - WaitForSingleObject(SemHandle_, INFINITE); +void IPC::receive() noexcept { + RcvTimeout = WaitForSingleObject(SemHandle_, 5000) == WAIT_TIMEOUT; +} + +void IPC::try_receive() noexcept { + RcvTimeout = WaitForSingleObject(SemHandle_, 0) == WAIT_TIMEOUT; } size_t IPC::size() const noexcept { @@ -59,6 +63,14 @@ const std::string& IPC::msg() noexcept { return Msg_; } +bool IPC::receive_timed_out() const noexcept { + return RcvTimeout; +} + +bool IPC::send_timed_out() const noexcept { + return SendTimeout; +} + IPC::~IPC() noexcept { UnmapViewOfFile(Data_); CloseHandle(SemHandle_); diff --git a/src/Memory/Memory.cpp b/src/Memory/Memory.cpp index 85b1dbe..4d088b3 100644 --- a/src/Memory/Memory.cpp +++ b/src/Memory/Memory.cpp @@ -40,17 +40,17 @@ uint32_t Memory::GetPID() { return GetCurrentProcessId(); } -uint64_t Memory::FindPattern(const char* module, const char* Pattern, const char* Mask) { +uint64_t Memory::FindPattern(const char* module, const char* Pattern[]) { MODULEINFO mInfo{nullptr}; GetModuleInformation(GetCurrentProcess(), GetModuleHandleA(module), &mInfo, sizeof(MODULEINFO)); auto base = uint64_t(mInfo.lpBaseOfDll); auto size = uint32_t(mInfo.SizeOfImage); - auto len = strlen(Mask); + auto len = strlen(Pattern[1]); for(auto i = 0; i < size - len; i++) { bool found = true; for(auto j = 0; j < len && found; j++) { - found &= Mask[j] == '?' || Pattern[j] == *(char*)(base+i+j); + found &= Pattern[1][j] == '?' || Pattern[0][j] == *(char*)(base+i+j); } if(found) { return base+i; diff --git a/src/Network/Compressor.cpp b/src/Network/Compressor.cpp new file mode 100644 index 0000000..a7e5f78 --- /dev/null +++ b/src/Network/Compressor.cpp @@ -0,0 +1,52 @@ +/// +/// Created by Anonymous275 on 1/30/22 +/// Copyright (c) 2021-present Anonymous275 read the LICENSE file for more info. +/// + +#include "Compressor.h" +#include + +#define Biggest 30000 +std::string Zlib::Comp(std::string Data){ + char*C = new char[Biggest]; + memset(C, 0, Biggest); + z_stream defstream; + defstream.zalloc = Z_NULL; + defstream.zfree = Z_NULL; + defstream.opaque = Z_NULL; + defstream.avail_in = (uInt)Data.length(); + defstream.next_in = (Bytef *)&Data[0]; + defstream.avail_out = Biggest; + defstream.next_out = reinterpret_cast(C); + deflateInit(&defstream, Z_BEST_COMPRESSION); + deflate(&defstream, Z_SYNC_FLUSH); + deflate(&defstream, Z_FINISH); + deflateEnd(&defstream); + uint32_t TO = defstream.total_out; + std::string Ret(TO,0); + memcpy_s(&Ret[0],TO,C,TO); + delete [] C; + return Ret; +} + +std::string Zlib::DeComp(std::string Compressed){ + char*C = new char[Biggest]; + memset(C, 0, Biggest); + z_stream infstream; + infstream.zalloc = Z_NULL; + infstream.zfree = Z_NULL; + infstream.opaque = Z_NULL; + infstream.avail_in = Biggest; + infstream.next_in = (Bytef *)(&Compressed[0]); + infstream.avail_out = Biggest; + infstream.next_out = (Bytef *)(C); + inflateInit(&infstream); + inflate(&infstream, Z_SYNC_FLUSH); + inflate(&infstream, Z_FINISH); + inflateEnd(&infstream); + uint32_t TO = infstream.total_out; + std::string Ret(TO,0); + memcpy_s(&Ret[0],TO,C,TO); + delete [] C; + return Ret; +} diff --git a/src/Network/Resources.cpp b/src/Network/Resources.cpp new file mode 100644 index 0000000..a3c7554 --- /dev/null +++ b/src/Network/Resources.cpp @@ -0,0 +1,317 @@ +/// +/// Created by Anonymous275 on 1/30/22 +/// Copyright (c) 2021-present Anonymous275 read the LICENSE file for more info. +/// + + +#include +#include +#include "Launcher.h" +#include "Server.h" +#include "Logger.h" +#include +#include +#include +#include +#include +#include +#include + +namespace fs = std::filesystem; +std::vector Split(const std::string& String, const std::string& delimiter){ + std::vector Val; + size_t pos; + std::string token,s = String; + while ((pos = s.find(delimiter)) != std::string::npos) { + token = s.substr(0, pos); + if(!token.empty())Val.push_back(token); + s.erase(0, pos + delimiter.length()); + } + if(!s.empty())Val.push_back(s); + return Val; +} + +void CheckForDir(){ + if(!fs::exists("Resources")){ + _wmkdir(L"Resources"); + } +} + +void Server::WaitForConfirm() { + while(!Terminate && !ModLoaded){ + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + } + ModLoaded = false; +} + +void Server::Abort() { + Terminate = true; + LOG(INFO) << "Terminated!"; +} + +std::string Server::Auth() { + TCPSend("VC" + LauncherInstance->getVersion()); + + auto Res = TCPRcv(); + + if(Res.empty() || Res[0] == 'E'){ + Abort(); + return ""; + } + + TCPSend(LauncherInstance->getPublicKey()); + if(Terminate)return ""; + + Res = TCPRcv(); + if(Res.empty() || Res[0] != 'P'){ + Abort(); + return ""; + } + + Res = Res.substr(1); + if(Res.find_first_not_of("0123456789") == std::string::npos){ + ClientID = std::stoi(Res); + }else{ + Abort(); + UUl("Authentication failed!"); + return ""; + } + TCPSend("SR"); + if(Terminate)return ""; + + Res = TCPRcv(); + + if(Res[0] == 'E'){ + Abort(); + return ""; + } + + if(Res.empty() || Res == "-"){ + LOG(INFO) << "Didn't Receive any mods..."; + ModList = "-"; + TCPSend("Done"); + LOG(INFO) << "Done!"; + return ""; + } + return Res; +} + +void Server::UpdateUl(bool D, const std::string& msg) { + if(D)UStatus = "UlDownloading Resource " + msg; + else UStatus = "UlLoading Resource " + msg; +} + +void Server::AsyncUpdate(uint64_t& Rcv,uint64_t Size,const std::string& Name) { + do { + double pr = double(Rcv) / double(Size) * 100; + std::string Per = std::to_string(trunc(pr * 10) / 10); + UpdateUl(true, Name + " (" + Per.substr(0, Per.find('.') + 2) + "%)"); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + }while(!Terminate && Rcv < Size); +} + +char* Server::TCPRcvRaw(uint64_t Sock, uint64_t& GRcv, uint64_t Size) { + if(Sock == -1){ + Terminate = true; + UUl("Invalid Socket"); + return nullptr; + } + char* File = new char[Size]; + uint64_t Rcv = 0; + do{ + int Len = int(Size-Rcv); + if(Len > 1000000)Len = 1000000; + int32_t Temp = recv(Sock, &File[Rcv], Len, MSG_WAITALL); + if(Temp < 1){ + UUl("Socket Closed Code 1"); + KillSocket(Sock); + Terminate = true; + delete[] File; + return nullptr; + } + Rcv += Temp; + GRcv += Temp; + }while(Rcv < Size && !Terminate); + return File; +} + +void Server::MultiKill(uint64_t Sock) { + KillSocket(TCPSocket); + KillSocket(Sock); + Terminate = true; +} + +uint64_t Server::InitDSock() { + SOCKET DSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + SOCKADDR_IN ServerAddr; + if(DSock < 1) { + KillSocket(DSock); + Terminate = true; + return 0; + } + ServerAddr.sin_family = AF_INET; + ServerAddr.sin_port = htons(Port); + inet_pton(AF_INET, IP.c_str(), &ServerAddr.sin_addr); + if(connect(DSock, (SOCKADDR *) &ServerAddr, sizeof(ServerAddr)) != 0){ + KillSocket(DSock); + Terminate = true; + return 0; + } + char Code[2] = {'D', char(ClientID)}; + if(send(DSock,Code,2,0) != 2){ + KillSocket(DSock); + Terminate = true; + return 0; + } + return DSock; +} + +std::string Server::MultiDownload(uint64_t DSock, uint64_t Size, const std::string& Name) { + + uint64_t GRcv = 0, MSize = Size/2, DSize = Size - MSize; + + std::thread Au(&Server::AsyncUpdate, this, std::ref(GRcv), Size, Name); + + std::packaged_task task([&] { return TCPRcvRaw(TCPSocket,GRcv,MSize); }); + std::future f1 = task.get_future(); + std::thread Dt(std::move(task)); + Dt.detach(); + + char* DData = TCPRcvRaw(DSock,GRcv,DSize); + + if(!DData){ + MultiKill(DSock); + return ""; + } + + f1.wait(); + char* MData = f1.get(); + + if(!MData){ + MultiKill(DSock); + return ""; + } + + if(Au.joinable())Au.join(); + + + ///omg yes very ugly my god but i was in a rush will revisit + std::string Ret(Size,0); + memcpy_s(&Ret[0],MSize,MData,MSize); + delete[]MData; + + memcpy_s(&Ret[MSize],DSize,DData,DSize); + delete[]DData; + + return Ret; +} + +void Server::InvalidResource(const std::string& File) { + UUl("Invalid mod \"" + File + "\""); + LOG(WARNING) << "The server tried to sync \"" << File << "\" that is not a .zip file!"; + Terminate = true; +} + +void Server::SyncResources() { + std::string Ret = Auth(); + if(Ret.empty())return; + LOG(INFO) << "Checking Resources..."; + CheckForDir(); + + std::vector list = Split(Ret, ";"); + std::vector FNames(list.begin(), list.begin() + (list.size() / 2)); + std::vector FSizes(list.begin() + (list.size() / 2), list.end()); + list.clear(); + Ret.clear(); + + int Amount = 0,Pos = 0; + std::string a,t; + for(const std::string&name : FNames){ + if(!name.empty()){ + t += name.substr(name.find_last_of('/') + 1) + ";"; + } + } + if(t.empty())ModList = "-"; + else ModList = t; + t.clear(); + for(auto FN = FNames.begin(),FS = FSizes.begin(); FN != FNames.end() && !Terminate; ++FN,++FS) { + auto pos = FN->find_last_of('/'); + auto ZIP = FN->find(".zip"); + if (ZIP == std::string::npos || FN->length() - ZIP != 4) { + InvalidResource(*FN); + return; + } + if (pos == std::string::npos)continue; + Amount++; + } + if(!FNames.empty())LOG(INFO) << "Syncing..."; + SOCKET DSock = InitDSock(); + for(auto FN = FNames.begin(),FS = FSizes.begin(); FN != FNames.end() && !Terminate; ++FN,++FS) { + auto pos = FN->find_last_of('/'); + if (pos != std::string::npos) { + a = "Resources" + FN->substr(pos); + } else continue; + Pos++; + if (fs::exists(a)) { + if (FS->find_first_not_of("0123456789") != std::string::npos)continue; + if (fs::file_size(a) == std::stoull(*FS)){ + UpdateUl(false,std::to_string(Pos) + "/" + std::to_string(Amount) + ": " + a.substr(a.find_last_of('/'))); + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + try { + if(!fs::exists(LauncherInstance->getMPUserPath())){ + fs::create_directories(LauncherInstance->getMPUserPath()); + } + fs::copy_file(a, LauncherInstance->getMPUserPath() + a.substr(a.find_last_of('/')), + fs::copy_options::overwrite_existing); + } catch (std::exception& e) { + LOG(ERROR) << "Failed copy to the mods folder! " << e.what(); + Terminate = true; + continue; + } + WaitForConfirm(); + continue; + }else remove(a.c_str()); + } + CheckForDir(); + std::string FName = a.substr(a.find_last_of('/')); + do { + TCPSend("f" + *FN); + + std::string Data = TCPRcv(); + if (Data == "CO" || Terminate){ + Terminate = true; + UUl("Server cannot find " + FName); + break; + } + + std::string Name = std::to_string(Pos) + "/" + std::to_string(Amount) + ": " + FName; + + Data = MultiDownload(DSock,std::stoull(*FS), Name); + + if(Terminate)break; + UpdateUl(false,std::to_string(Pos)+"/"+std::to_string(Amount)+": "+FName); + std::ofstream LFS; + LFS.open(a.c_str(), std::ios_base::app | std::ios::binary); + if (LFS.is_open()) { + LFS.write(&Data[0], std::streamsize(Data.size())); + LFS.close(); + } + + }while(fs::file_size(a) != std::stoull(*FS) && !Terminate); + if(!Terminate){ + if(!fs::exists(LauncherInstance->getMPUserPath())){ + fs::create_directories(LauncherInstance->getMPUserPath()); + } + fs::copy_file(a,LauncherInstance->getMPUserPath() + FName, fs::copy_options::overwrite_existing); + } + WaitForConfirm(); + } + KillSocket(DSock); + if(!Terminate){ + TCPSend("Done"); + LOG(INFO) << "Done!"; + }else{ + UStatus = "start"; + LOG(INFO) << "Connection Terminated!"; + } +} diff --git a/src/Network/Server.cpp b/src/Network/Server.cpp new file mode 100644 index 0000000..4f5b281 --- /dev/null +++ b/src/Network/Server.cpp @@ -0,0 +1,302 @@ +/// +/// Created by Anonymous275 on 1/30/22 +/// Copyright (c) 2021-present Anonymous275 read the LICENSE file for more info. +/// + +#define WIN32_LEAN_AND_MEAN +#include "Compressor.h" +#include "Server.h" +#include "Launcher.h" +#include +#include +#include +#include "Logger.h" + +Server::Server(Launcher* Instance) : LauncherInstance(Instance) { + WSADATA wsaData; + int iRes = WSAStartup(514, &wsaData); //2.2 + if (iRes != 0) { + LOG(ERROR) << "WSAStartup failed with error: " << iRes; + } + UDPSockAddress = std::make_unique(); +} + +Server::~Server() { + Close(); + WSACleanup(); +} + +void Server::TCPClientMain() { + SOCKADDR_IN ServerAddr; + TCPSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if(TCPSocket == -1) { + LOG(ERROR) << "Socket failed! Error code: " << WSAGetLastError(); + return; + } + ServerAddr.sin_family = AF_INET; + ServerAddr.sin_port = htons(Port); + inet_pton(AF_INET, IP.c_str(), &ServerAddr.sin_addr); + int status = connect(TCPSocket, (SOCKADDR *) &ServerAddr, sizeof(ServerAddr)); + if(status != 0){ + UStatus = "Connection Failed!"; + LOG(ERROR) << "Connect failed! Error code: " << WSAGetLastError(); + Close(); + return; + } + LOG(INFO) << "Connected!"; + char Code = 'C'; + send(TCPSocket, &Code, 1, 0); + SyncResources(); + UDPConnection = std::thread(&Server::UDPMain, this); + while(!Terminate.load()) { + ServerParser(TCPRcv()); + } + LauncherInstance->SendIPC("T", false); + KillSocket(TCPSocket); +} + +void Server::UDPSend(std::string Data) { + if(ClientID == -1 || UDPSocket == -1)return; + if(Data.length() > 400){ + std::string CMP(Zlib::Comp(Data)); + Data = "ABG:" + CMP; + } + std::string Packet = char(ClientID+1) + std::string(":") + Data; + int sendOk = sendto(UDPSocket, Packet.c_str(), int(Packet.size()), 0, (sockaddr*)UDPSockAddress.get(), sizeof(sockaddr_in)); + if (sendOk == SOCKET_ERROR)LOG(ERROR) << "UDP Socket Error Code : " << WSAGetLastError(); +} + +void Server::UDPParser(std::string Packet) { + if(Packet.substr(0,4) == "ABG:"){ + Packet = Zlib::DeComp(Packet.substr(4)); + } + ServerParser(Packet); +} + +void Server::UDPRcv() { + sockaddr_in FromServer{}; + int clientLength = sizeof(FromServer); + ZeroMemory(&FromServer, clientLength); + std::string Ret(10240,0); + if(UDPSocket == -1)return; + int32_t Rcv = recvfrom(UDPSocket, &Ret[0], 10240, 0, (sockaddr*)&FromServer, &clientLength); + if (Rcv == SOCKET_ERROR)return; + UDPParser(Ret.substr(0,Rcv)); +} + +void Server::UDPClient() { + UDPSockAddress->sin_family = AF_INET; + UDPSockAddress->sin_port = htons(Port); + inet_pton(AF_INET, IP.c_str(), &UDPSockAddress->sin_addr); + UDPSocket = socket(AF_INET, SOCK_DGRAM, 0); + LauncherInstance->SendIPC("P"+std::to_string(ClientID), false); + TCPSend("H"); + UDPSend("p"); + while(!Terminate)UDPRcv(); + KillSocket(UDPSocket); +} + +void Server::UDPMain() { + AutoPing = std::thread(&Server::PingLoop, this); + UDPClient(); + Terminate = true; + LOG(INFO) << "Connection terminated"; +} + +void Server::Connect(const std::string& Data) { + ModList.clear(); + Terminate.store(false); + IP = GetAddress(Data.substr(1,Data.find(':')-1)); + if(IP.find('.') == -1){ + if(IP == "DNS")UStatus ="Connection Failed! (DNS Lookup Failed)"; + else UStatus = "Connection Failed! (WSA failed to start)"; + ModList = "-"; + Terminate.store(true); + return; + } + Port = std::stoi(Data.substr(Data.find(':')+1)); + LauncherInstance->CheckKey(); + UStatus = "Loading..."; + Ping = -1; + TCPConnection = std::thread(&Server::TCPClientMain, this); + LOG(INFO) << "Connecting to server"; +} + +void Server::SendLarge(std::string Data) { + if(Data.length() > 400) { + std::string CMP(Zlib::Comp(Data)); + Data = "ABG:" + CMP; + } + TCPSend(Data); +} + +void Server::ServerSend(std::string Data, bool Rel) { + if(Terminate || Data.empty())return; + char C = 0; + int DLen = int(Data.length()); + if(DLen > 3)C = Data.at(0); + bool Ack = C == 'O' || C == 'T'; + if(C == 'N' || C == 'W' || C == 'Y' || C == 'V' || C == 'E' || C == 'C')Rel = true; + if(Ack || Rel) { + if(Ack || DLen > 1000)SendLarge(Data); + else TCPSend(Data); + }else UDPSend(Data); +} + +void Server::PingLoop() { + while(!Terminate){ + ServerSend("p", false); + PingStart = std::chrono::high_resolution_clock::now(); + std::this_thread::sleep_for(std::chrono::seconds (1)); + } +} + +void Server::Close() { + Terminate.store(true); + Ping = -1; + if(TCPConnection.joinable()) { + TCPConnection.join(); + } + if(UDPConnection.joinable()) { + UDPConnection.join(); + } + if(AutoPing.joinable()) { + AutoPing.join(); + } + KillSocket(TCPSocket); + KillSocket(UDPSocket); +} + +const std::string &Server::getMap() { + return MStatus; +} + +bool Server::Terminated() { + return Terminate; +} + +const std::string &Server::getModList() { + return ModList; +} + +int Server::getPing() const { + return Ping; +} + +const std::string& Server::getUIStatus() { + return UStatus; +} + +std::string Server::GetAddress(const std::string& Data) { + if(Data.find_first_not_of("0123456789.") == -1)return Data; + hostent* host; + host = gethostbyname(Data.c_str()); + if(!host){ + LOG(ERROR) << "DNS lookup failed! on " << Data; + return "DNS"; + } + std::string Ret = inet_ntoa(*((struct in_addr *)host->h_addr)); + return Ret; +} + +int Server::KillSocket(uint64_t Dead) { + if(Dead == (SOCKET)-1)return 0; + shutdown(Dead, SD_BOTH); + return closesocket(Dead); +} + +void Server::setModLoaded() { + ModLoaded.store(true); +} + +void Server::UUl(const std::string& R) { + UStatus = "Disconnected: " + R; +} + +bool Server::CheckBytes(int32_t Bytes) { + if (Bytes == 0){ + //debug("(TCP) Connection closing... CheckBytes(16)"); + Terminate = true; + return false; + }else if (Bytes < 0) { + //debug("(TCP CB) recv failed with error: " + std::to_string(WSAGetLastError())); + KillSocket(TCPSocket); + Terminate = true; + return false; + } + return true; +} + +void Server::TCPSend(const std::string& Data) { + + if(TCPSocket == -1) { + Terminate = true; + UUl("Invalid Socket"); + return; + } + + int32_t Size,Sent,Temp; + std::string Send(4,0); + Size = int32_t(Data.size()); + memcpy(&Send[0],&Size,sizeof(Size)); + Send += Data; + // Do not use Size before this point for anything but the header + Sent = 0; + Size += 4; + do{ + if (size_t(Sent) >= Send.size()) { + LOG(ERROR) << "string OOB in " << std::string(__func__); + UUl("TCP Send OOB"); + return; + } + Temp = send(TCPSocket, &Send[Sent], Size - Sent, 0); + if(!CheckBytes(Temp)){ + UUl("Socket Closed Code 2"); + return; + } + Sent += Temp; + }while(Sent < Size); +} + +std::string Server::TCPRcv() { + if(TCPSocket == -1){ + Terminate = true; + UUl("Invalid Socket"); + return ""; + } + int32_t Header,BytesRcv = 0,Temp; + std::vector Data(sizeof(Header)); + do{ + Temp = recv(TCPSocket, &Data[BytesRcv], 4-BytesRcv, 0); + if(!CheckBytes(Temp)){ + UUl("Socket Closed Code 3"); + return ""; + } + BytesRcv += Temp; + }while(BytesRcv < 4); + memcpy(&Header,&Data[0],sizeof(Header)); + + if(!CheckBytes(BytesRcv)){ + UUl("Socket Closed Code 4"); + return ""; + } + Data.resize(Header); + BytesRcv = 0; + do{ + Temp = recv(TCPSocket, &Data[BytesRcv], Header-BytesRcv, 0); + if(!CheckBytes(Temp)){ + UUl("Socket Closed Code 5"); + return ""; + } + BytesRcv += Temp; + }while(BytesRcv < Header); + + std::string Ret(Data.data(),Header); + + if (Ret.substr(0, 4) == "ABG:") { + Ret = Zlib::DeComp(Ret.substr(4)); + } + + if(Ret[0] == 'E')UUl(Ret.substr(1)); + return Ret; +} diff --git a/src/main.cpp b/src/main.cpp index ec0536f..a05e5f4 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -23,7 +23,7 @@ int main(int argc, char* argv[]) { } catch (const std::exception& e) { LOG(FATAL) << e.what(); } - std::this_thread::sleep_for(std::chrono::seconds(5)); + std::this_thread::sleep_for(std::chrono::seconds(2)); Launcher::setExit(true); return 0; }