add lua engine, lua file, server, client, vehicle data, other stuff

This commit is contained in:
Lion Kortlepel 2021-02-15 02:35:15 +01:00 committed by Anonymous275
parent e5e447c7af
commit 459814a6ec
21 changed files with 1538 additions and 15 deletions

View File

@ -22,7 +22,14 @@ find_package(Boost REQUIRED COMPONENTS system thread)
add_executable(BeamMP-Server
src/main.cpp
src/TConsole.cpp
src/TServer.cpp
src/Compat.cpp
src/Common.cpp
src/Client.cpp
src/VehicleData.cpp
src/TConfig.cpp
src/TLuaEngine.cpp
src/TLuaFile.cpp
)
target_include_directories(BeamMP-Server PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" "${CMAKE_CURRENT_SOURCE_DIR}/commandline")

53
include/Client.h Normal file
View File

@ -0,0 +1,53 @@
#pragma once
#include <memory>
#include <string>
#include <unordered_set>
#include "Common.h"
#include "Compat.h"
#include "VehicleData.h"
class TClient final {
public:
using TSetOfVehicleData = std::unordered_set<std::unique_ptr<TVehicleData>>;
void AddNewCar(int Ident, const std::string& Data);
void SetCarData(int Ident, const std::string& Data);
TSetOfVehicleData& 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) { _UDPAddress = Addr; }
void SetDownSock(SOCKET CSock) { _Socket[1] = CSock; }
void SetTCPSock(SOCKET CSock) { _Socket[0] = CSock; }
void SetStatus(int Status) { _Status = Status; }
void DeleteCar(int Ident);
sockaddr_in GetUDPAddr() { return _UDPAddress; }
std::string GetRoles() { return _Role; }
std::string GetName() { return _Name; }
SOCKET GetDownSock() { return _Socket[1]; }
SOCKET GetTCPSock() { return _Socket[0]; }
void SetID(int ID) { _ID = ID; }
int GetOpenCarID();
int GetCarCount();
void ClearCars();
int GetStatus() { return _Status; }
int GetID() { return _ID; }
bool IsConnected() const { return _IsConnected; }
bool IsSynced() const { return _IsSynced; }
bool IsGuest() const { return _IsGuest; }
private:
bool _IsConnected = false;
bool _IsSynced = false;
bool _IsGuest = false;
TSetOfVehicleData _VehicleData;
std::string _Name = "Unknown Client";
SOCKET _Socket[2] { SOCKET(-1) };
sockaddr_in _UDPAddress;
std::string _Role;
std::string _DID;
int _Status = 0;
int _ID = -1;
};

66
include/Common.h Normal file
View File

@ -0,0 +1,66 @@
#pragma once
#include <atomic>
#include <functional>
#include <memory>
#include <mutex>
#include <vector>
#include "TConsole.h"
// static class handling application start, shutdown, etc.
// yes, static classes, singletons, globals are all pretty
// bad idioms. In this case we need a central way to access
// stuff like graceful shutdown, global settings (its in the name),
// etc.
class Application final {
public:
// types
struct TSettings {
TSettings()
: DebugModeEnabled(true) { }
std::string ServerName;
std::string ServerDesc;
std::string Resource;
std::string MapName;
std::string Key;
int MaxPlayers;
bool Private;
int MaxCars;
bool DebugModeEnabled;
int Port;
std::string CustomIP;
bool HasCustomIP() const { return !CustomIP.empty(); }
// new settings
std::string ResourceFolder;
};
using TShutdownHandler = std::function<void()>;
// methods
Application() = delete;
// 'Handler' is called when GracefullyShutdown is called
static void RegisterShutdownHandler(const TShutdownHandler& Handler);
// Causes all threads to finish up and exit gracefull gracefully
static void GracefullyShutdown();
static TConsole& Console() { return *_Console; }
static std::string ServerVersion() { return "v1.20"; }
static inline TSettings Settings {};
private:
static std::unique_ptr<TConsole> _Console;
static inline std::mutex _ShutdownHandlersMutex {};
static inline std::vector<TShutdownHandler> _ShutdownHandlers {};
};
#define warn(x) Application::Console().Write(std::string("[WARN] ") + (x))
#define error(x) Application::Console().Write(std::string("[ERROR] ") + (x))
#define info(x) Application::Console().Write(std::string("[INFO] ") + (x))
#define debug(x) \
do { \
if (Application::Settings.DebugModeEnabled) { \
Application::Console().Write(std::string("[DEBUG] ") + (x)); \
} \
} while (false)

