mirror of
https://github.com/BeamMP/BeamMP-Server.git
synced 2026-07-18 04:33:58 +00:00
Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 38c6766b2b | |||
| bcb035bafc | |||
| 068f553fa9 | |||
| ca11f353b0 | |||
| b7cf304d49 | |||
| 03d3b873c4 | |||
| 40c8c0c5c2 | |||
| 7db40e068e | |||
| 6053aa6192 | |||
| 0bb18de9f6 | |||
| 7a439bb5b9 |
+1
-1
@@ -127,7 +127,7 @@ 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, 3 };
|
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);
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -59,6 +59,8 @@ 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_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 +79,8 @@ 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); } },
|
||||||
};
|
};
|
||||||
|
|
||||||
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;
|
||||||
|
|||||||
@@ -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 "";
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-1
@@ -258,7 +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);
|
||||||
}
|
}
|
||||||
TryReadValue(data, "General", StrIP, EnvStrIP, Settings::Key::General_IP);
|
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);
|
||||||
@@ -309,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) + "\"");
|
||||||
|
|||||||
+38
-10
@@ -208,16 +208,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 +264,32 @@ 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_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))) {
|
||||||
|
|||||||
+10
-1
@@ -786,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");
|
||||||
@@ -808,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)) {
|
||||||
|
|||||||
+104
-4
@@ -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());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user