begin rewrite: add lionkor/commandline

This commit is contained in:
Lion Kortlepel 2021-02-14 13:03:59 +01:00 committed by Anonymous275
parent 6a2ce7faab
commit e5e447c7af
43 changed files with 120 additions and 3536 deletions

6
.gitmodules vendored Normal file
View File

@ -0,0 +1,6 @@
[submodule "commandline"]
path = commandline
url = https://github.com/lionkor/commandline
[submodule "include/commandline"]
path = include/commandline
url = https://github.com/lionkor/commandline

View File

@ -4,16 +4,13 @@ set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DDEBUG")
add_subdirectory("include/commandline")
if (UNIX)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wpedantic -static-libstdc++")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Og -g")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O2 -s -fno-builtin")
elseif (WIN32)
# This might cause issues with old windows headers, but it's worth the trouble to keep the code
# completely cross platform. For fixes to common issues arising from /permissive- visit:
# https://docs.microsoft.com/en-us/cpp/build/reference/permissive-standards-conformance
#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W3 /permissive-")
message(STATUS "MSVC -> forcing use of statically-linked runtime.")
STRING(REPLACE "/MD" "/MT" CMAKE_CXX_FLAGS_RELEASE ${CMAKE_CXX_FLAGS_RELEASE})
STRING(REPLACE "/MDd" "/MTd" CMAKE_CXX_FLAGS_DEBUG ${CMAKE_CXX_FLAGS_DEBUG})
@ -22,10 +19,13 @@ endif ()
find_package(Boost REQUIRED COMPONENTS system thread)
file(GLOB source_files "src/*.cpp" "include/*.h" "include/*/*.h" "include/*/*/*.h" "include/*.hpp" "include/*/*.hpp" "src/*/*.cpp")
add_executable(BeamMP-Server ${source_files})
add_executable(BeamMP-Server
src/main.cpp
src/TConsole.cpp
src/Compat.cpp
)
target_include_directories(BeamMP-Server PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>)
target_include_directories(BeamMP-Server PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" "${CMAKE_CURRENT_SOURCE_DIR}/commandline")
find_package(Lua REQUIRED)
target_include_directories(BeamMP-Server PUBLIC ${Boost_INCLUDE_DIRS} ${LUA_INCLUDE_DIR})
@ -33,11 +33,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})
target_link_libraries(BeamMP-Server z pthread stdc++fs ${Boost_LINK_DIRS} ${LUA_LIBRARIES} dl crypto ${OPENSSL_LIBRARIES} ${Boost_LIBRARIES} commandline)
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})
target_link_libraries(BeamMP-Server PRIVATE ws2_32 ZLIB::ZLIB ${LUA_LIBRARIES} ${Boost_LIBRARIES} ${OPENSSL_LIBRARIES} commandline)
endif ()

View File

@ -1,91 +0,0 @@
// Copyright (c) 2019-present Anonymous275.
// BeamMP Server code is not in the public domain and is not free software.
// One must be granted explicit permission by the copyright holder in order to modify or distribute any part of the source or binaries.
// Anything else is prohibited. Modified works may not be published and have be upstreamed to the official repository.
///
/// Created by Anonymous275 on 5/8/2020
///
#pragma once
#ifdef WIN32
#include <WS2tcpip.h>
#else
#include <arpa/inet.h>
#define SOCKET int
#endif
#include "CustomAssert.h"
#include <algorithm>
#include <chrono>
#include <set>
#include <string>
#include <vector>
struct VData {
int ID = -1;
std::string Data;
};
class Client {
private:
std::set<std::unique_ptr<VData>> VehicleData; //ID and Data;
std::string Name = "Unknown Client";
SOCKET SOCK[2] { SOCKET(-1) };
sockaddr_in UDPADDR;
std::string Role;
std::string DID;
int Status = 0;
int ID = -1;
public:
bool isConnected = false;
bool isSynced = false;
bool isGuest = false;
void AddNewCar(int ident, const std::string& Data);
void SetCarData(int ident, const std::string& Data);
std::set<std::unique_ptr<VData>>& GetAllCars();
void SetName(const std::string& name) { Name = name; }
void SetRoles(const std::string& role) { Role = role; }
std::string GetCarData(int ident);
void SetUDPAddr(sockaddr_in Addr) { UDPADDR = Addr; }
void SetDownSock(SOCKET CSock) { SOCK[1] = CSock; }
void SetTCPSock(SOCKET CSock) { SOCK[0] = CSock; }
void SetStatus(int status) { Status = status; }
void DeleteCar(int ident);
sockaddr_in GetUDPAddr() { return UDPADDR; }
std::string GetRoles() { return Role; }
std::string GetName() { return Name; }
SOCKET GetDownSock() { return SOCK[1]; }
SOCKET GetTCPSock() { return SOCK[0]; }
void SetID(int ID) { this->ID = ID; }
int GetOpenCarID();
int GetCarCount();
void ClearCars();
int GetStatus() { return Status; }
int GetID() { return ID; }
};
struct ClientInterface {
std::set<std::unique_ptr<Client>> Clients;
void RemoveClient(Client*& c) {
Assert(c);
c->ClearCars();
auto Iter = std::find_if(Clients.begin(), Clients.end(), [&](auto& ptr) {
return c == ptr.get();
});
Assert(Iter != Clients.end());
if (Iter == Clients.end()) {
return;
}
Clients.erase(Iter);
c = nullptr;
}
void AddClient(Client*&& c) {
Assert(c);
Clients.insert(std::unique_ptr<Client>(c));
}
int Size() {
return int(Clients.size());
}
};
extern std::unique_ptr<ClientInterface> CI;

17
include/Compat.h Normal file
View File

@ -0,0 +1,17 @@
#pragma once
// Unix - Win32 compatibility stuff
#ifdef WIN32
#include <conio.h>
#include <windows.h>
#else // *nix
typedef unsigned long DWORD, *PDWORD, *LPDWORD;
#include <termios.h>
#include <unistd.h>
#endif // WIN32
#ifndef WIN32
char _getch(void);
#endif // !WIN32

View File

@ -1,11 +0,0 @@
// Copyright (c) 2019-present Anonymous275.
// BeamMP Server code is not in the public domain and is not free software.
// One must be granted explicit permission by the copyright holder in order to modify or distribute any part of the source or binaries.
// Anything else is prohibited. Modified works may not be published and have be upstreamed to the official repository.
///
/// Created by Anonymous275 on 7/24/2020
///
#pragma once
#include <string>
std::string Comp(std::string Data);
std::string DeComp(std::string Compressed);

View File

@ -1,12 +0,0 @@
// Copyright (c) 2019-present Anonymous275.
// BeamMP Server code is not in the public domain and is not free software.
// One must be granted explicit permission by the copyright holder in order to modify or distribute any part of the source or binaries.
// Anything else is prohibited. Modified works may not be published and have be upstreamed to the official repository.
///
/// Created by Anonymous275 on 7/18/2020
///
#pragma once
#include <string>
#include <unordered_map>
std::string HttpRequest(const std::string& host, int port, const std::string& target);
std::string PostHTTP(const std::string& host, const std::string& target, const std::unordered_map<std::string, std::string>& fields, const std::string& body, bool json);

View File

@ -1,67 +0,0 @@
// Author: lionkor
/*
* Asserts are to be used anywhere where assumptions about state are made
* implicitly. AssertNotReachable is used where code should never go, like in
* default switch cases which shouldn't trigger. They make it explicit
* that a place cannot normally be reached and make it an error if they do.
*/
#pragma once
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <sstream>
#include <thread>
#include "Logger.h"
static const char* const ANSI_RESET = "\u001b[0m";
static const char* const ANSI_BLACK = "\u001b[30m";
static const char* const ANSI_RED = "\u001b[31m";
static const char* const ANSI_GREEN = "\u001b[32m";
static const char* const ANSI_YELLOW = "\u001b[33m";
static const char* const ANSI_BLUE = "\u001b[34m";
static const char* const ANSI_MAGENTA = "\u001b[35m";
static const char* const ANSI_CYAN = "\u001b[36m";
static const char* const ANSI_WHITE = "\u001b[37m";
static const char* const ANSI_BLACK_BOLD = "\u001b[30;1m";
static const char* const ANSI_RED_BOLD = "\u001b[31;1m";
static const char* const ANSI_GREEN_BOLD = "\u001b[32;1m";
static const char* const ANSI_YELLOW_BOLD = "\u001b[33;1m";
static const char* const ANSI_BLUE_BOLD = "\u001b[34;1m";
static const char* const ANSI_MAGENTA_BOLD = "\u001b[35;1m";
static const char* const ANSI_CYAN_BOLD = "\u001b[36;1m";
static const char* const ANSI_WHITE_BOLD = "\u001b[37;1m";
static const char* const ANSI_BOLD = "\u001b[1m";
static const char* const ANSI_UNDERLINE = "\u001b[4m";
#if DEBUG
inline void _assert([[maybe_unused]] const char* file, [[maybe_unused]] const char* function, [[maybe_unused]] unsigned line,
[[maybe_unused]] const char* condition_string, [[maybe_unused]] bool result) {
if (!result) {
std::cout << std::flush << "(debug build) TID "
<< std::this_thread::get_id() << ": ASSERTION FAILED: at "
<< file << ":" << line << " \n\t-> in "
<< function << ", Line " << line << ": \n\t\t-> "
<< "Failed Condition: " << condition_string << std::endl;
std::cout << "... terminating ..." << std::endl;
abort();
}
}
#define Assert(cond) _assert(__FILE__, __func__, __LINE__, #cond, (cond))
#define AssertNotReachable() _assert(__FILE__, __func__, __LINE__, "reached unreachable code", false)
#else
// In release build, these macros turn into NOPs. The compiler will optimize these out.
#define Assert(x) \
do { \
} while (false)
#define AssertNotReachable() \
do { \
} while (false)
#endif // DEBUG

16
include/IThreaded.h Normal file
View File

@ -0,0 +1,16 @@
#pragma once
#include <thread>
// pure virtual class to be inherited from by classes which intend to be threaded
class IThreaded {
public:
IThreaded()
// invokes operator() on this object
: _Thread(std::thread([this] { (*this)(); })) { }
virtual void operator()() = 0;
protected:
std::thread _Thread;
};

View File

@ -1,12 +0,0 @@
// Copyright (c) 2019-present Anonymous275.
// BeamMP Server code is not in the public domain and is not free software.
// One must be granted explicit permission by the copyright holder in order to modify or distribute any part of the source or binaries.
// Anything else is prohibited. Modified works may not be published and have be upstreamed to the official repository.
///
/// Created by Anonymous275 on 11/27/2020
///
#pragma once
#include "rapidjson/document.h"
#include "rapidjson/stringbuffer.h"
#include "rapidjson/writer.h"
namespace json = rapidjson;

View File

@ -1,21 +0,0 @@
// Copyright (c) 2019-present Anonymous275.
// BeamMP Server code is not in the public domain and is not free software.
// One must be granted explicit permission by the copyright holder in order to modify or distribute any part of the source or binaries.
// Anything else is prohibited. Modified works may not be published and have be upstreamed to the official repository.
///
/// Created by Anonymous275 on 4/2/2020.
///
#pragma once
#include <iostream>
#include <mutex>
#include <string>
void InitLog();
#define DebugPrintTID() DebugPrintTIDInternal(__func__, false)
void DebugPrintTIDInternal(const std::string& func, bool overwrite = true); // prints the current thread id in debug mode, to make tracing of crashes and asserts easier
void ConsoleOut(const std::string& msg);
void except(const std::string& toPrint);
void debug(const std::string& toPrint);
void error(const std::string& toPrint);
void info(const std::string& toPrint);
void warn(const std::string& toPrint);

View File