View File

@ -1,17 +1,28 @@
#pragma once
// Unix - Win32 compatibility stuff
// ======================= UNIX ========================
#ifdef __unix
#include <arpa/inet.h>
#include <termios.h>
#include <unistd.h>
using SOCKET = int;
using DWORD = unsigned long;
using PDWORD = unsigned long*;
using LPDWORD = unsigned long*;
char _getch(void);
#endif // unix
// ======================= WIN32 =======================
#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
// ======================= OTHER =======================
char _getch(void);
#if !defined(WIN32) && !defined(__unix)
#error "OS not supported"
#endif
#endif // !WIN32

68
include/CustomAssert.h Normal file
View File

@ -0,0 +1,68 @@
// 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 "Common.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
#include <iostream>
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

19
include/RWMutex.h Normal file
View File

@ -0,0 +1,19 @@
// 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>;

12
include/TConfig.h Normal file
View File

@ -0,0 +1,12 @@
#pragma once
#include "Common.h"
class TConfig {
public:
TConfig(const std::string& ConfigFile);
private:
std::string RemoveComments(const std::string& Line);
void SetValues(const std::string& Line, int Index);
};

View File

@ -1,12 +1,16 @@
#pragma once
#include "commandline/commandline.h"
#include <atomic>
#include <commandline/commandline.h>
#include <fstream>
class TConsole {
public:
TConsole();
void Write(const std::string& str);
private:
Commandline _Commandline;
};

32
include/TLuaEngine.h Normal file
View File

@ -0,0 +1,32 @@
#ifndef TLUAENGINE_H
#define TLUAENGINE_H
#include "Common.h"
#include "IThreaded.h"
#include <lua.hpp>
#include <memory>
#include <set>
class TLuaFile;
class TLuaEngine : public IThreaded {
public:
using TSetOfLuaFile = std::set<std::unique_ptr<TLuaFile>>;
TLuaEngine();
virtual void operator()() override;
const TSetOfLuaFile& LuaFiles() const { return _LuaFiles; }
std::optional<std::reference_wrapper<TLuaFile>> GetScript(lua_State* L);
private:
void FolderList(const std::string& Path, bool HotSwap);
void RegisterFiles(const std::string& Path, bool HotSwap);
bool NewFile(const std::string& Path);
TSetOfLuaFile _LuaFiles;
};
#endif // TLUAENGINE_H

58
include/TLuaFile.h Normal file
View File

@ -0,0 +1,58 @@
#ifndef TLUAFILE_H
#define TLUAFILE_H
#include "TLuaEngine.h"
#include <any>
#include <filesystem>
#include <lua.hpp>
#include <mutex>
#include <set>
#include <string>
#include <vector>
namespace fs = std::filesystem;
struct TLuaArg {
std::vector<std::any> args;
void PushArgs(lua_State* State);
};
class TLuaFile {
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();
TLuaFile(TLuaEngine& Engine, const std::string& PluginName, const std::string& FileName, fs::file_time_type LastWrote, bool Console = false);
TLuaFile(TLuaEngine& Engine, bool Console = false);
~TLuaFile();
void SetStopThread(bool StopThread) { _StopThread = StopThread; }
bool GetStopThread() const { return _StopThread; }
TLuaEngine& Engine() { return _Engine; }
const TLuaEngine& Engine() const { return _Engine; }
private:
TLuaEngine& _Engine;
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;
};
#endif // TLUAFILE_H

26
include/TServer.h Normal file
View File

@ -0,0 +1,26 @@
#pragma once
#include <functional>
#include <memory>
#include <mutex>
#include <unordered_set>
#include "RWMutex.h"
class TClient;
class TServer final {
public:
using TClientSet = std::unordered_set<std::shared_ptr<TClient>>;
TServer(int argc, char** argv);
std::weak_ptr<TClient> InsertNewClient();
void RemoveClient(std::weak_ptr<TClient>);
void ForEachClient(std::function<bool(std::weak_ptr<TClient>)>);
size_t ClientCount() const;
private:
TClientSet _Clients;
mutable RWMutex _ClientsMutex;
};

