Remove runtime enable/disable, requires cleanup

This commit is contained in:
altalk23 2023-09-04 19:08:25 +03:00
parent e78eaf01e8
commit d81774957d
6 changed files with 88 additions and 140 deletions

View file

@ -33,6 +33,13 @@ namespace geode {
~HandleToSaved();
};
enum class ModRequestedAction {
None,
Enable,
Disable,
Uninstall,
};
GEODE_HIDDEN Mod* takeNextLoaderMod();
class ModImpl;
@ -337,6 +344,8 @@ namespace geode {
Result<> uninstall();
bool isUninstalled() const;
ModRequestedAction getRequestedAction() const;
/**
* Check whether or not this Mod
* depends on another mod

View file

@ -166,7 +166,6 @@ bool Loader::Impl::isModVersionSupported(VersionInfo const& version) {
Result<> Loader::Impl::saveData() {
// save mods' data
for (auto& [id, mod] : m_mods) {
Mod::get()->setSavedValue("should-load-" + id, mod->isUninstalled() || mod->isEnabled());
auto r = mod->saveData();
if (!r) {
log::warn("Unable to save data for mod \"{}\": {}", mod->getID(), r.unwrapErr());
@ -411,31 +410,16 @@ void Loader::Impl::loadModGraph(Mod* node, bool early) {
return;
}
log::debug("Load");
auto res = node->m_impl->loadBinary();
if (!res) {
m_problems.push_back({
LoadProblem::Type::LoadFailed,
node,
res.unwrapErr()
});
log::error("Failed to load binary: {}", res.unwrapErr());
log::popNest();
return;
}
if (Mod::get()->getSavedValue<bool>("should-load-" + node->getID(), true)) {
log::debug("Enable");
res = node->m_impl->enable();
log::debug("Load");
auto res = node->m_impl->loadBinary();
if (!res) {
node->m_impl->m_enabled = true;
(void)node->m_impl->disable();
m_problems.push_back({
LoadProblem::Type::EnableFailed,
LoadProblem::Type::LoadFailed,
node,
res.unwrapErr()
});
log::error("Failed to enable: {}", res.unwrapErr());
log::error("Failed to load binary: {}", res.unwrapErr());
log::popNest();
return;
}
@ -509,19 +493,19 @@ void Loader::Impl::findProblems() {
Mod* myEpicMod = mod; // clang fix
// if the mod is not loaded but there are no problems related to it
if (!mod->isLoaded() && !std::any_of(m_problems.begin(), m_problems.end(), [myEpicMod](auto& item) {
return std::holds_alternative<ModMetadata>(item.cause) &&
std::get<ModMetadata>(item.cause).getID() == myEpicMod->getID() ||
std::holds_alternative<Mod*>(item.cause) &&
std::get<Mod*>(item.cause) == myEpicMod;
})) {
m_problems.push_back({
LoadProblem::Type::Unknown,
mod,
""
});
log::error("{} failed to load for an unknown reason", id);
}
// if (!mod->isLoaded() && !std::any_of(m_problems.begin(), m_problems.end(), [myEpicMod](auto& item) {
// return std::holds_alternative<ModMetadata>(item.cause) &&
// std::get<ModMetadata>(item.cause).getID() == myEpicMod->getID() ||
// std::holds_alternative<Mod*>(item.cause) &&
// std::get<Mod*>(item.cause) == myEpicMod;
// })) {
// m_problems.push_back({
// LoadProblem::Type::Unknown,
// mod,
// ""
// });
// log::error("{} failed to load for an unknown reason", id);
// }
log::popNest();
}
@ -533,7 +517,7 @@ void Loader::Impl::refreshModGraph() {
auto begin = std::chrono::high_resolution_clock::now();
if (m_mods.size() > 1) {
if (m_isSetup) {
log::error("Cannot refresh mod graph after startup");
log::popNest();
return;
@ -564,7 +548,7 @@ void Loader::Impl::refreshModGraph() {
m_loadingState = LoadingState::EarlyMods;
log::debug("Loading early mods");
log::pushNest();
for (auto const& dep : Mod::get()->m_impl->m_dependants) {
for (auto const& dep : ModImpl::get()->m_dependants) {
this->loadModGraph(dep, true);
}
log::popNest();

View file

@ -198,6 +198,10 @@ bool Mod::isUninstalled() const {
return m_impl->isUninstalled();
}
ModRequestedAction Mod::getRequestedAction() const {
return m_impl->getRequestedAction();
}
bool Mod::depends(std::string const& id) const {
return m_impl->depends(id);
}

View file

@ -117,23 +117,15 @@ bool Mod::Impl::isLoaded() const {
}
bool Mod::Impl::supportsDisabling() const {
return m_metadata.getID() != "geode.loader" && !m_metadata.isAPI();
return m_metadata.getID() != "geode.loader";
}
bool Mod::Impl::canDisable() const {
auto deps = m_dependants;
return this->supportsDisabling() &&
(deps.empty() || std::all_of(deps.begin(), deps.end(), [&](auto& item) {
return item->canDisable();
}));
return true;
}
bool Mod::Impl::canEnable() const {
auto deps = m_metadata.getDependencies();
return !this->isUninstalled() &&
(deps.empty() || std::all_of(deps.begin(), deps.end(), [&](auto& item) {
return item.isResolved();
}));
return true;
}
bool Mod::Impl::needsEarlyLoad() const {
@ -343,33 +335,9 @@ Result<> Mod::Impl::loadBinary() {
}
m_binaryLoaded = true;
LoaderImpl::get()->releaseNextMod();
ModStateEvent(m_self, ModEventType::Loaded).post();
return Ok();
}
Result<> Mod::Impl::enable() {
if (!m_binaryLoaded)
return Err("Tried to enable {} but its binary is not loaded", m_metadata.getID());
bool enabledDependencies = true;
for (auto const& item : m_metadata.getDependencies()) {
if (!item.isResolved() || !item.mod)
continue;
auto res = item.mod->enable();
if (!res) {
enabledDependencies = false;
log::error("Failed to enable {}: {}", item.id, res.unwrapErr());
}
}
if (!enabledDependencies)
return Err("Mod cannot be enabled because one or more of its dependencies cannot be enabled.");
if (!this->canEnable())
return Err("Mod cannot be enabled because it has unresolved dependencies.");
LoaderImpl::get()->releaseNextMod();
for (auto const& hook : m_hooks) {
if (!hook) {
@ -377,7 +345,10 @@ Result<> Mod::Impl::enable() {
continue;
}
if (hook->getAutoEnable()) {
GEODE_UNWRAP(this->enableHook(hook));
auto res = this->enableHook(hook);
if (!res) {
log::error("Can't enable hook {} for mod {}: {}", hook->getDisplayName(), m_metadata.getID(), res.unwrapErr());
}
}
}
@ -389,75 +360,46 @@ Result<> Mod::Impl::enable() {
}
m_enabled = true;
ModStateEvent(m_self, ModEventType::Enabled).post();
return Ok();
}
Result<> Mod::Impl::enable() {
if (m_requestedAction != ModRequestedAction::None) {
return Err("Mod already has a requested action");
}
m_requestedAction = ModRequestedAction::Enable;
Mod::get()->setSavedValue("should-load-" + m_metadata.getID(), true);
return Ok();
}
Result<> Mod::Impl::disable() {
if (!m_enabled)
return Ok();
if (!this->supportsDisabling())
return Err("Mod does not support disabling.");
if (!this->canDisable())
return Err("Mod cannot be disabled because one or more of its dependants cannot be disabled.");
// disable dependants
bool disabledDependants = true;
for (auto& item : m_dependants) {
auto res = item->disable();
if (res)
continue;
disabledDependants = false;
log::error("Failed to disable {}: {}", item->getID(), res.unwrapErr());
if (m_requestedAction != ModRequestedAction::None) {
return Err("Mod already has a requested action");
}
if (!disabledDependants)
return Err("Mod cannot be disabled because one or more of its dependants cannot be disabled.");
std::vector<std::string> errors;
for (auto const& hook : m_hooks) {
auto res = this->disableHook(hook);
if (!res)
errors.push_back(res.unwrapErr());
}
for (auto const& patch : m_patches) {
auto res = this->unpatch(patch);
if (!res)
errors.push_back(res.unwrapErr());
}
m_enabled = false;
ModStateEvent(m_self, ModEventType::Disabled).post();
if (!errors.empty())
return Err(utils::string::join(errors, "\n"));
m_requestedAction = ModRequestedAction::Disable;
Mod::get()->setSavedValue("should-load-" + m_metadata.getID(), false);
return Ok();
}
Result<> Mod::Impl::uninstall() {
if (supportsDisabling()) {
GEODE_UNWRAP(this->disable());
}
else {
for (auto& item : m_dependants) {
if (!item->canDisable())
continue;
GEODE_UNWRAP(item->disable());
}
if (m_requestedAction != ModRequestedAction::None) {
return Err("Mod already has a requested action");
}
try {
ghc::filesystem::remove(m_metadata.getPath());
}
catch (std::exception& e) {
m_requestedAction = ModRequestedAction::Uninstall;
std::error_code ec;
ghc::filesystem::remove(m_metadata.getPath(), ec);
if (ec) {
return Err(
"Unable to delete mod's .geode file! "
"This might be due to insufficient permissions - "
"try running GD as administrator."
"Unable to delete mod's .geode file: " + ec.message()
);
}
@ -465,7 +407,11 @@ Result<> Mod::Impl::uninstall() {
}
bool Mod::Impl::isUninstalled() const {
return m_self != Mod::get() && !ghc::filesystem::exists(m_metadata.getPath());
return m_requestedAction == ModRequestedAction::Uninstall;
}
ModRequestedAction Mod::Impl::getRequestedAction() const {
return m_requestedAction;
}
// Dependencies
@ -661,7 +607,7 @@ ModJson Mod::Impl::getRuntimeInfo() const {
for (auto patch : m_patches) {
obj["patches"].as_array().push_back(ModJson(patch->getRuntimeInfo()));
}
obj["enabled"] = m_enabled;
// obj["enabled"] = m_enabled;
obj["loaded"] = m_binaryLoaded;
obj["temp-dir"] = this->getTempDir();
obj["save-dir"] = this->getSaveDir();

View file

@ -62,6 +62,9 @@ namespace geode {
*/
bool m_resourcesLoaded = false;
ModRequestedAction m_requestedAction = ModRequestedAction::None;
Impl(Mod* self, ModMetadata const& metadata);
~Impl();
@ -122,6 +125,10 @@ namespace geode {
Result<> disable();
Result<> uninstall();
bool isUninstalled() const;
// 1.3.0 additions
ModRequestedAction getRequestedAction() const;
bool depends(std::string const& id) const;
Result<> updateDependencies();
bool hasUnresolvedDependencies() const;

View file

@ -227,11 +227,7 @@ void ModCell::onEnable(CCObject* sender) {
else {
tryOrAlert(m_mod->disable(), "Error disabling mod");
}
Loader::get()->queueInMainThread([this]() {
if (m_layer) {
m_layer->updateAllStates();
}
});
m_layer->reloadList();
}
void ModCell::onUnresolvedInfo(CCObject*) {
@ -278,9 +274,9 @@ bool ModCell::init(
return false;
m_mod = mod;
this->setupInfo(mod->getMetadata(), false, display, m_mod->isUninstalled());
this->setupInfo(mod->getMetadata(), false, display, mod->getRequestedAction() != ModRequestedAction::None);
if (mod->isUninstalled()) {
if (mod->getRequestedAction() != ModRequestedAction::None) {
auto restartSpr = ButtonSprite::create("Restart", "bigFont.fnt", "GJ_button_03.png", .8f);
restartSpr->setScale(.65f);
@ -317,14 +313,16 @@ bool ModCell::init(
}
}
}
if (m_mod->wasSuccessfullyLoaded()) {
m_enableToggle =
CCMenuItemToggler::createWithStandardSprites(this, menu_selector(ModCell::onEnable), .7f);
m_enableToggle->setPosition(-45.f, 0.f);
m_menu->addChild(m_enableToggle);
}
}
if (m_mod->wasSuccessfullyLoaded() && m_mod->supportsDisabling() && !m_mod->isUninstalled()) {
m_enableToggle =
CCMenuItemToggler::createWithStandardSprites(this, menu_selector(ModCell::onEnable), .7f);
m_enableToggle->setPosition(-45.f, 0.f);
m_menu->addChild(m_enableToggle);
}
auto exMark = CCSprite::createWithSpriteFrameName("exMark_001.png");
exMark->setScale(.5f);