@ -1,81 +0,0 @@
// Copyright (c) 2019-present Anonymous275.
// BeamMP Server code is not in the public domain and is not free software.
// One must be granted explicit permission by the copyright holder in order to modify or distribute any part of the source or binaries.
// Anything else is prohibited. Modified works may not be published and have be upstreamed to the official repository.
///
/// Created by Anonymous275 on 5/20/2020
///
#pragma once
#include <any>
#include <filesystem>
#include <iostream>
#include <lua.hpp>
#include <memory>
#include <mutex>
#include <set>
#include <thread>
#include <vector>
namespace fs = std::filesystem;
struct LuaArg {
std::vector<std::any> args;
void PushArgs(lua_State* State) {
for (std::any arg : args) {
if (!arg.has_value())
return;
std::string Type = arg.type().name();
if (Type.find("bool") != std::string::npos) {
lua_pushboolean(State, std::any_cast<bool>(arg));
}
if (Type.find("basic_string") != std::string::npos || Type.find("char") != std::string::npos) {
lua_pushstring(State, std::any_cast<std::string>(arg).c_str());
}
if (Type.find("int") != std::string::npos) {
lua_pushinteger(State, std::any_cast<int>(arg));
}
if (Type.find("float") != std::string::npos) {
lua_pushnumber(State, std::any_cast<float>(arg));
}
}
}
};
class Lua {
private:
std::set<std::pair<std::string, std::string>> _RegisteredEvents;
lua_State* luaState { nullptr };
fs::file_time_type _LastWrote;
std::string _PluginName {};
std::string _FileName {};
bool _StopThread = false;
bool _Console = false;
public:
void Init();
void RegisterEvent(const std::string& Event, const std::string& FunctionName);
std::string GetRegistered(const std::string& Event) const;
void UnRegisterEvent(const std::string& Event);
void SetLastWrite(fs::file_time_type time);
bool IsRegistered(const std::string& Event);
void SetPluginName(const std::string& Name);
void Execute(const std::string& Command);
void SetFileName(const std::string& Name);
fs::file_time_type GetLastWrite();
std::string GetPluginName() const;
std::string GetFileName() const;
lua_State* GetState();
const lua_State* GetState() const;
std::string GetOrigin();
std::mutex Lock;
void Reload();
Lua(const std::string& PluginName, const std::string& FileName, fs::file_time_type LastWrote, bool Console = false);
Lua(bool Console = false);
~Lua();
void SetStopThread(bool StopThread) { _StopThread = StopThread; }
bool GetStopThread() const { return _StopThread; }
};
std::any CallFunction(Lua* lua, const std::string& FuncName, std::shared_ptr<LuaArg> args);
std::any TriggerLuaEvent(const std::string& Event, bool local, Lua* Caller, std::shared_ptr<LuaArg> arg, bool Wait);
extern std::set<std::unique_ptr<Lua>> PluginEngine;

View File

@ -1,25 +0,0 @@
// Copyright (c) 2019-present Anonymous275.
// BeamMP Server code is not in the public domain and is not free software.
// One must be granted explicit permission by the copyright holder in order to modify or distribute any part of the source or binaries.
// Anything else is prohibited. Modified works may not be published and have be upstreamed to the official repository.
///
/// Created by Anonymous275 on 7/31/2020
///
#pragma once
#include "Client.hpp"
#include <string>
void TCPServerMain();
void UpdatePlayers();
void OnConnect(Client* c);
void TCPClient(Client* c);
std::string TCPRcv(Client* c);
void SyncResources(Client* c);
[[noreturn]] void UDPServerMain();
void OnDisconnect(Client* c, bool kicked);
void UDPSend(Client* c, std::string Data);
void SendLarge(Client* c, std::string Data);
bool TCPSend(Client* c, const std::string& Data);
void GParser(Client* c, const std::string& Packet);
std::string StaticReason(bool Set, const std::string& R);
void Respond(Client* c, const std::string& MSG, bool Rel);
void SendToAll(Client* c, const std::string& Data, bool Self, bool Rel);

View File

@ -1,19 +0,0 @@
// Author: lionkor
#pragma once
/*
* An RWMutex allows multiple simultaneous readlocks but only one writelock at a time,
* and write locks and read locks are mutually exclusive.
*/
#include <shared_mutex>
// Use ReadLock(m) and WriteLock(m) to lock it.
using RWMutex = std::shared_mutex;
// Construct with an RWMutex as a non-const reference.
// locks the mutex in lock_shared mode (for reading). Locking in a thread that already owns a lock
// i.e. locking multiple times successively is UB. Construction may be blocking. Destruction is guaranteed to release the lock.
using ReadLock = std::shared_lock<RWMutex>;
// Construct with an RWMutex as a non-const reference.
// locks the mutex for writing. Construction may be blocking. Destruction is guaranteed to release the lock.
using WriteLock = std::unique_lock<RWMutex>;

View File

@ -1,15 +0,0 @@
// Copyright (c) 2019-present Anonymous275.
// BeamMP Server code is not in the public domain and is not free software.
// One must be granted explicit permission by the copyright holder in order to modify or distribute any part of the source or binaries.
// Anything else is prohibited. Modified works may not be published and have be upstreamed to the official repository.
///
/// Created by Anonymous275 on 7/28/2020
///
#pragma once
#ifdef __linux
#define EXCEPTION_POINTERS void
#else
#include <WS2tcpip.h>
#endif
#include <string>
int Handle(EXCEPTION_POINTERS* ep, char* Origin);

View File

@ -1,34 +0,0 @@
// Copyright (c) 2019-present Anonymous275.
// BeamMP Server code is not in the public domain and is not free software.
// One must be granted explicit permission by the copyright holder in order to modify or distribute any part of the source or binaries.
// Anything else is prohibited. Modified works may not be published and have be upstreamed to the official repository.
///
/// Created by Anonymous275 on 7/28/2020
///
#pragma once
#include <chrono>
#include <string>
extern std::string ServerName;
extern std::string ServerDesc;
extern std::string StatReport;
extern std::string FileSizes;
extern std::string Resource;
extern std::string FileList;
extern std::string CustomIP;
extern std::string MapName;
extern uint64_t MaxModSize;
extern std::string Key;
std::string GetSVer();
std::string GetCVer();
extern int MaxPlayers;
extern int ModsLoaded;
extern bool Private;
extern int MaxCars;
extern bool Debug;
extern int Port;
extern int PPS;
extern std::chrono::time_point<std::chrono::high_resolution_clock> StartTime;
inline std::chrono::seconds GetUptimeInSeconds() {
return std::chrono::duration_cast<std::chrono::seconds>(std::chrono::high_resolution_clock::now() - StartTime);
}

View File

@ -1,16 +0,0 @@
// Copyright (c) 2019-present Anonymous275.
// BeamMP Server code is not in the public domain and is not free software.
// One must be granted explicit permission by the copyright holder in order to modify or distribute any part of the source or binaries.
// Anything else is prohibited. Modified works may not be published and have be upstreamed to the official repository.
///
/// Created by Anonymous275 on 7/28/2020
///
#pragma once
void InitServer(int argc, char* argv[]);
void ConsoleInit();
void InitConfig();
void InitLua();
void InitRes();
void HBInit();
void StatInit();
void NetMain();

12
include/TConsole.h Normal file
View File

@ -0,0 +1,12 @@
#pragma once
#include <atomic>
#include <commandline/commandline.h>
class TConsole {
public:
TConsole();
private:
Commandline _Commandline;
};

View File

@ -1,38 +0,0 @@
// Author: lionkor
#pragma once
// This header defines unix equivalents of common win32 functions.
#ifndef WIN32
#include "CustomAssert.h"
#include <cstring>
#include <sys/socket.h>
#include <unistd.h>
// ZeroMemory is just a {0} or a memset(addr, 0, len), and it's a macro on MSVC
inline void ZeroMemory([[maybe_unused]] void* dst, [[maybe_unused]] size_t len) {
[[maybe_unused]] auto res = std::memset(dst, 0, len);
Assert(res != nullptr);
}
// provides unix equivalent of closesocket call in win32
inline void CloseSocketProper(int socket) {
shutdown(socket, SHUT_RDWR);
close(socket);
}
#ifndef __try
#define __try
#endif
#ifndef __except
#define __except (x) /**/
#endif
#else // win32
inline void CloseSocketProper(uint64_t socket) {
shutdown(socket, SD_BOTH);
closesocket(socket);
}
#endif // WIN32

1
include/commandline Submodule

@ -0,0 +1 @@
Subproject commit 01ec8e0388ee4759813abf12c9619ac294721eab

35
src/Compat.cpp Normal file
View File

@ -0,0 +1,35 @@
#include "Compat.h"
#ifndef WIN32
static struct termios old, current;
void initTermios(int echo) {
tcgetattr(0, &old); /* grab old terminal i/o settings */
current = old; /* make new settings same as old settings */
current.c_lflag &= ~ICANON; /* disable buffered i/o */
if (echo) {
current.c_lflag |= ECHO; /* set echo mode */
} else {
current.c_lflag &= ~ECHO; /* set no echo mode */
}
tcsetattr(0, TCSANOW, &current); /* use these new terminal i/o settings now */
}
void resetTermios(void) {
tcsetattr(0, TCSANOW, &old);
}
char getch_(int echo) {
char ch;
initTermios(echo);
read(STDIN_FILENO, &ch, 1);
resetTermios();
return ch;
}
char _getch(void) {
return getch_(0);
}
#endif // !WIN32

View File

@ -1,56 +0,0 @@
// Copyright (c) 2019-present Anonymous275.
// BeamMP Server code is not in the public domain and is not free software.
// One must be granted explicit permission by the copyright holder in order to modify or distribute any part of the source or binaries.
// Anything else is prohibited. Modified works may not be published and have be upstreamed to the official repository.
///
/// Created by Anonymous275 on 7/15/2020
///
#include <algorithm>
#include <array>
#include <iostream>
#include <zlib.h>
#define Biggest 30000
std::string Comp(std::string Data) {
std::array<char, Biggest> C {};
// obsolete
C.fill(0);
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<Bytef*>(C.data());
deflateInit(&defstream, Z_BEST_COMPRESSION);
deflate(&defstream, Z_SYNC_FLUSH);
deflate(&defstream, Z_FINISH);
deflateEnd(&defstream);
size_t TO = defstream.total_out;
std::string Ret(TO, 0);
std::copy_n(C.begin(), TO, Ret.begin());
return Ret;
}
std::string DeComp(std::string Compressed) {
std::array<char, Biggest> C {};
// not needed
C.fill(0);
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.data());
inflateInit(&infstream);
inflate(&infstream, Z_SYNC_FLUSH);
inflate(&infstream, Z_FINISH);
inflateEnd(&infstream);
size_t TO = infstream.total_out;
std::string Ret(TO, 0);
std::copy_n(C.begin(), TO, Ret.begin());
return Ret;
}

View File