18
include/VehicleData.h Normal file
View File

@ -0,0 +1,18 @@
#pragma once
#include <string>
class TVehicleData final {
public:
TVehicleData(int ID, const std::string& Data);
bool IsInvalid() const { return _ID == -1; }
int ID() const { return _ID; }
std::string Data() const { return _Data; }
void SetData(const std::string& Data) { _Data = Data; }
private:
int _ID { -1 };
std::string _Data;
};

64
src/Client.cpp Normal file
View File

@ -0,0 +1,64 @@
#include "Client.h"
#include <memory>
// FIXME: add debug prints
void TClient::DeleteCar(int Ident) {
for (auto& v : _VehicleData) {
if (v != nullptr && v->ID() == Ident) {
_VehicleData.erase(v);
break;
}
}
}
void TClient::ClearCars() {
_VehicleData.clear();
}
int TClient::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 TClient::AddNewCar(int Ident, const std::string& Data) {
_VehicleData.insert(std::make_unique<TVehicleData>(TVehicleData { Ident, Data }));
}
TClient::TSetOfVehicleData& TClient::GetAllCars() {
return _VehicleData;
}
std::string TClient::GetCarData(int Ident) {
for (auto& v : _VehicleData) {
if (v != nullptr && v->ID() == Ident) {
return v->Data();
}
}
DeleteCar(Ident);
return "";
}
void TClient::SetCarData(int Ident, const std::string& Data) {
for (auto& v : _VehicleData) {
if (v != nullptr && v->ID() == Ident) {
v->Data() = Data;
return;
}
}
DeleteCar(Ident);
}
int TClient::GetCarCount() {
return int(_VehicleData.size());
}

18
src/Common.cpp Normal file
View File

@ -0,0 +1,18 @@
#include "Common.h"
#include "TConsole.h"
std::unique_ptr<TConsole> Application::_Console = std::make_unique<TConsole>();
void Application::RegisterShutdownHandler(const TShutdownHandler& Handler) {
std::unique_lock Lock(_ShutdownHandlersMutex);
if (Handler) {
_ShutdownHandlers.push_back(Handler);
}
}
void Application::GracefullyShutdown() {
std::unique_lock Lock(_ShutdownHandlersMutex);
for (auto& Handler : _ShutdownHandlers) {
Handler();
}
}

126
src/TConfig.cpp Normal file
View File

@ -0,0 +1,126 @@
#include "../include/TConfig.h"
#include <fstream>
TConfig::TConfig(const std::string& ConfigFile) {
std::ifstream File(ConfigFile);
if (File.good()) {
std::string line;
int index = 1;
while (getline(File, 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);
}
File.close();
File.open(("Server.cfg"));
info(("Config found updating values"));
index = 1;
while (std::getline(File, 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++;
}
}
} else {
info(("Config not found generating default"));
std::ofstream FileStream;
FileStream.open(ConfigFile);
// TODO REPLACE THIS SHIT OMG
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();
error(("You are required to input the AuthKey"));
std::this_thread::sleep_for(std::chrono::seconds(3));
_Exit(0);
}
debug("Debug : " + std::string(Application::Settings.DebugModeEnabled ? "true" : "false"));
debug("Private : " + std::string(Application::Settings.Private ? "true" : "false"));
debug("Port : " + std::to_string(Application::Settings.Port));
debug("Max Cars : " + std::to_string(Application::Settings.MaxCars));
debug("MaxPlayers : " + std::to_string(Application::Settings.MaxPlayers));
debug("MapName : \"" + Application::Settings.MapName + "\"");
debug("ServerName : \"" + Application::Settings.ServerName + "\"");
debug("ServerDesc : \"" + Application::Settings.ServerDesc + "\"");
debug("File : \"" + Application::Settings.Resource + "\"");
debug("Key length : " + std::to_string(Application::Settings.Key.length()) + "");
}
std::string TConfig::RemoveComments(const std::string& Line) {
std::string Return;
for (char c : Line) {
if (c == '#')
break;
Return += c;
}
return Return;
}
void TConfig::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:
Application::Settings.DebugModeEnabled = FoundTrue; //checks and sets the Debug Value
break;
case 2:
Application::Settings.Private = FoundTrue; //checks and sets the Private Value
break;
case 3:
Application::Settings.Port = std::stoi(Data, &sz); //sets the Port
break;
case 4:
Application::Settings.MaxCars = std::stoi(Data, &sz); //sets the Max Car amount
break;
case 5:
Application::Settings.MaxPlayers = std::stoi(Data, &sz); //sets the Max Amount of player
break;
case 6:
Application::Settings.MapName = Data; //Map
break;
case 7:
Application::Settings.ServerName = Data; //Name
break;
case 8:
Application::Settings.ServerDesc = Data; //desc
break;
case 9:
Application::Settings.Resource = Data; //File name
break;
case 10:
Application::Settings.Key = Data; //File name
default:
break;
}
}

