index is now download correctly + that is reported in the UI

This commit is contained in:
HJfod 2022-12-07 21:21:50 +02:00
parent 996acf15aa
commit e0d7dbf15b
12 changed files with 175 additions and 186 deletions

View file

@ -5318,7 +5318,7 @@ class TextArea : cocos2d::CCSprite {
virtual void draw() {}
virtual void setOpacity(unsigned char) = mac 0x19f760, win 0x33800;
bool init(gd::string str, char const* font, float width, float height, cocos2d::CCPoint anchor, float scale, bool disableColor) = mac 0x19ec70, win 0x33370, ios 0x92444;
static TextArea* create(gd::string str, char const* font, float width, float height, cocos2d::CCPoint anchor, float scale, bool disableColor) = mac 0x19eb40, win 0x33270;
static TextArea* create(gd::string str, char const* font, float scale, float width, cocos2d::CCPoint anchor, float height, bool disableColor) = mac 0x19eb40, win 0x33270;
void colorAllCharactersTo(cocos2d::ccColor3B color) = win 0x33830;
void setString(gd::string str) = mac 0x19eda0, win 0x33480;

View file

@ -71,8 +71,9 @@ namespace geode {
this->enable();
}
EventListener(std::function<Callback> fn, T filter = T()) :
m_callback(fn), m_filter(filter) {
EventListener(std::function<Callback> fn, T filter = T())
: m_callback(fn), m_filter(filter)
{
this->enable();
}
@ -86,14 +87,16 @@ namespace geode {
this->enable();
}
// todo: maybe add these?
EventListener(EventListener const& other) = delete;
EventListener(EventListener&& other) = delete;
void bind(std::function<Callback> fn) {
std::cout << "this: " << this << "\n";
m_callback = fn;
}
template <typename C>
void bind(C* cls, MemberFn<C> fn) {
std::cout << "this: " << this << "\n";
m_callback = std::bind(fn, cls, std::placeholders::_1);
}

View file

@ -40,6 +40,7 @@ namespace geode {
};
}
// clang-format off
#define $on_mod(type) \
template<class> \
void GEODE_CONCAT(geodeExecFunction, __LINE__)(ModStateEvent*); \
@ -56,3 +57,4 @@ static inline auto GEODE_CONCAT(Exec, __LINE__) = (geode::Loader::get()->schedul
), 0); \
template<class> \
void GEODE_CONCAT(geodeExecFunction, __LINE__)(ModStateEvent*)
// clang-format on

View file