@ -1,263 +0,0 @@
// Copyright (c) 2019-present Anonymous275.
// BeamMP Server code is not in the public domain and is not free software.
// One must be granted explicit permission by the copyright holder in order to modify or distribute any part of the source or binaries.
// Anything else is prohibited. Modified works may not be published and have be upstreamed to the official repository.
///
/// Created by Anonymous275 on 10/29/2020
///
#include "Lua/LuaSystem.hpp"
#ifdef WIN32
#include <conio.h>
#include <windows.h>
#else // *nix
typedef unsigned long DWORD, *PDWORD, *LPDWORD;
#include <termios.h>
#include <unistd.h>
#endif // WIN32
#include "Logger.h"
#include <array>
#include <cstring>
#include <iostream>
#include <mutex>
#include <string>
#include <thread>
std::vector<std::string> QConsoleOut;
std::string CInputBuff;
std::mutex MLock;
std::unique_ptr<Lua> LuaConsole;
void HandleInput(const std::string& cmd) {
std::cout << std::endl;
if (cmd == ("exit")) {
_Exit(0);
} else if (cmd == ("clear") || cmd == ("cls")) {
// 2J is clearscreen, H is reset position to top-left
ConsoleOut(("\x1b[2J\x1b[H"));
} else {
LuaConsole->Execute(cmd);
}
}
void ProcessOut() {
printf("%c[2K\r", 27);
for (const std::string& msg : QConsoleOut)
if (!msg.empty())
std::cout << msg;
MLock.lock();
QConsoleOut.clear();
MLock.unlock();
std::cout << "> " << CInputBuff << std::flush;
}
void ConsoleOut(const std::string& msg) {
MLock.lock();
QConsoleOut.emplace_back(msg);
MLock.unlock();
}
[[noreturn]] void OutputRefresh() {
DebugPrintTID();
while (true) {
std::this_thread::sleep_for(std::chrono::milliseconds(10));
ProcessOut();
}
}
#ifndef WIN32
static struct termios old, current;
void initTermios(int echo) {
tcgetattr(0, &old); /* grab old terminal i/o settings */
current = old; /* make new settings same as old settings */
current.c_lflag &= ~ICANON; /* disable buffered i/o */
if (echo) {
current.c_lflag |= ECHO; /* set echo mode */
} else {
current.c_lflag &= ~ECHO; /* set no echo mode */
}
tcsetattr(0, TCSANOW, &current); /* use these new terminal i/o settings now */
}
void resetTermios(void) {
tcsetattr(0, TCSANOW, &old);
}
char getch_(int echo) {
char ch;
initTermios(echo);
read(STDIN_FILENO, &ch, 1);
resetTermios();
return ch;
}
char _getch(void) {
return getch_(0);
}
#endif // WIN32
void SetupConsole() {
#if defined(WIN32)
DWORD outMode = 0;
HANDLE stdoutHandle = GetStdHandle(STD_OUTPUT_HANDLE);
if (stdoutHandle == INVALID_HANDLE_VALUE) {
error("Invalid console handle! Inputs will not work properly");
return;
}
if (!GetConsoleMode(stdoutHandle, &outMode)) {
error("Invalid console mode! Inputs will not work properly");
return;
}
// Enable ANSI escape codes
outMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
if (!SetConsoleMode(stdoutHandle, outMode)) {
error("failed to set console mode! Inputs will not work properly");
return;
}
#else
#endif // WIN32
}
static std::vector<std::string> ConsoleHistory {};
// buffer used to revert back to what we were writing when we go all the way forward in history
static std::string LastInputBuffer {};
static size_t ConsoleHistoryReadIndex { 0 };
static inline void ConsoleHistoryAdd(const std::string& cmd) {
LastInputBuffer.clear();
ConsoleHistory.push_back(cmd);
ConsoleHistoryReadIndex = ConsoleHistory.size();
}
static inline void ConsoleHistoryEnsureBounds() {
if (ConsoleHistoryReadIndex >= ConsoleHistory.size()) {
ConsoleHistoryReadIndex = ConsoleHistory.size() - 1;
}
}
static inline void ConsoleHistoryGoBack() {
if (ConsoleHistoryReadIndex > 0) {
--ConsoleHistoryReadIndex;
ConsoleHistoryEnsureBounds();
}
}
static inline void ConsoleHistoryGoForward() {
++ConsoleHistoryReadIndex;
ConsoleHistoryEnsureBounds();
}
static std::string CompositeInput;
static bool CompositeInputExpected { false };
static void ProcessCompositeInput() {
#ifdef WIN32
if (CompositeInput.size() == 1 && memcmp(CompositeInput.data(), std::array<char, 1> { 72 }.data(), 1) == 0) {
#else // unix
if (CompositeInput.size() == 2 && memcmp(CompositeInput.data(), std::array<char, 2> { 91, 65 }.data(), 2) == 0) {
#endif // WIN32
// UP ARROW
if (!ConsoleHistory.empty()) {
ConsoleHistoryGoBack();
CInputBuff = ConsoleHistory.at(ConsoleHistoryReadIndex);
}
#ifdef WIN32
} else if (CompositeInput.size() == 1 && memcmp(CompositeInput.data(), std::array<char, 1> { 80 }.data(), 1) == 0) {
#else // unix
} else if (CompositeInput.size() == 2 && memcmp(CompositeInput.data(), std::array<char, 2> { 91, 66 }.data(), 2) == 0) {
#endif // WIN32
// DOWN ARROW
if (!ConsoleHistory.empty()) {
if (ConsoleHistoryReadIndex == ConsoleHistory.size() - 1) {
CInputBuff = LastInputBuffer;
} else {
ConsoleHistoryGoForward();
CInputBuff = ConsoleHistory.at(ConsoleHistoryReadIndex);
}
}
} else {
// not composite input, we made a mistake, so lets just add it to the buffer like usual
CInputBuff += CompositeInput;
}
// ensure history doesnt grow too far beyond a max
static constexpr size_t MaxHistory = 10;
if (ConsoleHistory.size() > 2 * MaxHistory) {
decltype(ConsoleHistory) NewHistory(ConsoleHistory.begin() + ConsoleHistory.size() - MaxHistory, ConsoleHistory.end());
ConsoleHistory = std::move(NewHistory);
ConsoleHistoryReadIndex = ConsoleHistory.size();
}
}
void ReadCin() {
DebugPrintTID();
size_t null_byte_counter = 0;
while (true) {
int In = _getch();
//info(std::to_string(In));
if (In == 0) {
++null_byte_counter;
if (null_byte_counter > 50) {
info(("too many null bytes in input, this is now assumed to be a background thread - console input is now disabled"));
break;
}
}
if (CompositeInputExpected) {
CompositeInput += char(In);
#ifdef WIN32
if (CompositeInput.size() == 1) {
#else // unix
if (CompositeInput.size() == 2) {
#endif // WIN32
CompositeInputExpected = false;
ProcessCompositeInput();
}
continue;
}
if (In == 13 || In == '\n') {
if (!CInputBuff.empty()) {
HandleInput(CInputBuff);
ConsoleHistoryAdd(CInputBuff);
CInputBuff.clear();
}
} else if (In == 8 || In == 127) {
if (!CInputBuff.empty())
CInputBuff.pop_back();
} else if (In == 4) {
CInputBuff = "exit";
HandleInput(CInputBuff);
CInputBuff.clear();
} else if (In == 12) {
CInputBuff = "clear";
HandleInput(CInputBuff);
CInputBuff.clear();
#ifdef WIN32
} else if (In == 224) {
#else // unix
} else if (In == 27) {
#endif // WIN32
// escape char, assume stuff follows
CompositeInputExpected = true;
CompositeInput.clear();
} else if (!isprint(In)) {
// ignore
} else {
CInputBuff += char(In);
LastInputBuffer = CInputBuff;
}
}
}
void ConsoleInit() {
SetupConsole();
LuaConsole = std::make_unique<Lua>(true);
LuaConsole->Init();
printf("> ");
std::thread In(ReadCin);
In.detach();
std::thread Out(OutputRefresh);
Out.detach();
}

View File

@ -1,27 +0,0 @@
// Copyright (c) 2019-present Anonymous275.
// BeamMP Server code is not in the public domain and is not free software.
// One must be granted explicit permission by the copyright holder in order to modify or distribute any part of the source or binaries.
// Anything else is prohibited. Modified works may not be published and have be upstreamed to the official repository.
///
/// Created by Anonymous275 on 7/28/2020
///
#include "Security/Enc.h"
#include "CustomAssert.h"
#include "Logger.h"
#include <sstream>
#ifdef WIN32
#include <WS2tcpip.h>
int Handle(EXCEPTION_POINTERS* ep, char* Origin) {
Assert(false);
std::stringstream R;
R << ("Code : ") << std::hex
<< ep->ExceptionRecord->ExceptionCode
<< std::dec << (" Origin : ") << Origin;
except(R.str());
return 1;
}
#else
// stub
int Handle(EXCEPTION_POINTERS*, char*) { return 1; }
#endif // WIN32

View File

@ -1,168 +0,0 @@
// Copyright (c) 2019-present Anonymous275.
// BeamMP Server code is not in the public domain and is not free software.
// One must be granted explicit permission by the copyright holder in order to modify or distribute any part of the source or binaries.
// Anything else is prohibited. Modified works may not be published and have be upstreamed to the official repository.
///
/// Created by Anonymous275 on 7/28/2020
///
#include "CustomAssert.h"
#include "Logger.h"
#include "Security/Enc.h"
#include <fstream>
#include <string>
#include <thread>
std::string ServerName;
std::string ServerDesc;
std::string Resource;
std::string MapName;
std::string Key;
int MaxPlayers;
bool Private;
int MaxCars;
bool Debug;
int Port;
std::chrono::time_point<std::chrono::high_resolution_clock> StartTime;
void SetValues(const std::string& Line, int Index) {
int state = 0;
std::string Data;
bool Switch = false;
if (Index > 5)
Switch = true;
for (char c : Line) {
if (Switch) {
if (c == '\"')
state++;
if (state > 0 && state < 2)
Data += c;
} else {
if (c == ' ')
state++;
if (state > 1)
Data += c;
}
}
Data = Data.substr(1);
std::string::size_type sz;
bool FoundTrue = std::string(Data).find("true") != std::string::npos; //searches for "true"
switch (Index) {
case 1:
Debug = FoundTrue; //checks and sets the Debug Value
break;
case 2:
Private = FoundTrue; //checks and sets the Private Value
break;
case 3:
Port = std::stoi(Data, &sz); //sets the Port
break;
case 4:
MaxCars = std::stoi(Data, &sz); //sets the Max Car amount
break;
case 5:
MaxPlayers = std::stoi(Data, &sz); //sets the Max Amount of player
break;
case 6:
MapName = Data; //Map
break;
case 7:
ServerName = Data; //Name
break;
case 8:
ServerDesc = Data; //desc
break;
case 9:
Resource = Data; //File name
break;
case 10:
Key = Data; //File name
default:
break;
}
}
std::string RemoveComments(const std::string& Line) {
std::string Return;
for (char c : Line) {
if (c == '#')
break;
Return += c;
}
return Return;
}
void LoadConfig(std::ifstream& IFS) {
Assert(IFS.is_open());
std::string line;
int index = 1;
while (getline(IFS, line)) {
index++;
}
if (index - 1 < 11) {
error(("Outdated/Incorrect config please remove it server will close in 5 secs"));
std::this_thread::sleep_for(std::chrono::seconds(3));
_Exit(0);
}
IFS.close();
IFS.open(("Server.cfg"));
info(("Config found updating values"));
index = 1;
while (getline(IFS, line)) {
if (line.rfind('#', 0) != 0 && line.rfind(' ', 0) != 0) { //Checks if it starts as Comment
std::string CleanLine = RemoveComments(line); //Cleans it from the Comments
SetValues(CleanLine, index); //sets the values
index++;
}
}
}
void GenerateConfig() {
std::ofstream FileStream;
FileStream.open(("Server.cfg"));
FileStream << ("# This is the BeamMP Server Configuration File v0.60\n"
"Debug = false # true or false to enable debug console output\n"
"Private = true # Private?\n"
"Port = 30814 # Port to run the server on UDP and TCP\n"
"Cars = 1 # Max cars for every player\n"
"MaxPlayers = 10 # Maximum Amount of Clients\n"
"Map = \"/levels/gridmap/info.json\" # Default Map\n"
"Name = \"BeamMP New Server\" # Server Name\n"
"Desc = \"BeamMP Default Description\" # Server Description\n"
"use = \"Resources\" # Resource file name\n"
"AuthKey = \"\" # Auth Key");
FileStream.close();
}
void Default() {
info(("Config not found generating default"));
GenerateConfig();
error(("You are required to input the AuthKey"));
std::this_thread::sleep_for(std::chrono::seconds(3));
_Exit(0);
}
void DebugData() {
debug(std::string("Debug : ") + (Debug ? "true" : "false"));
debug(std::string("Private : ") + (Private ? "true" : "false"));
debug("Port : " + std::to_string(Port));
debug("Max Cars : " + std::to_string(MaxCars));
debug("MaxPlayers : " + std::to_string(MaxPlayers));
debug("MapName : " + MapName);
debug("ServerName : " + ServerName);
debug("ServerDesc : " + ServerDesc);
debug("File : " + Resource);
debug("Key length: " + std::to_string(Key.length()));
}
void InitConfig() {
////TODO: Move to json after update 4
std::ifstream IFS;
IFS.open(("Server.cfg"));
if (IFS.good())
LoadConfig(IFS);
else
Default();
if (IFS.is_open())
IFS.close();
if (Key.empty()) {
error(("No AuthKey was found"));
std::this_thread::sleep_for(std::chrono::seconds(3));
_Exit(0);
}
if (Debug)
DebugData();
}

View File

@ -1,99 +0,0 @@
// Copyright (c) 2019-present Anonymous275.
// BeamMP Server code is not in the public domain and is not free software.
// One must be granted explicit permission by the copyright holder in order to modify or distribute any part of the source or binaries.
// Anything else is prohibited. Modified works may not be published and have be upstreamed to the official repository.
///
/// Created by Anonymous275 on 7/28/2020
///
#include "Client.hpp"
#include "Curl/Http.h"
#include "Logger.h"
#include "Settings.h"
#include <chrono>
#include <future>
#include <sstream>
#include <thread>
#include <unordered_map>
void WebsocketInit();
std::string GetPlayers() {
std::string Return;
for (auto& c : CI->Clients) {
if (c != nullptr) {
Return += c->GetName() + ";";
}
}
return Return;
}
std::string GenerateCall() {
std::stringstream Ret;
Ret << "uuid=" << Key << "&players=" << CI->Size()
<< "&maxplayers=" << MaxPlayers << "&port=" << Port
<< "&map=" << MapName << "&private=" << (Private ? "true" : "false")
<< "&version=" << GetSVer() << "&clientversion=" << GetCVer()
<< "&name=" << ServerName << "&pps=" << StatReport
<< "&modlist=" << FileList << "&modstotalsize=" << MaxModSize
<< "&modstotal=" << ModsLoaded << "&playerslist=" << GetPlayers()
<< "&desc=" << ServerDesc;
return Ret.str();
}
std::string RunPromise(const std::string& host, const std::string& target, const std::unordered_map<std::string, std::string>& R, const std::string& body) {
std::packaged_task<std::string()> task([&] { DebugPrintTIDInternal("Heartbeat_POST"); return PostHTTP(host, target, R, body, false); });
std::future<std::string> f1 = task.get_future();
std::thread t(std::move(task));
t.detach();
auto status = f1.wait_for(std::chrono::seconds(15));
if (status != std::future_status::timeout)
return f1.get();
error("Backend system Timeout please try again later");
return "";
}
[[noreturn]] void Heartbeat() {
DebugPrintTID();
std::string R;
std::string T;
// these are "hot-change" related variables
static std::string LastR = "";
static std::chrono::high_resolution_clock::time_point LastNormalUpdateTime = std::chrono::high_resolution_clock::now();
bool isAuth = false;
while (true) {
R = GenerateCall();
// a hot-change occurs when a setting has changed, to update the backend of that change.
auto Now = std::chrono::high_resolution_clock::now();
if (LastR == R && (Now - LastNormalUpdateTime) < std::chrono::seconds(30)) {
std::this_thread::sleep_for(std::chrono::seconds(5));
continue;
}
LastR = R;
LastNormalUpdateTime = Now;
if (!CustomIP.empty())
R += "&ip=" + CustomIP;
T = RunPromise("backend.beammp.com", "/heartbeat", {}, R);
if (T.substr(0, 2) != "20") {
//Backend system refused server startup!
std::this_thread::sleep_for(std::chrono::milliseconds(500));
T = RunPromise("backend.beammp.com", "/heartbeat", {}, R);
// TODO backup2 + HTTP flag (no TSL)
if (T.substr(0, 2) != "20") {
warn("Backend system refused server! Server might not show in the public list");
}
}
//Server Authenticated
if (!isAuth) {
WebsocketInit();
if (T.length() == 4)
info(("Authenticated!"));
else
info(("Resumed authenticated session!"));
isAuth = true;
}
}
}
void HBInit() {
std::thread HB(Heartbeat);
HB.detach();
}

View File

@ -1,39 +0,0 @@
// Copyright (c) 2019-present Anonymous275.
// BeamMP Server code is not in the public domain and is not free software.
// One must be granted explicit permission by the copyright holder in order to modify or distribute any part of the source or binaries.
// Anything else is prohibited. Modified works may not be published and have be upstreamed to the official repository.
///
/// Created by Anonymous275 on 7/28/2020
///
#include "Logger.h"
#include "Security/Enc.h"
#include "Settings.h"
#include <algorithm>
#include <filesystem>
namespace fs = std::filesystem;
uint64_t MaxModSize = 0;
std::string FileSizes;
std::string FileList;
int ModsLoaded = 0;
void InitRes() {
std::string Path = Resource + "/Client";
if (!fs::exists(Path))
fs::create_directory(Path);
for (const auto& entry : fs::directory_iterator(Path)) {
auto pos = entry.path().string().find(".zip");
if (pos != std::string::npos) {
if (entry.path().string().length() - pos == 4) {
FileList += entry.path().string() + ";";
FileSizes += std::to_string(uint64_t(fs::file_size(entry.path()))) + ";";
MaxModSize += uint64_t(fs::file_size(entry.path()));
ModsLoaded++;
}
}
}
std::replace(FileList.begin(), FileList.end(), '\\', '/');
if (ModsLoaded)
info("Loaded " + std::to_string(ModsLoaded) + " Mods");
}

View File

@ -1,38 +0,0 @@
// Copyright (c) 2019-present Anonymous275.
// BeamMP Server code is not in the public domain and is not free software.
// One must be granted explicit permission by the copyright holder in order to modify or distribute any part of the source or binaries.
// Anything else is prohibited. Modified works may not be published and have be upstreamed to the official repository.
///
/// Created by Anonymous275 on 7/28/2020
///
#include "Client.hpp"
#include "Logger.h"
#include "Security/Enc.h"
#include <algorithm>
#include <string>
std::string CustomIP;
std::string GetSVer() {
return "1.20";
}
std::string GetCVer() {
return "1.80";
}
void Args(int argc, char* argv[]) {
info("BeamMP Server Running version " + GetSVer());
if (argc > 1) {
CustomIP = argv[1];
size_t n = std::count(CustomIP.begin(), CustomIP.end(), '.');
auto p = CustomIP.find_first_not_of((".0123456789"));
if (p != std::string::npos || n != 3 || CustomIP.substr(0, 3) == ("127")) {
CustomIP.clear();
warn("IP Specified is invalid! Ignoring");
} else
info("Server started with custom IP");
}
}
void InitServer(int argc, char* argv[]) {
InitLog();
Args(argc, argv);
CI = std::make_unique<ClientInterface>();
}

View File

@ -1,92 +0,0 @@
// Copyright (c) 2019-present Anonymous275.
// BeamMP Server code is not in the public domain and is not free software.
// One must be granted explicit permission by the copyright holder in order to modify or distribute any part of the source or binaries.
// Anything else is prohibited. Modified works may not be published and have be upstreamed to the official repository.
///
/// Created by Anonymous275 on 5/20/2020
///
#include "Logger.h"
#include "Lua/LuaSystem.hpp"
#include "Security/Enc.h"
#include "Settings.h"
#include <thread>
#ifdef __linux
// we need this for `struct stat`
#include <sys/stat.h>
#endif // __linux
std::set<std::unique_ptr<Lua>> PluginEngine;
bool NewFile(const std::string& Path) {
for (auto& Script : PluginEngine) {
if (Path == Script->GetFileName())
return false;
}
return true;
}
void RegisterFiles(const std::string& Path, bool HotSwap) {
std::string Name = Path.substr(Path.find_last_of('\\') + 1);
if (!HotSwap)
info(("Loading plugin : ") + Name);
for (const auto& entry : fs::directory_iterator(Path)) {
auto pos = entry.path().string().find((".lua"));
if (pos != std::string::npos && entry.path().string().length() - pos == 4) {
if (!HotSwap || NewFile(entry.path().string())) {
auto FileName = entry.path().string();
std::unique_ptr<Lua> ScriptToInsert(new Lua(Name, FileName, fs::last_write_time(FileName)));
auto& Script = *ScriptToInsert;
PluginEngine.insert(std::move(ScriptToInsert));
Script.Init();
if (HotSwap)
info(("[HOTSWAP] Added : ") + Script.GetFileName().substr(Script.GetFileName().find('\\')));
}
}
}
}
void FolderList(const std::string& Path, bool HotSwap) {
for (const auto& entry : fs::directory_iterator(Path)) {
auto pos = entry.path().filename().string().find('.');
if (pos == std::string::npos) {
RegisterFiles(entry.path().string(), HotSwap);
}
}
}
[[noreturn]] void HotSwaps(const std::string& path) {
DebugPrintTID();
while (true) {
if (!PluginEngine.empty()) {
for (auto& Script : PluginEngine) {
struct stat Info { };
if (stat(Script->GetFileName().c_str(), &Info) != 0) {
Script->SetStopThread(true);
PluginEngine.erase(Script);
info(("[HOTSWAP] Removed removed script due to delete"));
break;
}
if (Script->GetLastWrite() != fs::last_write_time(Script->GetFileName())) {
Script->SetStopThread(true);
info(("[HOTSWAP] Updated Scripts due to edit"));
Script->SetLastWrite(fs::last_write_time(Script->GetFileName()));
Script->Reload();
}
}
}
FolderList(path, true);
std::this_thread::sleep_for(std::chrono::seconds(2));
}
}
void InitLua() {
if (!fs::exists(Resource)) {
fs::create_directory(Resource);
}
std::string Path = Resource + ("/Server");
if (!fs::exists(Path)) {
fs::create_directory(Path);
}
FolderList(Path, false);
std::thread t1(HotSwaps, Path);
t1.detach();
info(("Lua system online"));
}

View File

@ -1,705 +0,0 @@
// Copyright (c) 2019-present Anonymous275.
// BeamMP Server code is not in the public domain and is not free software.
// One must be granted explicit permission by the copyright holder in order to modify or distribute any part of the source or binaries.
// Anything else is prohibited. Modified works may not be published and have be upstreamed to the official repository.
///
/// Created by Anonymous275 on 5/19/2020
///
#include "Lua/LuaSystem.hpp"
#include "Client.hpp"
#include "Logger.h"
#include "Network.h"
#include "Security/Enc.h"
#include "Settings.h"
#include "UnixCompat.h"
#include <future>
#include <iostream>
#include <optional>
#include <utility>
std::shared_ptr<LuaArg> CreateArg(lua_State* L, int T, int S) {
if (S > T)
return nullptr;
std::shared_ptr<LuaArg> temp(new LuaArg);
for (int C = S; C <= T; C++) {
if (lua_isstring(L, C)) {
temp->args.emplace_back(std::string(lua_tostring(L, C)));
} else if (lua_isinteger(L, C)) {
temp->args.emplace_back(int(lua_tointeger(L, C)));
} else if (lua_isboolean(L, C)) {
temp->args.emplace_back(bool(lua_toboolean(L, C)));
} else if (lua_isnumber(L, C)) {
temp->args.emplace_back(float(lua_tonumber(L, C)));
}
}
return temp;
}
void ClearStack(lua_State* L) {
lua_settop(L, 0);
}
std::optional<std::reference_wrapper<Lua>> GetScript(lua_State* L) {
for (auto& Script : PluginEngine) {
if (Script->GetState() == L)
return *Script;
}
return std::nullopt;
}
void SendError(lua_State* L, const std::string& msg) {
Assert(L);
auto MaybeS = GetScript(L);
std::string a;
if (!MaybeS.has_value()) {
a = ("_Console");
} else {
Lua& S = MaybeS.value();
a = fs::path(S.GetFileName()).filename().string();
}
warn(a + (" | Incorrect Call of ") + msg);
}
std::any Trigger(Lua* lua, const std::string& R, std::shared_ptr<LuaArg> arg) {
std::lock_guard<std::mutex> lockGuard(lua->Lock);
std::packaged_task<std::any(std::shared_ptr<LuaArg>)> task([lua, R](std::shared_ptr<LuaArg> arg) { return CallFunction(lua, R, arg); });
std::future<std::any> f1 = task.get_future();
std::thread t(std::move(task), arg);
t.detach();
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;
}
std::any FutureWait(Lua* lua, const std::string& R, std::shared_ptr<LuaArg> arg, bool Wait) {
Assert(lua);
std::packaged_task<std::any(std::shared_ptr<LuaArg>)> task([lua, R](std::shared_ptr<LuaArg> arg) { return Trigger(lua, R, arg); });
std::future<std::any> f1 = task.get_future();
std::thread t(std::move(task), arg);
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;
}
std::any TriggerLuaEvent(const std::string& Event, bool local, Lua* Caller, std::shared_ptr<LuaArg> arg, bool Wait) {
std::any R;
std::string Type;
int Ret = 0;
for (auto& Script : PluginEngine) {
if (Script->IsRegistered(Event)) {
if (local) {
if (Script->GetPluginName() == Caller->GetPluginName()) {
R = FutureWait(Script.get(), Script->GetRegistered(Event), arg, Wait);
Type = R.type().name();
if (Type.find("int") != std::string::npos) {
if (std::any_cast<int>(R))
Ret++;
} else if (Event == "onPlayerAuth")
return R;
}
} else {
R = FutureWait(Script.get(), Script->GetRegistered(Event), arg, Wait);
Type = R.type().name();
if (Type.find("int") != std::string::npos) {
if (std::any_cast<int>(R))
Ret++;
} else if (Event == "onPlayerAuth")
return R;
}
}
}
return Ret;
}
bool ConsoleCheck(lua_State* L, int r) {
if (r != LUA_OK) {
std::string msg = lua_tostring(L, -1);
warn(("_Console | ") + msg);
return false;
}
return true;
}
bool CheckLua(lua_State* L, int r) {
if (r != LUA_OK) {
std::string msg = lua_tostring(L, -1);
auto MaybeS = GetScript(L);
if (MaybeS.has_value()) {
Lua& S = MaybeS.value();
std::string a = fs::path(S.GetFileName()).filename().string();
warn(a + " | " + msg);
return false;
}
// What the fuck, what do we do?!
AssertNotReachable();
}
return true;
}
int lua_RegisterEvent(lua_State* L) {
int Args = lua_gettop(L);
auto MaybeScript = GetScript(L);
Assert(MaybeScript.has_value());
Lua& Script = MaybeScript.value();
if (Args == 2 && lua_isstring(L, 1) && lua_isstring(L, 2)) {
Script.RegisterEvent(lua_tostring(L, 1), lua_tostring(L, 2));
} else
SendError(L, ("RegisterEvent invalid argument count expected 2 got ") + std::to_string(Args));
return 0;
}
int lua_TriggerEventL(lua_State* L) {
int Args = lua_gettop(L);
auto MaybeScript = GetScript(L);
Assert(MaybeScript.has_value());
Lua& Script = MaybeScript.value();
if (Args > 0) {
if (lua_isstring(L, 1)) {
TriggerLuaEvent(lua_tostring(L, 1), true, &Script, CreateArg(L, Args, 2), false);
} else
SendError(L, ("TriggerLocalEvent wrong argument [1] need string"));
} else {
SendError(L, ("TriggerLocalEvent not enough arguments expected 1 got 0"));
}
return 0;
}
int lua_TriggerEventG(lua_State* L) {
int Args = lua_gettop(L);
auto MaybeScript = GetScript(L);
Assert(MaybeScript.has_value());
Lua& Script = MaybeScript.value();
if (Args > 0) {
if (lua_isstring(L, 1)) {
TriggerLuaEvent(lua_tostring(L, 1), false, &Script, CreateArg(L, Args, 2), false);
} else
SendError(L, ("TriggerGlobalEvent wrong argument [1] need string"));
} else
SendError(L, ("TriggerGlobalEvent not enough arguments"));
return 0;
}
char* ThreadOrigin(Lua* lua) {
std::string T = "Thread in " + fs::path(lua->GetFileName()).filename().string();
char* Data = new char[T.size() + 1];
ZeroMemory(Data, T.size() + 1);
memcpy(Data, T.c_str(), T.size());
return Data;
}
void SafeExecution(Lua* lua, const std::string& FuncName) {
lua_State* luaState = lua->GetState();
lua_getglobal(luaState, FuncName.c_str());
if (lua_isfunction(luaState, -1)) {
char* Origin = ThreadOrigin(lua);
#ifdef WIN32
__try {
int R = lua_pcall(luaState, 0, 0, 0);
CheckLua(luaState, R);
} __except (Handle(GetExceptionInformation(), Origin)) {
}
#else // unix
int R = lua_pcall(luaState, 0, 0, 0);
CheckLua(luaState, R);
#endif // WIN32
delete[] Origin;
}
ClearStack(luaState);
}
void ExecuteAsync(Lua* lua, const std::string& FuncName) {
std::lock_guard<std::mutex> lockGuard(lua->Lock);
SafeExecution(lua, FuncName);
}
void CallAsync(Lua* lua, const std::string& Func, int U) {
DebugPrintTID();
lua->SetStopThread(false);
int D = 1000 / U;
while (!lua->GetStopThread()) {
ExecuteAsync(lua, Func);
std::this_thread::sleep_for(std::chrono::milliseconds(D));
}
}
int lua_StopThread(lua_State* L) {
auto MaybeScript = GetScript(L);
Assert(MaybeScript.has_value());
// ugly, but whatever, this is safe as fuck
MaybeScript.value().get().SetStopThread(true);
return 0;
}
int lua_CreateThread(lua_State* L) {
int Args = lua_gettop(L);
if (Args > 1) {
if (lua_isstring(L, 1)) {
std::string STR = lua_tostring(L, 1);
if (lua_isinteger(L, 2) || lua_isnumber(L, 2)) {
int U = int(lua_tointeger(L, 2));
if (U > 0 && U < 501) {
auto MaybeScript = GetScript(L);
Assert(MaybeScript.has_value());
Lua& Script = MaybeScript.value();
std::thread t1(CallAsync, &Script, STR, U);
t1.detach();
} else
SendError(L, ("CreateThread wrong argument [2] number must be between 1 and 500"));
} else
SendError(L, ("CreateThread wrong argument [2] need number"));
} else
SendError(L, ("CreateThread wrong argument [1] need string"));
} else
SendError(L, ("CreateThread not enough arguments"));
return 0;
}
int lua_Sleep(lua_State* L) {
if (lua_isnumber(L, 1)) {
int t = int(lua_tonumber(L, 1));
std::this_thread::sleep_for(std::chrono::milliseconds(t));
} else {
SendError(L, ("Sleep not enough arguments"));
return 0;
}
return 1;
}
Client* GetClient(int ID) {
for (auto& c : CI->Clients) {
if (c != nullptr && c->GetID() == ID) {
return c.get();
}
}
return nullptr;
}
int lua_isConnected(lua_State* L) {
if (lua_isnumber(L, 1)) {
int ID = int(lua_tonumber(L, 1));
Client* c = GetClient(ID);
if (c != nullptr)
lua_pushboolean(L, c->isConnected);
else
return 0;
} else {
SendError(L, ("isConnected not enough arguments"));
return 0;
}
return 1;
}
int lua_GetPlayerName(lua_State* L) {
if (lua_isnumber(L, 1)) {
int ID = int(lua_tonumber(L, 1));
Client* c = GetClient(ID);
if (c != nullptr)
lua_pushstring(L, c->GetName().c_str());
else
return 0;
} else {
SendError(L, ("GetPlayerName not enough arguments"));
return 0;
}
return 1;
}
int lua_GetPlayerCount(lua_State* L) {
lua_pushinteger(L, CI->Size());
return 1;
}
int lua_GetGuest(lua_State* L) {
if (lua_isnumber(L, 1)) {
int ID = int(lua_tonumber(L, 1));
Client* c = GetClient(ID);
if (c != nullptr)
lua_pushboolean(L, c->isGuest);
else
return 0;
} else {
SendError(L, "GetGuest not enough arguments");
return 0;
}
return 1;
}
int lua_GetAllPlayers(lua_State* L) {
lua_newtable(L);
int i = 1;
for (auto& c : CI->Clients) {
if (c == nullptr)
continue;
lua_pushinteger(L, c->GetID());
lua_pushstring(L, c->GetName().c_str());
lua_settable(L, -3);
i++;
}
if (CI->Clients.empty())
return 0;
return 1;
}
int lua_GetCars(lua_State* L) {
if (lua_isnumber(L, 1)) {
int ID = int(lua_tonumber(L, 1));
Client* c = GetClient(ID);
if (c != nullptr) {
int i = 1;
for (auto& v : c->GetAllCars()) {
if (v != nullptr) {
lua_pushinteger(L, v->ID);
lua_pushstring(L, v->Data.substr(3).c_str());
lua_settable(L, -3);
i++;
}
}
if (c->GetAllCars().empty())
return 0;
} else
return 0;
} else {
SendError(L, ("GetPlayerVehicles not enough arguments"));
return 0;
}
return 1;
}
int lua_dropPlayer(lua_State* L) {
int Args = lua_gettop(L);
if (lua_isnumber(L, 1)) {
int ID = int(lua_tonumber(L, 1));
Client* c = GetClient(ID);
if (c == nullptr)
return 0;
std::string Reason;
if (Args > 1 && lua_isstring(L, 2)) {
Reason = std::string((" Reason : ")) + lua_tostring(L, 2);
}
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());
} else
SendError(L, ("DropPlayer not enough arguments"));
return 0;
}
int lua_sendChat(lua_State* L) {
if (lua_isinteger(L, 1) || lua_isnumber(L, 1)) {
if (lua_isstring(L, 2)) {
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);
} else {
Client* c = GetClient(ID);
if (c != nullptr) {
if (!c->isSynced)
return 0;
std::string Packet = "C:Server: " + std::string(lua_tostring(L, 2));
Respond(c, Packet, true);
} else
SendError(L, ("SendChatMessage invalid argument [1] invalid ID"));
}
} else
SendError(L, ("SendChatMessage invalid argument [2] expected string"));
} else
SendError(L, ("SendChatMessage invalid argument [1] expected number"));
return 0;
}
int lua_RemoveVehicle(lua_State* L) {
int Args = lua_gettop(L);
if (Args != 2) {
SendError(L, ("RemoveVehicle invalid argument count expected 2 got ") + std::to_string(Args));
return 0;
}
if ((lua_isinteger(L, 1) || lua_isnumber(L, 1)) && (lua_isinteger(L, 2) || lua_isnumber(L, 2))) {
int PID = int(lua_tointeger(L, 1));
int VID = int(lua_tointeger(L, 2));
Client* c = GetClient(PID);
if (c == nullptr) {
SendError(L, ("RemoveVehicle invalid Player ID"));
return 0;
}
if (!c->GetCarData(VID).empty()) {
std::string Destroy = "Od:" + std::to_string(PID) + "-" + std::to_string(VID);
SendToAll(nullptr, Destroy, true, true);
c->DeleteCar(VID);
}
} else
SendError(L, ("RemoveVehicle invalid argument expected number"));
return 0;
}
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, ("TriggerClientEvent invalid argument count expected 3 got ") + std::to_string(Args));
return 0;
}
if (!lua_isnumber(L, 1)) {
SendError(L, ("TriggerClientEvent invalid argument [1] expected number"));
return 0;
}
if (!lua_isstring(L, 2)) {
SendError(L, ("TriggerClientEvent invalid argument [2] expected string"));
return 0;
}
if (!lua_isstring(L, 3)) {
SendError(L, ("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, ("TriggerClientEvent invalid Player ID"));
return 0;
}
Respond(c, Packet, true);
}
return 0;
}
int lua_ServerExit(lua_State* L) {
if (lua_gettop(L) > 0) {
if (lua_isnumber(L, 1)) {
_Exit(int(lua_tointeger(L, 1)));
}
}
_Exit(0);
}
int lua_Set(lua_State* L) {
int Args = lua_gettop(L);
if (Args != 2) {
SendError(L, ("set invalid argument count expected 2 got ") + std::to_string(Args));
return 0;
}
if (!lua_isnumber(L, 1)) {
SendError(L, ("set invalid argument [1] expected number"));
return 0;
}
auto MaybeSrc = GetScript(L);
std::string Name;
if (!MaybeSrc.has_value()) {
Name = ("_Console");
} else {
Name = MaybeSrc.value().get().GetPluginName();
}
int C = int(lua_tointeger(L, 1));
switch (C) {
case 0: //debug
if (lua_isboolean(L, 2)) {
Debug = lua_toboolean(L, 2);
info(Name + (" | Debug -> ") + (Debug ? "true" : "false"));
} else
SendError(L, ("set invalid argument [2] expected boolean for ID : 0"));
break;
case 1: //private
if (lua_isboolean(L, 2)) {
Private = lua_toboolean(L, 2);
info(Name + (" | Private -> ") + (Private ? "true" : "false"));
} else
SendError(L, ("set invalid argument [2] expected boolean for ID : 1"));
break;
case 2: //max cars
if (lua_isnumber(L, 2)) {
MaxCars = int(lua_tointeger(L, 2));
info(Name + (" | MaxCars -> ") + std::to_string(MaxCars));
} else
SendError(L, ("set invalid argument [2] expected number for ID : 2"));
break;
case 3: //max players
if (lua_isnumber(L, 2)) {
MaxPlayers = int(lua_tointeger(L, 2));
info(Name + (" | MaxPlayers -> ") + std::to_string(MaxPlayers));
} else
SendError(L, ("set invalid argument [2] expected number for ID : 3"));
break;
case 4: //Map
if (lua_isstring(L, 2)) {
MapName = lua_tostring(L, 2);
info(Name + (" | MapName -> ") + MapName);
} else
SendError(L, ("set invalid argument [2] expected string for ID : 4"));
break;
case 5: //Name
if (lua_isstring(L, 2)) {
ServerName = lua_tostring(L, 2);
info(Name + (" | ServerName -> ") + ServerName);
} else
SendError(L, ("set invalid argument [2] expected string for ID : 5"));
break;
case 6: //Desc
if (lua_isstring(L, 2)) {
ServerDesc = lua_tostring(L, 2);
info(Name + (" | ServerDesc -> ") + ServerDesc);
} else
SendError(L, ("set invalid argument [2] expected string for ID : 6"));
break;
default:
warn(("Invalid config ID : ") + std::to_string(C));
break;
}
return 0;
}
extern "C" {
int lua_Print(lua_State* L) {
int Arg = lua_gettop(L);
for (int i = 1; i <= Arg; i++) {
auto str = lua_tostring(L, i);
if (str != nullptr) {
ConsoleOut(str + std::string(("\n")));
} else {
ConsoleOut(("nil\n"));
}
}
return 0;
}
}
Lua::Lua(const std::string& PluginName, const std::string& FileName, fs::file_time_type LastWrote, bool Console)
: luaState(luaL_newstate()) {
Assert(luaState);
if (!PluginName.empty()) {
SetPluginName(PluginName);
}
if (!FileName.empty()) {
SetFileName(FileName);
}
SetLastWrite(LastWrote);
_Console = Console;
}
Lua::Lua(bool Console)
: luaState(luaL_newstate()) {
_Console = Console;
}
void Lua::Execute(const std::string& Command) {
if (ConsoleCheck(luaState, luaL_dostring(luaState, Command.c_str()))) {
lua_settop(luaState, 0);
}
}
void Lua::Reload() {
if (CheckLua(luaState, luaL_dofile(luaState, _FileName.c_str()))) {
CallFunction(this, ("onInit"), nullptr);
}
}
std::string Lua::GetOrigin() {
return fs::path(GetFileName()).filename().string();
}
std::any CallFunction(Lua* lua, const std::string& FuncName, std::shared_ptr<LuaArg> Arg) {
lua_State* luaState = lua->GetState();
lua_getglobal(luaState, FuncName.c_str());
if (lua_isfunction(luaState, -1)) {
int Size = 0;
if (Arg != nullptr) {
Size = int(Arg->args.size());
Arg->PushArgs(luaState);
}
int R = lua_pcall(luaState, Size, 1, 0);
if (CheckLua(luaState, R)) {
if (lua_isnumber(luaState, -1)) {
return int(lua_tointeger(luaState, -1));
} else if (lua_isstring(luaState, -1)) {
return std::string(lua_tostring(luaState, -1));
}
}
}
ClearStack(luaState);
return 0;
}
void Lua::SetPluginName(const std::string& Name) {
_PluginName = Name;
}
void Lua::SetFileName(const std::string& Name) {
_FileName = Name;
}
int lua_TempFix(lua_State* L) {
if (lua_isnumber(L, 1)) {
int ID = int(lua_tonumber(L, 1));
Client* c = GetClient(ID);
if (c == nullptr)
return 0;
std::string Ret;
if (c->isGuest) {
Ret = "Guest-" + c->GetName();
} else
Ret = c->GetName();
lua_pushstring(L, Ret.c_str());
} else
SendError(L, "GetDID not enough arguments");
return 1;
}
void Lua::Init() {
Assert(luaState);
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);
lua_register(luaState, "GetPlayerName", lua_GetPlayerName);
lua_register(luaState, "RemoveVehicle", lua_RemoveVehicle);
lua_register(luaState, "GetPlayerDiscordID", lua_TempFix);
lua_register(luaState, "CreateThread", lua_CreateThread);
lua_register(luaState, "GetPlayerVehicles", lua_GetCars);
lua_register(luaState, "SendChatMessage", lua_sendChat);
lua_register(luaState, "GetPlayers", lua_GetAllPlayers);
lua_register(luaState, "GetPlayerGuest", lua_GetGuest);
lua_register(luaState, "StopThread", lua_StopThread);
lua_register(luaState, "DropPlayer", lua_dropPlayer);
lua_register(luaState, "GetPlayerHWID", lua_HWID);
lua_register(luaState, "exit", lua_ServerExit);
lua_register(luaState, "Sleep", lua_Sleep);
lua_register(luaState, "print", lua_Print);
lua_register(luaState, "Set", lua_Set);
if (!_Console)
Reload();
}
void Lua::RegisterEvent(const std::string& Event, const std::string& FunctionName) {
_RegisteredEvents.insert(std::make_pair(Event, FunctionName));
}
void Lua::UnRegisterEvent(const std::string& Event) {
for (const std::pair<std::string, std::string>& a : _RegisteredEvents) {
if (a.first == Event) {
_RegisteredEvents.erase(a);
break;
}
}
}
bool Lua::IsRegistered(const std::string& Event) {
for (const std::pair<std::string, std::string>& a : _RegisteredEvents) {
if (a.first == Event)
return true;
}
return false;
}
std::string Lua::GetRegistered(const std::string& Event) const {
for (const std::pair<std::string, std::string>& a : _RegisteredEvents) {
if (a.first == Event)
return a.second;
}
return "";
}
std::string Lua::GetFileName() const {
return _FileName;
}
std::string Lua::GetPluginName() const {
return _PluginName;
}
lua_State* Lua::GetState() {
return luaState;
}
const lua_State* Lua::GetState() const {
return luaState;
}
void Lua::SetLastWrite(fs::file_time_type time) {
_LastWrote = time;
}
fs::file_time_type Lua::GetLastWrite() {
return _LastWrote;
}
Lua::~Lua() {
info("closing lua state");
lua_close(luaState);
}