View File

@ -1,13 +1,54 @@
#include "TConsole.h"
#include "Common.h"
#include "Compat.h"
#include <ctime>
#include <sstream>
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
<< "] ";
/* TODO
if (Debug) {
date << ThreadName()
<< " ";
}
*/
return date.str();
}
TConsole::TConsole() {
_Commandline.enable_history();
_Commandline.set_history_limit(20);
_Commandline.on_command = [](Commandline& c) {
_Commandline.set_prompt("> ");
_Commandline.on_command = [this](Commandline& c) {
auto cmd = c.get_command();
_Commandline.write("> " + cmd);
if (cmd == "exit") {
_Exit(0);
info("gracefully shutting down");
Application::GracefullyShutdown();
} else if (cmd == "clear" || cmd == "cls") {
// TODO: clear screen
} else {
@ -15,3 +56,10 @@ TConsole::TConsole() {
}
};
}
void TConsole::Write(const std::string& str) {
auto ToWrite = GetDate() + str;
_Commandline.write(ToWrite);
// TODO write to logfile, too
}

67
src/TLuaEngine.cpp Normal file
View File

@ -0,0 +1,67 @@
#include "TLuaEngine.h"
#include "TLuaFile.h"
#include <filesystem>
namespace fs = std::filesystem;
TLuaEngine::TLuaEngine() {
if (!fs::exists(Application::Settings.ResourceFolder)) {
fs::create_directory(Application::Settings.ResourceFolder);
}
std::string Path = Application::Settings.ResourceFolder + ("/Server");
if (!fs::exists(Path)) {
fs::create_directory(Path);
}
FolderList(Path, false);
}
void TLuaEngine::operator()() {
info("Lua system online");
// thread main
}
std::optional<std::reference_wrapper<TLuaFile>> TLuaEngine::GetScript(lua_State* L) {
for (auto& Script : _LuaFiles) {
if (Script->GetState() == L)
return *Script;
}
return std::nullopt;
}
void TLuaEngine::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);
}
}
}
void TLuaEngine::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<TLuaFile> ScriptToInsert(new TLuaFile(*this, Name, FileName, fs::last_write_time(FileName)));
auto& Script = *ScriptToInsert;
_LuaFiles.insert(std::move(ScriptToInsert));
Script.Init();
if (HotSwap)
info(("[HOTSWAP] Added : ") + Script.GetFileName().substr(Script.GetFileName().find('\\')));
}
}
}
}
bool TLuaEngine::NewFile(const std::string& Path) {
for (auto& Script : _LuaFiles) {
if (Path == Script->GetFileName())
return false;
}
return true;
}

722
src/TLuaFile.cpp Normal file
View File

