mirror of
https://github.com/BeamMP/BeamMP-Server.git
synced 2025-07-01 23:35:41 +00:00
add lua engine, lua file, server, client, vehicle data, other stuff
This commit is contained in:
parent
e5e447c7af
commit
459814a6ec
@ -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
53
include/Client.h
Normal 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
66
include/Common.h
Normal 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)
|
@ -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
68
include/CustomAssert.h
Normal 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
19
include/RWMutex.h
Normal 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
12
include/TConfig.h
Normal 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);
|
||||
};
|
@ -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
32
include/TLuaEngine.h
Normal 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
58
include/TLuaFile.h
Normal 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
26
include/TServer.h
Normal 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
18
include/VehicleData.h
Normal 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
64
src/Client.cpp
Normal 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
18
src/Common.cpp
Normal 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
126
src/TConfig.cpp
Normal 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;
|
||||
}
|
||||
}
|
@ -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
67
src/TLuaEngine.cpp
Normal 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
722
src/TLuaFile.cpp
Normal 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
49
src/TServer.cpp
Normal 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
6
src/VehicleData.cpp
Normal file
@ -0,0 +1,6 @@
|
||||
#include "VehicleData.h"
|
||||
|
||||
TVehicleData::TVehicleData(int ID, const std::string& Data)
|
||||
: _ID(ID)
|
||||
, _Data(Data) {
|
||||
}
|
55
src/main.cpp
55
src/main.cpp
@ -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>
|
||||
|
||||
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) {
|
||||
TConsole Console;
|
||||
while (true) {
|
||||
#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));
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user