View File

@ -1,227 +0,0 @@
// Copyright (c) 2019-present Anonymous275.
// BeamMP Server code is not in the public domain and is not free software.
// One must be granted explicit permission by the copyright holder in order to modify or distribute any part of the source or binaries.
// Anything else is prohibited. Modified works may not be published and have be upstreamed to the official repository.
///
/// Created by Anonymous275 on 7/31/2020
///
#include "Curl/Http.h"
#include "Json.h"
#include "Logger.h"
#include "Network.h"
#include "Security/Enc.h"
#include "Settings.h"
#include "UnixCompat.h"
#include <Lua/LuaSystem.hpp>
#include <cstring>
#include <string>
#include <thread>
std::string GetClientInfo(const std::string& PK) {
if (!PK.empty()) {
return PostHTTP("auth.beammp.com", "/pkToUser", {}, R"({"key":")" + PK + "\"}", true);
}
return "";
}
Client* CreateClient(SOCKET TCPSock) {
auto* c = new Client;
c->SetTCPSock(TCPSock);
return c;
}
void ClientKick(Client* c, const std::string& R) {
info("Client kicked: " + R);
TCPSend(c, "E" + R);
CloseSocketProper(c->GetTCPSock());
}
void Authentication(SOCKET TCPSock) {
DebugPrintTID();
auto* c = CreateClient(TCPSock);
info("Identifying new client...");
std::string Rc = TCPRcv(c);
if (Rc.size() > 3 && Rc.substr(0, 2) == "VC") {
Rc = Rc.substr(2);
if (Rc.length() > 4 || Rc != GetCVer()) {
ClientKick(c, "Outdated Version!");
return;
}
} else {
ClientKick(c, "Invalid version header!");
return;
}
TCPSend(c, "S");
Rc = TCPRcv(c);
if (Rc.size() > 50) {
ClientKick(c, "Invalid Key!");
return;
}
Rc = GetClientInfo(Rc);
json::Document d;
d.Parse(Rc.c_str());
if (Rc == "-1" || d.HasParseError()) {
ClientKick(c, "Invalid key! Please restart your game.");
return;
}
if (d["username"].IsString() && d["roles"].IsString() && d["guest"].IsBool()) {
c->SetName(d["username"].GetString());
c->SetRoles(d["roles"].GetString());
c->isGuest = d["guest"].GetBool();
} else {
ClientKick(c, "Invalid authentication data!");
return;
}
debug("Name -> " + c->GetName() + ", Guest -> " + std::to_string(c->isGuest) + ", Roles -> " + c->GetRoles());
for (auto& Cl : CI->Clients) {
if (Cl != nullptr) {
if (Cl->GetName() == c->GetName() && Cl->isGuest == c->isGuest) {
info("Old client (" + Cl->GetName() + ") kicked: Reconnecting");
CloseSocketProper(Cl->GetTCPSock());
Cl->SetStatus(-2);
break;
}
}
}
auto arg = std::make_unique<LuaArg>(LuaArg { { c->GetName(), c->GetRoles(), c->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(c, "you are not allowed on the server!");
return;
} else if (Type.find("string") != std::string::npos) {
ClientKick(c, std::any_cast<std::string>(Res));
return;
}
if (CI->Size() < MaxPlayers) {
info("Identification success");
Client& Client = *c;
CI->AddClient(std::move(c));
TCPClient(&Client);
} else
ClientKick(c, "Server full!");
}
void HandleDownload(SOCKET TCPSock) {
char D;
if (recv(TCPSock, &D, 1, 0) != 1) {
CloseSocketProper(TCPSock);
return;
}
auto ID = uint8_t(D);
for (auto& c : CI->Clients) {
if (c->GetID() == ID) {
c->SetDownSock(TCPSock);
}
}
}
void 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 TCPServerMain() {
DebugPrintTID();
#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(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(Identify, 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, 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(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(Identify, client);
ID.detach();
} catch (const std::exception& e) {
error(("fatal: ") + std::string(e.what()));
}
} while (client);
debug("all ok, arrived at " + std::string(__func__) + ":" + std::to_string(__LINE__));
CloseSocketProper(client);
#endif // WIN32
}