@ -0,0 +1,722 @@
#include "TLuaFile.h"
#include "Client.h"
#include "CustomAssert.h"
#include "TServer.h"
#include <future>
#include <thread>
// TODO: REWRITE >:(
void SendError(TLuaEngine& Engine, lua_State* L, const std::string& msg);
std::any CallFunction(TLuaFile* lua, const std::string& FuncName, std::shared_ptr<TLuaArg> Arg);
std::any TriggerLuaEvent(TLuaEngine& Engine, const std::string& Event, bool local, TLuaFile* Caller, std::shared_ptr<TLuaArg> arg, bool Wait);
std::shared_ptr<TLuaArg> CreateArg(lua_State* L, int T, int S) {
if (S > T)
return nullptr;
std::shared_ptr<TLuaArg> temp(new TLuaArg);
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::any Trigger(TLuaFile* lua, const std::string& R, std::shared_ptr<TLuaArg> arg) {
std::lock_guard<std::mutex> lockGuard(lua->Lock);
std::packaged_task<std::any(std::shared_ptr<TLuaArg>)> task([lua, R](std::shared_ptr<TLuaArg> 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->Engine(), lua->GetState(), R + " took too long to respond");
return 0;
}
std::any FutureWait(TLuaFile* lua, const std::string& R, std::shared_ptr<TLuaArg> arg, bool Wait) {
Assert(lua);
std::packaged_task<std::any(std::shared_ptr<TLuaArg>)> task([lua, R](std::shared_ptr<TLuaArg> 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(TLuaEngine& Engine, const std::string& Event, bool local, TLuaFile* Caller, std::shared_ptr<TLuaArg> arg, bool Wait) {
std::any R;
std::string Type;
int Ret = 0;
for (auto& Script : Engine.LuaFiles()) {
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(TLuaEngine& Engine, lua_State* L, int r) {
if (r != LUA_OK) {
std::string msg = lua_tostring(L, -1);
auto MaybeS = Engine.GetScript(L);
if (MaybeS.has_value()) {
TLuaFile& 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(TLuaEngine& Engine, lua_State* L) {
int Args = lua_gettop(L);
auto MaybeScript = Engine.GetScript(L);
Assert(MaybeScript.has_value());
TLuaFile& 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(Engine, L, ("RegisterEvent invalid argument count expected 2 got ") + std::to_string(Args));
return 0;
}
int lua_TriggerEventL(TLuaEngine& Engine, lua_State* L) {
int Args = lua_gettop(L);
auto MaybeScript = Engine.GetScript(L);
Assert(MaybeScript.has_value());
TLuaFile& Script = MaybeScript.value();
if (Args > 0) {
if (lua_isstring(L, 1)) {
TriggerLuaEvent(Engine, lua_tostring(L, 1), true, &Script, CreateArg(L, Args, 2), false);
} else
SendError(Engine, L, ("TriggerLocalEvent wrong argument [1] need string"));
} else {
SendError(Engine, L, ("TriggerLocalEvent not enough arguments expected 1 got 0"));
}
return 0;
}
int lua_TriggerEventG(TLuaEngine& Engine, lua_State* L) {
int Args = lua_gettop(L);
auto MaybeScript = Engine.GetScript(L);
Assert(MaybeScript.has_value());
TLuaFile& Script = MaybeScript.value();
if (Args > 0) {
if (lua_isstring(L, 1)) {
TriggerLuaEvent(Engine, lua_tostring(L, 1), false, &Script, CreateArg(L, Args, 2), false);
} else
SendError(Engine, L, ("TriggerGlobalEvent wrong argument [1] need string"));
} else
SendError(Engine, L, ("TriggerGlobalEvent not enough arguments"));
return 0;
}
char* ThreadOrigin(TLuaFile* lua) {
std::string T = "Thread in " + fs::path(lua->GetFileName()).filename().string();
char* Data = new char[T.size() + 1];
std::fill_n(Data, T.size() + 1, 0);
memcpy(Data, T.c_str(), T.size());
return Data;
}
void SafeExecution(TLuaEngine& Engine, TLuaFile* 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(Engine, luaState, R);
#endif // WIN32
delete[] Origin;
}
ClearStack(luaState);
}
void ExecuteAsync(TLuaEngine& Engine, TLuaFile* lua, const std::string& FuncName) {
std::lock_guard<std::mutex> lockGuard(lua->Lock);
SafeExecution(Engine, lua, FuncName);
}
void CallAsync(TLuaEngine& Engine, TLuaFile* lua, const std::string& Func, int U) {
//DebugPrintTID();
lua->SetStopThread(false);
int D = 1000 / U;
while (!lua->GetStopThread()) {
ExecuteAsync(Engine, lua, Func);
std::this_thread::sleep_for(std::chrono::milliseconds(D));
}
}
int lua_StopThread(TLuaEngine& Engine, lua_State* L) {
auto MaybeScript = Engine.GetScript(L);
Assert(MaybeScript.has_value());
// ugly, but whatever, this is safe as fuck
MaybeScript.value().get().SetStopThread(true);
return 0;
}
int lua_CreateThread(TLuaEngine& Engine, 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 = Engine.GetScript(L);
Assert(MaybeScript.has_value());
TLuaFile& Script = MaybeScript.value();
std::thread t1(CallAsync, &Script, STR, U);
t1.detach();
} else
SendError(Engine, L, ("CreateThread wrong argument [2] number must be between 1 and 500"));
} else
SendError(Engine, L, ("CreateThread wrong argument [2] need number"));
} else
SendError(Engine, L, ("CreateThread wrong argument [1] need string"));
} else
SendError(Engine, L, ("CreateThread not enough arguments"));
return 0;
}
int lua_Sleep(TLuaEngine& Engine, 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(Engine, L, ("Sleep not enough arguments"));
return 0;
}
return 1;
}
std::weak_ptr<TClient> GetClient(TServer& Server, int ID) {
std::weak_ptr<TClient> Client;
Server.ForEachClient([&](std::weak_ptr<TClient> CPtr) {
if (!CPtr.expired()) {
auto C = CPtr.lock();
if (C != nullptr && C->GetID() == ID) {
return false;
}
}
return true;
});
return std::weak_ptr<TClient>();
}
// CONTINUE
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;
}
}
TLuaFile::TLuaFile(TLuaEngine& Engine, const std::string& PluginName, const std::string& FileName, fs::file_time_type LastWrote, bool Console)
: _Engine(Engine)
, luaState(luaL_newstate()) {
Assert(luaState);
if (!PluginName.empty()) {
SetPluginName(PluginName);
}
if (!FileName.empty()) {
SetFileName(FileName);
}
SetLastWrite(LastWrote);
_Console = Console;
}
TLuaFile::TLuaFile(TLuaEngine& Engine, bool Console)
: _Engine(Engine)
, luaState(luaL_newstate()) {
_Console = Console;
}
void TLuaFile::Execute(const std::string& Command) {
if (ConsoleCheck(luaState, luaL_dostring(luaState, Command.c_str()))) {
lua_settop(luaState, 0);
}
}
void TLuaFile::Reload() {
if (CheckLua(luaState, luaL_dofile(luaState, _FileName.c_str()))) {
CallFunction(this, ("onInit"), nullptr);
}
}
std::string TLuaFile::GetOrigin() {
return fs::path(GetFileName()).filename().string();
}
std::any CallFunction(TLuaFile* lua, const std::string& FuncName, std::shared_ptr<TLuaArg> 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 TLuaFile::SetPluginName(const std::string& Name) {
_PluginName = Name;
}
void TLuaFile::SetFileName(const std::string& Name) {
_FileName = Name;
}
int lua_TempFix(TLuaEngine& Engine, 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(Engine, L, "GetDID not enough arguments");
return 1;
}
void TLuaFile::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 TLuaFile::RegisterEvent(const std::string& Event, const std::string& FunctionName) {
_RegisteredEvents.insert(std::make_pair(Event, FunctionName));
}
void TLuaFile::UnRegisterEvent(const std::string& Event) {
for (const std::pair<std::string, std::string>& a : _RegisteredEvents) {
if (a.first == Event) {
_RegisteredEvents.erase(a);
break;
}
}
}
bool TLuaFile::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 TLuaFile::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 TLuaFile::GetFileName() const {
return _FileName;
}
std::string TLuaFile::GetPluginName() const {
return _PluginName;
}
lua_State* TLuaFile::GetState() {
return luaState;
}
const lua_State* TLuaFile::GetState() const {
return luaState;
}
void TLuaFile::SetLastWrite(fs::file_time_type time) {
_LastWrote = time;
}
fs::file_time_type TLuaFile::GetLastWrite() {
return _LastWrote;
}
TLuaFile::~TLuaFile() {
info("closing lua state");
lua_close(luaState);
}
void SendError(TLuaEngine& Engine, lua_State* L, const std::string& msg) {
Assert(L);
auto MaybeS = Engine.GetScript(L);
std::string a;
if (!MaybeS.has_value()) {
a = ("_Console");
} else {
TLuaFile& S = MaybeS.value();
a = fs::path(S.GetFileName()).filename().string();
}
warn(a + (" | Incorrect Call of ") + msg);
}
void TLuaArg::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));
}
}
}

