diff --git a/.idea/.gitignore b/.idea/.gitignore
deleted file mode 100644
index e7e9d11..0000000
--- a/.idea/.gitignore
+++ /dev/null
@@ -1,2 +0,0 @@
-# Default ignored files
-/workspace.xml
diff --git a/.idea/.name b/.idea/.name
index f083ce8..53e4c14 100644
--- a/.idea/.name
+++ b/.idea/.name
@@ -1 +1 @@
-Server
\ No newline at end of file
+TUDPServer.cpp
\ No newline at end of file
diff --git a/.idea/BeamNG-MP-Server.iml b/.idea/BeamNG-MP-Server.iml
deleted file mode 100644
index f08604b..0000000
--- a/.idea/BeamNG-MP-Server.iml
+++ /dev/null
@@ -1,2 +0,0 @@
-
-
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
index 8822db8..79b3c94 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -1,7 +1,4 @@
-
-
-
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
index dd314cc..e87924b 100644
--- a/.idea/vcs.xml
+++ b/.idea/vcs.xml
@@ -2,6 +2,9 @@
+
+
+
\ No newline at end of file
diff --git a/CMakeLists.txt b/CMakeLists.txt
index f5a2e70..7115ef9 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -44,7 +44,7 @@ add_executable(BeamMP-Server
include/THeartbeatThread.h src/THeartbeatThread.cpp
include/Http.h src/Http.cpp
include/SocketIO.h src/SocketIO.cpp
- include/TPPSMonitor.h src/TPPSMonitor.cpp)
+ include/TPPSMonitor.h src/TPPSMonitor.cpp include/TUDPServer.h src/TUDPServer.cpp include/TTCPServer.h src/TTCPServer.cpp)
target_include_directories(BeamMP-Server PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" "${CMAKE_CURRENT_SOURCE_DIR}/commandline")
diff --git a/include/Client.h b/include/Client.h
index c9863de..23332ec 100644
--- a/include/Client.h
+++ b/include/Client.h
@@ -8,46 +8,52 @@
#include "Compat.h"
#include "VehicleData.h"
+class TServer;
+
class TClient final {
public:
using TSetOfVehicleData = std::unordered_set>;
+ explicit TClient(TServer& Server);
+
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; }
+ void SetName(const std::string& Name) { mName = Name; }
+ void SetRoles(const std::string& Role) { mRole = 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 SetUDPAddr(sockaddr_in Addr) { mUDPAddress = Addr; }
+ void SetDownSock(SOCKET CSock) { mSocket[1] = CSock; }
+ void SetTCPSock(SOCKET CSock) { mSocket[0] = CSock; }
+ void SetStatus(int Status) { mStatus = 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();
+ sockaddr_in GetUDPAddr() const { return mUDPAddress; }
+ std::string GetRoles() const { return mRole; }
+ std::string GetName() const { return mName; }
+ SOCKET GetDownSock() const { return mSocket[1]; }
+ SOCKET GetTCPSock() const { return mSocket[0]; }
+ void SetID(int ID) { mID = ID; }
+ int GetOpenCarID() const;
+ int GetCarCount() const;
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; }
+ int GetStatus() const { return mStatus; }
+ int GetID() const { return mID; }
+ bool IsConnected() const { return mIsConnected; }
+ bool IsSynced() const { return mIsSynced; }
+ bool IsGuest() const { return mIsGuest; }
+ TServer& Server() const;
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;
+ TServer& mServer;
+ bool mIsConnected = false;
+ bool mIsSynced = false;
+ bool mIsGuest = false;
+ TSetOfVehicleData mVehicleData;
+ std::string mName = "Unknown Client";
+ SOCKET mSocket[2] { SOCKET(-1) };
+ sockaddr_in mUDPAddress {}; // is this initialization OK?
+ std::string mRole;
+ std::string mDID;
+ int mStatus = 0;
+ int mID = -1;
};
diff --git a/include/Common.h b/include/Common.h
index 969ece2..1e9f5a9 100644
--- a/include/Common.h
+++ b/include/Common.h
@@ -76,3 +76,8 @@ static inline void debug(const std::string& str) {
static inline void luaprint(const std::string& str) {
Application::Console().Write(std::string("[LUA] ") + str);
}
+
+#define Biggest 30000
+std::string Comp(std::string Data);
+std::string DeComp(std::string Compressed);
+
diff --git a/include/Compat.h b/include/Compat.h
index f56cc8e..9fd3a1e 100644
--- a/include/Compat.h
+++ b/include/Compat.h
@@ -4,13 +4,18 @@
#ifdef __unix
#include
+#include
#include
#include
using SOCKET = int;
using DWORD = unsigned long;
using PDWORD = unsigned long*;
using LPDWORD = unsigned long*;
-char _getch(void);
+char _getch();
+inline void CloseSocketProper(int socket) {
+ shutdown(socket, SHUT_RDWR);
+ close(socket);
+}
#endif // unix
// ======================= WIN32 =======================
@@ -18,6 +23,10 @@ char _getch(void);
#ifdef WIN32
#include
#include
+inline void CloseSocketProper(int socket) {
+ shutdown(socket, SHUT_RDWR);
+ close(socket);
+}
#endif // WIN32
// ======================= OTHER =======================
@@ -25,4 +34,3 @@ char _getch(void);
#if !defined(WIN32) && !defined(__unix)
#error "OS not supported"
#endif
-
diff --git a/include/TPPSMonitor.h b/include/TPPSMonitor.h
index 6f5e8f2..5ce0406 100644
--- a/include/TPPSMonitor.h
+++ b/include/TPPSMonitor.h
@@ -10,8 +10,11 @@ public:
void operator()() override;
-private:
+ void SetInternalPPS(int NewPPS) { mInternalPPS = NewPPS; }
+ void IncrementInternalPPS() { ++mInternalPPS; }
+ [[nodiscard]] int InternalPPS() const { return mInternalPPS; }
+private:
TServer& mServer;
bool mShutdown { false };
int mInternalPPS { 0 };
diff --git a/include/TTCPServer.h b/include/TTCPServer.h
new file mode 100644
index 0000000..946741e
--- /dev/null
+++ b/include/TTCPServer.h
@@ -0,0 +1,14 @@
+#pragma once
+
+#include "Common.h"
+#include "Compat.h"
+#include "IThreaded.h"
+#include "TServer.h"
+
+class TTCPServer : public IThreaded {
+public:
+ explicit TTCPServer(TServer& Server);
+
+private:
+ TServer& mServer;
+};
\ No newline at end of file
diff --git a/include/TUDPServer.h b/include/TUDPServer.h
new file mode 100644
index 0000000..481b553
--- /dev/null
+++ b/include/TUDPServer.h
@@ -0,0 +1,26 @@
+#pragma once
+
+#include "Client.h"
+#include "Common.h"
+#include "Compat.h"
+#include "IThreaded.h"
+#include "TPPSMonitor.h"
+#include "TServer.h"
+
+class TUDPServer : public IThreaded {
+public:
+ explicit TUDPServer(TServer& Server, TPPSMonitor& PPSMonitor);
+
+ void operator()() override;
+
+ void UDPSend(TClient& Client, std::string Data) const;
+ void SendToAll(TClient* c, const std::string& Data, bool Self, bool Rel);
+
+private:
+ void UDPParser(TClient& Client, std::string Packet);
+
+ TServer& mServer;
+ TPPSMonitor& mPPSMonitor;
+ SOCKET mUDPSock;
+ std::string UDPRcvFromClient(sockaddr_in& client) const;
+};
\ No newline at end of file
diff --git a/src/Client.cpp b/src/Client.cpp
index 327617f..b730131 100644
--- a/src/Client.cpp
+++ b/src/Client.cpp
@@ -1,28 +1,29 @@
#include "Client.h"
+#include "CustomAssert.h"
#include
// FIXME: add debug prints
void TClient::DeleteCar(int Ident) {
- for (auto& v : _VehicleData) {
+ for (auto& v : mVehicleData) {
if (v != nullptr && v->ID() == Ident) {
- _VehicleData.erase(v);
+ mVehicleData.erase(v);
break;
}
}
}
void TClient::ClearCars() {
- _VehicleData.clear();
+ mVehicleData.clear();
}
-int TClient::GetOpenCarID() {
+int TClient::GetOpenCarID() const {
int OpenID = 0;
bool found;
do {
found = true;
- for (auto& v : _VehicleData) {
+ for (auto& v : mVehicleData) {
if (v != nullptr && v->ID() == OpenID) {
OpenID++;
found = false;
@@ -33,15 +34,15 @@ int TClient::GetOpenCarID() {
}
void TClient::AddNewCar(int Ident, const std::string& Data) {
- _VehicleData.insert(std::make_unique(TVehicleData { Ident, Data }));
+ mVehicleData.insert(std::make_unique(TVehicleData { Ident, Data }));
}
TClient::TSetOfVehicleData& TClient::GetAllCars() {
- return _VehicleData;
+ return mVehicleData;
}
std::string TClient::GetCarData(int Ident) {
- for (auto& v : _VehicleData) {
+ for (auto& v : mVehicleData) {
if (v != nullptr && v->ID() == Ident) {
return v->Data();
}
@@ -51,7 +52,7 @@ std::string TClient::GetCarData(int Ident) {
}
void TClient::SetCarData(int Ident, const std::string& Data) {
- for (auto& v : _VehicleData) {
+ for (auto& v : mVehicleData) {
if (v != nullptr && v->ID() == Ident) {
v->Data() = Data;
return;
@@ -59,6 +60,13 @@ void TClient::SetCarData(int Ident, const std::string& Data) {
}
DeleteCar(Ident);
}
-int TClient::GetCarCount() {
- return int(_VehicleData.size());
+int TClient::GetCarCount() const {
+ return int(mVehicleData.size());
+}
+TServer& TClient::Server() const {
+ return mServer;
+}
+
+TClient::TClient(TServer& Server)
+ : mServer(Server) {
}
diff --git a/src/Common.cpp b/src/Common.cpp
index e8a57fb..81bf7b8 100644
--- a/src/Common.cpp
+++ b/src/Common.cpp
@@ -1,5 +1,10 @@
#include "Common.h"
+
#include "TConsole.h"
+#include
+#include
+#include
+#include
std::unique_ptr Application::mConsole = std::make_unique();
@@ -16,3 +21,46 @@ void Application::GracefullyShutdown() {
Handler();
}
}
+std::string Comp(std::string Data) {
+ std::array C {};
+ // obsolete
+ C.fill(0);
+ z_stream defstream;
+ defstream.zalloc = Z_NULL;
+ defstream.zfree = Z_NULL;
+ defstream.opaque = Z_NULL;
+ defstream.avail_in = (uInt)Data.length();
+ defstream.next_in = (Bytef*)&Data[0];
+ defstream.avail_out = Biggest;
+ defstream.next_out = reinterpret_cast(C.data());
+ deflateInit(&defstream, Z_BEST_COMPRESSION);
+ deflate(&defstream, Z_SYNC_FLUSH);
+ deflate(&defstream, Z_FINISH);
+ deflateEnd(&defstream);
+ size_t TO = defstream.total_out;
+ std::string Ret(TO, 0);
+ std::copy_n(C.begin(), TO, Ret.begin());
+ return Ret;
+}
+
+std::string DeComp(std::string Compressed) {
+ std::array C {};
+ // not needed
+ C.fill(0);
+ z_stream infstream;
+ infstream.zalloc = Z_NULL;
+ infstream.zfree = Z_NULL;
+ infstream.opaque = Z_NULL;
+ infstream.avail_in = Biggest;
+ infstream.next_in = (Bytef*)(&Compressed[0]);
+ infstream.avail_out = Biggest;
+ infstream.next_out = (Bytef*)(C.data());
+ inflateInit(&infstream);
+ inflate(&infstream, Z_SYNC_FLUSH);
+ inflate(&infstream, Z_FINISH);
+ inflateEnd(&infstream);
+ size_t TO = infstream.total_out;
+ std::string Ret(TO, 0);
+ std::copy_n(C.begin(), TO, Ret.begin());
+ return Ret;
+}
diff --git a/src/TServer.cpp b/src/TServer.cpp
index 175c964..e6ba42c 100644
--- a/src/TServer.cpp
+++ b/src/TServer.cpp
@@ -30,7 +30,7 @@ void TServer::RemoveClient(std::weak_ptr WeakClientPtr) {
std::weak_ptr TServer::InsertNewClient() {
debug("inserting new client (" + std::to_string(ClientCount()) + ")");
WriteLock Lock(mClientsMutex);
- auto [Iter, Replaced] = mClients.insert(std::make_shared());
+ auto [Iter, Replaced] = mClients.insert(std::make_shared(*this));
return *Iter;
}
diff --git a/src/TTCPServer.cpp b/src/TTCPServer.cpp
new file mode 100644
index 0000000..de3b281
--- /dev/null
+++ b/src/TTCPServer.cpp
@@ -0,0 +1,5 @@
+#include "TTCPServer.h"
+
+TTCPServer::TTCPServer(TServer& Server)
+ : mServer(Server) {
+}
diff --git a/src/TUDPServer.cpp b/src/TUDPServer.cpp
new file mode 100644
index 0000000..a99bdc3
--- /dev/null
+++ b/src/TUDPServer.cpp
@@ -0,0 +1,224 @@
+#include "TUDPServer.h"
+#include "CustomAssert.h"
+#include
+#include
+
+TUDPServer::TUDPServer(TServer& Server, TPPSMonitor& PPSMonitor)
+ : mServer(Server)
+ , mPPSMonitor(PPSMonitor) {
+}
+
+void TUDPServer::operator()() {
+#ifdef WIN32
+ WSADATA data;
+ if (WSAStartup(514, &data)) {
+ error(("Can't start Winsock!"));
+ //return;
+ }
+
+ mUDPSock = socket(AF_INET, SOCK_DGRAM, 0);
+ // Create a server hint structure for the server
+ sockaddr_in serverAddr {};
+ serverAddr.sin_addr.S_un.S_addr = ADDR_ANY; //Any Local
+ serverAddr.sin_family = AF_INET; // Address format is IPv4
+ serverAddr.sin_port = htons(Application::Settings.Port); // Convert from little to big endian
+
+ // Try and bind the socket to the IP and port
+ if (bind(mUDPSock, (sockaddr*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR) {
+ error(("Can't bind socket!") + std::to_string(WSAGetLastError()));
+ std::this_thread::sleep_for(std::chrono::seconds(5));
+ exit(-1);
+ //return;
+ }
+#else // unix
+ mUDPSock = socket(AF_INET, SOCK_DGRAM, 0);
+ // Create a server hint structure for the server
+ sockaddr_in serverAddr {};
+ serverAddr.sin_addr.s_addr = INADDR_ANY; //Any Local
+ serverAddr.sin_family = AF_INET; // Address format is IPv4
+ serverAddr.sin_port = htons(uint16_t(Application::Settings.Port)); // Convert from little to big endian
+
+ // Try and bind the socket to the IP and port
+ if (bind(mUDPSock, (sockaddr*)&serverAddr, sizeof(serverAddr)) != 0) {
+ error(("Can't bind socket!") + std::string(strerror(errno)));
+ std::this_thread::sleep_for(std::chrono::seconds(5));
+ exit(-1);
+ //return;
+ }
+#endif
+
+ info(("Vehicle data network online on port ") + std::to_string(Application::Settings.Port) + (" with a Max of ")
+ + std::to_string(Application::Settings.MaxPlayers) + (" Clients"));
+ while (true) {
+ try {
+ sockaddr_in client {};
+ std::string Data = UDPRcvFromClient(client); //Receives any data from Socket
+ size_t Pos = Data.find(':');
+ if (Data.empty() || Pos > 2)
+ continue;
+ /*char clientIp[256];
+ ZeroMemory(clientIp, 256); ///Code to get IP we don't need that yet
+ inet_ntop(AF_INET, &client.sin_addr, clientIp, 256);*/
+ uint8_t ID = uint8_t(Data.at(0)) - 1;
+ mServer.ForEachClient([&](std::weak_ptr ClientPtr) -> bool {
+ if (!ClientPtr.expired()) {
+ auto Client = ClientPtr.lock();
+ if (Client->GetID() == ID) {
+ Client->SetUDPAddr(client);
+ Client->SetConnected(true);
+ UDPParser(*Client, Data.substr(2));
+ }
+ }
+ return true;
+ });
+ } catch (const std::exception& e) {
+ error(("fatal: ") + std::string(e.what()));
+ }
+ }
+}
+void TUDPServer::UDPParser(TClient& Client, std::string Packet) {
+ if (Packet.find("Zp") != std::string::npos && Packet.size() > 500) {
+ abort();
+ }
+ if (Packet.substr(0, 4) == "ABG:") {
+ Packet = DeComp(Packet.substr(4));
+ }
+ if (Packet.empty()) {
+ return;
+ }
+ std::any Res;
+ char Code = Packet.at(0);
+
+ //V to Z
+ if (Code <= 90 && Code >= 86) {
+ mPPSMonitor.IncrementInternalPPS();
+ SendToAll(&Client, Packet, false, false);
+ return;
+ }
+ switch (Code) {
+ case 'H': // initial connection
+#ifdef DEBUG
+ debug(std::string("got 'H' packet: '") + Packet + "' (" + std::to_string(Packet.size()) + ")");
+#endif
+ SyncClient(Client);
+ return;
+ case 'p':
+ Respond(Client, ("p"), false);
+ UpdatePlayers();
+ return;
+ case 'O':
+ if (Packet.length() > 1000) {
+ debug(("Received data from: ") + Client->GetName() + (" Size: ") + std::to_string(Packet.length()));
+ }
+ ParseVeh(Client, Packet);
+ return;
+ case 'J':
+#ifdef DEBUG
+ debug(std::string(("got 'J' packet: '")) + Packet + ("' (") + std::to_string(Packet.size()) + (")"));
+#endif
+ SendToAll(Client, Packet, false, true);
+ return;
+ case 'C':
+#ifdef DEBUG
+ debug(std::string(("got 'C' packet: '")) + Packet + ("' (") + std::to_string(Packet.size()) + (")"));
+#endif
+ if (Packet.length() < 4 || Packet.find(':', 3) == std::string::npos)
+ break;
+ Res = TriggerLuaEvent("onChatMessage", false, nullptr, std::make_unique(LuaArg { { Client->GetID(), Client->GetName(), Packet.substr(Packet.find(':', 3) + 1) } }), true);
+ if (std::any_cast(Res))
+ break;
+ SendToAll(nullptr, Packet, true, true);
+ return;
+ case 'E':
+#ifdef DEBUG
+ debug(std::string(("got 'E' packet: '")) + Packet + ("' (") + std::to_string(Packet.size()) + (")"));
+#endif
+ HandleEvent(Client, Packet);
+ return;
+ default:
+ return;
+ }
+}
+
+void TUDPServer::SendToAll(TClient* c, const std::string& Data, bool Self, bool Rel) {
+ if (!Self)
+ Assert(c);
+ char C = Data.at(0);
+ mServer.ForEachClient([&](std::weak_ptr ClientPtr) -> bool {
+ if (!ClientPtr.expired()) {
+ auto Client = ClientPtr.lock();
+ if (Self || Client.get() != c) {
+ if (Client->IsSynced()) {
+ if (Rel || C == 'W' || C == 'Y' || C == 'V' || C == 'E') {
+ if (C == 'O' || C == 'T' || Data.length() > 1000)
+ SendLarge(*Client, Data);
+ else
+ TCPSend(*Client, Data);
+ } else
+ UDPSend(*Client, Data);
+ }
+ }
+ }
+ return true;
+ });
+}
+
+void TUDPServer::UDPSend(TClient& Client, std::string Data) const {
+ if (!Client.IsConnected() || Client.GetStatus() < 0) {
+#ifdef DEBUG
+ debug(Client.GetName() + ": !IsConnected() or GetStatus() < 0");
+#endif // DEBUG
+ return;
+ }
+ sockaddr_in Addr = Client.GetUDPAddr();
+ socklen_t AddrSize = sizeof(Client.GetUDPAddr());
+ if (Data.length() > 400) {
+ std::string CMP(Comp(Data));
+ Data = "ABG:" + CMP;
+ }
+#ifdef WIN32
+ int sendOk;
+ int len = static_cast(Data.size());
+#else
+ int64_t sendOk;
+ size_t len = Data.size();
+#endif // WIN32
+
+ sendOk = sendto(mUDPSock, Data.c_str(), len, 0, (sockaddr*)&Addr, AddrSize);
+#ifdef WIN32
+ if (sendOk == -1) {
+ debug(("(UDP) Send Failed Code : ") + std::to_string(WSAGetLastError()));
+ if (Client.GetStatus() > -1)
+ Client.SetStatus(-1);
+ } else if (sendOk == 0) {
+ debug(("(UDP) sendto returned 0"));
+ if (Client.GetStatus() > -1)
+ Client.SetStatus(-1);
+ }
+#else // unix
+ if (sendOk == -1) {
+ debug(("(UDP) Send Failed Code : ") + std::string(strerror(errno)));
+ if (Client.GetStatus() > -1)
+ Client.SetStatus(-1);
+ } else if (sendOk == 0) {
+ debug(("(UDP) sendto returned 0"));
+ if (Client.GetStatus() > -1)
+ Client.SetStatus(-1);
+ }
+#endif // WIN32
+}
+
+std::string TUDPServer::UDPRcvFromClient(sockaddr_in& client) const {
+ size_t clientLength = sizeof(client);
+ std::array Ret {};
+ int64_t Rcv = recvfrom(mUDPSock, Ret.data(), Ret.size(), 0, (sockaddr*)&client, (socklen_t*)&clientLength);
+ if (Rcv == -1) {
+#ifdef WIN32
+ error(("(UDP) Error receiving from Client! Code : ") + std::to_string(WSAGetLastError()));
+#else // unix
+ error(("(UDP) Error receiving from Client! Code : ") + std::string(strerror(errno)));
+#endif // WIN32
+ return "";
+ }
+ return std::string(Ret.begin(), Ret.begin() + Rcv);
+}
diff --git a/src/main.cpp b/src/main.cpp
index a4d45a6..e64b4a1 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -8,6 +8,7 @@
#include "TResourceManager.h"
#include "TServer.h"
#include "TPPSMonitor.h"
+#include "TUDPServer.h"
#include
#include
#include
@@ -46,10 +47,12 @@ int main(int argc, char** argv) {
TServer Server(argc, argv);
[[maybe_unused]] TConfig Config("Server.cfg");
- TLuaEngine LuaEngine(Server);
TResourceManager ResourceManager;
[[maybe_unused]] TPPSMonitor PPSMonitor(Server);
THeartbeatThread Heartbeat(ResourceManager, Server);
+ TTCPServer TCPServer(Server);
+ TUDPServer UDPServer(Server, PPSMonitor);
+ TLuaEngine LuaEngine(Server);
// TODO: replace
bool Shutdown = false;