View File

@ -1,66 +0,0 @@
// Copyright (c) 2019-present Anonymous275.
// BeamMP Server code is not in the public domain and is not free software.
// One must be granted explicit permission by the copyright holder in order to modify or distribute any part of the source or binaries.
// Anything else is prohibited. Modified works may not be published and have be upstreamed to the official repository.
///
/// Created by Anonymous275 on 5/8/2020
///
#include "Client.hpp"
#include <memory>
void Client::DeleteCar(int ident) {
for (auto& v : VehicleData) {
if (v != nullptr && v->ID == ident) {
VehicleData.erase(v);
break;
}
}
}
void Client::ClearCars() {
VehicleData.clear();
}
int Client::GetOpenCarID() {
int OpenID = 0;
bool found;
do {
found = true;
for (auto& v : VehicleData) {
if (v != nullptr && v->ID == OpenID) {
OpenID++;
found = false;
}
}
} while (!found);
return OpenID;
}
void Client::AddNewCar(int ident, const std::string& Data) {
VehicleData.insert(std::make_unique<VData>(VData { ident, Data }));
}
std::set<std::unique_ptr<VData>>& Client::GetAllCars() {
return VehicleData;
}
std::string Client::GetCarData(int ident) {
for (auto& v : VehicleData) {
if (v != nullptr && v->ID == ident) {
return v->Data;
}
}
DeleteCar(ident);
return "";
}
void Client::SetCarData(int ident, const std::string& Data) {
for (auto& v : VehicleData) {
if (v != nullptr && v->ID == ident) {
v->Data = Data;
return;
}
}
DeleteCar(ident);
}
int Client::GetCarCount() {
return int(VehicleData.size());
}

