add event 'onPlayerRequestMods', restructure modloading

now every player has their own list of allowed client mods, this can be modified by lua upon joining and is later used as a whitelist to ensure only those files can be sent to each client
This commit is contained in:
20dka
2022-11-14 00:10:36 +01:00
committed by Lion Kortlepel
parent 056827546e
commit 468a6b340e
10 changed files with 140 additions and 50 deletions

View File

@@ -5,31 +5,80 @@
namespace fs = std::filesystem;
std::string TResourceManager::FormatForBackend(const ModMap& mods) {
std::string monkey;
for (const auto& [name, size] : mods) {
monkey += fs::path(name).filename().string() + ';';
}
return monkey;
}
std::string TResourceManager::FormatForClient(const ModMap& mods) {
std::string monkey;
for (const auto& [name, size] : mods) {
monkey += '/' + name + ';';
}
for (const auto& [name, size] : mods) {
monkey += std::to_string(size) + ';';
}
return monkey;
}
/// @brief Sanitizes a requested mod string
/// @param pathString Raw mod path string
/// @param mods List of allowed mods for this client
/// @return Error, if any
std::optional<std::string> TResourceManager::IsModValid(std::string& pathString, const ModMap& mods) {
auto path = fs::path(pathString);
if (!path.has_filename()) {
beammp_warn("File " + pathString + " is not a file!");
return { "the requested file doesn't contain a valid filename" };
}
auto BasePath = fs::path(Application::GetSettingString(StrResourceFolder) + "/Client");
auto CombinedPath = fs::path(BasePath.string() + pathString).lexically_normal();
// beammp_infof("path: {}, base: {}, combined: {}", pathString, BasePath.string(), CombinedPath.string());
if (!std::filesystem::exists(CombinedPath)) {
beammp_warn("File " + pathString + " could not be accessed!");
return { "the requested file doesn't exist or couldn't be accessed" };
}
auto relative = fs::relative(CombinedPath, BasePath);
if (mods.count(relative.string()) == 0) {
beammp_warn("File " + pathString + " is disallowed for this player!");
return { "the requested file is disallowed for this player" };
}
pathString = CombinedPath.string();
return {};
}
TResourceManager::TResourceManager() {
Application::SetSubsystemStatus("ResourceManager", Application::Status::Starting);
std::string Path = Application::GetSettingString(StrResourceFolder) + "/Client";
if (!fs::exists(Path))
fs::create_directories(Path);
for (const auto& entry : fs::directory_iterator(Path)) {
std::string File(entry.path().string());
if (auto pos = File.find(".zip"); pos != std::string::npos) {
if (File.length() - pos == 4) {
std::replace(File.begin(), File.end(), '\\', '/');
mFileList += File + ';';
if (auto i = File.find_last_of('/'); i != std::string::npos) {
++i;
File = File.substr(i, pos - i);
}
mTrimmedList += "/" + fs::path(File).filename().string() + ';';
mFileSizes += std::to_string(size_t(fs::file_size(entry.path()))) + ';';
mMaxModSize += size_t(fs::file_size(entry.path()));
mModsLoaded++;
}
std::string BasePath = Application::GetSettingString(StrResourceFolder) + "/Client";
if (!fs::exists(BasePath))
fs::create_directories(BasePath);
std::vector<std::string> modNames;
auto iterator = fs::recursive_directory_iterator(BasePath, fs::directory_options::follow_directory_symlink | fs::directory_options::skip_permission_denied);
for (const auto& entry : iterator) {
if (iterator.depth() > 0 && !Application::GetSettingBool(StrIncludeSubdirectories))
continue;
if ((entry.is_regular_file() || entry.is_symlink()) && entry.path().extension() == ".zip") {
auto relativePath = fs::relative(entry.path(), BasePath);
beammp_infof("mod entry: {}", relativePath.string());
mMods[relativePath.string()] = entry.file_size();
mTotalModSize += entry.file_size();
}
}
if (mModsLoaded) {
beammp_info("Loaded " + std::to_string(mModsLoaded) + " Mods");
if (!mMods.empty()) {
beammp_infof("Loaded {} mod{}", mMods.size(), mMods.size() != 1 ? 's' : ' ');
}
Application::SetSubsystemStatus("ResourceManager", Application::Status::Good);