49
src/TServer.cpp Normal file
View File

@ -0,0 +1,49 @@
#include "TServer.h"
#include "Client.h"
#include "Common.h"
TServer::TServer(int argc, char** argv) {
info("BeamMP Server running version " + Application::ServerVersion());
if (argc > 1) {
Application::Settings.CustomIP = argv[1];
size_t n = std::count(Application::Settings.CustomIP.begin(), Application::Settings.CustomIP.end(), '.');
auto p = Application::Settings.CustomIP.find_first_not_of((".0123456789"));
if (p != std::string::npos || n != 3 || Application::Settings.CustomIP.substr(0, 3) == ("127")) {
Application::Settings.CustomIP.clear();
warn("IP Specified is invalid! Ignoring");
} else {
info("server started with custom IP");
}
}
}
void TServer::RemoveClient(std::weak_ptr<TClient> WeakClientPtr) {
if (!WeakClientPtr.expired()) {
TClient& Client = *WeakClientPtr.lock();
debug("removing client " + Client.GetName() + " (" + std::to_string(ClientCount()) + ")");
Client.ClearCars();
WriteLock Lock(_ClientsMutex);
_Clients.erase(WeakClientPtr.lock());
}
}
std::weak_ptr<TClient> TServer::InsertNewClient() {
debug("inserting new client (" + std::to_string(ClientCount()) + ")");
WriteLock Lock(_ClientsMutex);
auto [Iter, Replaced] = _Clients.insert(std::make_shared<TClient>());
return *Iter;
}
void TServer::ForEachClient(std::function<bool(std::weak_ptr<TClient>)> Fn) {
ReadLock Lock(_ClientsMutex);
for (auto& Client : _Clients) {
if (!Fn(Client)) {
break;
}
}
}
size_t TServer::ClientCount() const {
ReadLock Lock(_ClientsMutex);
return _Clients.size();
}