View File

@ -1,268 +0,0 @@
// Copyright (c) 2019-present Anonymous275.
// BeamMP Server code is not in the public domain and is not free software.
// One must be granted explicit permission by the copyright holder in order to modify or distribute any part of the source or binaries.
// Anything else is prohibited. Modified works may not be published and have be upstreamed to the official repository.
///
/// Created by Anonymous275 on 8/1/2020
///
#include "Client.hpp"
#include "Logger.h"
#include "Lua/LuaSystem.hpp"
#include "Network.h"
#include "Security/Enc.h"
#include "Settings.h"
#include "UnixCompat.h"
#undef GetObject //to fix microsoft bs
#include "Json.h"
void Apply(Client* c, int VID, const std::string& pckt) {
Assert(c);
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());
}
void VehicleParser(Client* c, const std::string& Pckt) {
Assert(c);
if (c == nullptr || 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<LuaArg>(LuaArg { { c->GetID(), CarID, Packet.substr(3) } }), true);
if (c->GetCarCount() >= MaxCars || std::any_cast<int>(Res)) {
Respond(c, Packet, true);
std::string Destroy = "Od:" + std::to_string(c->GetID()) + "-" + std::to_string(CarID);
Respond(c, Destroy, true);
debug(c->GetName() + (" (force : car limit/lua) removed ID ") + std::to_string(CarID));
} else {
c->AddNewCar(CarID, Packet);
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<LuaArg>(LuaArg { { c->GetID(), VID, Packet.substr(3) } }),
true);
if (!std::any_cast<int>(Res)) {
SendToAll(c, Packet, false, true);
Apply(c, VID, Packet);
} else {
std::string Destroy = "Od:" + std::to_string(c->GetID()) + "-" + std::to_string(VID);
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()) {
SendToAll(nullptr, Packet, true, true);
TriggerLuaEvent(("onVehicleDeleted"), false, nullptr,
std::make_unique<LuaArg>(LuaArg { { 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
SendToAll(c, Packet, false, true);
return;
case 't':
#ifdef DEBUG
debug(std::string(("got 'Ot' packet: '")) + Packet + ("' (") + std::to_string(Packet.size()) + (")"));
#endif
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 SyncClient(Client* c) {
Assert(c);
if (c->isSynced)
return;
c->isSynced = true;
std::this_thread::sleep_for(std::chrono::seconds(1));
Respond(c, ("Sn") + c->GetName(), true);
SendToAll(c, ("JWelcome ") + c->GetName() + "!", false, true);
TriggerLuaEvent(("onPlayerJoin"), false, nullptr, std::make_unique<LuaArg>(LuaArg { { c->GetID() } }), false);
for (auto& client : CI->Clients) {
if (client != nullptr) {
if (client.get() != c) {
for (auto& v : client->GetAllCars()) {
if (v != nullptr) {
if (c->GetStatus() < 0)
return;
Respond(c, v->Data, true);
std::this_thread::sleep_for(std::chrono::seconds(2));
}
}
}
}
}
info(c->GetName() + (" is now synced!"));
}
void ParseVeh(Client* c, const std::string& Packet) {
Assert(c);
#ifdef WIN32
__try {
VehicleParser(c, Packet);
} __except (Handle(GetExceptionInformation(), ("Vehicle Handler"))) { }
#else // unix
VehicleParser(c, Packet);
#endif // WIN32
}
void HandleEvent(Client* c, const std::string& Data) {
Assert(c);
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<LuaArg>(LuaArg { { c->GetID(), t } }), false);
break;
default:
break;
}
if (a == 2)
break;
a++;
}
}
void GlobalParser(Client* c, const std::string& Packet) {
Assert(c);
if (Packet.empty() || c == nullptr)
return;
std::any Res;
char Code = Packet.at(0);
//V to Z
if (Code <= 90 && Code >= 86) {
PPS++;
SendToAll(c, 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(c);
return;
case 'p':
Respond(c, ("p"), false);
UpdatePlayers();
return;
case 'O':
if (Packet.length() > 1000) {
debug(("Received data from: ") + c->GetName() + (" Size: ") + std::to_string(Packet.length()));
}
ParseVeh(c, Packet);
return;
case 'J':
#ifdef DEBUG
debug(std::string(("got 'J' packet: '")) + Packet + ("' (") + std::to_string(Packet.size()) + (")"));
#endif
SendToAll(c, 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 { { c->GetID(), c->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(c, Packet);
return;
default:
return;
}
}
void GParser(Client* c, const std::string& Packet) {
Assert(c);
if (Packet.find("Zp") != std::string::npos && Packet.size() > 500) {
abort();
}
#ifdef WIN32
__try {
GlobalParser(c, Packet);
} __except (Handle(GetExceptionInformation(), ("Global Handler"))) { }
#else
GlobalParser(c, Packet);
#endif // WIN32
}

View File

@ -1,151 +0,0 @@
// Copyright (c) 2019-present Anonymous275.
// BeamMP Server code is not in the public domain and is not free software.
// One must be granted explicit permission by the copyright holder in order to modify or distribute any part of the source or binaries.
// Anything else is prohibited. Modified works may not be published and have be upstreamed to the official repository.
///
/// Created by Anonymous275 on 4/9/2020
///
#include "CustomAssert.h"
#include <boost/asio/connect.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/beast.hpp>
#include <boost/beast/ssl.hpp>
#include <string>
namespace beast = boost::beast; // from <boost/beast.hpp>
namespace http = beast::http; // from <boost/beast/http.hpp>
namespace net = boost::asio; // from <boost/asio.hpp>
namespace ssl = net::ssl; // from <boost/asio/ssl.hpp>
using tcp = net::ip::tcp; // from <boost/asio/ip/tcp.hpp>
// UNUSED?!
std::string HttpRequest(const std::string& host, int port, const std::string& target) {
// FIXME: this is likely not very well written.
// if it causes issues, yell at me and I'll fix it asap. - Lion
try {
net::io_context io;
tcp::resolver resolver(io);
beast::tcp_stream stream(io);
auto const results = resolver.resolve(host, std::to_string(port));
stream.connect(results);
http::request<http::string_body> req { http::verb::get, target, 11 /* http 1.1 */ };
req.set(http::field::host, host);
// tell the server what we are (boost beast)
req.set(http::field::user_agent, BOOST_BEAST_VERSION_STRING);
http::write(stream, req);
// used for reading
beast::flat_buffer buffer;
http::response<http::string_body> response;
http::read(stream, buffer, response);
std::string result(response.body());
beast::error_code ec;
stream.socket().shutdown(tcp::socket::shutdown_both, ec);
if (ec && ec != beast::errc::not_connected) {
throw beast::system_error { ec }; // goes down to `return "-1"` anyways
}
return result;
} catch (const std::exception& e) {
error(e.what());
return "-1";
}
}
std::string PostHTTP(const std::string& host, const std::string& target, const std::unordered_map<std::string, std::string>& fields, const std::string& body, bool json) {
try {
net::io_context io;
// The SSL context is required, and holds certificates
ssl::context ctx(ssl::context::tlsv13);
ctx.set_verify_mode(ssl::verify_none);
tcp::resolver resolver(io);
beast::ssl_stream<beast::tcp_stream> stream(io, ctx);
decltype(resolver)::results_type results;
auto try_connect_with_protocol = [&](tcp protocol) {
try {
results = resolver.resolve(protocol, host, std::to_string(443));
if (!SSL_set_tlsext_host_name(stream.native_handle(), host.c_str())) {
boost::system::error_code ec { static_cast<int>(::ERR_get_error()), boost::asio::error::get_ssl_category() };
// FIXME: we could throw and crash, if we like
// throw boost::system::system_error { ec };
debug("POST " + host + target + " failed.");
return false;
}
beast::get_lowest_layer(stream).connect(results);
} catch (const boost::system::system_error&) {
return false;
}
return true;
};
//bool ok = try_connect_with_protocol(tcp::v6());
//if (!ok) {
//debug("IPv6 connect failed, trying IPv4");
bool ok = try_connect_with_protocol(tcp::v4());
if (!ok) {
error("failed to resolve or connect in POST " + host + target);
return "-1";
}
//}
stream.handshake(ssl::stream_base::client);
http::request<http::string_body> req { http::verb::post, target, 11 /* http 1.1 */ };
req.set(http::field::host, host);
if (!body.empty()) {
if (json) {
// FIXME: json is untested.
req.set(http::field::content_type, "application/json");
} else {
req.set(http::field::content_type, "application/x-www-form-urlencoded");
}
req.set(http::field::content_length, std::to_string(body.size()));
req.body() = body;
// info("body is " + body + " (" + req.body() + ")");
// info("content size is " + std::to_string(body.size()) + " (" + boost::lexical_cast<std::string>(body.size()) + ")");
}
for (const auto& pair : fields) {
// info("setting " + pair.first + " to " + pair.second);
req.set(pair.first, pair.second);
}
std::stringstream oss;
oss << req;
beast::get_lowest_layer(stream).expires_after(std::chrono::seconds(5));
http::write(stream, req);
// used for reading
beast::flat_buffer buffer;
http::response<http::string_body> response;
http::read(stream, buffer, response);
std::stringstream result;
result << response;
beast::error_code ec;
stream.shutdown(ec);
// IGNORING ec
// info(result.str());
std::string debug_response_str;
std::getline(result, debug_response_str);
debug("POST " + host + target + ": " + debug_response_str);
return std::string(response.body());
} catch (const std::exception& e) {
error(e.what());
return "-1";
}
}

View File

@ -1,109 +0,0 @@
// Copyright (c) 2019-present Anonymous275.
// BeamMP Server code is not in the public domain and is not free software.
// One must be granted explicit permission by the copyright holder in order to modify or distribute any part of the source or binaries.
// Anything else is prohibited. Modified works may not be published and have be upstreamed to the official repository.
///
/// Created by Anonymous275 on 8/1/2020
///
#include "Client.hpp"
#include "Logger.h"
#include "Lua/LuaSystem.hpp"
#include "Network.h"
#include "Settings.h"
#include "UnixCompat.h"
#include <memory>
int OpenID() {
int ID = 0;
bool found;
do {
found = true;
for (auto& c : CI->Clients) {
if (c != nullptr) {
if (c->GetID() == ID) {
found = false;
ID++;
}
}
}
} while (!found);
return ID;
}
void Respond(Client* c, const std::string& MSG, bool Rel) {
Assert(c);
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 {
UDPSend(c, MSG);
}
}
void SendToAll(Client* c, const std::string& Data, bool Self, bool Rel) {
if (!Self)
Assert(c);
char C = Data.at(0);
for (auto& client : CI->Clients) {
if (client != nullptr) {
if (Self || client.get() != c) {
if (client->isSynced) {
if (Rel || C == 'W' || C == 'Y' || C == 'V' || C == 'E') {
if (C == 'O' || C == 'T' || Data.length() > 1000)
SendLarge(client.get(), Data);
else
TCPSend(client.get(), Data);
} else
UDPSend(client.get(), Data);
}
}
}
}
}
void UpdatePlayers() {
std::string Packet = ("Ss") + std::to_string(CI->Size()) + "/" + std::to_string(MaxPlayers) + ":";
for (auto& c : CI->Clients) {
if (c != nullptr)
Packet += c->GetName() + ",";
}
Packet = Packet.substr(0, Packet.length() - 1);
SendToAll(nullptr, Packet, true, true);
}
void OnDisconnect(Client* c, bool kicked) {
Assert(c);
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);
SendToAll(c, Packet, false, true);
}
}
if (kicked)
Packet = ("L") + c->GetName() + (" was kicked!");
else
Packet = ("L") + c->GetName() + (" left the server!");
SendToAll(c, Packet, false, true);
Packet.clear();
TriggerLuaEvent(("onPlayerDisconnect"), false, nullptr, std::make_unique<LuaArg>(LuaArg { { c->GetID() } }), false);
if (c->GetTCPSock())
CloseSocketProper(c->GetTCPSock());
if (c->GetDownSock())
CloseSocketProper(c->GetDownSock());
CI->RemoveClient(c);
}
void OnConnect(Client* c) {
Assert(c);
info("Client connected");
c->SetID(OpenID());
info("Assigned ID " + std::to_string(c->GetID()) + " to " + c->GetName());
TriggerLuaEvent("onPlayerConnecting", false, nullptr, std::make_unique<LuaArg>(LuaArg { { c->GetID() } }), false);
SyncResources(c);
if (c->GetStatus() < 0)
return;
Respond(c, "M" + MapName, true); //Send the Map on connect
info(c->GetName() + " : Connected");
TriggerLuaEvent("onPlayerJoining", false, nullptr, std::make_unique<LuaArg>(LuaArg { { c->GetID() } }), false);
}

View File

@ -1,9 +0,0 @@
#include "Network.h"
#include <memory>
#include <thread>
std::unique_ptr<ClientInterface> CI;
void NetMain() {
std::thread TCP(TCPServerMain);
TCP.detach();
UDPServerMain();
}

View File

@ -1,48 +0,0 @@
// Copyright (c) 2019-present Anonymous275.
// BeamMP Server code is not in the public domain and is not free software.
// One must be granted explicit permission by the copyright holder in order to modify or distribute any part of the source or binaries.
// Anything else is prohibited. Modified works may not be published and have be upstreamed to the official repository.
///
/// Created by Anonymous275 on 6/18/2020
///
#include "Client.hpp"
#include "Security/Enc.h"
#include <iostream>
#include <string>
#include <thread>
std::string StatReport;
int PPS = 0;
void Monitor() {
int C = 0, V = 0;
if (CI->Clients.empty()) {
StatReport = "-";
return;
}
for (auto& c : CI->Clients) {
if (c != nullptr && c->GetCarCount() > 0) {
C++;
V += c->GetCarCount();
}
}
if (C == 0 || PPS == 0) {
StatReport = "-";
} else {
int R = (PPS / C) / V;
StatReport = std::to_string(R);
}
PPS = 0;
}
[[noreturn]] void Stat() {
DebugPrintTID();
while (true) {
Monitor();
std::this_thread::sleep_for(std::chrono::seconds(1));
}
}
void StatInit() {
StatReport = "-";
std::thread Init(Stat);
Init.detach();
}

View File

@ -1,154 +0,0 @@
// Copyright (c) 2019-present Anonymous275.
// BeamMP Server code is not in the public domain and is not free software.
// One must be granted explicit permission by the copyright holder in order to modify or distribute any part of the source or binaries.
// Anything else is prohibited. Modified works may not be published and have be upstreamed to the official repository.
///
/// Created by Anonymous275 on 8/1/2020
///
#include "Client.hpp"
#include "Logger.h"
#include "Network.h"
#include "Security/Enc.h"
#include "Settings.h"
#include "UnixCompat.h"
#include <filesystem>
#include <fstream>
bool 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 SplitLoad(Client* 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();
}
void SendFile(Client* c, const std::string& Name) {
Assert(c);
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 Dt(SplitLoad, c, 0, MSize, false, Name);
Dt.detach();
SplitLoad(c, MSize, Size, true, Name);
if (Dt.joinable())
Dt.join();
}
void Parse(Client* c, const std::string& Packet) {
Assert(c);
if (c == nullptr || 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 = FileList + FileSizes;
if (ToSend.empty())
ToSend = "-";
TCPSend(c, ToSend);
}
return;
default:
return;
}
}
void SyncResources(Client* c) {
Assert(c);
if (c == nullptr)
return;
#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
}

View File

@ -1,137 +0,0 @@
// Copyright (c) 2019-present Anonymous275.
// BeamMP Server code is not in the public domain and is not free software.
// One must be granted explicit permission by the copyright holder in order to modify or distribute any part of the source or binaries.
// Anything else is prohibited. Modified works may not be published and have be upstreamed to the official repository.
///
/// Created by Anonymous275 on 8/1/2020
///
#include "Compressor.h"
#include "Logger.h"
#include "Network.h"
#include "Security/Enc.h"
#include "UnixCompat.h"
#include <thread>
bool TCPSend(Client* c, const std::string& Data) {
Assert(c);
if (c == nullptr)
return false;
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 CheckBytes(Client* c, int32_t BytesRcv) {
Assert(c);
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 TCPRcv(Client* c) {
Assert(c);
int32_t Header, BytesRcv = 0, Temp;
if (c == nullptr || 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 TCPClient(Client* c) {
DebugPrintTIDInternal("Client(" + c->GetName() + ")", true);
Assert(c);
if (c->GetTCPSock() == -1) {
CI->RemoveClient(c);
return;
}
OnConnect(c);
while (c->GetStatus() > -1) {
GParser(c, TCPRcv(c));
}
OnDisconnect(c, c->GetStatus() == -2);
}

View File

@ -1,193 +0,0 @@
// Copyright (c) 2019-present Anonymous275.
// BeamMP Server code is not in the public domain and is not free software.
// One must be granted explicit permission by the copyright holder in order to modify or distribute any part of the source or binaries.
// Anything else is prohibited. Modified works may not be published and have be upstreamed to the official repository.
///
/// Created by Anonymous275 on 5/8/2020
///
#include "Client.hpp"
#include "Compressor.h"
#include "Logger.h"
#include "Network.h"
#include "Security/Enc.h"
#include "Settings.h"
#include "UnixCompat.h"
#include <cmath>
#include <cstring>
#include <sstream>
#include <thread>
#include <vector>
#include <array>
SOCKET UDPSock;
void UDPSend(Client* c, std::string Data) {
Assert(c);
if (c == nullptr || !c->isConnected || c->GetStatus() < 0)
return;
sockaddr_in Addr = c->GetUDPAddr();
socklen_t AddrSize = sizeof(c->GetUDPAddr());
if (Data.length() > 400) {
std::string CMP(Comp(Data));
Data = "ABG:" + CMP;
}
#ifdef WIN32
int sendOk;
int len = static_cast<int>(Data.size());
#else
int64_t sendOk;
size_t len = Data.size();
#endif // WIN32
sendOk = sendto(UDPSock, Data.c_str(), len, 0, (sockaddr*)&Addr, AddrSize);
#ifdef WIN32
if (sendOk == -1) {
debug(("(UDP) Send Failed Code : ") + std::to_string(WSAGetLastError()));
if (c->GetStatus() > -1)
c->SetStatus(-1);
} else if (sendOk == 0) {
debug(("(UDP) sendto returned 0"));
if (c->GetStatus() > -1)
c->SetStatus(-1);
}
#else // unix
if (sendOk == -1) {
debug(("(UDP) Send Failed Code : ") + std::string(strerror(errno)));
if (c->GetStatus() > -1)
c->SetStatus(-1);
} else if (sendOk == 0) {
debug(("(UDP) sendto returned 0"));
if (c->GetStatus() > -1)
c->SetStatus(-1);
}
#endif // WIN32
}
void SendLarge(Client* c, std::string Data) {
Assert(c);
if (Data.length() > 400) {
std::string CMP(Comp(Data));
Data = "ABG:" + CMP;
}
TCPSend(c, Data);
}
std::string UDPRcvFromClient(sockaddr_in& client) {
size_t clientLength = sizeof(client);
std::array<char, 1024> Ret{};
int64_t Rcv = recvfrom(UDPSock, Ret.data(), Ret.size(), 0, (sockaddr*)&client, (socklen_t*)&clientLength);
if (Rcv == -1) {
#ifdef WIN32
error(("(UDP) Error receiving from Client! Code : ") + std::to_string(WSAGetLastError()));
#else // unix
error(("(UDP) Error receiving from Client! Code : ") + std::string(strerror(errno)));
#endif // WIN32
return "";
}
return std::string(Ret.begin(), Ret.begin() + Rcv);
}
void UDPParser(Client* c, std::string Packet) {
if (Packet.find("Zp") != std::string::npos && Packet.size() > 500) {
abort();
}
Assert(c);
if (Packet.substr(0, 4) == "ABG:") {
Packet = DeComp(Packet.substr(4));
}
GParser(c, Packet);
}
[[noreturn]] void UDPServerMain() {
#ifdef WIN32
WSADATA data;
if (WSAStartup(514, &data)) {
error(("Can't start Winsock!"));
//return;
}
UDPSock = socket(AF_INET, SOCK_DGRAM, 0);
// Create a server hint structure for the server
sockaddr_in serverAddr {};
serverAddr.sin_addr.S_un.S_addr = ADDR_ANY; //Any Local
serverAddr.sin_family = AF_INET; // Address format is IPv4
serverAddr.sin_port = htons(Port); // Convert from little to big endian
// Try and bind the socket to the IP and port
if (bind(UDPSock, (sockaddr*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR) {
error(("Can't bind socket!") + std::to_string(WSAGetLastError()));
std::this_thread::sleep_for(std::chrono::seconds(5));
_Exit(-1);
//return;
}
info(("Vehicle data network online on port ") + std::to_string(Port) + (" with a Max of ") + std::to_string(MaxPlayers) + (" Clients"));
while (true) {
try {
sockaddr_in client {};
std::string Data = UDPRcvFromClient(client); //Receives any data from Socket
auto Pos = Data.find(':');
if (Data.empty() || Pos == std::string::npos || Pos > 2)
continue;
/*char clientIp[256];
ZeroMemory(clientIp, 256); ///Code to get IP we don't need that yet
inet_ntop(AF_INET, &client.sin_addr, clientIp, 256);*/
uint8_t ID = Data.at(0) - 1;
for (auto& c : CI->Clients) {
if (c != nullptr && c->GetID() == ID) {
c->SetUDPAddr(client);
c->isConnected = true;
UDPParser(c.get(), Data.substr(2));
}
}
} catch (const std::exception& e) {
error(("fatal: ") + std::string(e.what()));
}
}
/*CloseSocketProper(UDPSock);
WSACleanup();
return;*/
#else // unix
UDPSock = socket(AF_INET, SOCK_DGRAM, 0);
// Create a server hint structure for the server
sockaddr_in serverAddr {};
serverAddr.sin_addr.s_addr = INADDR_ANY; //Any Local
serverAddr.sin_family = AF_INET; // Address format is IPv4
serverAddr.sin_port = htons(uint16_t(Port)); // Convert from little to big endian
// Try and bind the socket to the IP and port
if (bind(UDPSock, (sockaddr*)&serverAddr, sizeof(serverAddr)) != 0) {
error(("Can't bind socket!") + std::string(strerror(errno)));
std::this_thread::sleep_for(std::chrono::seconds(5));
_Exit(-1);
//return;
}
info(("Vehicle data network online on port ") + std::to_string(Port) + (" with a Max of ") + std::to_string(MaxPlayers) + (" Clients"));
while (true) {
try {
sockaddr_in client {};
std::string Data = UDPRcvFromClient(client); //Receives any data from Socket
size_t Pos = Data.find(':');
if (Data.empty() || Pos > 2)
continue;
/*char clientIp[256];
ZeroMemory(clientIp, 256); ///Code to get IP we don't need that yet
inet_ntop(AF_INET, &client.sin_addr, clientIp, 256);*/
uint8_t ID = uint8_t(Data.at(0)) - 1;
for (auto& c : CI->Clients) {
if (c != nullptr && c->GetID() == ID) {
c->SetUDPAddr(client);
c->isConnected = true;
UDPParser(c.get(), Data.substr(2));
}
}
} catch (const std::exception& e) {
error(("fatal: ") + std::string(e.what()));
}
}
/*CloseSocketProper(UDPSock); // TODO: Why not this? We did this in TCPServerMain?
return;
*/
#endif // WIN32
}

View File

@ -1,60 +0,0 @@
// Copyright (c) 2019-present Anonymous275.
// BeamMP Server code is not in the public domain and is not free software.
// One must be granted explicit permission by the copyright holder in order to modify or distribute any part of the source or binaries.
// Anything else is prohibited. Modified works may not be published and have be upstreamed to the official repository.
///
/// Created by Anonymous275 on 11/6/2020
///
/*#include <boost/beast/core.hpp>
#include "Logger.h"
#include "Security/Enc.h"
#include <boost/asio/connect.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/beast/websocket.hpp>
#include <iostream>
#include <string>
#include <thread>*/
/*namespace beast = boost::beast;
namespace http = beast::http;
namespace websocket = beast::websocket;
namespace net = boost::asio;
using tcp = boost::asio::ip::tcp;
std::string GetRes(const beast::flat_buffer& buff) {
return (char*)buff.data().data();
}*/
void SyncData() {
/*DebugPrintTID();
try {
std::string const host = ("95.216.35.232");
net::io_context ioc;
tcp::resolver r(ioc);
websocket::stream<tcp::socket> ws(ioc);
auto const results = r.resolve(host, ("3600"));
net::connect(ws.next_layer(), results.begin(), results.end());
ws.handshake(host, "/");
beast::flat_buffer buffer;
ws.write(boost::asio::buffer("Hello, world!"));
ws.read(buffer);
std::cout << GetRes(buffer) << std::endl;
ws.close(websocket::close_code::normal);
}catch(std::exception const& e){
error(e.what());
std::this_thread::sleep_for(std::chrono::seconds(3));
_Exit(0);
}*/
}
void WebsocketInit() {
/*std::thread t1(SyncData);
t1.detach();*/
}

17
src/TConsole.cpp Normal file
View File

@ -0,0 +1,17 @@
#include "TConsole.h"
#include "Compat.h"
TConsole::TConsole() {
_Commandline.enable_history();
_Commandline.set_history_limit(20);
_Commandline.on_command = [](Commandline& c) {
auto cmd = c.get_command();
if (cmd == "exit") {
_Exit(0);
} else if (cmd == "clear" || cmd == "cls") {
// TODO: clear screen
} else {
// TODO: execute as lua
}
};
}

View File

@ -1,131 +0,0 @@
// Copyright (c) 2019-present Anonymous275.
// BeamMP Server code is not in the public domain and is not free software.
// One must be granted explicit permission by the copyright holder in order to modify or distribute any part of the source or binaries.
// Anything else is prohibited. Modified works may not be published and have be upstreamed to the official repository.
///
/// Created by Anonymous275 on 7/17/2020
///
#include "Logger.h"
#include "RWMutex.h"
#include "Security/Enc.h"
#include "Settings.h"
#include <chrono>
#include <fstream>
#include <mutex>
#include <sstream>
#include <thread>
#include <unordered_map>
static RWMutex ThreadNameMapMutex;
static std::unordered_map<std::thread::id, std::string> ThreadNameMap;
std::string ThreadName() {
ReadLock lock(ThreadNameMapMutex);
std::string Name;
if (ThreadNameMap.find(std::this_thread::get_id()) != ThreadNameMap.end()) {
Name = ThreadNameMap.at(std::this_thread::get_id());
} else {
std::stringstream ss;
ss << std::this_thread::get_id();
Name = ss.str();
}
return Name;
}
void SetThreadName(const std::string& Name, bool overwrite) {
WriteLock lock(ThreadNameMapMutex);
if (overwrite || ThreadNameMap.find(std::this_thread::get_id()) == ThreadNameMap.end()) {
ThreadNameMap[std::this_thread::get_id()] = Name;
}
}
std::string getDate() {
std::chrono::system_clock::time_point now = std::chrono::system_clock::now();
time_t tt = std::chrono::system_clock::to_time_t(now);
tm local_tm {};
#ifdef WIN32
localtime_s(&local_tm, &tt);
#else // unix
localtime_r(&tt, &local_tm);
#endif // WIN32
std::stringstream date;
int S = local_tm.tm_sec;
int M = local_tm.tm_min;
int H = local_tm.tm_hour;
std::string Secs = (S > 9 ? std::to_string(S) : "0" + std::to_string(S));
std::string Min = (M > 9 ? std::to_string(M) : "0" + std::to_string(M));
std::string Hour = (H > 9 ? std::to_string(H) : "0" + std::to_string(H));
date
<< "["
<< local_tm.tm_mday << "/"
<< local_tm.tm_mon + 1 << "/"
<< local_tm.tm_year + 1900 << " "
<< Hour << ":"
<< Min << ":"
<< Secs
<< "] ";
if (Debug) {
date << ThreadName()
<< " ";
}
return date.str();
}
void InitLog() {
std::ofstream LFS;
LFS.open(("Server.log"));
if (!LFS.is_open()) {
error(("logger file init failed!"));
} else
LFS.close();
}
std::mutex LogLock;
void DebugPrintTIDInternal(const std::string& func, bool overwrite) {
// we need to print to cout here as we might crash before all console output is handled,
// due to segfaults or asserts.
SetThreadName(func, overwrite);
#ifdef DEBUG
std::scoped_lock Guard(LogLock);
std::stringstream Print;
Print << "(debug build) Thread '" << std::this_thread::get_id() << "' is " << func << "\n";
ConsoleOut(Print.str());
#endif // DEBUG
}
void addToLog(const std::string& Line) {
std::ofstream LFS("Server.log", std::ios_base::app);
LFS << Line.c_str();
}
void info(const std::string& toPrint) {
std::scoped_lock Guard(LogLock);
std::string Print = getDate() + ("[INFO] ") + toPrint + "\n";
ConsoleOut(Print);
addToLog(Print);
}
void debug(const std::string& toPrint) {
if (!Debug)
return;
std::scoped_lock Guard(LogLock);
std::string Print = getDate() + ("[DEBUG] ") + toPrint + "\n";
ConsoleOut(Print);
addToLog(Print);
}
void warn(const std::string& toPrint) {
std::scoped_lock Guard(LogLock);
std::string Print = getDate() + ("[WARN] ") + toPrint + "\n";
ConsoleOut(Print);
addToLog(Print);
}
void error(const std::string& toPrint) {
std::scoped_lock Guard(LogLock);
std::string Print = getDate() + ("[ERROR] ") + toPrint + "\n";
ConsoleOut(Print);
addToLog(Print);
}
void except(const std::string& toPrint) {
std::scoped_lock Guard(LogLock);
std::string Print = getDate() + ("[EXCEP] ") + toPrint + "\n";
ConsoleOut(Print);
addToLog(Print);
}

View File

@ -1,50 +1,12 @@
#include "CustomAssert.h"
#include "Settings.h"
#include "Startup.h"
#include <atomic>
#include <functional>
#include <iostream>
#include <thread>
#ifndef WIN32
#include <csignal>
void UnixSignalHandler(int sig) {
switch (sig) {
case SIGPIPE:
warn(("ignored signal SIGPIPE: Pipe broken"));
break;
default:
error(("Signal arrived in handler but was not handled: ") + std::to_string(sig));
break;
}
}
#endif // WIN32
[[noreturn]] void loop() {
DebugPrintTID();
#include "TConsole.h"
int main(int argc, char** argv) {
TConsole Console;
while (true) {
std::cout.flush();
std::this_thread::sleep_for(std::chrono::milliseconds(600));
}
}
int main(int argc, char* argv[]) {
#ifndef WIN32
// ignore SIGPIPE, the signal that is sent for example when a client
// disconnects while data is being sent to him ("broken pipe").
signal(SIGPIPE, UnixSignalHandler);
#endif // WIN32
DebugPrintTID();
#ifdef DEBUG
std::thread t1(loop);
t1.detach();
#endif
StartTime = std::chrono::high_resolution_clock::now();
ConsoleInit();
InitServer(argc, argv);
InitConfig();
InitLua();
InitRes();
HBInit();
StatInit();
NetMain();
return 0;
}