mirror of
https://github.com/geode-sdk/geode.git
synced 2025-02-17 00:30:26 -05:00
index work
- installing mods works again - add EventListener::setFilter - fix loader panicking if some mods failed to load
This commit is contained in:
parent
92c22d25e4
commit
3b1a11e11f
7 changed files with 412 additions and 58 deletions
|
@ -60,9 +60,11 @@ namespace geode {
|
|||
using MemberFn = typename to_member<C, Callback>::value;
|
||||
|
||||
ListenerResult passThrough(Event* e) override {
|
||||
// it is so silly to use dynamic cast in an interbinary context
|
||||
if (auto myev = cast::typeinfo_cast<typename T::Event*>(e)) {
|
||||
return m_filter.handle(m_callback, myev);
|
||||
if (m_callback) {
|
||||
// it is so silly to use dynamic cast in an interbinary context
|
||||
if (auto myev = cast::typeinfo_cast<typename T::Event*>(e)) {
|
||||
return m_filter.handle(m_callback, myev);
|
||||
}
|
||||
}
|
||||
return ListenerResult::Propagate;
|
||||
}
|
||||
|
@ -100,12 +102,16 @@ namespace geode {
|
|||
m_callback = std::bind(fn, cls, std::placeholders::_1);
|
||||
}
|
||||
|
||||
void setFilter(T filter) {
|
||||
m_filter = filter;
|
||||
}
|
||||
|
||||
protected:
|
||||
std::function<Callback> m_callback = nullptr;
|
||||
T m_filter;
|
||||
};
|
||||
|
||||
class GEODE_DLL Event {
|
||||
class GEODE_DLL [[nodiscard]] Event {
|
||||
private:
|
||||
static std::unordered_set<EventListenerProtocol*> s_listeners;
|
||||
friend EventListenerProtocol;
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include "ModInfo.hpp"
|
||||
#include "Event.hpp"
|
||||
#include "../utils/Result.hpp"
|
||||
#include "../utils/web.hpp"
|
||||
#include <unordered_set>
|
||||
|
||||
namespace geode {
|
||||
|
@ -15,6 +16,7 @@ namespace geode {
|
|||
struct GEODE_DLL ModInstallEvent : public Event {
|
||||
const std::string modID;
|
||||
const UpdateStatus status;
|
||||
ModInstallEvent(std::string const& id, const UpdateStatus status);
|
||||
};
|
||||
|
||||
class GEODE_DLL ModInstallFilter : public EventFilter<ModInstallEvent> {
|
||||
|
@ -70,6 +72,17 @@ namespace geode {
|
|||
};
|
||||
using IndexItemHandle = std::shared_ptr<IndexItem>;
|
||||
|
||||
struct IndexInstallList {
|
||||
/**
|
||||
* Mod being installed
|
||||
*/
|
||||
IndexItemHandle target;
|
||||
/**
|
||||
* The mod, its dependencies, everything needed to install it
|
||||
*/
|
||||
std::vector<IndexItemHandle> list;
|
||||
};
|
||||
|
||||
class GEODE_DLL Index final {
|
||||
protected:
|
||||
// for once, the fact that std::map is ordered is useful (this makes
|
||||
|
@ -78,6 +91,10 @@ namespace geode {
|
|||
|
||||
std::vector<IndexSourcePtr> m_sources;
|
||||
std::unordered_map<std::string, UpdateStatus> m_sourceStatuses;
|
||||
std::unordered_map<
|
||||
IndexItemHandle,
|
||||
utils::web::SentAsyncWebRequestHandle
|
||||
> m_runningInstallations;
|
||||
std::atomic<bool> m_triedToUpdate = false;
|
||||
std::unordered_map<std::string, ItemVersions> m_items;
|
||||
|
||||
|
@ -89,6 +106,8 @@ namespace geode {
|
|||
void updateSourceFromLocal(IndexSourceImpl* src);
|
||||
void cleanupItems();
|
||||
|
||||
void installNext(size_t index, IndexInstallList const& list);
|
||||
|
||||
public:
|
||||
static Index* get();
|
||||
|
||||
|
@ -96,27 +115,107 @@ namespace geode {
|
|||
void removeSource(std::string const& repository);
|
||||
std::vector<std::string> getSources() const;
|
||||
|
||||
/**
|
||||
* Get all index items available on this platform
|
||||
*/
|
||||
std::vector<IndexItemHandle> getItems() const;
|
||||
bool isKnownItem(std::string const& id, std::optional<VersionInfo> version) const;
|
||||
/**
|
||||
* Get all index items regardless of platform
|
||||
*/
|
||||
std::vector<IndexItemHandle> getAllItems() const;
|
||||
/**
|
||||
* Check if an item with this ID is found on the index, and optionally
|
||||
* provide the version sought after
|
||||
*/
|
||||
bool isKnownItem(
|
||||
std::string const& id,
|
||||
std::optional<VersionInfo> version
|
||||
) const;
|
||||
/**
|
||||
* Get an item from the index by its ID and optionally version
|
||||
* @param id ID of the mod
|
||||
* @param version Version to match exactly; if you need to match a range
|
||||
* of versions, use the getItem overload that takes a
|
||||
* ComparableVersionInfo
|
||||
* @returns The item, or nullptr if the item was not found
|
||||
*/
|
||||
IndexItemHandle getItem(
|
||||
std::string const& id,
|
||||
std::optional<VersionInfo> version
|
||||
) const;
|
||||
/**
|
||||
* Get an item from the index by its ID and version range
|
||||
* @param id ID of the mod
|
||||
* @param version Version to match
|
||||
* @returns The item, or nullptr if the item was not found
|
||||
*/
|
||||
IndexItemHandle getItem(
|
||||
std::string const& id,
|
||||
ComparableVersionInfo version
|
||||
) const;
|
||||
/**
|
||||
* Get an item from the index by its mod.json
|
||||
* @param info The mod's info
|
||||
* @returns The item, or nullptr if the item was not found
|
||||
*/
|
||||
IndexItemHandle getItem(ModInfo const& info) const;
|
||||
/**
|
||||
* Get an item from the index that corresponds to an installed mod
|
||||
* @param mod An installed mod
|
||||
* @returns The item, or nullptr if the item was not found
|
||||
*/
|
||||
IndexItemHandle getItem(Mod* mod) const;
|
||||
bool isUpdateAvailable(IndexItemHandle item) const;
|
||||
bool areUpdatesAvailable() const;
|
||||
void install(IndexItemHandle item);
|
||||
Result<std::vector<IndexItemHandle>> getInstallList(
|
||||
IndexItemHandle item
|
||||
) const;
|
||||
|
||||
/**
|
||||
* Check if an item has updates available
|
||||
* @param item Item to check updates for
|
||||
* @returns True if the version of the item on the index is newer than
|
||||
* its installed counterpart
|
||||
*/
|
||||
bool isUpdateAvailable(IndexItemHandle item) const;
|
||||
/**
|
||||
* Check if any of the mods on the index have updates available
|
||||
*/
|
||||
bool areUpdatesAvailable() const;
|
||||
/**
|
||||
* Get the list of items needed to install this item (dependencies, etc.)
|
||||
* @param item Item to get the list for
|
||||
* @returns The list, or an error if some items on the list cannot be installed
|
||||
*/
|
||||
Result<IndexInstallList> getInstallList(IndexItemHandle item) const;
|
||||
/**
|
||||
* Install an index item. Add an event listener for the ModInstallEvent
|
||||
* class to track the installation progress
|
||||
* @param item Item to install
|
||||
*/
|
||||
void install(IndexItemHandle item);
|
||||
/**
|
||||
* Install a list of index items. Add an event listener for the
|
||||
* ModInstallEvent class to track the installation progress
|
||||
* @param list List of items to install
|
||||
*/
|
||||
void install(IndexInstallList const& list);
|
||||
/**
|
||||
* Cancel an installation in progress
|
||||
* @param item Installation to cancel
|
||||
*/
|
||||
void cancelInstall(IndexItemHandle item);
|
||||
|
||||
/**
|
||||
* Check if it has been attempted to update the index. You can check
|
||||
* for errors by doing hasTriedToUpdate() && !isUpToDate()
|
||||
*/
|
||||
bool hasTriedToUpdate() const;
|
||||
/**
|
||||
* Whether the index is up-to-date, i.e. all sources are up-to-date
|
||||
*/
|
||||
bool isUpToDate() const;
|
||||
/**
|
||||
* Update the index. Add an event listener for the IndexUpdateEvent
|
||||
* class to track updating progress
|
||||
* @param force Forcefully update all sources, even if some have are
|
||||
* already up-to-date
|
||||
*/
|
||||
void update(bool force = false);
|
||||
};
|
||||
}
|
||||
|
|
|
@ -5,9 +5,39 @@
|
|||
#include <Geode/utils/web.hpp>
|
||||
#include <Geode/utils/string.hpp>
|
||||
#include <Geode/utils/map.hpp>
|
||||
#include <hash/hash.hpp>
|
||||
|
||||
USE_GEODE_NAMESPACE();
|
||||
|
||||
// Save data
|
||||
|
||||
struct IndexSourceSaveData {
|
||||
std::string downloadedCommitSHA;
|
||||
};
|
||||
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(IndexSourceSaveData, downloadedCommitSHA);
|
||||
|
||||
struct IndexSaveData {
|
||||
std::unordered_map<std::string, IndexSourceSaveData> sources;
|
||||
};
|
||||
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(IndexSaveData, sources);
|
||||
|
||||
// ModInstallEvent
|
||||
|
||||
ModInstallEvent::ModInstallEvent(
|
||||
std::string const& id, const UpdateStatus status
|
||||
) : modID(id), status(status) {}
|
||||
|
||||
ListenerResult ModInstallFilter::handle(std::function<Callback> fn, ModInstallEvent* event) {
|
||||
if (m_id == event->modID) {
|
||||
fn(event);
|
||||
}
|
||||
return ListenerResult::Propagate;
|
||||
}
|
||||
|
||||
ModInstallFilter::ModInstallFilter(std::string const& id) : m_id(id) {}
|
||||
|
||||
// IndexUpdateEvent implementation
|
||||
|
||||
// The reason sources have private implementation events that are
|
||||
// turned into the global IndexUpdateEvent is because it makes it much
|
||||
// simpler to keep track of progress, what errors were received, etc.
|
||||
|
@ -48,36 +78,9 @@ public:
|
|||
SourceUpdateFilter() {}
|
||||
};
|
||||
|
||||
// Save data
|
||||
|
||||
struct IndexSourceSaveData {
|
||||
std::string downloadedCommitSHA;
|
||||
};
|
||||
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(IndexSourceSaveData, downloadedCommitSHA);
|
||||
|
||||
struct IndexSaveData {
|
||||
std::unordered_map<std::string, IndexSourceSaveData> sources;
|
||||
};
|
||||
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(IndexSaveData, sources);
|
||||
|
||||
// ModInstallEvent
|
||||
|
||||
ListenerResult ModInstallFilter::handle(std::function<Callback> fn, ModInstallEvent* event) {
|
||||
if (m_id == event->modID) {
|
||||
fn(event);
|
||||
}
|
||||
return ListenerResult::Propagate;
|
||||
}
|
||||
|
||||
ModInstallFilter::ModInstallFilter(
|
||||
std::string const& id
|
||||
) : m_id(id) {}
|
||||
|
||||
// IndexUpdateEvent
|
||||
|
||||
IndexUpdateEvent::IndexUpdateEvent(
|
||||
const UpdateStatus status
|
||||
) : status(status) {}
|
||||
IndexUpdateEvent::IndexUpdateEvent(const UpdateStatus status) : status(status) {}
|
||||
|
||||
ListenerResult IndexUpdateFilter::handle(
|
||||
std::function<Callback> fn,
|
||||
|
@ -153,7 +156,7 @@ static Result<> flattenGithubRepo(ghc::filesystem::path const& dir) {
|
|||
return Ok();
|
||||
}
|
||||
|
||||
// Index
|
||||
// Index globals
|
||||
|
||||
Index::Index() {
|
||||
new EventListener(
|
||||
|
@ -168,6 +171,8 @@ Index* Index::get() {
|
|||
return inst;
|
||||
}
|
||||
|
||||
// Sources
|
||||
|
||||
void Index::addSource(std::string const& repository) {
|
||||
m_sources.emplace_back(new IndexSourceImpl {
|
||||
.repository = repository
|
||||
|
@ -188,6 +193,8 @@ std::vector<std::string> Index::getSources() const {
|
|||
return res;
|
||||
}
|
||||
|
||||
// Updating
|
||||
|
||||
void Index::onSourceUpdate(SourceUpdateEvent* event) {
|
||||
// save status for aggregating SourceUpdateEvents to a single global
|
||||
// IndexUpdateEvent
|
||||
|
@ -424,7 +431,9 @@ void Index::update(bool force) {
|
|||
|
||||
m_triedToUpdate = true;
|
||||
|
||||
// update all sources in GD thread
|
||||
// update all sources in GD thread for synchronization (m_sourceStatuses
|
||||
// and every other member access happens in AsyncWebRequest callbacks
|
||||
// which are always run in the GD thread aswell)
|
||||
Loader::get()->queueInGDThread([force, this]() {
|
||||
// check if some sources are already being updated
|
||||
if (m_sourceStatuses.size()) {
|
||||
|
@ -442,7 +451,22 @@ void Index::update(bool force) {
|
|||
});
|
||||
}
|
||||
|
||||
// Items
|
||||
|
||||
std::vector<IndexItemHandle> Index::getItems() const {
|
||||
std::vector<IndexItemHandle> res;
|
||||
for (auto& items : map::values(m_items)) {
|
||||
if (items.size()) {
|
||||
auto item = items.rbegin()->second;
|
||||
if (item->download.platforms.count(GEODE_PLATFORM_TARGET)) {
|
||||
res.push_back(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
std::vector<IndexItemHandle> Index::getAllItems() const {
|
||||
std::vector<IndexItemHandle> res;
|
||||
for (auto& items : map::values(m_items)) {
|
||||
if (items.size()) {
|
||||
|
@ -466,9 +490,11 @@ IndexItemHandle Index::getItem(
|
|||
if (m_items.count(id)) {
|
||||
auto versions = m_items.at(id);
|
||||
if (version) {
|
||||
auto major = version.value().getMajor();
|
||||
if (versions.count(major)) {
|
||||
return versions.at(major);
|
||||
// prefer most major version
|
||||
for (auto& [_, item] : ranges::reverse(m_items.at(id))) {
|
||||
if (version.value() == item->info.m_version) {
|
||||
return item;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (versions.size()) {
|
||||
|
@ -513,24 +539,31 @@ bool Index::isUpdateAvailable(IndexItemHandle item) const {
|
|||
bool Index::areUpdatesAvailable() const {
|
||||
for (auto& mod : Loader::get()->getAllMods()) {
|
||||
auto item = this->getItem(mod);
|
||||
if (item->info.m_version > mod->getVersion()) {
|
||||
if (item && item->info.m_version > mod->getVersion()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Result<std::vector<IndexItemHandle>> Index::getInstallList(
|
||||
IndexItemHandle item
|
||||
) const {
|
||||
std::vector<IndexItemHandle> list;
|
||||
// Item installation
|
||||
|
||||
Result<IndexInstallList> Index::getInstallList(IndexItemHandle item) const {
|
||||
IndexInstallList list;
|
||||
list.target = item;
|
||||
for (auto& dep : item->info.m_dependencies) {
|
||||
if (!dep.isResolved()) {
|
||||
// check if this dep is available in the index
|
||||
if (auto depItem = this->getItem(dep.id, dep.version)) {
|
||||
if (!depItem->download.platforms.count(GEODE_PLATFORM_TARGET)) {
|
||||
return Err(
|
||||
"Dependency {} is not available on {}",
|
||||
dep.id, GEODE_PLATFORM_NAME
|
||||
);
|
||||
}
|
||||
// recursively add dependencies
|
||||
GEODE_UNWRAP_INTO(auto deps, this->getInstallList(depItem));
|
||||
ranges::push(list, deps);
|
||||
ranges::push(list.list, deps.list);
|
||||
}
|
||||
// otherwise user must get this dependency manually from somewhere
|
||||
// else
|
||||
|
@ -548,10 +581,146 @@ Result<std::vector<IndexItemHandle>> Index::getInstallList(
|
|||
// already in order for that to have happened
|
||||
}
|
||||
// add this item to the end of the list
|
||||
list.push_back(item);
|
||||
list.list.push_back(item);
|
||||
return Ok(list);
|
||||
}
|
||||
|
||||
void Index::install(IndexItemHandle item) {
|
||||
// todo
|
||||
void Index::installNext(size_t index, IndexInstallList const& list) {
|
||||
auto postError = [this, list](std::string const& error) {
|
||||
m_runningInstallations.erase(list.target);
|
||||
ModInstallEvent(list.target->info.m_id, error).post();
|
||||
};
|
||||
|
||||
// If we're at the end of the list, move the downloaded items to mods
|
||||
if (index >= list.list.size()) {
|
||||
m_runningInstallations.erase(list.target);
|
||||
// Move all downloaded files
|
||||
for (auto& item : list.list) {
|
||||
// If the mod is already installed, delete the old .geode file
|
||||
if (auto mod = Loader::get()->getInstalledMod(item->info.m_id)) {
|
||||
auto res = mod->uninstall();
|
||||
if (!res) {
|
||||
return postError(fmt::format(
|
||||
"Unable to uninstall old version of {}: {}",
|
||||
item->info.m_id, res.unwrapErr()
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
// Move the temp file
|
||||
try {
|
||||
ghc::filesystem::rename(
|
||||
dirs::getTempDir() / (item->info.m_id + ".index"),
|
||||
dirs::getModsDir() / (item->info.m_id + ".geode")
|
||||
);
|
||||
} catch(std::exception& e) {
|
||||
return postError(fmt::format(
|
||||
"Unable to install {}: {}",
|
||||
item->info.m_id, e.what()
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
// load mods
|
||||
(void)Loader::get()->refreshModsList();
|
||||
|
||||
ModInstallEvent(list.target->info.m_id, UpdateFinished()).post();
|
||||
return;
|
||||
}
|
||||
|
||||
auto scaledProgress = [index, list](double progress) -> uint8_t {
|
||||
return static_cast<uint8_t>(
|
||||
progress * (static_cast<double>(index + 1) / list.list.size())
|
||||
);
|
||||
};
|
||||
|
||||
auto item = list.list.at(index);
|
||||
auto tempFile = dirs::getTempDir() / (item->info.m_id + ".index");
|
||||
m_runningInstallations[list.target] = web::AsyncWebRequest()
|
||||
.join("install_item_" + item->info.m_id)
|
||||
.fetch(item->download.url)
|
||||
.into(tempFile)
|
||||
.then([=](auto) {
|
||||
// Check for 404
|
||||
auto notFound = utils::file::readString(tempFile);
|
||||
if (notFound && notFound.unwrap() == "Not Found") {
|
||||
return postError(fmt::format(
|
||||
"Binary file download for {} returned \"404 Not found\". "
|
||||
"Report this to the Geode development team.",
|
||||
item->info.m_id
|
||||
));
|
||||
}
|
||||
|
||||
// Verify checksum
|
||||
ModInstallEvent(
|
||||
list.target->info.m_id,
|
||||
UpdateProgress(
|
||||
scaledProgress(100),
|
||||
fmt::format("Verifying {}", item->info.m_id)
|
||||
)
|
||||
).post();
|
||||
|
||||
if (::calculateHash(tempFile) != item->download.hash) {
|
||||
return postError(fmt::format(
|
||||
"Checksum mismatch with {}! (Downloaded file did not match what "
|
||||
"was expected. Try again, and if the download fails another time, "
|
||||
"report this to the Geode development team.)",
|
||||
item->info.m_id
|
||||
));
|
||||
}
|
||||
|
||||
// Install next item in queue
|
||||
this->installNext(index + 1, list);
|
||||
})
|
||||
.expect([postError, list, item](std::string const& err) {
|
||||
postError(fmt::format(
|
||||
"Unable to download {}: {}",
|
||||
item->info.m_id, err
|
||||
));
|
||||
})
|
||||
.progress([this, item, list, scaledProgress](auto&, double now, double total) {
|
||||
ModInstallEvent(
|
||||
list.target->info.m_id,
|
||||
UpdateProgress(
|
||||
scaledProgress(now / total * 100.0),
|
||||
fmt::format("Downloading {}", item->info.m_id)
|
||||
)
|
||||
).post();
|
||||
})
|
||||
.cancelled([postError](auto&) {
|
||||
postError("Download cancelled");
|
||||
})
|
||||
.send();
|
||||
}
|
||||
|
||||
void Index::cancelInstall(IndexItemHandle item) {
|
||||
Loader::get()->queueInGDThread([this, item]() {
|
||||
if (m_runningInstallations.count(item)) {
|
||||
m_runningInstallations.at(item)->cancel();
|
||||
m_runningInstallations.erase(item);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void Index::install(IndexInstallList const& list) {
|
||||
Loader::get()->queueInGDThread([this, list]() {
|
||||
this->installNext(0, list);
|
||||
});
|
||||
}
|
||||
|
||||
void Index::install(IndexItemHandle item) {
|
||||
Loader::get()->queueInGDThread([this, item]() {
|
||||
if (m_runningInstallations.count(item)) {
|
||||
return;
|
||||
}
|
||||
auto list = this->getInstallList(item);
|
||||
if (list) {
|
||||
this->install(list.unwrap());
|
||||
} else {
|
||||
ModInstallEvent(
|
||||
item->info.m_id,
|
||||
UpdateFailed(list.unwrapErr())
|
||||
).post();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -114,7 +114,7 @@ Result<> Loader::setup() {
|
|||
if (!sett) {
|
||||
log::warn("Unable to load loader settings: {}", sett.unwrapErr());
|
||||
}
|
||||
GEODE_UNWRAP(this->refreshModsList());
|
||||
(void)this->refreshModsList();
|
||||
|
||||
this->queueInGDThread([]() {
|
||||
Loader::get()->addSearchPaths();
|
||||
|
|
|
@ -303,11 +303,14 @@ Result<> Mod::disable() {
|
|||
Result<> Mod::uninstall() {
|
||||
if (m_info.m_supportsDisabling) {
|
||||
GEODE_UNWRAP(this->disable());
|
||||
|
||||
if (m_info.m_supportsUnloading) GEODE_UNWRAP(this->unloadBinary());
|
||||
if (m_info.m_supportsUnloading) {
|
||||
GEODE_UNWRAP(this->unloadBinary());
|
||||
}
|
||||
}
|
||||
|
||||
if (!ghc::filesystem::remove(m_info.m_path)) {
|
||||
try {
|
||||
ghc::filesystem::remove(m_info.m_path);
|
||||
} catch(std::exception& e) {
|
||||
return Err(
|
||||
"Unable to delete mod's .geode file! "
|
||||
"This might be due to insufficient permissions - "
|
||||
|
|
|
@ -273,6 +273,16 @@ void ModInfoPopup::onClose(CCObject* pSender) {
|
|||
this->removeFromParentAndCleanup(true);
|
||||
};
|
||||
|
||||
void ModInfoPopup::setInstallStatus(std::optional<UpdateProgress> const& progress) {
|
||||
if (progress) {
|
||||
m_installStatus->setVisible(true);
|
||||
m_installStatus->setStatus(progress.value().second);
|
||||
m_installStatus->setProgress(progress.value().first);
|
||||
} else {
|
||||
m_installStatus->setVisible(false);
|
||||
}
|
||||
}
|
||||
|
||||
// LocalModInfoPopup
|
||||
|
||||
bool LocalModInfoPopup::init(Mod* mod, ModListLayer* list) {
|
||||
|
@ -569,8 +579,15 @@ LocalModInfoPopup* LocalModInfoPopup::create(Mod* mod, ModListLayer* list) {
|
|||
|
||||
// IndexItemInfoPopup
|
||||
|
||||
IndexItemInfoPopup::IndexItemInfoPopup()
|
||||
: m_installListener(
|
||||
this, &IndexItemInfoPopup::onInstallProgress,
|
||||
ModInstallFilter("")
|
||||
) {}
|
||||
|
||||
bool IndexItemInfoPopup::init(IndexItemHandle item, ModListLayer* list) {
|
||||
m_item = item;
|
||||
m_installListener.setFilter(m_item->info.m_id);
|
||||
|
||||
auto winSize = CCDirector::sharedDirector()->getWinSize();
|
||||
|
||||
|
@ -600,6 +617,44 @@ bool IndexItemInfoPopup::init(IndexItemHandle item, ModListLayer* list) {
|
|||
return true;
|
||||
}
|
||||
|
||||
void IndexItemInfoPopup::onInstallProgress(ModInstallEvent* event) {
|
||||
std::visit(makeVisitor {
|
||||
[&](UpdateFinished) {
|
||||
this->setInstallStatus(std::nullopt);
|
||||
|
||||
FLAlertLayer::create(
|
||||
"Install complete",
|
||||
"Mod succesfully installed! :) "
|
||||
"(You may need to <cy>restart the game</c> "
|
||||
"for the mod to take full effect)",
|
||||
"OK"
|
||||
)->show();
|
||||
|
||||
if (m_layer) {
|
||||
m_layer->reloadList();
|
||||
}
|
||||
this->onClose(nullptr);
|
||||
},
|
||||
[&](UpdateProgress const& progress) {
|
||||
this->setInstallStatus(progress);
|
||||
},
|
||||
[&](UpdateFailed const& info) {
|
||||
this->setInstallStatus(std::nullopt);
|
||||
|
||||
FLAlertLayer::create(
|
||||
"Installation failed :(", info, "OK"
|
||||
)->show();
|
||||
|
||||
m_installBtn->setEnabled(true);
|
||||
m_installBtn->setTarget(
|
||||
this, menu_selector(IndexItemInfoPopup::onInstall)
|
||||
);
|
||||
m_installBtnSpr->setString("Install");
|
||||
m_installBtnSpr->setBG("GE_button_01.png"_spr, false);
|
||||
}
|
||||
}, event->status);
|
||||
}
|
||||
|
||||
void IndexItemInfoPopup::onInstall(CCObject*) {
|
||||
auto list = Index::get()->getInstallList(m_item);
|
||||
if (!list) {
|
||||
|
@ -617,7 +672,7 @@ void IndexItemInfoPopup::onInstall(CCObject*) {
|
|||
// le nest
|
||||
ranges::join(
|
||||
ranges::map<std::vector<std::string>>(
|
||||
list.unwrap(),
|
||||
list.unwrap().list,
|
||||
[](IndexItemHandle handle) {
|
||||
return fmt::format(
|
||||
" - <cr>{}</c> (<cy>{}</c>)",
|
||||
|
@ -632,7 +687,22 @@ void IndexItemInfoPopup::onInstall(CCObject*) {
|
|||
)->show();
|
||||
}
|
||||
|
||||
void IndexItemInfoPopup::onCancel(CCObject*) {
|
||||
Index::get()->cancelInstall(m_item);
|
||||
}
|
||||
|
||||
void IndexItemInfoPopup::doInstall() {
|
||||
if (m_updateVersionLabel) {
|
||||
m_updateVersionLabel->setVisible(false);
|
||||
}
|
||||
this->setInstallStatus(UpdateProgress(0, "Starting install"));
|
||||
|
||||
m_installBtn->setTarget(
|
||||
this, menu_selector(IndexItemInfoPopup::onCancel)
|
||||
);
|
||||
m_installBtnSpr->setString("Cancel");
|
||||
m_installBtnSpr->setBG("GJ_button_06.png", false);
|
||||
|
||||
Index::get()->install(m_item);
|
||||
}
|
||||
|
||||
|
|
|
@ -48,6 +48,8 @@ protected:
|
|||
void keyDown(cocos2d::enumKeyCodes) override;
|
||||
void onClose(cocos2d::CCObject*);
|
||||
|
||||
void setInstallStatus(std::optional<UpdateProgress> const& progress);
|
||||
|
||||
virtual CCNode* createLogo(CCSize const& size) = 0;
|
||||
virtual ModInfo getModInfo() const = 0;
|
||||
};
|
||||
|
@ -80,10 +82,13 @@ public:
|
|||
class IndexItemInfoPopup : public ModInfoPopup, public FLAlertLayerProtocol {
|
||||
protected:
|
||||
IndexItemHandle m_item;
|
||||
EventListener<ModInstallFilter> m_installListener;
|
||||
|
||||
bool init(IndexItemHandle item, ModListLayer* list);
|
||||
|
||||
void onInstallProgress(ModInstallEvent* event);
|
||||
void onInstall(CCObject*);
|
||||
void onCancel(CCObject*);
|
||||
void doInstall();
|
||||
|
||||
void FLAlert_Clicked(FLAlertLayer*, bool) override;
|
||||
|
@ -91,6 +96,8 @@ protected:
|
|||
CCNode* createLogo(CCSize const& size) override;
|
||||
ModInfo getModInfo() const override;
|
||||
|
||||
IndexItemInfoPopup();
|
||||
|
||||
public:
|
||||
static IndexItemInfoPopup* create(IndexItemHandle item, ModListLayer* list);
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue