mirror of
https://github.com/quizhizhe/LiteLoaderBDS-1.16.40.git
synced 2025-06-06 12:03:39 +00:00
677 lines
26 KiB
C++
677 lines
26 KiB
C++
#include "AddonsHelper.h"
|
|
#include <Main/Config.h>
|
|
#include <EventAPI.h>
|
|
#include <RegCommandAPI.h>
|
|
#include <LLAPI.h>
|
|
#include <MC/CommandOrigin.hpp>
|
|
#include <MC/CommandOutput.hpp>
|
|
#include <MC/CommandPosition.hpp>
|
|
#include <MC/CommandRegistry.hpp>
|
|
#include <MC/PropertiesSettings.hpp>
|
|
#include <MC/Level.hpp>
|
|
#include <GlobalServiceAPI.h>
|
|
#include <Utils/WinHelper.h>
|
|
#include <Utils/FileHelper.h>
|
|
#include <Nlohmann/json.hpp>
|
|
#include <Main/Config.h>
|
|
#include <LoggerAPI.h>
|
|
#include <I18nAPI.h>
|
|
#include <filesystem>
|
|
#include <set>
|
|
#include <vector>
|
|
#include <MC/ColorFormat.hpp>
|
|
#include <magic_enum/magic_enum.hpp>
|
|
using namespace std;
|
|
using namespace RegisterCommandHelper;
|
|
|
|
Logger addonLogger("AddonHelper");
|
|
std::vector<Addon> 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 <MC/JsonHelpers.hpp>
|
|
inline std::string FixMojangJson(std::string const& content) {
|
|
Json::Value value;
|
|
JsonHelpers::parseJson(content, value);
|
|
return JsonHelpers::serialize(value);
|
|
}
|
|
|
|
std::optional<Addon> 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<string>& 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<string> 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<Addon*> AddonsManager::getAllAddons() {
|
|
std::vector<Addon*> 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<Operation>(-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<int>(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<string> addonsList;
|
|
for (auto& addon : addons)
|
|
addonsList.push_back(addon.name);
|
|
registry->addSoftEnum("AddonName", addonsList);
|
|
|
|
// addons list
|
|
registry->addEnum<Operation>("Operation_Addons_List", {{"list", Operation::List}});
|
|
registry->registerOverload<AddonsCommand>(
|
|
"addons",
|
|
makeMandatory<CommandParameterDataType::ENUM>(&AddonsCommand::operation, "operation", "Operation_Addons_List").addOptions((CommandParameterOption)1),
|
|
makeOptional<CommandParameterDataType::SOFT_ENUM>(&AddonsCommand::target, "addonName", "AddonName", &AddonsCommand::target_isSet));
|
|
registry->registerOverload<AddonsCommand>(
|
|
"addons",
|
|
makeMandatory<CommandParameterDataType::ENUM>(&AddonsCommand::operation, "operation", "Operation_Addons_List").addOptions((CommandParameterOption)1),
|
|
makeOptional<CommandParameterDataType::NORMAL>(&AddonsCommand::index, "addonIndex", nullptr, &AddonsCommand::index_isSet));
|
|
|
|
// addons install
|
|
registry->addEnum<Operation>("Operation_Addons_Install", {{"install", Operation::Install}});
|
|
registry->registerOverload<AddonsCommand>(
|
|
"addons",
|
|
makeMandatory<CommandParameterDataType::ENUM>(&AddonsCommand::operation, "operation", "Operation_Addons_Install").addOptions((CommandParameterOption)1),
|
|
makeMandatory<CommandParameterDataType::NORMAL>(&AddonsCommand::target, "addonName"));
|
|
|
|
// addons uninstall
|
|
|
|
registry->addEnum<Operation>("Operation_Addons_Others", {
|
|
{"uninstall", Operation::Uninstall},
|
|
{"remove", Operation::Uninstall},
|
|
{"enable", Operation::Enable},
|
|
{"disable", Operation::Disable},
|
|
});
|
|
registry->registerOverload<AddonsCommand>(
|
|
"addons",
|
|
makeMandatory<CommandParameterDataType::ENUM>(&AddonsCommand::operation, "operation", "Operation_Addons_Others").addOptions((CommandParameterOption)1),
|
|
makeMandatory<CommandParameterDataType::SOFT_ENUM>(&AddonsCommand::target, "addonName", "AddonName", &AddonsCommand::target_isSet));
|
|
registry->registerOverload<AddonsCommand>(
|
|
"addons",
|
|
makeMandatory<CommandParameterDataType::ENUM>(&AddonsCommand::operation, "operation", "Operation_Addons_Others").addOptions((CommandParameterOption)1),
|
|
makeMandatory<CommandParameterDataType::NORMAL>(&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<string> 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<string> 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;
|
|
});
|
|
} |