mirror of
https://github.com/BeamMP/BeamMP-Server.git
synced 2025-07-01 23:35:41 +00:00
Compare commits
42 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
add0b86b37 | ||
|
403c1d5f78 | ||
|
6318ca79e7 | ||
|
2bd4ee9321 | ||
|
22c0a966bb | ||
|
731599f16e | ||
|
38c6766b2b | ||
|
bcb035bafc | ||
|
068f553fa9 | ||
|
ca11f353b0 | ||
|
b7cf304d49 | ||
|
03d3b873c4 | ||
|
ea9c808233 | ||
|
40bd050ca6 | ||
|
a0d75c01f0 | ||
|
8098431fad | ||
|
40c8c0c5c2 | ||
|
cd39f387c2 | ||
|
a5ca50866f | ||
|
10ea0cf59e | ||
|
7db40e068e | ||
|
6053aa6192 | ||
|
0bb18de9f6 | ||
|
7a439bb5b9 | ||
|
6c3174ac08 | ||
|
73e9595d14 | ||
|
3f7cf7a258 | ||
|
093310c124 | ||
|
6286457fa4 | ||
|
71b8a61c97 | ||
|
f0141e4fd3 | ||
|
00560f7646 | ||
|
27d50fc2b5 | ||
|
52a1d9a99e | ||
|
2f577a2358 | ||
|
6014536f52 | ||
|
fbce8a946e | ||
|
bd9b6212e2 | ||
|
b112ee20d8 | ||
|
8593aeb21d | ||
|
840f9b9f9d | ||
|
9c3042280d |
2
.github/workflows/linux.yml
vendored
2
.github/workflows/linux.yml
vendored
@ -84,7 +84,7 @@ jobs:
|
|||||||
run: ./bin/BeamMP-Server-tests
|
run: ./bin/BeamMP-Server-tests
|
||||||
|
|
||||||
arm64-matrix:
|
arm64-matrix:
|
||||||
runs-on: [Linux, ARM64]
|
runs-on: ubuntu-22.04-arm
|
||||||
env:
|
env:
|
||||||
VCPKG_DEFAULT_TRIPLET: "arm64-linux"
|
VCPKG_DEFAULT_TRIPLET: "arm64-linux"
|
||||||
strategy:
|
strategy:
|
||||||
|
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@ -104,7 +104,7 @@ jobs:
|
|||||||
asset_content_type: application/x-elf
|
asset_content_type: application/x-elf
|
||||||
|
|
||||||
arm64-matrix:
|
arm64-matrix:
|
||||||
runs-on: [Linux, ARM64]
|
runs-on: ubuntu-22.04-arm
|
||||||
needs: create-release
|
needs: create-release
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
|
@ -56,14 +56,14 @@ public:
|
|||||||
~TClient();
|
~TClient();
|
||||||
TClient& operator=(const TClient&) = delete;
|
TClient& operator=(const TClient&) = delete;
|
||||||
|
|
||||||
void AddNewCar(int Ident, const std::string& Data);
|
void AddNewCar(int Ident, const nlohmann::json& Data);
|
||||||
void SetCarData(int Ident, const std::string& Data);
|
void SetCarData(int Ident, const nlohmann::json& Data);
|
||||||
void SetCarPosition(int Ident, const std::string& Data);
|
void SetCarPosition(int Ident, const std::string& Data);
|
||||||
TVehicleDataLockPair GetAllCars();
|
TVehicleDataLockPair GetAllCars();
|
||||||
void SetName(const std::string& Name) { mName = Name; }
|
void SetName(const std::string& Name) { mName = Name; }
|
||||||
void SetRoles(const std::string& Role) { mRole = Role; }
|
void SetRoles(const std::string& Role) { mRole = Role; }
|
||||||
void SetIdentifier(const std::string& key, const std::string& value) { mIdentifiers[key] = value; }
|
void SetIdentifier(const std::string& key, const std::string& value) { mIdentifiers[key] = value; }
|
||||||
std::string GetCarData(int Ident);
|
nlohmann::json GetCarData(int Ident);
|
||||||
std::string GetCarPositionRaw(int Ident);
|
std::string GetCarPositionRaw(int Ident);
|
||||||
void SetUDPAddr(const ip::udp::endpoint& Addr) { mUDPAddress = Addr; }
|
void SetUDPAddr(const ip::udp::endpoint& Addr) { mUDPAddress = Addr; }
|
||||||
void SetTCPSock(ip::tcp::socket&& CSock) { mSocket = std::move(CSock); }
|
void SetTCPSock(ip::tcp::socket&& CSock) { mSocket = std::move(CSock); }
|
||||||
|
@ -86,6 +86,8 @@ public:
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static std::string GetServerCheckUrl() { return "https://check.beammp.com"; }
|
||||||
|
|
||||||
static std::string GetBackendUrlForAuth() { return "https://auth.beammp.com"; }
|
static std::string GetBackendUrlForAuth() { return "https://auth.beammp.com"; }
|
||||||
static std::string GetBackendUrlForSocketIO() { return "https://backend.beammp.com"; }
|
static std::string GetBackendUrlForSocketIO() { return "https://backend.beammp.com"; }
|
||||||
static void CheckForUpdates();
|
static void CheckForUpdates();
|
||||||
@ -127,10 +129,11 @@ private:
|
|||||||
static inline std::mutex mShutdownHandlersMutex {};
|
static inline std::mutex mShutdownHandlersMutex {};
|
||||||
static inline std::deque<TShutdownHandler> mShutdownHandlers {};
|
static inline std::deque<TShutdownHandler> mShutdownHandlers {};
|
||||||
|
|
||||||
static inline Version mVersion { 3, 8, 0 };
|
static inline Version mVersion { 3, 8, 4 };
|
||||||
};
|
};
|
||||||
|
|
||||||
void SplitString(std::string const& str, const char delim, std::vector<std::string>& out);
|
void SplitString(std::string const& str, const char delim, std::vector<std::string>& out);
|
||||||
|
std::string LowerString(std::string str);
|
||||||
|
|
||||||
std::string ThreadName(bool DebugModeOverride = false);
|
std::string ThreadName(bool DebugModeOverride = false);
|
||||||
void RegisterThread(const std::string& str);
|
void RegisterThread(const std::string& str);
|
||||||
|
@ -27,6 +27,7 @@ enum class Key {
|
|||||||
PROVIDER_UPDATE_MESSAGE,
|
PROVIDER_UPDATE_MESSAGE,
|
||||||
PROVIDER_DISABLE_CONFIG,
|
PROVIDER_DISABLE_CONFIG,
|
||||||
PROVIDER_PORT_ENV,
|
PROVIDER_PORT_ENV,
|
||||||
|
PROVIDER_IP_ENV
|
||||||
};
|
};
|
||||||
|
|
||||||
std::optional<std::string> Get(Key key);
|
std::optional<std::string> Get(Key key);
|
||||||
|
@ -36,6 +36,7 @@ namespace MP {
|
|||||||
std::pair<bool, std::string> DropPlayer(int ID, std::optional<std::string> MaybeReason);
|
std::pair<bool, std::string> DropPlayer(int ID, std::optional<std::string> MaybeReason);
|
||||||
std::pair<bool, std::string> SendChatMessage(int ID, const std::string& Message);
|
std::pair<bool, std::string> SendChatMessage(int ID, const std::string& Message);
|
||||||
std::pair<bool, std::string> SendNotification(int ID, const std::string& Message, const std::string& Icon, const std::string& Category);
|
std::pair<bool, std::string> SendNotification(int ID, const std::string& Message, const std::string& Icon, const std::string& Category);
|
||||||
|
std::pair<bool, std::string> ConfirmationDialog(int ID, const std::string& Title, const std::string& Body, const sol::table& buttons, const std::string& InteractionID, const bool& warning = false, const bool& reportToServer = true, const bool& reportToExtensions = true);
|
||||||
std::pair<bool, std::string> RemoveVehicle(int PlayerID, int VehicleID);
|
std::pair<bool, std::string> RemoveVehicle(int PlayerID, int VehicleID);
|
||||||
void Set(int ConfigID, sol::object NewValue);
|
void Set(int ConfigID, sol::object NewValue);
|
||||||
TLuaValue Get(int ConfigID);
|
TLuaValue Get(int ConfigID);
|
||||||
|
@ -79,6 +79,7 @@ struct Settings {
|
|||||||
General_Map,
|
General_Map,
|
||||||
General_AuthKey,
|
General_AuthKey,
|
||||||
General_Private,
|
General_Private,
|
||||||
|
General_IP,
|
||||||
General_Port,
|
General_Port,
|
||||||
General_MaxCars,
|
General_MaxCars,
|
||||||
General_LogChat,
|
General_LogChat,
|
||||||
|
@ -59,6 +59,9 @@ private:
|
|||||||
void Command_Settings(const std::string& cmd, const std::vector<std::string>& args);
|
void Command_Settings(const std::string& cmd, const std::vector<std::string>& args);
|
||||||
void Command_Clear(const std::string&, const std::vector<std::string>& args);
|
void Command_Clear(const std::string&, const std::vector<std::string>& args);
|
||||||
void Command_Version(const std::string& cmd, const std::vector<std::string>& args);
|
void Command_Version(const std::string& cmd, const std::vector<std::string>& args);
|
||||||
|
void Command_ProtectMod(const std::string& cmd, const std::vector<std::string>& args);
|
||||||
|
void Command_ReloadMods(const std::string& cmd, const std::vector<std::string>& args);
|
||||||
|
void Command_NetTest(const std::string& cmd, const std::vector<std::string>& args);
|
||||||
|
|
||||||
void Command_Say(const std::string& FullCommand);
|
void Command_Say(const std::string& FullCommand);
|
||||||
bool EnsureArgsCount(const std::vector<std::string>& args, size_t n);
|
bool EnsureArgsCount(const std::vector<std::string>& args, size_t n);
|
||||||
@ -77,6 +80,9 @@ private:
|
|||||||
{ "clear", [this](const auto& a, const auto& b) { Command_Clear(a, b); } },
|
{ "clear", [this](const auto& a, const auto& b) { Command_Clear(a, b); } },
|
||||||
{ "say", [this](const auto&, const auto&) { Command_Say(""); } }, // shouldn't actually be called
|
{ "say", [this](const auto&, const auto&) { Command_Say(""); } }, // shouldn't actually be called
|
||||||
{ "version", [this](const auto& a, const auto& b) { Command_Version(a, b); } },
|
{ "version", [this](const auto& a, const auto& b) { Command_Version(a, b); } },
|
||||||
|
{ "protectmod", [this](const auto& a, const auto& b) { Command_ProtectMod(a, b); } },
|
||||||
|
{ "reloadmods", [this](const auto& a, const auto& b) { Command_ReloadMods(a, b); } },
|
||||||
|
{ "nettest", [this](const auto& a, const auto& b) { Command_NetTest(a, b); } },
|
||||||
};
|
};
|
||||||
|
|
||||||
std::unique_ptr<Commandline> mCommandline { nullptr };
|
std::unique_ptr<Commandline> mCommandline { nullptr };
|
||||||
|
@ -45,6 +45,8 @@ public:
|
|||||||
void SendToAll(TClient* c, const std::vector<uint8_t>& Data, bool Self, bool Rel);
|
void SendToAll(TClient* c, const std::vector<uint8_t>& Data, bool Self, bool Rel);
|
||||||
void UpdatePlayer(TClient& Client);
|
void UpdatePlayer(TClient& Client);
|
||||||
|
|
||||||
|
TResourceManager& ResourceManager() const { return mResourceManager; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void UDPServerMain();
|
void UDPServerMain();
|
||||||
void TCPServerMain();
|
void TCPServerMain();
|
||||||
|
@ -30,10 +30,10 @@ public:
|
|||||||
[[nodiscard]] std::string TrimmedList() const { return mTrimmedList; }
|
[[nodiscard]] std::string TrimmedList() const { return mTrimmedList; }
|
||||||
[[nodiscard]] std::string FileSizes() const { return mFileSizes; }
|
[[nodiscard]] std::string FileSizes() const { return mFileSizes; }
|
||||||
[[nodiscard]] int ModsLoaded() const { return mModsLoaded; }
|
[[nodiscard]] int ModsLoaded() const { return mModsLoaded; }
|
||||||
|
[[nodiscard]] nlohmann::json GetMods() const { return mMods; }
|
||||||
[[nodiscard]] std::string NewFileList() const;
|
|
||||||
|
|
||||||
void RefreshFiles();
|
void RefreshFiles();
|
||||||
|
void SetProtected(const std::string& ModName, bool Protected);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
size_t mMaxModSize = 0;
|
size_t mMaxModSize = 0;
|
||||||
|
@ -18,11 +18,12 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
class TVehicleData final {
|
class TVehicleData final {
|
||||||
public:
|
public:
|
||||||
TVehicleData(int ID, std::string Data);
|
TVehicleData(int ID, nlohmann::json Data);
|
||||||
~TVehicleData();
|
~TVehicleData();
|
||||||
// We cannot delete this, since vector needs to be able to copy when it resizes.
|
// We cannot delete this, since vector needs to be able to copy when it resizes.
|
||||||
// Deleting this causes some wacky template errors which are hard to decipher,
|
// Deleting this causes some wacky template errors which are hard to decipher,
|
||||||
@ -32,14 +33,16 @@ public:
|
|||||||
[[nodiscard]] bool IsInvalid() const { return mID == -1; }
|
[[nodiscard]] bool IsInvalid() const { return mID == -1; }
|
||||||
[[nodiscard]] int ID() const { return mID; }
|
[[nodiscard]] int ID() const { return mID; }
|
||||||
|
|
||||||
[[nodiscard]] std::string Data() const { return mData; }
|
[[nodiscard]] nlohmann::json Data() const { return mData; }
|
||||||
void SetData(const std::string& Data) { mData = Data; }
|
[[nodiscard]] std::string DataAsPacket(const std::string& Role, const std::string& Name, int ID) const;
|
||||||
|
|
||||||
|
void SetData(const nlohmann::json& Data) { mData = Data; }
|
||||||
|
|
||||||
bool operator==(const TVehicleData& v) const { return mID == v.mID; }
|
bool operator==(const TVehicleData& v) const { return mID == v.mID; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int mID { -1 };
|
int mID { -1 };
|
||||||
std::string mData;
|
nlohmann::json mData;
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: unused now, remove?
|
// TODO: unused now, remove?
|
||||||
|
@ -57,7 +57,7 @@ int TClient::GetOpenCarID() const {
|
|||||||
return OpenID;
|
return OpenID;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TClient::AddNewCar(int Ident, const std::string& Data) {
|
void TClient::AddNewCar(int Ident, const nlohmann::json& Data) {
|
||||||
std::unique_lock lock(mVehicleDataMutex);
|
std::unique_lock lock(mVehicleDataMutex);
|
||||||
mVehicleData.emplace_back(Ident, Data);
|
mVehicleData.emplace_back(Ident, Data);
|
||||||
}
|
}
|
||||||
@ -98,7 +98,7 @@ void TClient::SetCarPosition(int Ident, const std::string& Data) {
|
|||||||
mVehiclePosition[size_t(Ident)] = Data;
|
mVehiclePosition[size_t(Ident)] = Data;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string TClient::GetCarData(int Ident) {
|
nlohmann::json TClient::GetCarData(int Ident) {
|
||||||
{ // lock
|
{ // lock
|
||||||
std::unique_lock lock(mVehicleDataMutex);
|
std::unique_lock lock(mVehicleDataMutex);
|
||||||
for (auto& v : mVehicleData) {
|
for (auto& v : mVehicleData) {
|
||||||
@ -108,10 +108,10 @@ std::string TClient::GetCarData(int Ident) {
|
|||||||
}
|
}
|
||||||
} // unlock
|
} // unlock
|
||||||
DeleteCar(Ident);
|
DeleteCar(Ident);
|
||||||
return "";
|
return nlohmann::detail::value_t::null;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TClient::SetCarData(int Ident, const std::string& Data) {
|
void TClient::SetCarData(int Ident, const nlohmann::json& Data) {
|
||||||
{ // lock
|
{ // lock
|
||||||
std::unique_lock lock(mVehicleDataMutex);
|
std::unique_lock lock(mVehicleDataMutex);
|
||||||
for (auto& v : mVehicleData) {
|
for (auto& v : mVehicleData) {
|
||||||
|
@ -384,6 +384,13 @@ void SplitString(const std::string& str, const char delim, std::vector<std::stri
|
|||||||
out.push_back(str.substr(start, end - start));
|
out.push_back(str.substr(start, end - start));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string LowerString(std::string str) {
|
||||||
|
std::ranges::transform(str, str.begin(), ::tolower);
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static constexpr size_t STARTING_MAX_DECOMPRESSION_BUFFER_SIZE = 15 * 1024 * 1024;
|
static constexpr size_t STARTING_MAX_DECOMPRESSION_BUFFER_SIZE = 15 * 1024 * 1024;
|
||||||
static constexpr size_t MAX_DECOMPRESSION_BUFFER_SIZE = 30 * 1024 * 1024;
|
static constexpr size_t MAX_DECOMPRESSION_BUFFER_SIZE = 30 * 1024 * 1024;
|
||||||
|
|
||||||
|
@ -39,6 +39,9 @@ std::string_view Env::ToString(Env::Key key) {
|
|||||||
case Key::PROVIDER_PORT_ENV:
|
case Key::PROVIDER_PORT_ENV:
|
||||||
return "BEAMMP_PROVIDER_PORT_ENV";
|
return "BEAMMP_PROVIDER_PORT_ENV";
|
||||||
break;
|
break;
|
||||||
|
case Key::PROVIDER_IP_ENV:
|
||||||
|
return "BEAMMP_PROVIDER_IP_ENV";
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
@ -44,6 +44,7 @@ std::string Http::GET(const std::string& url, unsigned int* status) {
|
|||||||
CURLcode res;
|
CURLcode res;
|
||||||
char errbuf[CURL_ERROR_SIZE];
|
char errbuf[CURL_ERROR_SIZE];
|
||||||
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
|
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
|
||||||
|
curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
|
||||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, CurlWriteCallback);
|
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, CurlWriteCallback);
|
||||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void*)&Ret);
|
curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void*)&Ret);
|
||||||
curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 10); // seconds
|
curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 10); // seconds
|
||||||
@ -75,6 +76,7 @@ std::string Http::POST(const std::string& url, const std::string& body, const st
|
|||||||
CURLcode res;
|
CURLcode res;
|
||||||
char errbuf[CURL_ERROR_SIZE];
|
char errbuf[CURL_ERROR_SIZE];
|
||||||
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
|
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
|
||||||
|
curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
|
||||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, CurlWriteCallback);
|
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, CurlWriteCallback);
|
||||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void*)&Ret);
|
curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void*)&Ret);
|
||||||
curl_easy_setopt(curl, CURLOPT_POST, 1);
|
curl_easy_setopt(curl, CURLOPT_POST, 1);
|
||||||
|
@ -148,6 +148,11 @@ static inline std::pair<bool, std::string> InternalTriggerClientEvent(int Player
|
|||||||
return { false, "Invalid Player ID" };
|
return { false, "Invalid Player ID" };
|
||||||
}
|
}
|
||||||
auto c = MaybeClient.value().lock();
|
auto c = MaybeClient.value().lock();
|
||||||
|
|
||||||
|
if (!c->IsSyncing() && !c->IsSynced()) {
|
||||||
|
return { false, "Player hasn't joined yet" };
|
||||||
|
}
|
||||||
|
|
||||||
if (!LuaAPI::MP::Engine->Network().Respond(*c, StringToVector(Packet), true)) {
|
if (!LuaAPI::MP::Engine->Network().Respond(*c, StringToVector(Packet), true)) {
|
||||||
beammp_lua_errorf("Respond failed, dropping client {}", PlayerID);
|
beammp_lua_errorf("Respond failed, dropping client {}", PlayerID);
|
||||||
LuaAPI::MP::Engine->Network().ClientKick(*c, "Disconnected after failing to receive packets");
|
LuaAPI::MP::Engine->Network().ClientKick(*c, "Disconnected after failing to receive packets");
|
||||||
@ -236,6 +241,48 @@ std::pair<bool, std::string> LuaAPI::MP::SendNotification(int ID, const std::str
|
|||||||
return Result;
|
return Result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::pair<bool, std::string> LuaAPI::MP::ConfirmationDialog(int ID, const std::string& Title, const std::string& Body, const sol::table& buttons, const std::string& InteractionID, const bool& warning, const bool& reportToServer, const bool& reportToExtensions) {
|
||||||
|
std::pair<bool, std::string> Result;
|
||||||
|
|
||||||
|
const nlohmann::json PacketBody = {
|
||||||
|
{ "title", Title },
|
||||||
|
{ "body", Body },
|
||||||
|
{ "buttons", nlohmann::json::parse(JsonEncode(buttons), nullptr, false) },
|
||||||
|
{ "interactionID", InteractionID },
|
||||||
|
{ "class", warning ? "experimental" : "" },
|
||||||
|
{ "reportToServer", reportToServer },
|
||||||
|
{ "reportToExtensions", reportToExtensions }
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string Packet = "D" + PacketBody.dump();
|
||||||
|
if (ID == -1) {
|
||||||
|
Engine->Network().SendToAll(nullptr, StringToVector(Packet), true, true);
|
||||||
|
Result.first = true;
|
||||||
|
} else {
|
||||||
|
auto MaybeClient = GetClient(Engine->Server(), ID);
|
||||||
|
if (MaybeClient) {
|
||||||
|
auto c = MaybeClient.value().lock();
|
||||||
|
if (!c->IsSynced()) {
|
||||||
|
Result.first = false;
|
||||||
|
Result.second = "Player is not synced yet";
|
||||||
|
return Result;
|
||||||
|
}
|
||||||
|
if (!Engine->Network().Respond(*c, StringToVector(Packet), true)) {
|
||||||
|
beammp_errorf("Failed to send confirmation dialog to player (id {}) - did the player disconnect?", ID);
|
||||||
|
Result.first = false;
|
||||||
|
Result.second = "Failed to send packet";
|
||||||
|
}
|
||||||
|
Result.first = true;
|
||||||
|
} else {
|
||||||
|
beammp_lua_error("ConfirmationDialog invalid argument [1] invalid ID");
|
||||||
|
Result.first = false;
|
||||||
|
Result.second = "Invalid Player ID";
|
||||||
|
}
|
||||||
|
return Result;
|
||||||
|
}
|
||||||
|
return Result;
|
||||||
|
}
|
||||||
|
|
||||||
std::pair<bool, std::string> LuaAPI::MP::RemoveVehicle(int PID, int VID) {
|
std::pair<bool, std::string> LuaAPI::MP::RemoveVehicle(int PID, int VID) {
|
||||||
std::pair<bool, std::string> Result;
|
std::pair<bool, std::string> Result;
|
||||||
auto MaybeClient = GetClient(Engine->Server(), PID);
|
auto MaybeClient = GetClient(Engine->Server(), PID);
|
||||||
@ -246,7 +293,7 @@ std::pair<bool, std::string> LuaAPI::MP::RemoveVehicle(int PID, int VID) {
|
|||||||
return Result;
|
return Result;
|
||||||
}
|
}
|
||||||
auto c = MaybeClient.value().lock();
|
auto c = MaybeClient.value().lock();
|
||||||
if (!c->GetCarData(VID).empty()) {
|
if (c->GetCarData(VID) != nlohmann::detail::value_t::null) {
|
||||||
std::string Destroy = "Od:" + std::to_string(PID) + "-" + std::to_string(VID);
|
std::string Destroy = "Od:" + std::to_string(PID) + "-" + std::to_string(VID);
|
||||||
LuaAPI::MP::Engine->ReportErrors(LuaAPI::MP::Engine->TriggerEvent("onVehicleDeleted", "", PID, VID));
|
LuaAPI::MP::Engine->ReportErrors(LuaAPI::MP::Engine->TriggerEvent("onVehicleDeleted", "", PID, VID));
|
||||||
Engine->Network().SendToAll(nullptr, StringToVector(Destroy), true, true);
|
Engine->Network().SendToAll(nullptr, StringToVector(Destroy), true, true);
|
||||||
|
@ -28,6 +28,7 @@ Settings::Settings() {
|
|||||||
{ General_Map, std::string("/levels/gridmap_v2/info.json") },
|
{ General_Map, std::string("/levels/gridmap_v2/info.json") },
|
||||||
{ General_AuthKey, std::string("") },
|
{ General_AuthKey, std::string("") },
|
||||||
{ General_Private, true },
|
{ General_Private, true },
|
||||||
|
{ General_IP, "::"},
|
||||||
{ General_Port, 30814 },
|
{ General_Port, 30814 },
|
||||||
{ General_MaxCars, 1 },
|
{ General_MaxCars, 1 },
|
||||||
{ General_LogChat, true },
|
{ General_LogChat, true },
|
||||||
@ -47,6 +48,7 @@ Settings::Settings() {
|
|||||||
{ { "General", "Map" }, { General_Map, READ_WRITE } },
|
{ { "General", "Map" }, { General_Map, READ_WRITE } },
|
||||||
{ { "General", "AuthKey" }, { General_AuthKey, NO_ACCESS } },
|
{ { "General", "AuthKey" }, { General_AuthKey, NO_ACCESS } },
|
||||||
{ { "General", "Private" }, { General_Private, READ_ONLY } },
|
{ { "General", "Private" }, { General_Private, READ_ONLY } },
|
||||||
|
{ { "General", "IP" }, { General_IP, READ_ONLY } },
|
||||||
{ { "General", "Port" }, { General_Port, READ_ONLY } },
|
{ { "General", "Port" }, { General_Port, READ_ONLY } },
|
||||||
{ { "General", "MaxCars" }, { General_MaxCars, READ_WRITE } },
|
{ { "General", "MaxCars" }, { General_MaxCars, READ_WRITE } },
|
||||||
{ { "General", "LogChat" }, { General_LogChat, READ_ONLY } },
|
{ { "General", "LogChat" }, { General_LogChat, READ_ONLY } },
|
||||||
|
@ -34,6 +34,8 @@ static constexpr std::string_view StrDebug = "Debug";
|
|||||||
static constexpr std::string_view EnvStrDebug = "BEAMMP_DEBUG";
|
static constexpr std::string_view EnvStrDebug = "BEAMMP_DEBUG";
|
||||||
static constexpr std::string_view StrPrivate = "Private";
|
static constexpr std::string_view StrPrivate = "Private";
|
||||||
static constexpr std::string_view EnvStrPrivate = "BEAMMP_PRIVATE";
|
static constexpr std::string_view EnvStrPrivate = "BEAMMP_PRIVATE";
|
||||||
|
static constexpr std::string_view StrIP = "IP";
|
||||||
|
static constexpr std::string_view EnvStrIP = "BEAMMP_IP";
|
||||||
static constexpr std::string_view StrPort = "Port";
|
static constexpr std::string_view StrPort = "Port";
|
||||||
static constexpr std::string_view EnvStrPort = "BEAMMP_PORT";
|
static constexpr std::string_view EnvStrPort = "BEAMMP_PORT";
|
||||||
static constexpr std::string_view StrMaxCars = "MaxCars";
|
static constexpr std::string_view StrMaxCars = "MaxCars";
|
||||||
@ -62,7 +64,9 @@ static constexpr std::string_view StrPassword = "Password";
|
|||||||
|
|
||||||
// Misc
|
// Misc
|
||||||
static constexpr std::string_view StrHideUpdateMessages = "ImScaredOfUpdates";
|
static constexpr std::string_view StrHideUpdateMessages = "ImScaredOfUpdates";
|
||||||
|
static constexpr std::string_view EnvStrHideUpdateMessages = "BEAMMP_IM_SCARED_OF_UPDATES";
|
||||||
static constexpr std::string_view StrUpdateReminderTime = "UpdateReminderTime";
|
static constexpr std::string_view StrUpdateReminderTime = "UpdateReminderTime";
|
||||||
|
static constexpr std::string_view EnvStrUpdateReminderTime = "BEAMMP_UPDATE_REMINDER_TIME";
|
||||||
|
|
||||||
TEST_CASE("TConfig::TConfig") {
|
TEST_CASE("TConfig::TConfig") {
|
||||||
const std::string CfgFile = "beammp_server_testconfig.toml";
|
const std::string CfgFile = "beammp_server_testconfig.toml";
|
||||||
@ -136,6 +140,8 @@ void TConfig::FlushToFile() {
|
|||||||
data["General"][StrInformationPacket.data()] = Application::Settings.getAsBool(Settings::Key::General_InformationPacket);
|
data["General"][StrInformationPacket.data()] = Application::Settings.getAsBool(Settings::Key::General_InformationPacket);
|
||||||
data["General"][StrAllowGuests.data()] = Application::Settings.getAsBool(Settings::Key::General_AllowGuests);
|
data["General"][StrAllowGuests.data()] = Application::Settings.getAsBool(Settings::Key::General_AllowGuests);
|
||||||
SetComment(data["General"][StrAllowGuests.data()].comments(), " Whether to allow guests");
|
SetComment(data["General"][StrAllowGuests.data()].comments(), " Whether to allow guests");
|
||||||
|
data["General"][StrIP.data()] = Application::Settings.getAsString(Settings::Key::General_IP);
|
||||||
|
SetComment(data["General"][StrIP.data()].comments(), " The IP address to bind the server to, this is NOT related to your public IP. Can be used if your machine has multiple network interfaces");
|
||||||
data["General"][StrPort.data()] = Application::Settings.getAsInt(Settings::Key::General_Port);
|
data["General"][StrPort.data()] = Application::Settings.getAsInt(Settings::Key::General_Port);
|
||||||
data["General"][StrName.data()] = Application::Settings.getAsString(Settings::Key::General_Name);
|
data["General"][StrName.data()] = Application::Settings.getAsString(Settings::Key::General_Name);
|
||||||
SetComment(data["General"][StrTags.data()].comments(), " Add custom identifying tags to your server to make it easier to find. Format should be TagA,TagB,TagC. Note the comma seperation.");
|
SetComment(data["General"][StrTags.data()].comments(), " Add custom identifying tags to your server to make it easier to find. Format should be TagA,TagB,TagC. Note the comma seperation.");
|
||||||
@ -252,6 +258,11 @@ void TConfig::ParseFromFile(std::string_view name) {
|
|||||||
} else {
|
} else {
|
||||||
TryReadValue(data, "General", StrPort, EnvStrPort, Settings::Key::General_Port);
|
TryReadValue(data, "General", StrPort, EnvStrPort, Settings::Key::General_Port);
|
||||||
}
|
}
|
||||||
|
if (Env::Get(Env::Key::PROVIDER_IP_ENV).has_value()) {
|
||||||
|
TryReadValue(data, "General", StrIP, Env::Get(Env::Key::PROVIDER_IP_ENV).value(), Settings::Key::General_IP);
|
||||||
|
} else {
|
||||||
|
TryReadValue(data, "General", StrIP, EnvStrIP, Settings::Key::General_IP);
|
||||||
|
}
|
||||||
TryReadValue(data, "General", StrMaxCars, EnvStrMaxCars, Settings::Key::General_MaxCars);
|
TryReadValue(data, "General", StrMaxCars, EnvStrMaxCars, Settings::Key::General_MaxCars);
|
||||||
TryReadValue(data, "General", StrMaxPlayers, EnvStrMaxPlayers, Settings::Key::General_MaxPlayers);
|
TryReadValue(data, "General", StrMaxPlayers, EnvStrMaxPlayers, Settings::Key::General_MaxPlayers);
|
||||||
TryReadValue(data, "General", StrMap, EnvStrMap, Settings::Key::General_Map);
|
TryReadValue(data, "General", StrMap, EnvStrMap, Settings::Key::General_Map);
|
||||||
@ -263,8 +274,8 @@ void TConfig::ParseFromFile(std::string_view name) {
|
|||||||
TryReadValue(data, "General", StrLogChat, EnvStrLogChat, Settings::Key::General_LogChat);
|
TryReadValue(data, "General", StrLogChat, EnvStrLogChat, Settings::Key::General_LogChat);
|
||||||
TryReadValue(data, "General", StrAllowGuests, EnvStrAllowGuests, Settings::Key::General_AllowGuests);
|
TryReadValue(data, "General", StrAllowGuests, EnvStrAllowGuests, Settings::Key::General_AllowGuests);
|
||||||
// Misc
|
// Misc
|
||||||
TryReadValue(data, "Misc", StrHideUpdateMessages, "", Settings::Key::Misc_ImScaredOfUpdates);
|
TryReadValue(data, "Misc", StrHideUpdateMessages, EnvStrHideUpdateMessages, Settings::Key::Misc_ImScaredOfUpdates);
|
||||||
TryReadValue(data, "Misc", StrUpdateReminderTime, "", Settings::Key::Misc_UpdateReminderTime);
|
TryReadValue(data, "Misc", StrUpdateReminderTime, EnvStrUpdateReminderTime, Settings::Key::Misc_UpdateReminderTime);
|
||||||
|
|
||||||
} catch (const std::exception& err) {
|
} catch (const std::exception& err) {
|
||||||
beammp_error("Error parsing config file value: " + std::string(err.what()));
|
beammp_error("Error parsing config file value: " + std::string(err.what()));
|
||||||
@ -302,6 +313,7 @@ void TConfig::PrintDebug() {
|
|||||||
beammp_debug(std::string(StrPrivate) + ": " + std::string(Application::Settings.getAsBool(Settings::Key::General_Private) ? "true" : "false"));
|
beammp_debug(std::string(StrPrivate) + ": " + std::string(Application::Settings.getAsBool(Settings::Key::General_Private) ? "true" : "false"));
|
||||||
beammp_debug(std::string(StrInformationPacket) + ": " + std::string(Application::Settings.getAsBool(Settings::Key::General_InformationPacket) ? "true" : "false"));
|
beammp_debug(std::string(StrInformationPacket) + ": " + std::string(Application::Settings.getAsBool(Settings::Key::General_InformationPacket) ? "true" : "false"));
|
||||||
beammp_debug(std::string(StrPort) + ": " + std::to_string(Application::Settings.getAsInt(Settings::Key::General_Port)));
|
beammp_debug(std::string(StrPort) + ": " + std::to_string(Application::Settings.getAsInt(Settings::Key::General_Port)));
|
||||||
|
beammp_debug(std::string(StrIP) + ": \"" + Application::Settings.getAsString(Settings::Key::General_IP) + "\"");
|
||||||
beammp_debug(std::string(StrMaxCars) + ": " + std::to_string(Application::Settings.getAsInt(Settings::Key::General_MaxCars)));
|
beammp_debug(std::string(StrMaxCars) + ": " + std::to_string(Application::Settings.getAsInt(Settings::Key::General_MaxCars)));
|
||||||
beammp_debug(std::string(StrMaxPlayers) + ": " + std::to_string(Application::Settings.getAsInt(Settings::Key::General_MaxPlayers)));
|
beammp_debug(std::string(StrMaxPlayers) + ": " + std::to_string(Application::Settings.getAsInt(Settings::Key::General_MaxPlayers)));
|
||||||
beammp_debug(std::string(StrMap) + ": \"" + Application::Settings.getAsString(Settings::Key::General_Map) + "\"");
|
beammp_debug(std::string(StrMap) + ": \"" + Application::Settings.getAsString(Settings::Key::General_Map) + "\"");
|
||||||
|
@ -24,6 +24,7 @@
|
|||||||
#include "CustomAssert.h"
|
#include "CustomAssert.h"
|
||||||
#include "LuaAPI.h"
|
#include "LuaAPI.h"
|
||||||
#include "TLuaEngine.h"
|
#include "TLuaEngine.h"
|
||||||
|
#include "Http.h"
|
||||||
|
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
#include <lua.hpp>
|
#include <lua.hpp>
|
||||||
@ -208,16 +209,18 @@ void TConsole::Command_Help(const std::string&, const std::vector<std::string>&
|
|||||||
}
|
}
|
||||||
static constexpr const char* sHelpString = R"(
|
static constexpr const char* sHelpString = R"(
|
||||||
Commands:
|
Commands:
|
||||||
help displays this help
|
help displays this help
|
||||||
exit shuts down the server
|
exit shuts down the server
|
||||||
kick <name> [reason] kicks specified player with an optional reason
|
kick <name> [reason] kicks specified player with an optional reason
|
||||||
list lists all players and info about them
|
list lists all players and info about them
|
||||||
say <message> sends the message to all players in chat
|
say <message> sends the message to all players in chat
|
||||||
lua [state id] switches to lua, optionally into a specific state id's lua
|
lua [state id] switches to lua, optionally into a specific state id's lua
|
||||||
settings [command] sets or gets settings for the server, run `settings help` for more info
|
settings [command] sets or gets settings for the server, run `settings help` for more info
|
||||||
status how the server is doing and what it's up to
|
status how the server is doing and what it's up to
|
||||||
clear clears the console window
|
clear clears the console window
|
||||||
version displays the server version)";
|
version displays the server version
|
||||||
|
protectmod <name> <value> sets whether a mod is protected, value can be true or false
|
||||||
|
reloadmods reloads all mods from the Resources Client folder)";
|
||||||
Application::Console().WriteRaw("BeamMP-Server Console: " + std::string(sHelpString));
|
Application::Console().WriteRaw("BeamMP-Server Console: " + std::string(sHelpString));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -262,6 +265,56 @@ void TConsole::Command_Version(const std::string& cmd, const std::vector<std::st
|
|||||||
std::string openssl_version = fmt::format("OpenSSL: v{}.{}.{}", OPENSSL_VERSION_MAJOR, OPENSSL_VERSION_MINOR, OPENSSL_VERSION_PATCH);
|
std::string openssl_version = fmt::format("OpenSSL: v{}.{}.{}", OPENSSL_VERSION_MAJOR, OPENSSL_VERSION_MINOR, OPENSSL_VERSION_PATCH);
|
||||||
Application::Console().WriteRaw(openssl_version);
|
Application::Console().WriteRaw(openssl_version);
|
||||||
}
|
}
|
||||||
|
void TConsole::Command_ProtectMod(const std::string& cmd, const std::vector<std::string>& args) {
|
||||||
|
if (!EnsureArgsCount(args, 2)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& ModName = args.at(0);
|
||||||
|
const auto& Protect = args.at(1);
|
||||||
|
|
||||||
|
for (auto mod : mLuaEngine->Network().ResourceManager().GetMods()) {
|
||||||
|
if (mod["file_name"].get<std::string>() == ModName) {
|
||||||
|
mLuaEngine->Network().ResourceManager().SetProtected(ModName, Protect == "true");
|
||||||
|
Application::Console().WriteRaw("Mod " + ModName + " is now " + (Protect == "true" ? "protected" : "unprotected"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Application::Console().WriteRaw("Mod " + ModName + " not found.");
|
||||||
|
}
|
||||||
|
void TConsole::Command_ReloadMods(const std::string& cmd, const std::vector<std::string>& args) {
|
||||||
|
if (!EnsureArgsCount(args, 0)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mLuaEngine->Network().ResourceManager().RefreshFiles();
|
||||||
|
Application::Console().WriteRaw("Mods reloaded.");
|
||||||
|
}
|
||||||
|
|
||||||
|
void TConsole::Command_NetTest(const std::string& cmd, const std::vector<std::string>& args) {
|
||||||
|
unsigned int status = 0;
|
||||||
|
|
||||||
|
std::string T = Http::GET(
|
||||||
|
Application::GetServerCheckUrl() + "/api/v2/beammp/" + std::to_string(Application::Settings.getAsInt(Settings::Key::General_Port)), &status
|
||||||
|
);
|
||||||
|
|
||||||
|
beammp_debugf("Status and response from Server Check API: {0}, {1}", status, T);
|
||||||
|
|
||||||
|
auto Doc = nlohmann::json::parse(T, nullptr, false);
|
||||||
|
|
||||||
|
if (Doc.is_discarded() || !Doc.is_object()) {
|
||||||
|
beammp_warn("Failed to parse Server Check API response, however the server will most likely still work correctly.");
|
||||||
|
} else {
|
||||||
|
std::string status = Doc["status"];
|
||||||
|
std::string details = "Response from Server Check API: " + std::string(Doc["details"]);
|
||||||
|
if (status == "ok") {
|
||||||
|
beammp_info(details);
|
||||||
|
} else {
|
||||||
|
beammp_warn(details);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void TConsole::Command_Kick(const std::string&, const std::vector<std::string>& args) {
|
void TConsole::Command_Kick(const std::string&, const std::vector<std::string>& args) {
|
||||||
if (!EnsureArgsCount(args, 1, size_t(-1))) {
|
if (!EnsureArgsCount(args, 1, size_t(-1))) {
|
||||||
|
@ -40,18 +40,6 @@ TLuaEngine* LuaAPI::MP::Engine;
|
|||||||
|
|
||||||
static sol::protected_function AddTraceback(sol::state_view StateView, sol::protected_function RawFn);
|
static sol::protected_function AddTraceback(sol::state_view StateView, sol::protected_function RawFn);
|
||||||
|
|
||||||
static std::optional<sol::function> GetLuaHandler(sol::state_view StateView, const std::string Handler, const std::string EventName);
|
|
||||||
|
|
||||||
static std::optional<sol::function> GetLuaHandler(sol::state_view StateView, const std::string Handler, const std::string EventName) {
|
|
||||||
auto Res = StateView.safe_script("return " + Handler, sol::script_pass_on_error);
|
|
||||||
if (!Res.valid()) {
|
|
||||||
beammp_errorf("invalid handler for event \"{}\". handler: \"{}\"", EventName, Handler);
|
|
||||||
} else if (Res.get_type() == sol::type::function) {
|
|
||||||
return Res.get<sol::function>();
|
|
||||||
}
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
|
|
||||||
TLuaEngine::TLuaEngine()
|
TLuaEngine::TLuaEngine()
|
||||||
: mResourceServerPath(fs::path(Application::Settings.getAsString(Settings::Key::General_ResourceFolder)) / "Server") {
|
: mResourceServerPath(fs::path(Application::Settings.getAsString(Settings::Key::General_ResourceFolder)) / "Server") {
|
||||||
Application::SetSubsystemStatus("LuaEngine", Application::Status::Starting);
|
Application::SetSubsystemStatus("LuaEngine", Application::Status::Starting);
|
||||||
@ -80,10 +68,11 @@ TEST_CASE("TLuaEngine ctor & dtor") {
|
|||||||
|
|
||||||
void TLuaEngine::operator()() {
|
void TLuaEngine::operator()() {
|
||||||
RegisterThread("LuaEngine");
|
RegisterThread("LuaEngine");
|
||||||
Application::SetSubsystemStatus("LuaEngine", Application::Status::Good);
|
|
||||||
// lua engine main thread
|
// lua engine main thread
|
||||||
beammp_infof("Lua v{}.{}.{}", LUA_VERSION_MAJOR, LUA_VERSION_MINOR, LUA_VERSION_RELEASE);
|
beammp_infof("Lua v{}.{}.{}", LUA_VERSION_MAJOR, LUA_VERSION_MINOR, LUA_VERSION_RELEASE);
|
||||||
CollectAndInitPlugins();
|
CollectAndInitPlugins();
|
||||||
|
|
||||||
|
Application::SetSubsystemStatus("LuaEngine", Application::Status::Good);
|
||||||
// now call all onInit's
|
// now call all onInit's
|
||||||
auto Futures = TriggerEvent("onInit", "");
|
auto Futures = TriggerEvent("onInit", "");
|
||||||
WaitForAll(Futures, std::chrono::seconds(5));
|
WaitForAll(Futures, std::chrono::seconds(5));
|
||||||
@ -506,11 +495,9 @@ sol::table TLuaEngine::StateThreadData::Lua_TriggerGlobalEvent(const std::string
|
|||||||
|
|
||||||
sol::variadic_results LocalArgs = JsonStringToArray(Str);
|
sol::variadic_results LocalArgs = JsonStringToArray(Str);
|
||||||
for (const auto& Handler : MyHandlers) {
|
for (const auto& Handler : MyHandlers) {
|
||||||
auto Res = GetLuaHandler(mStateView, Handler, EventName);
|
auto Fn = mStateView[Handler];
|
||||||
if (Res.has_value()) {
|
Fn = AddTraceback(mStateView, Fn);
|
||||||
sol::function Fn = Res.value();
|
if (Fn.valid()) {
|
||||||
Fn = AddTraceback(mStateView, Fn);
|
|
||||||
|
|
||||||
auto LuaResult = Fn(LocalArgs);
|
auto LuaResult = Fn(LocalArgs);
|
||||||
auto Result = std::make_shared<TLuaResult>();
|
auto Result = std::make_shared<TLuaResult>();
|
||||||
if (LuaResult.valid()) {
|
if (LuaResult.valid()) {
|
||||||
@ -562,9 +549,8 @@ sol::table TLuaEngine::StateThreadData::Lua_TriggerLocalEvent(const std::string&
|
|||||||
sol::table Result = mStateView.create_table();
|
sol::table Result = mStateView.create_table();
|
||||||
int i = 1;
|
int i = 1;
|
||||||
for (const auto& Handler : mEngine->GetEventHandlersForState(EventName, mStateId)) {
|
for (const auto& Handler : mEngine->GetEventHandlersForState(EventName, mStateId)) {
|
||||||
auto Res = GetLuaHandler(mStateView, Handler, EventName);
|
auto Fn = mStateView[Handler];
|
||||||
if (Res.has_value()) {
|
if (Fn.valid() && Fn.get_type() == sol::type::function) {
|
||||||
sol::function Fn = Res.value();
|
|
||||||
auto FnRet = Fn(EventArgs);
|
auto FnRet = Fn(EventArgs);
|
||||||
if (FnRet.valid()) {
|
if (FnRet.valid()) {
|
||||||
Result.set(i, FnRet);
|
Result.set(i, FnRet);
|
||||||
@ -682,7 +668,7 @@ sol::table TLuaEngine::StateThreadData::Lua_GetPlayerVehicles(int ID) {
|
|||||||
sol::state_view StateView(mState);
|
sol::state_view StateView(mState);
|
||||||
sol::table Result = StateView.create_table();
|
sol::table Result = StateView.create_table();
|
||||||
for (const auto& v : VehicleData) {
|
for (const auto& v : VehicleData) {
|
||||||
Result[v.ID()] = v.Data().substr(3);
|
Result[v.ID()] = v.DataAsPacket(Client->GetRoles(), Client->GetName(), Client->GetID()).substr(3);
|
||||||
}
|
}
|
||||||
return Result;
|
return Result;
|
||||||
} else
|
} else
|
||||||
@ -890,6 +876,12 @@ TLuaEngine::StateThreadData::StateThreadData(const std::string& Name, TLuaStateI
|
|||||||
beammp_lua_error("SendNotification expects 2, 3 or 4 arguments.");
|
beammp_lua_error("SendNotification expects 2, 3 or 4 arguments.");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
MPTable.set_function("ConfirmationDialog", sol::overload(
|
||||||
|
&LuaAPI::MP::ConfirmationDialog,
|
||||||
|
[&](const int& ID, const std::string& Title, const std::string& Body, const sol::table& Buttons, const std::string& InteractionID) {
|
||||||
|
LuaAPI::MP::ConfirmationDialog(ID, Title, Body, Buttons, InteractionID);
|
||||||
|
}
|
||||||
|
));
|
||||||
MPTable.set_function("GetPlayers", [&]() -> sol::table {
|
MPTable.set_function("GetPlayers", [&]() -> sol::table {
|
||||||
return Lua_GetPlayers();
|
return Lua_GetPlayers();
|
||||||
});
|
});
|
||||||
@ -1176,10 +1168,8 @@ void TLuaEngine::StateThreadData::operator()() {
|
|||||||
// TODO: Use TheQueuedFunction.EventName for errors, warnings, etc
|
// TODO: Use TheQueuedFunction.EventName for errors, warnings, etc
|
||||||
Result->StateId = mStateId;
|
Result->StateId = mStateId;
|
||||||
sol::state_view StateView(mState);
|
sol::state_view StateView(mState);
|
||||||
|
auto RawFn = StateView[FnName];
|
||||||
auto Res = GetLuaHandler(StateView, FnName, TheQueuedFunction.EventName);
|
if (RawFn.valid() && RawFn.get_type() == sol::type::function) {
|
||||||
if (Res.has_value()) {
|
|
||||||
sol::function Fn = Res.value();
|
|
||||||
std::vector<sol::object> LuaArgs;
|
std::vector<sol::object> LuaArgs;
|
||||||
for (const auto& Arg : Args) {
|
for (const auto& Arg : Args) {
|
||||||
if (Arg.valueless_by_exception()) {
|
if (Arg.valueless_by_exception()) {
|
||||||
@ -1214,7 +1204,7 @@ void TLuaEngine::StateThreadData::operator()() {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Fn = AddTraceback(StateView, Fn);
|
auto Fn = AddTraceback(StateView, RawFn);
|
||||||
auto Res = Fn(sol::as_args(LuaArgs));
|
auto Res = Fn(sol::as_args(LuaArgs));
|
||||||
if (Res.valid()) {
|
if (Res.valid()) {
|
||||||
Result->Error = false;
|
Result->Error = false;
|
||||||
|
@ -85,9 +85,17 @@ TNetwork::TNetwork(TServer& Server, TPPSMonitor& PPSMonitor, TResourceManager& R
|
|||||||
|
|
||||||
void TNetwork::UDPServerMain() {
|
void TNetwork::UDPServerMain() {
|
||||||
RegisterThread("UDPServer");
|
RegisterThread("UDPServer");
|
||||||
// listen on all ipv6 addresses
|
|
||||||
ip::udp::endpoint UdpListenEndpoint(ip::make_address("::"), Application::Settings.getAsInt(Settings::Key::General_Port));
|
|
||||||
boost::system::error_code ec;
|
boost::system::error_code ec;
|
||||||
|
auto address = ip::make_address(Application::Settings.getAsString(Settings::Key::General_IP), ec);
|
||||||
|
|
||||||
|
if (ec) {
|
||||||
|
beammp_errorf("Failed to parse IP: {}", ec.message());
|
||||||
|
Application::GracefullyShutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
ip::udp::endpoint UdpListenEndpoint(address, Application::Settings.getAsInt(Settings::Key::General_Port));
|
||||||
|
|
||||||
mUDPSock.open(UdpListenEndpoint.protocol(), ec);
|
mUDPSock.open(UdpListenEndpoint.protocol(), ec);
|
||||||
if (ec) {
|
if (ec) {
|
||||||
beammp_error("open() failed: " + ec.message());
|
beammp_error("open() failed: " + ec.message());
|
||||||
@ -107,7 +115,7 @@ void TNetwork::UDPServerMain() {
|
|||||||
Application::GracefullyShutdown();
|
Application::GracefullyShutdown();
|
||||||
}
|
}
|
||||||
Application::SetSubsystemStatus("UDPNetwork", Application::Status::Good);
|
Application::SetSubsystemStatus("UDPNetwork", Application::Status::Good);
|
||||||
beammp_info(("Vehicle data network online on port ") + std::to_string(Application::Settings.getAsInt(Settings::Key::General_Port)) + (" with a Max of ")
|
beammp_info(("Vehicle data network online on port ") + std::to_string(UdpListenEndpoint.port()) + (" with a Max of ")
|
||||||
+ std::to_string(Application::Settings.getAsInt(Settings::Key::General_MaxPlayers)) + (" Clients"));
|
+ std::to_string(Application::Settings.getAsInt(Settings::Key::General_MaxPlayers)) + (" Clients"));
|
||||||
while (!Application::IsShuttingDown()) {
|
while (!Application::IsShuttingDown()) {
|
||||||
try {
|
try {
|
||||||
@ -170,12 +178,17 @@ void TNetwork::UDPServerMain() {
|
|||||||
void TNetwork::TCPServerMain() {
|
void TNetwork::TCPServerMain() {
|
||||||
RegisterThread("TCPServer");
|
RegisterThread("TCPServer");
|
||||||
|
|
||||||
// listen on all ipv6 addresses
|
|
||||||
auto port = uint16_t(Application::Settings.getAsInt(Settings::Key::General_Port));
|
|
||||||
ip::tcp::endpoint ListenEp(ip::make_address("::"), port);
|
|
||||||
beammp_infof("Listening on 0.0.0.0:{0} and [::]:{0}", port);
|
|
||||||
ip::tcp::socket Listener(mServer.IoCtx());
|
|
||||||
boost::system::error_code ec;
|
boost::system::error_code ec;
|
||||||
|
auto address = ip::make_address(Application::Settings.getAsString(Settings::Key::General_IP), ec);
|
||||||
|
if (ec) {
|
||||||
|
beammp_errorf("Failed to parse IP: {}", ec.message());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ip::tcp::endpoint ListenEp(address,
|
||||||
|
uint16_t(Application::Settings.getAsInt(Settings::Key::General_Port)));
|
||||||
|
|
||||||
|
ip::tcp::socket Listener(mServer.IoCtx());
|
||||||
Listener.open(ListenEp.protocol(), ec);
|
Listener.open(ListenEp.protocol(), ec);
|
||||||
if (ec) {
|
if (ec) {
|
||||||
beammp_errorf("Failed to open socket: {}", ec.message());
|
beammp_errorf("Failed to open socket: {}", ec.message());
|
||||||
@ -209,6 +222,7 @@ void TNetwork::TCPServerMain() {
|
|||||||
Application::GracefullyShutdown();
|
Application::GracefullyShutdown();
|
||||||
}
|
}
|
||||||
Application::SetSubsystemStatus("TCPNetwork", Application::Status::Good);
|
Application::SetSubsystemStatus("TCPNetwork", Application::Status::Good);
|
||||||
|
beammp_infof("Listening on {0} port {1}", ListenEp.address().to_string(), static_cast<uint16_t>(ListenEp.port()));
|
||||||
beammp_info("Vehicle event network online");
|
beammp_info("Vehicle event network online");
|
||||||
do {
|
do {
|
||||||
try {
|
try {
|
||||||
@ -307,6 +321,11 @@ std::shared_ptr<TClient> TNetwork::Authentication(TConnection&& RawConnection) {
|
|||||||
Client->SetIdentifier("ip", ip);
|
Client->SetIdentifier("ip", ip);
|
||||||
beammp_tracef("This thread is ip {} ({})", ip, RawConnection.SockAddr.address().to_v6().is_v4_mapped() ? "IPv4 mapped IPv6" : "IPv6");
|
beammp_tracef("This thread is ip {} ({})", ip, RawConnection.SockAddr.address().to_v6().is_v4_mapped() ? "IPv4 mapped IPv6" : "IPv6");
|
||||||
|
|
||||||
|
if (Application::GetSubsystemStatuses().at("Main") == Application::Status::Starting) {
|
||||||
|
ClientKick(*Client, "The server is still starting, please try joining again later.");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
beammp_info("Identifying new ClientConnection...");
|
beammp_info("Identifying new ClientConnection...");
|
||||||
|
|
||||||
auto Data = TCPRcv(*Client);
|
auto Data = TCPRcv(*Client);
|
||||||
@ -767,7 +786,7 @@ void TNetwork::Parse(TClient& c, const std::vector<uint8_t>& Packet) {
|
|||||||
case 'S':
|
case 'S':
|
||||||
if (SubCode == 'R') {
|
if (SubCode == 'R') {
|
||||||
beammp_debug("Sending Mod Info");
|
beammp_debug("Sending Mod Info");
|
||||||
std::string ToSend = mResourceManager.NewFileList();
|
std::string ToSend = mResourceManager.GetMods().dump();
|
||||||
beammp_debugf("Mod Info: {}", ToSend);
|
beammp_debugf("Mod Info: {}", ToSend);
|
||||||
if (!TCPSend(c, StringToVector(ToSend))) {
|
if (!TCPSend(c, StringToVector(ToSend))) {
|
||||||
ClientKick(c, "TCP Send 'SY' failed");
|
ClientKick(c, "TCP Send 'SY' failed");
|
||||||
@ -789,6 +808,15 @@ void TNetwork::SendFile(TClient& c, const std::string& UnsafeName) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
auto FileName = fs::path(UnsafeName).filename().string();
|
auto FileName = fs::path(UnsafeName).filename().string();
|
||||||
|
|
||||||
|
for (auto mod : mResourceManager.GetMods()) {
|
||||||
|
if (mod["file_name"].get<std::string>() == FileName && mod["protected"] == true) {
|
||||||
|
beammp_warn("Client tried to access protected file " + UnsafeName);
|
||||||
|
c.Disconnect("Mod is protected thus cannot be downloaded");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
FileName = Application::Settings.getAsString(Settings::Key::General_ResourceFolder) + "/Client/" + FileName;
|
FileName = Application::Settings.getAsString(Settings::Key::General_ResourceFolder) + "/Client/" + FileName;
|
||||||
|
|
||||||
if (!std::filesystem::exists(FileName)) {
|
if (!std::filesystem::exists(FileName)) {
|
||||||
@ -946,7 +974,7 @@ bool TNetwork::SyncClient(const std::weak_ptr<TClient>& c) {
|
|||||||
res = false;
|
res = false;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
res = Respond(*LockedClient, StringToVector(v.Data()), true, true);
|
res = Respond(*LockedClient, StringToVector(v.DataAsPacket(client->GetRoles(), client->GetName(), client->GetID())), true, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,21 +57,26 @@ void TPluginMonitor::operator()() {
|
|||||||
mFileTimes[Pair.first] = CurrentTime;
|
mFileTimes[Pair.first] = CurrentTime;
|
||||||
// grandparent of the path should be Resources/Server
|
// grandparent of the path should be Resources/Server
|
||||||
if (fs::equivalent(fs::path(Pair.first).parent_path().parent_path(), mPath)) {
|
if (fs::equivalent(fs::path(Pair.first).parent_path().parent_path(), mPath)) {
|
||||||
beammp_infof("File \"{}\" changed, reloading", Pair.first);
|
if (LowerString(fs::path(Pair.first).extension().string()) == ".lua") {
|
||||||
// is in root folder, so reload
|
beammp_infof("File \"{}\" changed, reloading", Pair.first);
|
||||||
std::ifstream FileStream(Pair.first, std::ios::in | std::ios::binary);
|
// is in root folder, so reload
|
||||||
auto Size = std::filesystem::file_size(Pair.first);
|
std::ifstream FileStream(Pair.first, std::ios::in | std::ios::binary);
|
||||||
auto Contents = std::make_shared<std::string>();
|
auto Size = std::filesystem::file_size(Pair.first);
|
||||||
Contents->resize(Size);
|
auto Contents = std::make_shared<std::string>();
|
||||||
FileStream.read(Contents->data(), Contents->size());
|
Contents->resize(Size);
|
||||||
TLuaChunk Chunk(Contents, Pair.first, fs::path(Pair.first).parent_path().string());
|
FileStream.read(Contents->data(), Contents->size());
|
||||||
auto StateID = mEngine->GetStateIDForPlugin(fs::path(Pair.first).parent_path());
|
TLuaChunk Chunk(Contents, Pair.first, fs::path(Pair.first).parent_path().string());
|
||||||
auto Res = mEngine->EnqueueScript(StateID, Chunk);
|
auto StateID = mEngine->GetStateIDForPlugin(fs::path(Pair.first).parent_path());
|
||||||
Res->WaitUntilReady();
|
auto Res = mEngine->EnqueueScript(StateID, Chunk);
|
||||||
if (Res->Error) {
|
Res->WaitUntilReady();
|
||||||
beammp_lua_errorf("Error while hot-reloading \"{}\": {}", Pair.first, Res->ErrorMessage);
|
if (Res->Error) {
|
||||||
|
beammp_lua_errorf("Error while hot-reloading \"{}\": {}", Pair.first, Res->ErrorMessage);
|
||||||
|
} else {
|
||||||
|
mEngine->ReportErrors(mEngine->TriggerLocalEvent(StateID, "onInit"));
|
||||||
|
mEngine->ReportErrors(mEngine->TriggerEvent("onFileChanged", "", Pair.first));
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
mEngine->ReportErrors(mEngine->TriggerLocalEvent(StateID, "onInit"));
|
beammp_debugf("File \"{}\" changed, not reloading because it's not a lua file. Triggering 'onFileChanged' event instead", Pair.first);
|
||||||
mEngine->ReportErrors(mEngine->TriggerEvent("onFileChanged", "", Pair.first));
|
mEngine->ReportErrors(mEngine->TriggerEvent("onFileChanged", "", Pair.first));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -58,22 +58,58 @@ TResourceManager::TResourceManager() {
|
|||||||
Application::SetSubsystemStatus("ResourceManager", Application::Status::Good);
|
Application::SetSubsystemStatus("ResourceManager", Application::Status::Good);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string TResourceManager::NewFileList() const {
|
|
||||||
return mMods.dump();
|
|
||||||
}
|
|
||||||
void TResourceManager::RefreshFiles() {
|
void TResourceManager::RefreshFiles() {
|
||||||
mMods.clear();
|
mMods.clear();
|
||||||
std::unique_lock Lock(mModsMutex);
|
std::unique_lock Lock(mModsMutex);
|
||||||
|
|
||||||
std::string Path = Application::Settings.getAsString(Settings::Key::General_ResourceFolder) + "/Client";
|
std::string Path = Application::Settings.getAsString(Settings::Key::General_ResourceFolder) + "/Client";
|
||||||
|
|
||||||
|
nlohmann::json modsDB;
|
||||||
|
|
||||||
|
if (std::filesystem::exists(Path + "/mods.json")) {
|
||||||
|
try {
|
||||||
|
std::ifstream stream(Path + "/mods.json");
|
||||||
|
|
||||||
|
stream >> modsDB;
|
||||||
|
|
||||||
|
stream.close();
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
beammp_errorf("Failed to load mods.json: {}", e.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (const auto& entry : fs::directory_iterator(Path)) {
|
for (const auto& entry : fs::directory_iterator(Path)) {
|
||||||
std::string File(entry.path().string());
|
std::string File(entry.path().string());
|
||||||
|
|
||||||
|
if (entry.path().filename().string() == "mods.json") {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (entry.path().extension() != ".zip" || std::filesystem::is_directory(entry.path())) {
|
if (entry.path().extension() != ".zip" || std::filesystem::is_directory(entry.path())) {
|
||||||
beammp_warnf("'{}' is not a ZIP file and will be ignored", File);
|
beammp_warnf("'{}' is not a ZIP file and will be ignored", File);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (modsDB.contains(entry.path().filename().string())) {
|
||||||
|
auto& dbEntry = modsDB[entry.path().filename().string()];
|
||||||
|
if (entry.last_write_time().time_since_epoch().count() > dbEntry["lastwrite"] || std::filesystem::file_size(File) != dbEntry["filesize"].get<size_t>()) {
|
||||||
|
beammp_infof("File '{}' has been modified, rehashing", File);
|
||||||
|
} else {
|
||||||
|
dbEntry["exists"] = true;
|
||||||
|
|
||||||
|
mMods.push_back(nlohmann::json {
|
||||||
|
{ "file_name", std::filesystem::path(File).filename() },
|
||||||
|
{ "file_size", std::filesystem::file_size(File) },
|
||||||
|
{ "hash_algorithm", "sha256" },
|
||||||
|
{ "hash", dbEntry["hash"] },
|
||||||
|
{ "protected", dbEntry["protected"] } });
|
||||||
|
|
||||||
|
beammp_debugf("Mod '{}' loaded from cache", File);
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
EVP_MD_CTX* mdctx;
|
EVP_MD_CTX* mdctx;
|
||||||
const EVP_MD* md;
|
const EVP_MD* md;
|
||||||
@ -133,9 +169,73 @@ void TResourceManager::RefreshFiles() {
|
|||||||
{ "file_size", std::filesystem::file_size(File) },
|
{ "file_size", std::filesystem::file_size(File) },
|
||||||
{ "hash_algorithm", "sha256" },
|
{ "hash_algorithm", "sha256" },
|
||||||
{ "hash", result },
|
{ "hash", result },
|
||||||
});
|
{ "protected", false } });
|
||||||
|
|
||||||
|
modsDB[std::filesystem::path(File).filename().string()] = {
|
||||||
|
{ "lastwrite", entry.last_write_time().time_since_epoch().count() },
|
||||||
|
{ "hash", result },
|
||||||
|
{ "filesize", std::filesystem::file_size(File) },
|
||||||
|
{ "protected", false },
|
||||||
|
{ "exists", true }
|
||||||
|
};
|
||||||
|
|
||||||
} catch (const std::exception& e) {
|
} catch (const std::exception& e) {
|
||||||
beammp_errorf("Sha256 hashing of '{}' failed: {}", File, e.what());
|
beammp_errorf("Sha256 hashing of '{}' failed: {}", File, e.what());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (auto it = modsDB.begin(); it != modsDB.end();) {
|
||||||
|
if (!it.value().contains("exists")) {
|
||||||
|
it = modsDB.erase(it);
|
||||||
|
} else {
|
||||||
|
it.value().erase("exists");
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
std::ofstream stream(Path + "/mods.json");
|
||||||
|
|
||||||
|
stream << modsDB.dump(4);
|
||||||
|
|
||||||
|
stream.close();
|
||||||
|
} catch (std::exception& e) {
|
||||||
|
beammp_error("Failed to update mod DB: " + std::string(e.what()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TResourceManager::SetProtected(const std::string& ModName, bool Protected) {
|
||||||
|
std::unique_lock Lock(mModsMutex);
|
||||||
|
|
||||||
|
for (auto& mod : mMods) {
|
||||||
|
if (mod["file_name"].get<std::string>() == ModName) {
|
||||||
|
mod["protected"] = Protected;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto modsDBPath = Application::Settings.getAsString(Settings::Key::General_ResourceFolder) + "/Client/mods.json";
|
||||||
|
|
||||||
|
if (std::filesystem::exists(modsDBPath)) {
|
||||||
|
try {
|
||||||
|
nlohmann::json modsDB;
|
||||||
|
|
||||||
|
std::fstream stream(modsDBPath);
|
||||||
|
|
||||||
|
stream >> modsDB;
|
||||||
|
|
||||||
|
if (modsDB.contains(ModName)) {
|
||||||
|
modsDB[ModName]["protected"] = Protected;
|
||||||
|
}
|
||||||
|
|
||||||
|
stream.clear();
|
||||||
|
stream.seekp(0, std::ios::beg);
|
||||||
|
|
||||||
|
stream << modsDB.dump(4);
|
||||||
|
|
||||||
|
stream.close();
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
beammp_errorf("Failed to update mods.json: {}", e.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -195,6 +195,18 @@ void TServer::GlobalParser(const std::weak_ptr<TClient>& Client, std::vector<uin
|
|||||||
|
|
||||||
// V to Y
|
// V to Y
|
||||||
if (Code <= 89 && Code >= 86) {
|
if (Code <= 89 && Code >= 86) {
|
||||||
|
int PID = -1;
|
||||||
|
int VID = -1;
|
||||||
|
|
||||||
|
auto MaybePidVid = GetPidVid(StringPacket.substr(3).substr(0, StringPacket.substr(3).find(':', 1)));
|
||||||
|
if (MaybePidVid) {
|
||||||
|
std::tie(PID, VID) = MaybePidVid.value();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (PID == -1 || VID == -1 || PID != LockedClient->GetID()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
PPSMonitor.IncrementInternalPPS();
|
PPSMonitor.IncrementInternalPPS();
|
||||||
Network.SendToAll(LockedClient.get(), Packet, false, false);
|
Network.SendToAll(LockedClient.get(), Packet, false, false);
|
||||||
return;
|
return;
|
||||||
@ -255,11 +267,25 @@ void TServer::GlobalParser(const std::weak_ptr<TClient>& Client, std::vector<uin
|
|||||||
case 'N':
|
case 'N':
|
||||||
Network.SendToAll(LockedClient.get(), Packet, false, true);
|
Network.SendToAll(LockedClient.get(), Packet, false, true);
|
||||||
return;
|
return;
|
||||||
case 'Z': // position packet
|
case 'Z': { // position packet
|
||||||
PPSMonitor.IncrementInternalPPS();
|
PPSMonitor.IncrementInternalPPS();
|
||||||
|
|
||||||
|
int PID = -1;
|
||||||
|
int VID = -1;
|
||||||
|
|
||||||
|
auto MaybePidVid = GetPidVid(StringPacket.substr(3).substr(0, StringPacket.substr(3).find(':', 1)));
|
||||||
|
if (MaybePidVid) {
|
||||||
|
std::tie(PID, VID) = MaybePidVid.value();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (PID == -1 || VID == -1 || PID != LockedClient->GetID()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Network.SendToAll(LockedClient.get(), Packet, false, false);
|
Network.SendToAll(LockedClient.get(), Packet, false, false);
|
||||||
HandlePosition(*LockedClient, StringPacket);
|
HandlePosition(*LockedClient, StringPacket);
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -328,8 +354,9 @@ void TServer::ParseVehicle(TClient& c, const std::string& Pckt, TNetwork& Networ
|
|||||||
});
|
});
|
||||||
|
|
||||||
bool SpawnConfirmed = false;
|
bool SpawnConfirmed = false;
|
||||||
if (ShouldSpawn(c, CarJson, CarID) && !ShouldntSpawn) {
|
auto CarJsonDoc = nlohmann::json::parse(CarJson, nullptr, false);
|
||||||
c.AddNewCar(CarID, Packet);
|
if (ShouldSpawn(c, CarJson, CarID) && !ShouldntSpawn && !CarJsonDoc.is_discarded()) {
|
||||||
|
c.AddNewCar(CarID, CarJsonDoc);
|
||||||
Network.SendToAll(nullptr, StringToVector(Packet), true, true);
|
Network.SendToAll(nullptr, StringToVector(Packet), true, true);
|
||||||
SpawnConfirmed = true;
|
SpawnConfirmed = true;
|
||||||
} else {
|
} else {
|
||||||
@ -446,6 +473,17 @@ void TServer::ParseVehicle(TClient& c, const std::string& Pckt, TNetwork& Networ
|
|||||||
Data = Data.substr(Data.find('['));
|
Data = Data.substr(Data.find('['));
|
||||||
LuaAPI::MP::Engine->ReportErrors(LuaAPI::MP::Engine->TriggerEvent("onVehiclePaintChanged", "", c.GetID(), VID, Data));
|
LuaAPI::MP::Engine->ReportErrors(LuaAPI::MP::Engine->TriggerEvent("onVehiclePaintChanged", "", c.GetID(), VID, Data));
|
||||||
Network.SendToAll(&c, StringToVector(Packet), false, true);
|
Network.SendToAll(&c, StringToVector(Packet), false, true);
|
||||||
|
|
||||||
|
auto CarData = c.GetCarData(VID);
|
||||||
|
if (CarData == nlohmann::detail::value_t::null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (CarData.contains("vcf") && CarData.at("vcf").is_object())
|
||||||
|
if (CarData.at("vcf").contains("paints") && CarData.at("vcf").at("paints").is_array()) {
|
||||||
|
CarData.at("vcf")["paints"] = nlohmann::json::parse(Data);
|
||||||
|
c.SetCarData(VID, CarData);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -461,42 +499,22 @@ void TServer::Apply(TClient& c, int VID, const std::string& pckt) {
|
|||||||
beammp_error("Malformed packet received, no '{' found");
|
beammp_error("Malformed packet received, no '{' found");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Packet = pckt.substr(FoundPos);
|
std::string Packet = pckt.substr(FoundPos);
|
||||||
std::string VD = c.GetCarData(VID);
|
nlohmann::json VD = c.GetCarData(VID);
|
||||||
if (VD.empty()) {
|
if (VD == nlohmann::detail::value_t::null) {
|
||||||
beammp_error("Tried to apply change to vehicle that does not exist");
|
beammp_error("Tried to apply change to vehicle that does not exist");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
std::string Header = VD.substr(0, VD.find('{'));
|
|
||||||
|
|
||||||
FoundPos = VD.find('{');
|
nlohmann::json Pack = nlohmann::json::parse(Packet, nullptr, false);
|
||||||
if (FoundPos == std::string::npos) {
|
|
||||||
return;
|
if (Pack.is_discarded()) {
|
||||||
}
|
|
||||||
VD = VD.substr(FoundPos);
|
|
||||||
rapidjson::Document Veh, Pack;
|
|
||||||
Veh.Parse(VD.c_str());
|
|
||||||
if (Veh.HasParseError()) {
|
|
||||||
beammp_error("Could not get vehicle config!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Pack.Parse(Packet.c_str());
|
|
||||||
if (Pack.HasParseError() || Pack.IsNull()) {
|
|
||||||
beammp_error("Could not get active vehicle config!");
|
beammp_error("Could not get active vehicle config!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto& M : Pack.GetObject()) {
|
c.SetCarData(VID, Pack);
|
||||||
if (Veh[M.name].IsNull()) {
|
|
||||||
Veh.AddMember(M.name, M.value, Veh.GetAllocator());
|
|
||||||
} else {
|
|
||||||
Veh[M.name] = Pack[M.name];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
rapidjson::StringBuffer Buffer;
|
|
||||||
rapidjson::Writer<rapidjson::StringBuffer> writer(Buffer);
|
|
||||||
Veh.Accept(writer);
|
|
||||||
c.SetCarData(VID, Header + Buffer.GetString());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TServer::InsertClient(const std::shared_ptr<TClient>& NewClient) {
|
void TServer::InsertClient(const std::shared_ptr<TClient>& NewClient) {
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
#include "Common.h"
|
#include "Common.h"
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
TVehicleData::TVehicleData(int ID, std::string Data)
|
TVehicleData::TVehicleData(int ID, nlohmann::json Data)
|
||||||
: mID(ID)
|
: mID(ID)
|
||||||
, mData(std::move(Data)) {
|
, mData(std::move(Data)) {
|
||||||
beammp_trace("vehicle " + std::to_string(mID) + " constructed");
|
beammp_trace("vehicle " + std::to_string(mID) + " constructed");
|
||||||
@ -30,3 +30,7 @@ TVehicleData::TVehicleData(int ID, std::string Data)
|
|||||||
TVehicleData::~TVehicleData() {
|
TVehicleData::~TVehicleData() {
|
||||||
beammp_trace("vehicle " + std::to_string(mID) + " destroyed");
|
beammp_trace("vehicle " + std::to_string(mID) + " destroyed");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string TVehicleData::DataAsPacket(const std::string& Role, const std::string& Name, const int ID) const {
|
||||||
|
return "Os:" + Role + ":" + Name + ":" + std::to_string(ID) + "-" + std::to_string(this->mID) + ":" + this->mData.dump();
|
||||||
|
}
|
15
src/main.cpp
15
src/main.cpp
@ -182,10 +182,6 @@ int BeamMPServerMain(MainArguments Arguments) {
|
|||||||
|
|
||||||
TServer Server(Arguments.List);
|
TServer Server(Arguments.List);
|
||||||
|
|
||||||
auto LuaEngine = std::make_shared<TLuaEngine>();
|
|
||||||
LuaEngine->SetServer(&Server);
|
|
||||||
Application::Console().InitializeLuaConsole(*LuaEngine);
|
|
||||||
|
|
||||||
RegisterThread("Main");
|
RegisterThread("Main");
|
||||||
|
|
||||||
beammp_trace("Running in debug mode on a debug build");
|
beammp_trace("Running in debug mode on a debug build");
|
||||||
@ -194,13 +190,16 @@ int BeamMPServerMain(MainArguments Arguments) {
|
|||||||
TPPSMonitor PPSMonitor(Server);
|
TPPSMonitor PPSMonitor(Server);
|
||||||
THeartbeatThread Heartbeat(ResourceManager, Server);
|
THeartbeatThread Heartbeat(ResourceManager, Server);
|
||||||
TNetwork Network(Server, PPSMonitor, ResourceManager);
|
TNetwork Network(Server, PPSMonitor, ResourceManager);
|
||||||
|
|
||||||
|
auto LuaEngine = std::make_shared<TLuaEngine>();
|
||||||
|
LuaEngine->SetServer(&Server);
|
||||||
|
Application::Console().InitializeLuaConsole(*LuaEngine);
|
||||||
LuaEngine->SetNetwork(&Network);
|
LuaEngine->SetNetwork(&Network);
|
||||||
PPSMonitor.SetNetwork(Network);
|
PPSMonitor.SetNetwork(Network);
|
||||||
Application::CheckForUpdates();
|
Application::CheckForUpdates();
|
||||||
|
|
||||||
TPluginMonitor PluginMonitor(fs::path(Application::Settings.getAsString(Settings::Key::General_ResourceFolder)) / "Server", LuaEngine);
|
TPluginMonitor PluginMonitor(fs::path(Application::Settings.getAsString(Settings::Key::General_ResourceFolder)) / "Server", LuaEngine);
|
||||||
|
|
||||||
Application::SetSubsystemStatus("Main", Application::Status::Good);
|
|
||||||
RegisterThread("Main(Waiting)");
|
RegisterThread("Main(Waiting)");
|
||||||
|
|
||||||
std::set<std::string> IgnoreSubsystems {
|
std::set<std::string> IgnoreSubsystems {
|
||||||
@ -215,6 +214,10 @@ int BeamMPServerMain(MainArguments Arguments) {
|
|||||||
std::string SystemsBadList {};
|
std::string SystemsBadList {};
|
||||||
auto Statuses = Application::GetSubsystemStatuses();
|
auto Statuses = Application::GetSubsystemStatuses();
|
||||||
for (const auto& NameStatusPair : Statuses) {
|
for (const auto& NameStatusPair : Statuses) {
|
||||||
|
if (NameStatusPair.first == "Main") {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (IgnoreSubsystems.count(NameStatusPair.first) > 0) {
|
if (IgnoreSubsystems.count(NameStatusPair.first) > 0) {
|
||||||
continue; // ignore
|
continue; // ignore
|
||||||
}
|
}
|
||||||
@ -228,6 +231,8 @@ int BeamMPServerMain(MainArguments Arguments) {
|
|||||||
// remove ", "
|
// remove ", "
|
||||||
SystemsBadList = SystemsBadList.substr(0, SystemsBadList.size() - 2);
|
SystemsBadList = SystemsBadList.substr(0, SystemsBadList.size() - 2);
|
||||||
if (FullyStarted) {
|
if (FullyStarted) {
|
||||||
|
Application::SetSubsystemStatus("Main", Application::Status::Good);
|
||||||
|
|
||||||
if (!WithErrors) {
|
if (!WithErrors) {
|
||||||
beammp_info("ALL SYSTEMS STARTED SUCCESSFULLY, EVERYTHING IS OKAY");
|
beammp_info("ALL SYSTEMS STARTED SUCCESSFULLY, EVERYTHING IS OKAY");
|
||||||
} else {
|
} else {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user