From fa8472b94783ba2ec3224221119e273786297e1f Mon Sep 17 00:00:00 2001 From: Qiuzhizhe <42761326+quizhizhe@users.noreply.github.com> Date: Sat, 29 Oct 2022 01:12:57 -0700 Subject: [PATCH] Support AddonsHelper --- LiteLoader/Header/MC/JsonHelpers.hpp | 15 + LiteLoader/Header/MC/ResourcePack.hpp | 34 + .../Header/MC/ResourcePackRepository.hpp | 13 +- LiteLoader/Kernel/MC/JsonHelpers.cpp | 16 + .../Kernel/MC/ResourcePackRepositoryAPI.cpp | 14 +- LiteLoader/Main/AddonsHelper.cpp | 1354 ++++++++--------- LiteLoader/Main/LiteLoader.cpp | 6 +- 7 files changed, 764 insertions(+), 688 deletions(-) create mode 100644 LiteLoader/Header/MC/JsonHelpers.hpp create mode 100644 LiteLoader/Kernel/MC/JsonHelpers.cpp diff --git a/LiteLoader/Header/MC/JsonHelpers.hpp b/LiteLoader/Header/MC/JsonHelpers.hpp new file mode 100644 index 0000000..3637320 --- /dev/null +++ b/LiteLoader/Header/MC/JsonHelpers.hpp @@ -0,0 +1,15 @@ + +#pragma once +#include "../Global.h" +#include "Json.hpp" + + +namespace JsonHelpers { +//void addObjectArrayField(class Json::Value &, std::string const &, std::vector const &); +//void addObjectField(class Json::Value &, std::string const &, class Json::Value const &); +//void addStringField(class Json::Value &, std::string const &, std::string const &); +//void addUint32Field(class Json::Value &, std::string const &, unsigned int const &); +bool parseJson(std::string const &str, class Json::Value &value); +std::string serialize(class Json::Value const &value); + +}; \ No newline at end of file diff --git a/LiteLoader/Header/MC/ResourcePack.hpp b/LiteLoader/Header/MC/ResourcePack.hpp index 6d50237..a9d4a37 100644 --- a/LiteLoader/Header/MC/ResourcePack.hpp +++ b/LiteLoader/Header/MC/ResourcePack.hpp @@ -5,6 +5,40 @@ #include "Core.hpp" #define BEFORE_EXTRA +enum class PackOrigin : int { + PackOrigin_Unknown = 0x0, + PackOrigin_RealmsUnknown = 0x1, + PackOrigin_Package = 0x2, + PackOrigin_Treatment = 0x3, + PackOrigin_Dev = 0x4, + PackOrigin_World = 0x5, + PackOrigin_User = 0x6, + PackOrigin_TempCache = 0x7, + PackOrigin_PremiumCache = 0x8, + PackOrigin_PremiumTempCache = 0x9, +}; + +enum class PackType : char { + PackType_Invalid = 0x0, + PackType_Addon = 0x1, + PackType_Cached = 0x2, + PackType_CopyProtected = 0x3, + PackType_Behavior = 0x4, + PackType_PersonaPiece = 0x5, + PackType_Resources = 0x6, + PackType_Skins = 0x7, + PackType_WorldTemplate = 0x8, + PackType_Count = 0x9, +}; + +enum class PackCategory : int { + PackCategory_Unknown = 0x0, + PackCategory_RealmsUnknown = 0x1, + PackCategory_Standard = 0x2, + PackCategory_Premium = 0x3, + PackCategory_Custom = 0x4, + PackCategory_Subpack = 0x5, +}; #undef BEFORE_EXTRA diff --git a/LiteLoader/Header/MC/ResourcePackRepository.hpp b/LiteLoader/Header/MC/ResourcePackRepository.hpp index cf830ea..8e46e49 100644 --- a/LiteLoader/Header/MC/ResourcePackRepository.hpp +++ b/LiteLoader/Header/MC/ResourcePackRepository.hpp @@ -5,13 +5,24 @@ #include "Core.hpp" #define BEFORE_EXTRA - +#include "ResourcePack.hpp" #undef BEFORE_EXTRA class ResourcePackRepository { #define AFTER_EXTRA +public: + struct KnownPackContainer { + KnownPackContainer() = delete; + KnownPackContainer(KnownPackContainer const&) = delete; + KnownPackContainer(KnownPackContainer const&&) = delete; + }; + LIAPI void setCustomResourcePackPath(PackType, const std::string& path); + inline class PackSourceFactory & getPackSourceFactory(){ + //ResourcePackRepository::_initializeWorldPackSource Line62 + return dAccess(this, 46 * 8); + }; #undef AFTER_EXTRA #ifndef DISABLE_CONSTRUCTOR_PREVENTION_RESOURCEPACKREPOSITORY public: diff --git a/LiteLoader/Kernel/MC/JsonHelpers.cpp b/LiteLoader/Kernel/MC/JsonHelpers.cpp new file mode 100644 index 0000000..e22018a --- /dev/null +++ b/LiteLoader/Kernel/MC/JsonHelpers.cpp @@ -0,0 +1,16 @@ +// +// Created by User on 2022/10/29. +// +#include "MC/JsonHelpers.hpp" + +bool JsonHelpers::parseJson(std::string const &str, class Json::Value &value){ + auto jsonRead = new Json::Reader(); + jsonRead->parse(str,value); + return true; +}; + +std::string JsonHelpers::serialize(class Json::Value const &value){ + if(value.empty()) + return ""; + return value.toStyledString(); +}; \ No newline at end of file diff --git a/LiteLoader/Kernel/MC/ResourcePackRepositoryAPI.cpp b/LiteLoader/Kernel/MC/ResourcePackRepositoryAPI.cpp index 6514031..0f147d4 100644 --- a/LiteLoader/Kernel/MC/ResourcePackRepositoryAPI.cpp +++ b/LiteLoader/Kernel/MC/ResourcePackRepositoryAPI.cpp @@ -4,10 +4,10 @@ #include #include -// void ResourcePackRepository::setCustomResourcePackPath(PackType type, const std::string& path) { -// auto CompositePack = dAccess(this, 48); -// auto& PackSourceFactory = getPackSourceFactory(); -// auto& DirectoryPackSource = PackSourceFactory.createDirectoryPackSource(Core::Path(path), type, PackOrigin::PackOrigin_Dev, 0); -// CompositePack->addPackSource((PackSource*)&DirectoryPackSource); -// refreshPacks(); -// } \ No newline at end of file + void ResourcePackRepository::setCustomResourcePackPath(PackType type, const std::string& path) { + auto CompositePack = dAccess(this, 48); + auto& PackSourceFactory = getPackSourceFactory(); + auto& DirectoryPackSource = PackSourceFactory.createDirectoryPackSource(Core::Path(path), type, PackOrigin::PackOrigin_Dev, 0); + CompositePack->addPackSource((PackSource*)&DirectoryPackSource); + refreshPacks(); + } \ No newline at end of file diff --git a/LiteLoader/Main/AddonsHelper.cpp b/LiteLoader/Main/AddonsHelper.cpp index d4c5484..9d5274a 100644 --- a/LiteLoader/Main/AddonsHelper.cpp +++ b/LiteLoader/Main/AddonsHelper.cpp @@ -1,677 +1,677 @@ -//#include "AddonsHelper.h" -//#include
-//#include -//#include -//#include -//#include -//#include -//#include -//#include -//#include -//#include -//#include -//#include -//#include -//#include -//#include
-//#include -//#include -//#include -//#include -//#include -//#include -//#include -//using namespace std; -//using namespace RegisterCommandHelper; -// -//Logger addonLogger("AddonHelper"); -//std::vector addons; -// -//bool AutoInstallAddons(string path); -// -//// Helper -//std::string GetAddonJsonFile(Addon::Type type) { -// string addonListFile = Level::getCurrentLevelPath(); -// switch (type) { -// case Addon::Type::BehaviorPack: -// return addonListFile + "/world_behavior_packs.json"; -// break; -// case Addon::Type::ResourcePack: -// return addonListFile + "/world_resource_packs.json"; -// break; -// default: -// break; -// } -// return ""; -//} -// -//inline bool isManifestFile(std::string const& filename) { -// return filename == "manifest.json" || filename == "pack_manifest.json"; -//} -// -//#include -//inline std::string FixMojangJson(std::string const& content) { -// Json::Value value; -// JsonHelpers::parseJson(content, value); -// return JsonHelpers::serialize(value); -//} -// -//std::optional parseAddonFromPath(std::filesystem::path addonPath) { -// try { -// auto manifestPath = addonPath; -// manifestPath.append("manifest.json"); -// if (!filesystem::exists(manifestPath)) { -// manifestPath = addonPath; -// manifestPath.append("pack_manifest.json"); -// } -// auto manifestFile = ReadAllFile(UTF82String(manifestPath.u8string())); -// if (!manifestFile || manifestFile->empty()) -// throw std::exception("manifest.json not found!"); -// std::string content = FixMojangJson(*manifestFile); -// -// auto manifest = nlohmann::json::parse(content, nullptr, true, true); -// auto header = manifest["header"]; -// auto uuid = header["uuid"]; -// Addon addon; -// addon.name = header["name"]; -// addon.description = header["description"]; -// addon.uuid = uuid; -// addon.directory = UTF82String(addonPath.u8string()); -// -// auto ver = header["version"]; -// addon.version = LL::Version(ver[0], ver[1], ver[2]); -// -// string type = manifest["modules"][0]["type"]; -// if (type == "resources") -// addon.type = Addon::Type::ResourcePack; -// else if (type == "data" || type == "script") -// addon.type = Addon::Type::BehaviorPack; -// else -// throw std::exception("Unknown type of addon pack!"); -// -// return addon; -// } catch (const seh_exception& e) { -// addonLogger.error("Uncaught SEH Exception Detected!"); -// addonLogger.error("In " __FUNCTION__ " " + UTF82String(addonPath.u8string())); -// addonLogger.error("Error: Code[{}] {}", e.code(), TextEncoding::toUTF8(e.what())); -// } catch (const std::exception& e) { -// addonLogger.error("Uncaught C++ Exception Detected!"); -// addonLogger.error("In " __FUNCTION__ " " + UTF82String(addonPath.u8string())); -// addonLogger.error("Error: Code[{}] {}", -1, TextEncoding::toUTF8(e.what())); -// } catch (...) { -// addonLogger.error("Uncaught Exception Detected!"); -// addonLogger.error("In " __FUNCTION__ " " + UTF82String(addonPath.u8string())); -// } -// return std::nullopt; -//} -// -//bool RemoveAddonFromList(Addon& addon) { -// auto jsonFile = GetAddonJsonFile(addon.type); -// if (!filesystem::exists(str2wstr(jsonFile))) { -// addonLogger.error(tr("ll.addonsHelper.error.addonConfigNotFound")); -// return false; -// } -// -// auto addonJsonContent = ReadAllFile(jsonFile); -// if (!addonJsonContent || addonJsonContent->empty()) { -// addonLogger.error(tr("ll.addonsHelper.error.addonConfigNotFound")); -// return false; -// } -// auto addonJson = fifo_json::parse(*addonJsonContent, nullptr, true, true); -// int id = 0; -// for (auto item : addonJson) { -// if (item["pack_id"] == addon.uuid) { -// addonJson.erase(id); -// bool res = WriteAllFile(jsonFile, addonJson.dump(4)); -// if (!res) { -// addonLogger.error(tr("ll.addonsHelper.removeAddonFromList.fail", addon.getPrintName())); -// return false; -// } -// addonLogger.info(tr("ll.addonsHelper.removeAddonFromList.success", addon.getPrintName())); -// return true; -// } -// ++id; -// } -// addonLogger.error(tr("ll.addonsHelper.error.addonNotFound", addon.getPrintName())); -// return false; -//} -// -//bool AddAddonToList(Addon& addon) { -// string addonListFile = GetAddonJsonFile(addon.type); -// if (!filesystem::exists(str2wstr(addonListFile))) { -// ofstream fout(addonListFile); -// fout << "[]" << flush; -// } -// -// try { -// -// bool exists = false; -// auto addonList = nlohmann::json::parse(*ReadAllFile(addonListFile), nullptr, false, true); -// // Auto fix Addon List File -// if (!addonList.is_array()) { -// auto backupPath = UTF82String(filesystem::path(str2wstr(addonListFile)).stem().u8string()) + "_error.json"; -// addonLogger.error(tr("ll.addonsHelper.addAddonToList.invalidList", addonListFile, backupPath)); -// std::error_code ec; -// std::filesystem::rename(str2wstr(addonListFile), filesystem::path(addonListFile).remove_filename().append(str2wstr(backupPath)), ec); -// addonList = "[]"_json; -// } -// for (auto& addonData : addonList) { -// if (addonData["pack_id"] == addon.uuid) { -// addonData["version"] = {addon.version.major, addon.version.minor, addon.version.revision}; -// exists = true; -// break; -// } -// } -// if (!exists) { -// auto newAddonData = nlohmann::json::object(); -// newAddonData["pack_id"] = addon.uuid; -// newAddonData["version"] = {addon.version.major, addon.version.minor, addon.version.revision}; -// addonList.push_back(newAddonData); -// } -// bool res = WriteAllFile(addonListFile, addonList.dump(4)); -// if (!res) -// throw std::exception("Fail to write data back to addon list file!"); -// addonLogger.info(tr("ll.addonsHelper.addAddonToList.success", addon.getPrintName())); -// return true; -// } catch (const std::exception& e) { -// addonLogger.error(tr("ll.addonsHelper.addAddonToList.fail", addon.getPrintName(), addonListFile)); -// addonLogger.error(tr("ll.addonsHelper.displayError", TextEncoding::toUTF8(e.what()))); -// addonLogger.error(tr("ll.addonsHelper.error.installationAborted")); -// return false; -// } -//} -//bool InstallAddonToLevel(std::string addonDir, std::string addonName) { -// auto addon = parseAddonFromPath(str2wstr(addonDir)); -// if (!addon.has_value()) -// return false; -// std::string subPath; -// if (addon->type == Addon::Type::ResourcePack) -// subPath = "/resource_packs"; -// else if (addon->type == Addon::Type::BehaviorPack) -// subPath = "/behavior_packs"; -// -// -// // copy files -// string levelPath = Level::getCurrentLevelPath(); -// string toPath = levelPath + subPath + "/" + addonName; -// -// // Avoid duplicate names or update addon if same uuid -// while (filesystem::exists(str2wstr(toPath))) { -// auto tmp = parseAddonFromPath(str2wstr(toPath)); -// if (tmp.has_value() && tmp->uuid != addon->uuid) { -// toPath += "_"; -// } else { -// std::error_code ec; -// filesystem::remove_all(str2wstr(toPath), ec); -// break; -// } -// } -// std::error_code ec; -// filesystem::create_directories(str2wstr(toPath), ec); -// filesystem::copy(str2wstr(addonDir), str2wstr(toPath), filesystem::copy_options::recursive, ec); -// -// // add addon to list file -// return AddAddonToList(*addon); -//} -// -//void FindManifest(vector& result, const string& path) { -// filesystem::directory_iterator ent(str2wstr(path)); -// -// bool foundManifest = false; -// for (auto& file : ent) { -// auto path = file.path(); -// if (isManifestFile(UTF82String(path.filename().u8string()))) { -// result.push_back(UTF82String(filesystem::canonical(path).parent_path().u8string())); -// foundManifest = true; -// break; -// } -// } -// if (!foundManifest) { -// // No manifest file -// if (!AutoInstallAddons(path)) { -// filesystem::directory_iterator ent2(str2wstr(path)); -// for (auto& file : ent2) -// if (file.is_directory()) -// FindManifest(result, UTF82String(file.path().u8string())); -// } -// } -// return; -//} -// -//std::string Addon::getPrintName() const { -// if (LL::globalConfig.colorLog) -// return ColorFormat::convertToConsole(std::string(name)); -// else -// return ColorFormat::removeColorCode(std::string(name)); -//} -// -//bool AddonsManager::install(std::string packPath) { -// try { -// if (!filesystem::exists(str2wstr(packPath))) { -// addonLogger.error(tr("ll.addonsHelper.error.addonFileNotFound", packPath)); -// return false; -// } -// if (VALID_ADDON_FILE_EXTENSION.find(UTF82String(filesystem::path(str2wstr(packPath)).extension().u8string())) == VALID_ADDON_FILE_EXTENSION.end()) { -// addonLogger.error(tr("ll.addonsHelper.error.unsupportedFileType")); -// return false; -// } -// -// string name = UTF82String(filesystem::path(str2wstr(packPath)).filename().u8string()); -// addonLogger.warn(tr("ll.addonsHelper.install.installing", name)); -// -// std::error_code ec; -// if (EndsWith(packPath, ".mcpack")) { -// string newPath = packPath; -// ReplaceStr(newPath, ".mcpack", ".zip"); -// filesystem::rename(str2wstr(packPath), str2wstr(newPath), ec); -// packPath = newPath; -// } -// if (EndsWith(packPath, ".mcaddon")) { -// string newPath = packPath; -// ReplaceStr(newPath, ".mcaddon", ".zip"); -// filesystem::rename(str2wstr(packPath), str2wstr(newPath), ec); -// packPath = newPath; -// } -// -// name = UTF82String(filesystem::path(str2wstr(packPath)).filename().u8string()); -// -// // filesystem::remove_all(ADDON_INSTALL_TEMP_DIR + name + "/", ec); //? -// // filesystem::create_directories(ADDON_INSTALL_TEMP_DIR + name + "/", ec); -// -// auto res = NewProcessSync(fmt::format("{} x \"{}\" -o{} -aoa", ZIP_PROGRAM_PATH, packPath, "\"" ADDON_INSTALL_TEMP_DIR + name + "/\""), ADDON_INSTALL_MAX_WAIT); -// if (res.first != 0) { -// addonLogger.error(tr("ll.addonsHelper.install.error.failToUncompress.msg", name)); -// addonLogger.error(tr("ll.addonsHelper.install.error.failToUncompress.exitCode"), res.first); -// addonLogger.error(tr("ll.addonsHelper.install.error.failToUncompress.programOutput"), res.second); -// addonLogger.error(tr("ll.addonsHelper.error.installationAborted")); -// filesystem::remove_all(ADDON_INSTALL_TEMP_DIR + name + "/", ec); -// return false; -// } -// -// vector paths; -// FindManifest(paths, ADDON_INSTALL_TEMP_DIR + name + "/"); -// -// for (auto& dir : paths) { -// string addonName = UTF82String(filesystem::path(str2wstr(dir)).filename().u8string()); -// if (addonName.empty() || addonName == "Temp") -// addonName = UTF82String(filesystem::path(str2wstr(packPath)).stem().u8string()); -// if (!InstallAddonToLevel(dir, addonName)) -// throw std::exception("Error in Install Addon To Level "); -// } -// -// filesystem::remove_all(ADDON_INSTALL_TEMP_DIR + name + "/", ec); -// filesystem::remove_all(str2wstr(packPath), ec); -// return true; -// } catch (const seh_exception& e) { -// addonLogger.error("Uncaught SEH Exception Detected!"); -// addonLogger.error("In " __FUNCTION__); -// addonLogger.error("Error: Code[{}] {}", e.code(), TextEncoding::toUTF8(e.what())); -// } catch (const std::exception& e) { -// addonLogger.error("Uncaught C++ Exception Detected!"); -// addonLogger.error("In " __FUNCTION__); -// addonLogger.error("Error: Code[{}] {}", -1, TextEncoding::toUTF8(e.what())); -// } catch (...) { -// addonLogger.error("Uncaught Exception Detected!"); -// addonLogger.error("In " __FUNCTION__); -// } -// return false; -//} -// -//bool AddonsManager::disable(std::string nameOrUuid) { -// try { -// auto addon = findAddon(nameOrUuid, true); -// if (!addon) -// return false; -// if (RemoveAddonFromList(*addon)) { -// addon->enable = false; -// return true; -// } -// -// } catch (...) {} -// return false; -//} -// -//bool AddonsManager::enable(std::string nameOrUuid) { -// try { -// auto addon = findAddon(nameOrUuid, true); -// if (!addon) -// return false; -// if (AddAddonToList(*addon)) { -// addon->enable = true; -// return true; -// } -// -// } catch (...) {} -// return false; -//} -// -//bool AddonsManager::uninstall(std::string nameOrUuid) { -// try { -// auto addon = findAddon(nameOrUuid, true); -// if (!addon) { -// addonLogger.error(tr("ll.addonsHelper.error.addonNotFound")); -// return false; -// } -// RemoveAddonFromList(*addon); -// std::error_code ec; -// filesystem::remove_all(str2wstr(addon->directory), ec); -// for (auto i = addons.begin(); i != addons.end(); ++i) -// if (i->uuid == addon->uuid) { -// addons.erase(i); -// break; -// } -// addonLogger.info(tr("ll.addonsHelper.uninstall.success", addon->getPrintName())); -// } catch (...) {} -// return false; -//} -// -//std::vector AddonsManager::getAllAddons() { -// std::vector res; -// for (auto& addon : addons) -// res.push_back(&addon); -// return res; -//} -// -//Addon* AddonsManager::findAddon(std::string nameOrUuid, bool fuzzy) { -// Addon* possible = nullptr; -// bool multiMatch = false; -// for (auto& addon : addons) { -// if (addon.uuid == nameOrUuid) -// return &addon; -// std::string addonName = addon.name; -// std::string targetName = nameOrUuid; -// if (ColorFormat::removeColorCode(addonName) == ColorFormat::removeColorCode(targetName)) -// return &addon; -// if (!fuzzy) -// continue; -// // Simple fuzzy matching -// std::transform(addonName.begin(), addonName.end(), addonName.begin(), ::tolower); -// std::transform(targetName.begin(), targetName.end(), targetName.begin(), ::tolower); -// if (StartsWith(addonName, targetName)) { -// if (possible) -// multiMatch = true; -// else -// possible = &addon; -// } -// } -// if (multiMatch) -// return nullptr; -// else -// return possible; -//} -// -//void ListAllAddons(CommandOutput& output) { -// if (addons.empty()) { -// output.trSuccess("ll.addonsHelper.error.noAddonInstalled"); -// return; -// } -// -// output.trSuccess("ll.addonsHelper.cmd.output.list.overview", addons.size()); -// for (auto index = 0; index < addons.size(); ++index) { -// auto& addon = addons[index]; -// string addonName = addon.name; -// if (addonName.find("§") == string::npos) -// addonName = "§b" + addonName; -// string desc = addon.description; -// if (desc.find("§") == string::npos) -// desc = "§7" + desc; -// -// std::string addonType = (addon.type == Addon::Type::ResourcePack ? "ResourcePack" : "BehaviorPack"); -// if (addon.enable) { -// output.success(fmt::format("§e{:>2}§r: {} §a[v{}] §8({})", index + 1, addonName, addon.version.toString(), addonType)); -// output.success(fmt::format(" {}", desc)); -// } else { -// output.success(fmt::format("§e{:>2}§r: §8{} [v{}] ({})", index + 1, ColorFormat::removeColorCode(addonName), addon.version.toString(), addonType)); -// output.success(fmt::format(" §8Disabled")); -// } -// } -//} -// -//void ShowAddon(CommandOutput& output, Addon& addon) { -// std::ostringstream oss; -// oss << "Addon <" << addon.name << "§r>" << (addon.enable ? " §aEnabled" : " §cDisabled") << "\n\n"; -// oss << "- §aName§r: " << addon.name << "\n"; -// oss << "- §aUUID§r: " << addon.uuid << "\n"; -// oss << "- §aDescription§r: " << addon.description << "\n"; -// oss << "- §aVersion§r: v" << addon.version.toString(true) << "\n"; -// oss << "- §aType§r: " << magic_enum::enum_name(addon.type) << "\n"; -// oss << "- §aDirectory§r: " << addon.directory << "\n"; -// output.success(oss.str()); -//} -// -//class AddonsCommand : public Command { -// enum class Operation { -// List, -// Install, -// Uninstall, -// Enable, -// Disable -// }; -// -// Operation operation = static_cast(-1); -// std::string target; -// int index = -1; -// bool target_isSet = false; -// bool index_isSet = false; -// -// inline Addon* getSelectedAddon(CommandOutput& output) const { -// Addon* addon = nullptr; -// if (target_isSet) { -// auto addon = AddonsManager::findAddon(target, true); -// if (addon) { -// return addon; -// } else { -// output.trError("ll.addonsHelper.error.addonNotfound", target); -// } -// } else { -// auto addons = AddonsManager::getAllAddons(); -// if (index - 1 >= 0 && index - 1 < static_cast(addons.size())) -// return addons[index - 1]; -// else -// output.trError("ll.addonsHelper.error.outOfRange", index); -// } -// return nullptr; -// } -// -//public: -// void execute(CommandOrigin const& ori, CommandOutput& output) const override { -// output.setLanguageCode(ori); -// switch (operation) { -// case Operation::List: -// if (target_isSet || index_isSet) { -// if (auto addon = getSelectedAddon(output)) -// ShowAddon(output, *addon); -// } else -// ListAllAddons(output); -// break; -// case Operation::Install: -// if (AddonsManager::install(target)) -// filesystem::remove_all(ADDON_INSTALL_TEMP_DIR); -// output.success(); -// break; -// case Operation::Uninstall: { -// auto addon = getSelectedAddon(output); -// if (addon && AddonsManager::uninstall(addon->uuid)) -// output.success(); -// break; -// } -// case Operation::Enable: { -// auto addon = getSelectedAddon(output); -// if (addon && AddonsManager::enable(addon->uuid)) -// output.success(); -// break; -// } -// case Operation::Disable: { -// auto addon = getSelectedAddon(output); -// if (addon && AddonsManager::disable(addon->uuid)) -// output.success(); -// break; -// } -// default: -// break; -// } -// } -// -// static void setup(CommandRegistry* registry) { -// registry->registerCommand("addons", "LiteLoaderBDS Addons Helper (Restart required after addon changes)", -// CommandPermissionLevel::GameMasters, {(CommandFlagValue)0}, {(CommandFlagValue)0x80}); -// -// vector addonsList; -// for (auto& addon : addons) -// addonsList.push_back(addon.name); -// registry->addSoftEnum("AddonName", addonsList); -// -// // addons list -// registry->addEnum("Operation_Addons_List", {{"list", Operation::List}}); -// registry->registerOverload( -// "addons", -// makeMandatory(&AddonsCommand::operation, "operation", "Operation_Addons_List").addOptions((CommandParameterOption)1), -// makeOptional(&AddonsCommand::target, "addonName", "AddonName", &AddonsCommand::target_isSet)); -// registry->registerOverload( -// "addons", -// makeMandatory(&AddonsCommand::operation, "operation", "Operation_Addons_List").addOptions((CommandParameterOption)1), -// makeOptional(&AddonsCommand::index, "addonIndex", nullptr, &AddonsCommand::index_isSet)); -// -// // addons install -// registry->addEnum("Operation_Addons_Install", {{"install", Operation::Install}}); -// registry->registerOverload( -// "addons", -// makeMandatory(&AddonsCommand::operation, "operation", "Operation_Addons_Install").addOptions((CommandParameterOption)1), -// makeMandatory(&AddonsCommand::target, "addonName")); -// -// // addons uninstall -// -// registry->addEnum("Operation_Addons_Others", { -// {"uninstall", Operation::Uninstall}, -// {"remove", Operation::Uninstall}, -// {"enable", Operation::Enable}, -// {"disable", Operation::Disable}, -// }); -// registry->registerOverload( -// "addons", -// makeMandatory(&AddonsCommand::operation, "operation", "Operation_Addons_Others").addOptions((CommandParameterOption)1), -// makeMandatory(&AddonsCommand::target, "addonName", "AddonName", &AddonsCommand::target_isSet)); -// registry->registerOverload( -// "addons", -// makeMandatory(&AddonsCommand::operation, "operation", "Operation_Addons_Others").addOptions((CommandParameterOption)1), -// makeMandatory(&AddonsCommand::index, "addonIndex", nullptr, &AddonsCommand::index_isSet)); -// } -//}; -// -//void FindAddons(string jsonPath, string packsDir) { -// try { -// if (!filesystem::exists(str2wstr(jsonPath)) && !filesystem::exists(str2wstr(packsDir))) -// return; -// if (!filesystem::exists(str2wstr(jsonPath))) -// WriteAllFile(jsonPath, "[]"); -// if (!filesystem::exists(str2wstr(packsDir))) -// filesystem::create_directories(str2wstr(packsDir)); -// -// auto content = ReadAllFile(jsonPath); -// if (!content || content->empty()) { -// WriteAllFile(jsonPath, "[]"); -// content = "[]"; -// } -// std::set validPackIDs; -// try { -// auto addonList = nlohmann::json::parse(*content, nullptr, true, true); -// for (auto addon : addonList) { -// std::string pktid = addon["pack_id"]; -// validPackIDs.insert(pktid); -// } -// } catch (const std::exception&) { -// addonLogger.error(tr("ll.addonsHelper.error.parsingEnabledAddonsList")); -// } -// -// filesystem::directory_iterator ent(str2wstr(packsDir)); -// for (auto& dir : ent) { -// if (!dir.is_directory()) -// continue; -// auto addon = parseAddonFromPath(dir); -// if (!addon) -// continue; -// if (validPackIDs.find(addon->uuid) != validPackIDs.end()) -// addon->enable = true; -// addons.emplace_back(std::move(*addon)); -// } -// } catch (...) { -// return; -// } -//} -// -//void BuildAddonsList() { -// string levelPath = Level::getCurrentLevelPath(); -// -// FindAddons(levelPath + "/world_behavior_packs.json", levelPath + "/behavior_packs"); -// FindAddons(levelPath + "/world_resource_packs.json", levelPath + "/resource_packs"); -// -// std::sort(addons.begin(), addons.end(), -// [](Addon const& _Left, Addon const& _Right) { -// if (_Left.enable && !_Right.enable) -// return true; -// if (_Left.type == Addon::Type::ResourcePack && _Right.type == Addon::Type::BehaviorPack) -// return true; -// return false; -// }); -//} -// -//bool AutoInstallAddons(string path) { -// std::error_code ec; -// if (!filesystem::exists(str2wstr(path))) { -// filesystem::create_directories(str2wstr(path), ec); -// addonLogger.info(tr("ll.addonsHelper.autoInstall.tip.dirCreated", LL::globalConfig.addonsInstallPath)); -// return false; -// } -// std::vector toInstallList; -// -// filesystem::directory_iterator ent(str2wstr(path)); -// for (auto& file : ent) { -// if (!file.is_regular_file()) -// continue; -// -// if (VALID_ADDON_FILE_EXTENSION.count(UTF82String(file.path().extension().u8string())) > 0) { -// toInstallList.push_back(UTF82String(file.path().lexically_normal().u8string())); -// } -// } -// -// if (toInstallList.empty()) -// return false; -// -// addonLogger.info(tr("ll.addonsHelper.autoInstall.working", toInstallList.size())); -// int cnt = 0; -// for (auto& path : toInstallList) { -// addonLogger.debug("Installing \"{}\"...", path); -// if (!AddonsManager::install(path)) { -// // filesystem::remove_all(ADDON_INSTALL_TEMP_DIR, ec); -// break; -// } else { -// ++cnt; -// addonLogger.info(tr("ll.addonsHelper.autoInstall.installed", path)); -// } -// } -// -// if (cnt == 0) { -// addonLogger.error(tr("ll.addonsHelper.error.noAddonInstalled")); -// } else { -// addonLogger.info(tr("ll.addonsHelper.autoInstall.installedCount", cnt)); -// } -// return true; -//} -// -//void InitAddonsHelper() { -// if (LL::isDebugMode()) -// addonLogger.consoleLevel = addonLogger.debug.level; -// -// filesystem::remove_all(ADDON_INSTALL_TEMP_DIR); -// filesystem::create_directories(ADDON_INSTALL_TEMP_DIR); -// -// AutoInstallAddons(LL::globalConfig.addonsInstallPath); -// BuildAddonsList(); -// -// filesystem::remove_all(ADDON_INSTALL_TEMP_DIR); -// -// Event::RegCmdEvent::subscribe([](Event::RegCmdEvent ev) { // Register commands -// AddonsCommand::setup(ev.mCommandRegistry); -// return true; -// }); -//} \ No newline at end of file +#include "AddonsHelper.h" +#include
+#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include
+#include +#include +#include +#include +#include +#include +#include +using namespace std; +using namespace RegisterCommandHelper; + +Logger addonLogger("AddonHelper"); +std::vector addons; + +bool AutoInstallAddons(string path); + +// Helper +std::string GetAddonJsonFile(Addon::Type type) { + string addonListFile = Level::getCurrentLevelPath(); + switch (type) { + case Addon::Type::BehaviorPack: + return addonListFile + "/world_behavior_packs.json"; + break; + case Addon::Type::ResourcePack: + return addonListFile + "/world_resource_packs.json"; + break; + default: + break; + } + return ""; +} + +inline bool isManifestFile(std::string const& filename) { + return filename == "manifest.json" || filename == "pack_manifest.json"; +} + +#include +inline std::string FixMojangJson(std::string const& content) { + Json::Value value; + JsonHelpers::parseJson(content, value); + return JsonHelpers::serialize(value); +} + +std::optional parseAddonFromPath(std::filesystem::path addonPath) { + try { + auto manifestPath = addonPath; + manifestPath.append("manifest.json"); + if (!filesystem::exists(manifestPath)) { + manifestPath = addonPath; + manifestPath.append("pack_manifest.json"); + } + auto manifestFile = ReadAllFile(UTF82String(manifestPath.u8string())); + if (!manifestFile || manifestFile->empty()) + throw std::exception("manifest.json not found!"); + std::string content = FixMojangJson(*manifestFile); + + auto manifest = nlohmann::json::parse(content, nullptr, true, true); + auto header = manifest["header"]; + auto uuid = header["uuid"]; + Addon addon; + addon.name = header["name"]; + addon.description = header["description"]; + addon.uuid = uuid; + addon.directory = UTF82String(addonPath.u8string()); + + auto ver = header["version"]; + addon.version = LL::Version(ver[0], ver[1], ver[2]); + + string type = manifest["modules"][0]["type"]; + if (type == "resources") + addon.type = Addon::Type::ResourcePack; + else if (type == "data" || type == "script") + addon.type = Addon::Type::BehaviorPack; + else + throw std::exception("Unknown type of addon pack!"); + + return addon; + } catch (const seh_exception& e) { + addonLogger.error("Uncaught SEH Exception Detected!"); + addonLogger.error("In " __FUNCTION__ " " + UTF82String(addonPath.u8string())); + addonLogger.error("Error: Code[{}] {}", e.code(), TextEncoding::toUTF8(e.what())); + } catch (const std::exception& e) { + addonLogger.error("Uncaught C++ Exception Detected!"); + addonLogger.error("In " __FUNCTION__ " " + UTF82String(addonPath.u8string())); + addonLogger.error("Error: Code[{}] {}", -1, TextEncoding::toUTF8(e.what())); + } catch (...) { + addonLogger.error("Uncaught Exception Detected!"); + addonLogger.error("In " __FUNCTION__ " " + UTF82String(addonPath.u8string())); + } + return std::nullopt; +} + +bool RemoveAddonFromList(Addon& addon) { + auto jsonFile = GetAddonJsonFile(addon.type); + if (!filesystem::exists(str2wstr(jsonFile))) { + addonLogger.error(tr("ll.addonsHelper.error.addonConfigNotFound")); + return false; + } + + auto addonJsonContent = ReadAllFile(jsonFile); + if (!addonJsonContent || addonJsonContent->empty()) { + addonLogger.error(tr("ll.addonsHelper.error.addonConfigNotFound")); + return false; + } + auto addonJson = fifo_json::parse(*addonJsonContent, nullptr, true, true); + int id = 0; + for (auto item : addonJson) { + if (item["pack_id"] == addon.uuid) { + addonJson.erase(id); + bool res = WriteAllFile(jsonFile, addonJson.dump(4)); + if (!res) { + addonLogger.error(tr("ll.addonsHelper.removeAddonFromList.fail", addon.getPrintName())); + return false; + } + addonLogger.info(tr("ll.addonsHelper.removeAddonFromList.success", addon.getPrintName())); + return true; + } + ++id; + } + addonLogger.error(tr("ll.addonsHelper.error.addonNotFound", addon.getPrintName())); + return false; +} + +bool AddAddonToList(Addon& addon) { + string addonListFile = GetAddonJsonFile(addon.type); + if (!filesystem::exists(str2wstr(addonListFile))) { + ofstream fout(addonListFile); + fout << "[]" << flush; + } + + try { + + bool exists = false; + auto addonList = nlohmann::json::parse(*ReadAllFile(addonListFile), nullptr, false, true); + // Auto fix Addon List File + if (!addonList.is_array()) { + auto backupPath = UTF82String(filesystem::path(str2wstr(addonListFile)).stem().u8string()) + "_error.json"; + addonLogger.error(tr("ll.addonsHelper.addAddonToList.invalidList", addonListFile, backupPath)); + std::error_code ec; + std::filesystem::rename(str2wstr(addonListFile), filesystem::path(addonListFile).remove_filename().append(str2wstr(backupPath)), ec); + addonList = "[]"_json; + } + for (auto& addonData : addonList) { + if (addonData["pack_id"] == addon.uuid) { + addonData["version"] = {addon.version.major, addon.version.minor, addon.version.revision}; + exists = true; + break; + } + } + if (!exists) { + auto newAddonData = nlohmann::json::object(); + newAddonData["pack_id"] = addon.uuid; + newAddonData["version"] = {addon.version.major, addon.version.minor, addon.version.revision}; + addonList.push_back(newAddonData); + } + bool res = WriteAllFile(addonListFile, addonList.dump(4)); + if (!res) + throw std::exception("Fail to write data back to addon list file!"); + addonLogger.info(tr("ll.addonsHelper.addAddonToList.success", addon.getPrintName())); + return true; + } catch (const std::exception& e) { + addonLogger.error(tr("ll.addonsHelper.addAddonToList.fail", addon.getPrintName(), addonListFile)); + addonLogger.error(tr("ll.addonsHelper.displayError", TextEncoding::toUTF8(e.what()))); + addonLogger.error(tr("ll.addonsHelper.error.installationAborted")); + return false; + } +} +bool InstallAddonToLevel(std::string addonDir, std::string addonName) { + auto addon = parseAddonFromPath(str2wstr(addonDir)); + if (!addon.has_value()) + return false; + std::string subPath; + if (addon->type == Addon::Type::ResourcePack) + subPath = "/resource_packs"; + else if (addon->type == Addon::Type::BehaviorPack) + subPath = "/behavior_packs"; + + + // copy files + string levelPath = Level::getCurrentLevelPath(); + string toPath = levelPath + subPath + "/" + addonName; + + // Avoid duplicate names or update addon if same uuid + while (filesystem::exists(str2wstr(toPath))) { + auto tmp = parseAddonFromPath(str2wstr(toPath)); + if (tmp.has_value() && tmp->uuid != addon->uuid) { + toPath += "_"; + } else { + std::error_code ec; + filesystem::remove_all(str2wstr(toPath), ec); + break; + } + } + std::error_code ec; + filesystem::create_directories(str2wstr(toPath), ec); + filesystem::copy(str2wstr(addonDir), str2wstr(toPath), filesystem::copy_options::recursive, ec); + + // add addon to list file + return AddAddonToList(*addon); +} + +void FindManifest(vector& result, const string& path) { + filesystem::directory_iterator ent(str2wstr(path)); + + bool foundManifest = false; + for (auto& file : ent) { + auto path = file.path(); + if (isManifestFile(UTF82String(path.filename().u8string()))) { + result.push_back(UTF82String(filesystem::canonical(path).parent_path().u8string())); + foundManifest = true; + break; + } + } + if (!foundManifest) { + // No manifest file + if (!AutoInstallAddons(path)) { + filesystem::directory_iterator ent2(str2wstr(path)); + for (auto& file : ent2) + if (file.is_directory()) + FindManifest(result, UTF82String(file.path().u8string())); + } + } + return; +} + +std::string Addon::getPrintName() const { + if (LL::globalConfig.colorLog) + return ColorFormat::convertToConsole(std::string(name)); + else + return ColorFormat::removeColorCode(std::string(name)); +} + +bool AddonsManager::install(std::string packPath) { + try { + if (!filesystem::exists(str2wstr(packPath))) { + addonLogger.error(tr("ll.addonsHelper.error.addonFileNotFound", packPath)); + return false; + } + if (VALID_ADDON_FILE_EXTENSION.find(UTF82String(filesystem::path(str2wstr(packPath)).extension().u8string())) == VALID_ADDON_FILE_EXTENSION.end()) { + addonLogger.error(tr("ll.addonsHelper.error.unsupportedFileType")); + return false; + } + + string name = UTF82String(filesystem::path(str2wstr(packPath)).filename().u8string()); + addonLogger.warn(tr("ll.addonsHelper.install.installing", name)); + + std::error_code ec; + if (EndsWith(packPath, ".mcpack")) { + string newPath = packPath; + ReplaceStr(newPath, ".mcpack", ".zip"); + filesystem::rename(str2wstr(packPath), str2wstr(newPath), ec); + packPath = newPath; + } + if (EndsWith(packPath, ".mcaddon")) { + string newPath = packPath; + ReplaceStr(newPath, ".mcaddon", ".zip"); + filesystem::rename(str2wstr(packPath), str2wstr(newPath), ec); + packPath = newPath; + } + + name = UTF82String(filesystem::path(str2wstr(packPath)).filename().u8string()); + + // filesystem::remove_all(ADDON_INSTALL_TEMP_DIR + name + "/", ec); //? + // filesystem::create_directories(ADDON_INSTALL_TEMP_DIR + name + "/", ec); + + auto res = NewProcessSync(fmt::format("{} x \"{}\" -o{} -aoa", ZIP_PROGRAM_PATH, packPath, "\"" ADDON_INSTALL_TEMP_DIR + name + "/\""), ADDON_INSTALL_MAX_WAIT); + if (res.first != 0) { + addonLogger.error(tr("ll.addonsHelper.install.error.failToUncompress.msg", name)); + addonLogger.error(tr("ll.addonsHelper.install.error.failToUncompress.exitCode"), res.first); + addonLogger.error(tr("ll.addonsHelper.install.error.failToUncompress.programOutput"), res.second); + addonLogger.error(tr("ll.addonsHelper.error.installationAborted")); + filesystem::remove_all(ADDON_INSTALL_TEMP_DIR + name + "/", ec); + return false; + } + + vector paths; + FindManifest(paths, ADDON_INSTALL_TEMP_DIR + name + "/"); + + for (auto& dir : paths) { + string addonName = UTF82String(filesystem::path(str2wstr(dir)).filename().u8string()); + if (addonName.empty() || addonName == "Temp") + addonName = UTF82String(filesystem::path(str2wstr(packPath)).stem().u8string()); + if (!InstallAddonToLevel(dir, addonName)) + throw std::exception("Error in Install Addon To Level "); + } + + filesystem::remove_all(ADDON_INSTALL_TEMP_DIR + name + "/", ec); + filesystem::remove_all(str2wstr(packPath), ec); + return true; + } catch (const seh_exception& e) { + addonLogger.error("Uncaught SEH Exception Detected!"); + addonLogger.error("In " __FUNCTION__); + addonLogger.error("Error: Code[{}] {}", e.code(), TextEncoding::toUTF8(e.what())); + } catch (const std::exception& e) { + addonLogger.error("Uncaught C++ Exception Detected!"); + addonLogger.error("In " __FUNCTION__); + addonLogger.error("Error: Code[{}] {}", -1, TextEncoding::toUTF8(e.what())); + } catch (...) { + addonLogger.error("Uncaught Exception Detected!"); + addonLogger.error("In " __FUNCTION__); + } + return false; +} + +bool AddonsManager::disable(std::string nameOrUuid) { + try { + auto addon = findAddon(nameOrUuid, true); + if (!addon) + return false; + if (RemoveAddonFromList(*addon)) { + addon->enable = false; + return true; + } + + } catch (...) {} + return false; +} + +bool AddonsManager::enable(std::string nameOrUuid) { + try { + auto addon = findAddon(nameOrUuid, true); + if (!addon) + return false; + if (AddAddonToList(*addon)) { + addon->enable = true; + return true; + } + + } catch (...) {} + return false; +} + +bool AddonsManager::uninstall(std::string nameOrUuid) { + try { + auto addon = findAddon(nameOrUuid, true); + if (!addon) { + addonLogger.error(tr("ll.addonsHelper.error.addonNotFound")); + return false; + } + RemoveAddonFromList(*addon); + std::error_code ec; + filesystem::remove_all(str2wstr(addon->directory), ec); + for (auto i = addons.begin(); i != addons.end(); ++i) + if (i->uuid == addon->uuid) { + addons.erase(i); + break; + } + addonLogger.info(tr("ll.addonsHelper.uninstall.success", addon->getPrintName())); + } catch (...) {} + return false; +} + +std::vector AddonsManager::getAllAddons() { + std::vector res; + for (auto& addon : addons) + res.push_back(&addon); + return res; +} + +Addon* AddonsManager::findAddon(std::string nameOrUuid, bool fuzzy) { + Addon* possible = nullptr; + bool multiMatch = false; + for (auto& addon : addons) { + if (addon.uuid == nameOrUuid) + return &addon; + std::string addonName = addon.name; + std::string targetName = nameOrUuid; + if (ColorFormat::removeColorCode(addonName) == ColorFormat::removeColorCode(targetName)) + return &addon; + if (!fuzzy) + continue; + // Simple fuzzy matching + std::transform(addonName.begin(), addonName.end(), addonName.begin(), ::tolower); + std::transform(targetName.begin(), targetName.end(), targetName.begin(), ::tolower); + if (StartsWith(addonName, targetName)) { + if (possible) + multiMatch = true; + else + possible = &addon; + } + } + if (multiMatch) + return nullptr; + else + return possible; +} + +void ListAllAddons(CommandOutput& output) { + if (addons.empty()) { + output.trSuccess("ll.addonsHelper.error.noAddonInstalled"); + return; + } + + output.trSuccess("ll.addonsHelper.cmd.output.list.overview", addons.size()); + for (auto index = 0; index < addons.size(); ++index) { + auto& addon = addons[index]; + string addonName = addon.name; + if (addonName.find("§") == string::npos) + addonName = "§b" + addonName; + string desc = addon.description; + if (desc.find("§") == string::npos) + desc = "§7" + desc; + + std::string addonType = (addon.type == Addon::Type::ResourcePack ? "ResourcePack" : "BehaviorPack"); + if (addon.enable) { + output.success(fmt::format("§e{:>2}§r: {} §a[v{}] §8({})", index + 1, addonName, addon.version.toString(), addonType)); + output.success(fmt::format(" {}", desc)); + } else { + output.success(fmt::format("§e{:>2}§r: §8{} [v{}] ({})", index + 1, ColorFormat::removeColorCode(addonName), addon.version.toString(), addonType)); + output.success(fmt::format(" §8Disabled")); + } + } +} + +void ShowAddon(CommandOutput& output, Addon& addon) { + std::ostringstream oss; + oss << "Addon <" << addon.name << "§r>" << (addon.enable ? " §aEnabled" : " §cDisabled") << "\n\n"; + oss << "- §aName§r: " << addon.name << "\n"; + oss << "- §aUUID§r: " << addon.uuid << "\n"; + oss << "- §aDescription§r: " << addon.description << "\n"; + oss << "- §aVersion§r: v" << addon.version.toString(true) << "\n"; + oss << "- §aType§r: " << magic_enum::enum_name(addon.type) << "\n"; + oss << "- §aDirectory§r: " << addon.directory << "\n"; + output.success(oss.str()); +} + +class AddonsCommand : public Command { + enum class Operation { + List, + Install, + Uninstall, + Enable, + Disable + }; + + Operation operation = static_cast(-1); + std::string target; + int index = -1; + bool target_isSet = false; + bool index_isSet = false; + + inline Addon* getSelectedAddon(CommandOutput& output) const { + Addon* addon = nullptr; + if (target_isSet) { + auto addon = AddonsManager::findAddon(target, true); + if (addon) { + return addon; + } else { + output.trError("ll.addonsHelper.error.addonNotfound", target); + } + } else { + auto addons = AddonsManager::getAllAddons(); + if (index - 1 >= 0 && index - 1 < static_cast(addons.size())) + return addons[index - 1]; + else + output.trError("ll.addonsHelper.error.outOfRange", index); + } + return nullptr; + } + +public: + void execute(CommandOrigin const& ori, CommandOutput& output) const override { + output.setLanguageCode(ori); + switch (operation) { + case Operation::List: + if (target_isSet || index_isSet) { + if (auto addon = getSelectedAddon(output)) + ShowAddon(output, *addon); + } else + ListAllAddons(output); + break; + case Operation::Install: + if (AddonsManager::install(target)) + filesystem::remove_all(ADDON_INSTALL_TEMP_DIR); + output.success(); + break; + case Operation::Uninstall: { + auto addon = getSelectedAddon(output); + if (addon && AddonsManager::uninstall(addon->uuid)) + output.success(); + break; + } + case Operation::Enable: { + auto addon = getSelectedAddon(output); + if (addon && AddonsManager::enable(addon->uuid)) + output.success(); + break; + } + case Operation::Disable: { + auto addon = getSelectedAddon(output); + if (addon && AddonsManager::disable(addon->uuid)) + output.success(); + break; + } + default: + break; + } + } + + static void setup(CommandRegistry* registry) { + registry->registerCommand("addons", "LiteLoaderBDS Addons Helper (Restart required after addon changes)", + CommandPermissionLevel::GameMasters, {(CommandFlagValue)0}, {(CommandFlagValue)0x80}); + + vector addonsList; + for (auto& addon : addons) + addonsList.push_back(addon.name); + registry->addSoftEnum("AddonName", addonsList); + + // addons list + registry->addEnum("Operation_Addons_List", {{"list", Operation::List}}); + registry->registerOverload( + "addons", + makeMandatory(&AddonsCommand::operation, "operation", "Operation_Addons_List").addOptions((CommandParameterOption)1), + makeOptional(&AddonsCommand::target, "addonName", "AddonName", &AddonsCommand::target_isSet)); + registry->registerOverload( + "addons", + makeMandatory(&AddonsCommand::operation, "operation", "Operation_Addons_List").addOptions((CommandParameterOption)1), + makeOptional(&AddonsCommand::index, "addonIndex", nullptr, &AddonsCommand::index_isSet)); + + // addons install + registry->addEnum("Operation_Addons_Install", {{"install", Operation::Install}}); + registry->registerOverload( + "addons", + makeMandatory(&AddonsCommand::operation, "operation", "Operation_Addons_Install").addOptions((CommandParameterOption)1), + makeMandatory(&AddonsCommand::target, "addonName")); + + // addons uninstall + + registry->addEnum("Operation_Addons_Others", { + {"uninstall", Operation::Uninstall}, + {"remove", Operation::Uninstall}, + {"enable", Operation::Enable}, + {"disable", Operation::Disable}, + }); + registry->registerOverload( + "addons", + makeMandatory(&AddonsCommand::operation, "operation", "Operation_Addons_Others").addOptions((CommandParameterOption)1), + makeMandatory(&AddonsCommand::target, "addonName", "AddonName", &AddonsCommand::target_isSet)); + registry->registerOverload( + "addons", + makeMandatory(&AddonsCommand::operation, "operation", "Operation_Addons_Others").addOptions((CommandParameterOption)1), + makeMandatory(&AddonsCommand::index, "addonIndex", nullptr, &AddonsCommand::index_isSet)); + } +}; + +void FindAddons(string jsonPath, string packsDir) { + try { + if (!filesystem::exists(str2wstr(jsonPath)) && !filesystem::exists(str2wstr(packsDir))) + return; + if (!filesystem::exists(str2wstr(jsonPath))) + WriteAllFile(jsonPath, "[]"); + if (!filesystem::exists(str2wstr(packsDir))) + filesystem::create_directories(str2wstr(packsDir)); + + auto content = ReadAllFile(jsonPath); + if (!content || content->empty()) { + WriteAllFile(jsonPath, "[]"); + content = "[]"; + } + std::set validPackIDs; + try { + auto addonList = nlohmann::json::parse(*content, nullptr, true, true); + for (auto addon : addonList) { + std::string pktid = addon["pack_id"]; + validPackIDs.insert(pktid); + } + } catch (const std::exception&) { + addonLogger.error(tr("ll.addonsHelper.error.parsingEnabledAddonsList")); + } + + filesystem::directory_iterator ent(str2wstr(packsDir)); + for (auto& dir : ent) { + if (!dir.is_directory()) + continue; + auto addon = parseAddonFromPath(dir); + if (!addon) + continue; + if (validPackIDs.find(addon->uuid) != validPackIDs.end()) + addon->enable = true; + addons.emplace_back(std::move(*addon)); + } + } catch (...) { + return; + } +} + +void BuildAddonsList() { + string levelPath = Level::getCurrentLevelPath(); + + FindAddons(levelPath + "/world_behavior_packs.json", levelPath + "/behavior_packs"); + FindAddons(levelPath + "/world_resource_packs.json", levelPath + "/resource_packs"); + + std::sort(addons.begin(), addons.end(), + [](Addon const& _Left, Addon const& _Right) { + if (_Left.enable && !_Right.enable) + return true; + if (_Left.type == Addon::Type::ResourcePack && _Right.type == Addon::Type::BehaviorPack) + return true; + return false; + }); +} + +bool AutoInstallAddons(string path) { + std::error_code ec; + if (!filesystem::exists(str2wstr(path))) { + filesystem::create_directories(str2wstr(path), ec); + addonLogger.info(tr("ll.addonsHelper.autoInstall.tip.dirCreated", LL::globalConfig.addonsInstallPath)); + return false; + } + std::vector toInstallList; + + filesystem::directory_iterator ent(str2wstr(path)); + for (auto& file : ent) { + if (!file.is_regular_file()) + continue; + + if (VALID_ADDON_FILE_EXTENSION.count(UTF82String(file.path().extension().u8string())) > 0) { + toInstallList.push_back(UTF82String(file.path().lexically_normal().u8string())); + } + } + + if (toInstallList.empty()) + return false; + + addonLogger.info(tr("ll.addonsHelper.autoInstall.working", toInstallList.size())); + int cnt = 0; + for (auto& path : toInstallList) { + addonLogger.debug("Installing \"{}\"...", path); + if (!AddonsManager::install(path)) { + // filesystem::remove_all(ADDON_INSTALL_TEMP_DIR, ec); + break; + } else { + ++cnt; + addonLogger.info(tr("ll.addonsHelper.autoInstall.installed", path)); + } + } + + if (cnt == 0) { + addonLogger.error(tr("ll.addonsHelper.error.noAddonInstalled")); + } else { + addonLogger.info(tr("ll.addonsHelper.autoInstall.installedCount", cnt)); + } + return true; +} + +void InitAddonsHelper() { + if (LL::isDebugMode()) + addonLogger.consoleLevel = addonLogger.debug.level; + + filesystem::remove_all(ADDON_INSTALL_TEMP_DIR); + filesystem::create_directories(ADDON_INSTALL_TEMP_DIR); + + AutoInstallAddons(LL::globalConfig.addonsInstallPath); + BuildAddonsList(); + + filesystem::remove_all(ADDON_INSTALL_TEMP_DIR); + + Event::RegCmdEvent::subscribe([](Event::RegCmdEvent ev) { // Register commands + AddonsCommand::setup(ev.mCommandRegistry); + return true; + }); +} \ No newline at end of file diff --git a/LiteLoader/Main/LiteLoader.cpp b/LiteLoader/Main/LiteLoader.cpp index e3800c4..17224f7 100644 --- a/LiteLoader/Main/LiteLoader.cpp +++ b/LiteLoader/Main/LiteLoader.cpp @@ -306,9 +306,9 @@ void LLMain() { CheckDevMode(); // Addon Helper -// if (LL::globalConfig.enableAddonsHelper) { -// InitAddonsHelper(); -// } + if (LL::globalConfig.enableAddonsHelper) { + InitAddonsHelper(); + } // Load plugins LL::LoadMain();