6
src/VehicleData.cpp Normal file
View File

@ -0,0 +1,6 @@
#include "VehicleData.h"
TVehicleData::TVehicleData(int ID, const std::string& Data)
: _ID(ID)
, _Data(Data) {
}

View File

@ -1,12 +1,61 @@
#include "Client.h"
#include "Common.h"
#include "IThreaded.h"
#include "TConfig.h"
#include "TConsole.h"
#include "TServer.h"
#include <atomic>
#include <functional>
#include <iostream>
#include <thread>
#include "TConsole.h"
#ifdef __unix
#include <csignal>
int main(int argc, char** argv) {
TConsole Console;
while (true) {
void UnixSignalHandler(int sig) {
switch (sig) {
case SIGPIPE:
warn("ignoring SIGPIPE");
break;
case SIGTERM:
info("gracefully shutting down via SIGTERM");
Application::GracefullyShutdown();
break;
case SIGINT:
info("gracefully shutting down via SIGINT");
Application::GracefullyShutdown();
break;
}
}
#endif // __unix
int main(int argc, char** argv) {
#ifdef __unix
info("registering SIGPIPE and SIGTERM handlers");
signal(SIGPIPE, UnixSignalHandler);
signal(SIGTERM, UnixSignalHandler);
signal(SIGINT, UnixSignalHandler);
#endif // __unix
TServer Server(argc, argv);
TConfig Config("Server.cfg");
auto Client = Server.InsertNewClient();
if (!Client.expired()) {
Client.lock()->SetName("Lion");
} else {
error("fuckj");
_Exit(-1);
}
Server.ForEachClient([](auto client) -> bool { debug(client.lock()->GetName()); return true; });
Server.RemoveClient(Client);
// TODO: replace with blocking heartbeat
bool Shutdown = false;
Application::RegisterShutdownHandler([&Shutdown] { Shutdown = true; });
while (!Shutdown) {
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
}