@ -8,6 +8,7 @@
namespace geode {
constexpr auto NOTIFICATION_DEFAULT_TIME = 1.f;
constexpr auto NOTIFICATION_LONG_TIME = 4.f;
enum class NotificationIcon {
None,
@ -71,6 +72,12 @@ namespace geode {
void setIcon(cocos2d::CCSprite* icon);
void setTime(float time);
/**
* Set the wait time to default, wait the time and hide the notification.
* Equivalent to setTime(NOTIFICATION_DEFAULT_TIME)
*/
void waitAndHide();
/**
* Adds the notification to the current scene if it doesn't have a
* parent yet, and displays the show animation. If the time for the

View file

@ -306,6 +306,53 @@ namespace geode {
return m_obj > other.m_obj;
}
};
template <class Filter>
class EventListenerNode : public cocos2d::CCNode {
protected:
EventListener<Filter> m_listener;
public:
static EventListenerNode* create(EventListener<Filter> listener) {
auto ret = new EventListenerNode();
if (ret && ret->init()) {
ret->m_listener = listener;
ret->autorelease();
return ret;
}
CC_SAFE_DELETE(ret);
return nullptr;
}
static EventListenerNode* create(typename Filter::Callback callback) {
auto ret = new EventListenerNode();
if (ret && ret->init()) {
ret->m_listener = EventListener(callback);
ret->autorelease();
return ret;
}
CC_SAFE_DELETE(ret);
return nullptr;
}
template <class C>
static EventListenerNode* create(
C* cls, typename EventListener<Filter>::MemberFn<C> callback
) {
// for some reason msvc won't let me just call EventListenerNode::create...
// it claims no return value...
// despite me writing return EventListenerNode::create()......
auto ret = new EventListenerNode();
if (ret && ret->init()) {
ret->m_listener.bind(cls, callback);
ret->autorelease();
return ret;
}
CC_SAFE_DELETE(ret);
return nullptr;
}
};
}
// Cocos2d utils
@ -937,133 +984,4 @@ namespace geode::cocos {
return m_dict->allKeys(key)->count();
}
};
// namespace for storing implementation stuff for
// inline member functions
namespace {
// class that holds the lambda (probably should've just used
// std::function but hey, this one's heap-free!)
template <class F, class Ret, class... Args>
struct LambdaHolder {
bool m_assigned = false;
// lambdas don't implement operator= so we
// gotta do this wacky union stuff
union {
F m_lambda;
};
LambdaHolder() {}
~LambdaHolder() {
if (m_assigned) {
m_lambda.~F();
}
}
LambdaHolder(F&& func) {
this->assign(std::forward<F>(func));
}
Ret operator()(Args... args) {
if (m_assigned) {
return m_lambda(std::forward<Args>(args)...);
}
else {
return Ret();
}
}
void assign(F&& func) {
if (m_assigned) {
m_lambda.~F();
}
new (&m_lambda) F(func);
m_assigned = true;
}
};
// Extract parameters and return type from a lambda
template <class Func>
struct ExtractLambda : public ExtractLambda<decltype(&Func::operator())> {};
template <class C, class R, class... Args>
struct ExtractLambda<R (C::*)(Args...) const> {
using Ret = R;
using Params = std::tuple<Args...>;
};
// Class for storing the member function
template <class Base, class Func, class Args>
struct InlineMemberFunction;
template <class Base, class Func, class... Args>
struct InlineMemberFunction<Base, Func, std::tuple<Args...>> : public Base {
using Ret = typename ExtractLambda<Func>::Ret;
using Selector = Ret (Base::*)(Args...);
using Holder = LambdaHolder<Func, Ret, Args...>;
static inline Holder s_selector{};
Ret selector(Args... args) {
return s_selector(std::forward<Args>(args)...);
}
static Selector get(Func&& function) {
s_selector.assign(std::move(function));
return static_cast<Selector>(&InlineMemberFunction::selector);
}
};
}
/**
* Wrap a lambda into a member function pointer. Useful for creating
* callbacks that have to be members of a class without having to deal
* with all of the boilerplate associated with defining a new class
* member function.
*
* Do note that due to implementation problems, captures may have
* unexpected side-effects. In practice, lambda member functions with
* captures do not work properly in loops. If you assign the same
* member lambda to multiple different targets, they will share the
* same captured values.
*/
template <class Base, class Func>
[[deprecated(
"Due to too many implementation problems, "
"makeMemberFunction will be removed in the future."
)]] static auto
makeMemberFunction(Func&& function) {
return InlineMemberFunction<Base, Func, typename ExtractLambda<Func>::Params>::get(
std::move(function)
);
}
/**
* Create a SEL_MenuHandler out of a lambda with optional captures. Useful
* for adding callbacks to CCMenuItemSpriteExtras without needing to add
* the callback as a member to a class. Use the GEODE_MENU_SELECTOR class
* for even more concise code.
*
* Do note that due to implementation problems, captures may have
* unexpected side-effects. In practice, **you should not expect to be able
* to pass any more information than you can pass to a normal menu selector
* through captures**. If you assign the same member lambda to multiple
* different targets, they will share the same captured values.
*/
template <class Func>
[[deprecated(
"Due to too many implementation problems, "
"makeMenuSelector will be removed in the future."
)]] static cocos2d::SEL_MenuHandler
makeMenuSelector(Func&& selector) {
return reinterpret_cast<cocos2d::SEL_MenuHandler>(
makeMemberFunction<cocos2d::CCObject, Func>(std::move(selector))
);
}
#define GEODE_MENU_SELECTOR(senderArg, ...) \
makeMenuSelector([this](senderArg) { \
__VA_ARGS__; \
})
}

View file

@ -3,12 +3,12 @@
#include <array>
#include <Geode/modify/LoadingLayer.hpp>
#include <fmt/format.h>
#include <Geode/utils/cocos.hpp>
USE_GEODE_NAMESPACE();
struct CustomLoadingLayer : Modify<CustomLoadingLayer, LoadingLayer> {
bool m_updatingResources;
EventListener<ResourceDownloadFilter> m_resourceListener;
CustomLoadingLayer() : m_updatingResources(false) {}
@ -28,9 +28,11 @@ struct CustomLoadingLayer : Modify<CustomLoadingLayer, LoadingLayer> {
label->setID("geode-loaded-info");
this->addChild(label);
m_fields->m_resourceListener.bind(
// for some reason storing the listener as a field caused the
// destructor for the field not to be run
this->addChild(EventListenerNode<ResourceDownloadFilter>::create(
this, &CustomLoadingLayer::updateResourcesProgress
);
));
// verify loader resources
if (!InternalLoader::get()->verifyLoaderResources()) {

View file

@ -19,7 +19,7 @@ USE_GEODE_NAMESPACE();
class CustomMenuLayer;
static Ref<Notification> g_indexUpdateNotif = nullptr;
static Ref<Notification> INDEX_UPDATE_NOTIF = nullptr;
static Ref<CCSprite> g_geodeButton = nullptr;
struct CustomMenuLayer : Modify<CustomMenuLayer, MenuLayer> {
@ -99,18 +99,39 @@ struct CustomMenuLayer : Modify<CustomMenuLayer, MenuLayer> {
}
// update mods index
if (!g_indexUpdateNotif && !Index::get()->hasTriedToUpdate()) {
g_indexUpdateNotif = Notification::create(
if (!INDEX_UPDATE_NOTIF && !Index::get()->hasTriedToUpdate()) {
this->addChild(EventListenerNode<IndexUpdateFilter>::create(
this, &CustomMenuLayer::onIndexUpdate
));
INDEX_UPDATE_NOTIF = Notification::create(
"Updating Index", NotificationIcon::Loading, 0
);
g_indexUpdateNotif->show();
INDEX_UPDATE_NOTIF->show();
Index::get()->update();
}
return true;
}
void onIndexUpdate(IndexUpdateEvent* event) {
if (!INDEX_UPDATE_NOTIF) return;
std::visit(makeVisitor {
[](UpdateProgress const& prog) {},
[](UpdateFinished const&) {
INDEX_UPDATE_NOTIF->setIcon(NotificationIcon::Success);
INDEX_UPDATE_NOTIF->setString("Index Up-to-Date");
INDEX_UPDATE_NOTIF->waitAndHide();
INDEX_UPDATE_NOTIF = nullptr;
},
[](UpdateError const& info) {
INDEX_UPDATE_NOTIF->setIcon(NotificationIcon::Error);
INDEX_UPDATE_NOTIF->setString(info);
INDEX_UPDATE_NOTIF->setTime(NOTIFICATION_LONG_TIME);
INDEX_UPDATE_NOTIF = nullptr;
},
}, event->status);
}
void onGeode(CCObject*) {
ModListLayer::scene();
}

View file

@ -5,12 +5,10 @@ USE_GEODE_NAMESPACE();
std::unordered_set<EventListenerProtocol*> Event::s_listeners = {};
void EventListenerProtocol::enable() {
std::cout << "enable " << this << ": " << typeid(*this).name() << "\n";
Event::s_listeners.insert(this);
}
void EventListenerProtocol::disable() {
std::cout << "disable " << this << "\n";
Event::s_listeners.erase(this);
}
@ -24,13 +22,8 @@ void Event::postFrom(Mod* m) {
if (m) this->sender = m;
for (auto h : Event::s_listeners) {
try {
std::cout << h << ": " << typeid(*h).name() << "\n";
if (h->passThrough(this) == ListenerResult::Stop) {
break;
}
} catch(std::exception& e) {
std::cout << "fuck: " << h << ": " << e.what() << "\n";
if (h->passThrough(this) == ListenerResult::Stop) {
break;
}
}
}

View file

@ -116,7 +116,7 @@ Index::Index() {
std::bind(&Index::onSourceUpdate, this, std::placeholders::_1),
SourceUpdateFilter()
);
this->addSource("https://github.com/geode-sdk/index-test");
this->addSource("geode-sdk/index-test");
}
Index* Index::get() {
@ -261,10 +261,11 @@ void Index::downloadSource(IndexSource& src) {
}
// unzip new index
auto unzip = file::Unzip::intoDir(targetFile, targetDir, true);
auto unzip = file::Unzip::intoDir(targetFile, targetDir, true)
.expect("Unable to unzip new index");
if (!unzip) {
return SourceUpdateEvent(
src, UpdateError("Unable to unzip new index")
src, UpdateError(unzip.unwrapErr())
).post();
}
@ -302,26 +303,33 @@ void Index::updateSourceFromLocal(IndexSource& src) {
this->cleanupItems();
// read directory and add new items
for (auto& dir : ghc::filesystem::directory_iterator(src.dirname())) {
auto addRes = IndexItem::createFromDir(src.repository, dir);
if (!addRes) {
log::warn("Unable to add index item from {}: {}", dir, addRes.unwrapErr());
continue;
try {
for (auto& dir : ghc::filesystem::directory_iterator(
dirs::getIndexDir() / src.dirname()
)) {
auto addRes = IndexItem::createFromDir(src.repository, dir);
if (!addRes) {
log::warn("Unable to add index item from {}: {}", dir, addRes.unwrapErr());
continue;
}
auto add = addRes.unwrap();
// check if this major version of this item has already been added
if (m_items[add->info.m_id].count(add->info.m_version.getMajor())) {
log::warn(
"Item {}@{} has already been added, skipping",
add->info.m_id, add->info.m_version
);
continue;
}
// add new major version of this item
m_items[add->info.m_id].insert({
add->info.m_version.getMajor(),
add
});
}
auto add = addRes.unwrap();
// check if this major version of this item has already been added
if (m_items[add->info.m_id].count(add->info.m_version.getMajor())) {
log::warn(
"Item {}@{} has already been added, skipping",
add->info.m_id, add->info.m_version
);
continue;
}
// add new major version of this item
m_items[add->info.m_id].insert({
add->info.m_version.getMajor(),
add
});
} catch(std::exception& e) {
log::warn("Unable to read index source {}: {}", src.dirname(), e.what());
return;
}
// mark source as finished

View file

@ -193,6 +193,8 @@ void ModCell::updateState() {
}
void ModCell::loadFromMod(Mod* mod) {
m_mod = mod;
this->setupInfo(mod->getModInfo(), false);
auto viewSpr = ButtonSprite::create(
@ -264,6 +266,8 @@ IndexItemCell* IndexItemCell::create(
}
void IndexItemCell::loadFromItem(IndexItemHandle item) {
m_item = item;
this->setupInfo(item->info, true);
auto viewSpr = ButtonSprite::create(

View file

@ -145,6 +145,10 @@ void Notification::animateOut() {
m_bg->runAction(CCFadeTo::create(NOTIFICATION_FADEOUT, 0));
}
void Notification::waitAndHide() {
this->setTime(NOTIFICATION_DEFAULT_TIME);
}
void Notification::show() {
if (!m_showing) {
if (!s_queue->containsObject(this)) {

View file

@ -133,9 +133,10 @@ Result<std::vector<std::string>> utils::file::listFilesRecursively(std::string c
static constexpr auto MAX_ENTRY_PATH_LEN = 256;
struct ZipEntry {
unz_file_pos m_pos;
ZPOS64_T m_compressedSize;
ZPOS64_T m_uncompressedSize;
bool isDirectory;
unz_file_pos pos;
ZPOS64_T compressedSize;
ZPOS64_T uncompressedSize;
};
class file::UnzipImpl final {
@ -164,12 +165,17 @@ public:
// Read file and add to entries
unz_file_pos pos;
if (unzGetFilePos(m_zip, &pos) == UNZ_OK) {
auto len = strlen(fileName);
m_entries.insert({
fileName,
ZipEntry {
.m_pos = pos,
.m_compressedSize = fileInfo.compressed_size,
.m_uncompressedSize = fileInfo.uncompressed_size,
.isDirectory =
fileInfo.uncompressed_size == 0 &&
len > 0 &&
(fileName[len - 1] == '/' || fileName[len - 1] == '\\'),
.pos = pos,
.compressedSize = fileInfo.compressed_size,
.uncompressedSize = fileInfo.uncompressed_size,
},
});
}
@ -188,16 +194,20 @@ public:
auto entry = m_entries.at(name);
if (unzGoToFilePos(m_zip, &entry.m_pos) != UNZ_OK) {
if (entry.isDirectory) {
return Err("Entry is directory");
}
if (unzGoToFilePos(m_zip, &entry.pos) != UNZ_OK) {
return Err("Unable to navigate to entry");
}
if (unzOpenCurrentFile(m_zip) != UNZ_OK) {
return Err("Unable to open entry");
}
byte_array res;
res.resize(entry.m_uncompressedSize);
auto size = unzReadCurrentFile(m_zip, res.data(), entry.m_uncompressedSize);
if (size < 0 || size != entry.m_uncompressedSize) {
res.resize(entry.uncompressedSize);
auto size = unzReadCurrentFile(m_zip, res.data(), entry.uncompressedSize);
if (size < 0 || size != entry.uncompressedSize) {
return Err("Unable to extract entry");
}
unzCloseCurrentFile(m_zip);
@ -264,14 +274,31 @@ Result<byte_array> Unzip::extract(Path const& name) {
Result<> Unzip::extractTo(Path const& name, Path const& path) {
GEODE_UNWRAP_INTO(auto bytes, m_impl->extract(name));
GEODE_UNWRAP(file::writeBinary(path, bytes));
// create containing directories for target path
if (path.has_parent_path()) {
GEODE_UNWRAP(file::createDirectoryAll(path.parent_path()));
}
GEODE_UNWRAP(file::writeBinary(path, bytes).expect("Unable to write file {}: {error}", path.string()));
return Ok();
}
Result<> Unzip::extractAllTo(Path const& dir) {
GEODE_UNWRAP(file::createDirectoryAll(dir));
for (auto& [entry, _] : m_impl->entries()) {
GEODE_UNWRAP(this->extractTo(entry, dir / entry));
for (auto& [entry, info] : m_impl->entries()) {
if (info.isDirectory) {
GEODE_UNWRAP(file::createDirectoryAll(entry));
} else {
// make sure zip files like root/../../file.txt don't get extracted to
// avoid zip attacks
if (!ghc::filesystem::relative(dir / entry, dir).empty()) {
GEODE_UNWRAP(this->extractTo(entry, dir / entry));
} else {
log::error(
"Zip entry '{}' is not contained within zip bounds",
dir / entry
);
}
}
}
return Ok();
}