This commit is contained in:
altalk23 2023-04-09 18:29:18 +03:00
commit 22d138bee2
13 changed files with 179 additions and 58 deletions

View file

@ -1,5 +1,18 @@
# Geode Changelog # Geode Changelog
## v1.0.0-beta.13
* Add `SpacerNode` for layouts
* Add the `Dispatch` system for using events without linking to optional dependencies
* Add `EventFilter::getCallback` and make `EventListener::getFilter` return a reference
* Add `CCNode::getEventListenerCount`
* Add IDs to `CustomSongWidget`
* Rework event listeners to have them not all be placed in the same queue aswell as fixing issues with dangling pointers
* Make `SentAsyncWebRequest` dllexported, allowing mods to use async web requests
* Fix issues with `WeakRef`
* Fix being able to recursively click on developer names to open the list of their created mods
Thank you to [Fleeym](https://github.com/Fleeym/Fleeym) for contributing to this release!
## v1.0.0-beta.12 ## v1.0.0-beta.12
* Fix crash when installing mods * Fix crash when installing mods
* FMOD is now linked on MacOS * FMOD is now linked on MacOS

View file

@ -16,7 +16,7 @@ namespace geode {
std::tuple<Args...> m_args; std::tuple<Args...> m_args;
public: public:
DispatchEvent(std::string const& id, Args&&... args) DispatchEvent(std::string const& id, Args... args)
: m_id(id), m_args(std::make_tuple(args...)) {} : m_id(id), m_args(std::make_tuple(args...)) {}
std::tuple<Args...> getArgs() const { std::tuple<Args...> getArgs() const {

View file

@ -67,9 +67,12 @@ namespace geode {
template <typename T> template <typename T>
concept is_event = std::is_base_of_v<Event, T>; concept is_event = std::is_base_of_v<Event, T>;
template <is_event T> template <is_event T>
class EventFilter { class EventFilter {
protected:
EventListenerProtocol* m_listener = nullptr;
public: public:
using Callback = ListenerResult(T*); using Callback = ListenerResult(T*);
using Event = T; using Event = T;
@ -81,6 +84,14 @@ namespace geode {
EventListenerPool* getPool() const { EventListenerPool* getPool() const {
return DefaultEventListenerPool::get(); return DefaultEventListenerPool::get();
} }
void setListener(EventListenerProtocol* listener) {
m_listener = listener;
}
EventListenerProtocol* getListener() const {
return m_listener;
}
}; };
template <typename T> template <typename T>
@ -110,23 +121,28 @@ namespace geode {
return m_filter.getPool(); return m_filter.getPool();
} }
EventListener(T filter = T()) { EventListener(T filter = T()) : m_filter(filter) {
m_filter.setListener(this);
this->enable(); this->enable();
} }
EventListener(utils::MiniFunction<Callback> fn, T filter = T()) EventListener(utils::MiniFunction<Callback> fn, T filter = T())
: m_callback(fn), m_filter(filter) : m_callback(fn), m_filter(filter)
{ {
m_filter.setListener(this);
this->enable(); this->enable();
} }
EventListener(Callback* fnptr, T filter = T()) : m_callback(fnptr), m_filter(filter) { EventListener(Callback* fnptr, T filter = T()) : m_callback(fnptr), m_filter(filter) {
m_filter.setListener(this);
this->enable(); this->enable();
} }
template <class C> template <class C>
EventListener(C* cls, MemberFn<C> fn, T filter = T()) : EventListener(C* cls, MemberFn<C> fn, T filter = T()) :
EventListener(std::bind(fn, cls, std::placeholders::_1), filter) { EventListener(std::bind(fn, cls, std::placeholders::_1), filter)
{
m_filter.setListener(this);
this->enable(); this->enable();
} }
@ -134,6 +150,7 @@ namespace geode {
: m_callback(std::move(other.m_callback)), : m_callback(std::move(other.m_callback)),
m_filter(std::move(other.m_filter)) m_filter(std::move(other.m_filter))
{ {
m_filter.setListener(this);
other.disable(); other.disable();
this->enable(); this->enable();
} }
@ -142,6 +159,7 @@ namespace geode {
: m_callback(other.m_callback), : m_callback(other.m_callback),
m_filter(other.m_filter) m_filter(other.m_filter)
{ {
m_filter.setListener(this);
other.disable(); other.disable();
this->enable(); this->enable();
} }
@ -157,9 +175,14 @@ namespace geode {
void setFilter(T filter) { void setFilter(T filter) {
m_filter = filter; m_filter = filter;
m_filter.setListener(this);
} }
T getFilter() const { T& getFilter() {
return m_filter;
}
T const& getFilter() const {
return m_filter; return m_filter;
} }

View file

@ -17,6 +17,9 @@ namespace geode {
DataSaved, DataSaved,
}; };
/**
* Event that is fired when a mod is loaded / unloaded / enabled / disabled
*/
class GEODE_DLL ModStateEvent : public Event { class GEODE_DLL ModStateEvent : public Event {
protected: protected:
ModEventType m_type; ModEventType m_type;
@ -28,6 +31,9 @@ namespace geode {
Mod* getMod() const; Mod* getMod() const;
}; };
/**
* Listener for mod load/enable/disable/unload/data save events
*/
class GEODE_DLL ModStateFilter : public EventFilter<ModStateEvent> { class GEODE_DLL ModStateFilter : public EventFilter<ModStateEvent> {
public: public:
using Callback = void(ModStateEvent*); using Callback = void(ModStateEvent*);
@ -38,6 +44,13 @@ namespace geode {
public: public:
ListenerResult handle(utils::MiniFunction<Callback> fn, ModStateEvent* event); ListenerResult handle(utils::MiniFunction<Callback> fn, ModStateEvent* event);
/**
* Create a mod state listener
* @param mod The mod whose events to listen to, or nullptr to listen to
* all mods' all state events
* @param type Type of event to listen to. Ignored if mod is nullptr
*/
ModStateFilter(Mod* mod, ModEventType type); ModStateFilter(Mod* mod, ModEventType type);
}; };
} }

View file

@ -356,15 +356,36 @@ namespace geode {
} }
}; };
class GEODE_DLL WeakRefPool { class WeakRefPool;
std::unordered_set<cocos2d::CCObject*> m_pool;
class GEODE_DLL WeakRefController final {
private:
cocos2d::CCObject* m_obj;
WeakRefController(WeakRefController const&) = delete;
WeakRefController(WeakRefController&&) = delete;
friend class WeakRefPool;
public: public:
static WeakRefPool* get(); WeakRefController() = default;
bool isManaged();
void swap(cocos2d::CCObject* other);
cocos2d::CCObject* get() const;
};
bool isManaged(cocos2d::CCObject* obj); class GEODE_DLL WeakRefPool final {
void manage(cocos2d::CCObject* obj); std::unordered_map<cocos2d::CCObject*, std::shared_ptr<WeakRefController>> m_pool;
void check(cocos2d::CCObject* obj); void check(cocos2d::CCObject* obj);
friend class WeakRefController;
public:
static WeakRefPool* get();
std::shared_ptr<WeakRefController> manage(cocos2d::CCObject* obj);
}; };
/** /**
@ -392,7 +413,9 @@ namespace geode {
"WeakRef can only be used with a CCObject-inheriting class!" "WeakRef can only be used with a CCObject-inheriting class!"
); );
T* m_obj = nullptr; std::shared_ptr<WeakRefController> m_controller;
WeakRef(std::shared_ptr<WeakRefController> obj) : m_controller(obj) {}
public: public:
/** /**
@ -404,14 +427,12 @@ namespace geode {
* to it is freed or locked * to it is freed or locked
* @param obj Object to construct the WeakRef from * @param obj Object to construct the WeakRef from
*/ */
WeakRef(T* obj) : m_obj(obj) { WeakRef(T* obj) : m_controller(WeakRefPool::get()->manage(obj)) {}
WeakRefPool::get()->manage(obj);
}
WeakRef(WeakRef<T> const& other) : WeakRef(other.m_obj) {} WeakRef(WeakRef<T> const& other) : WeakRef(other.m_controller) {}
WeakRef(WeakRef<T>&& other) : m_obj(other.m_obj) { WeakRef(WeakRef<T>&& other) : m_controller(std::move(other.m_controller)) {
other.m_obj = nullptr; other.m_controller = nullptr;
} }
/** /**
@ -419,7 +440,10 @@ namespace geode {
*/ */
WeakRef() = default; WeakRef() = default;
~WeakRef() { ~WeakRef() {
WeakRefPool::get()->check(m_obj); // If the WeakRef is moved, m_controller is null
if (m_controller) {
m_controller->isManaged();
}
} }
/** /**
@ -427,8 +451,8 @@ namespace geode {
* a null Ref if the object has been freed * a null Ref if the object has been freed
*/ */
Ref<T> lock() const { Ref<T> lock() const {
if (WeakRefPool::get()->isManaged(m_obj)) { if (m_controller->isManaged()) {
return Ref(m_obj); return Ref(static_cast<T*>(m_controller->get()));
} }
return Ref<T>(nullptr); return Ref<T>(nullptr);
} }
@ -437,7 +461,7 @@ namespace geode {
* Check if the WeakRef points to a valid object * Check if the WeakRef points to a valid object
*/ */
bool valid() const { bool valid() const {
return WeakRefPool::get()->isManaged(m_obj); return m_controller->isManaged();
} }
/** /**
@ -446,9 +470,7 @@ namespace geode {
* @param other The new object to swap to * @param other The new object to swap to
*/ */
void swap(T* other) { void swap(T* other) {
WeakRefPool::get()->check(m_obj); m_controller->swap(other);
m_obj = other;
WeakRefPool::get()->manage(other);
} }
Ref<T> operator=(T* obj) { Ref<T> operator=(T* obj) {
@ -457,12 +479,12 @@ namespace geode {
} }
WeakRef<T>& operator=(WeakRef<T> const& other) { WeakRef<T>& operator=(WeakRef<T> const& other) {
this->swap(other.m_obj); this->swap(static_cast<T*>(other.m_controller->get()));
return *this; return *this;
} }
WeakRef<T>& operator=(WeakRef<T>&& other) { WeakRef<T>& operator=(WeakRef<T>&& other) {
this->swap(other.m_obj); this->swap(static_cast<T*>(other.m_controller->get()));
return *this; return *this;
} }
@ -471,33 +493,33 @@ namespace geode {
} }
bool operator==(T* other) const { bool operator==(T* other) const {
return m_obj == other; return m_controller->get() == other;
} }
bool operator==(WeakRef<T> const& other) const { bool operator==(WeakRef<T> const& other) const {
return m_obj == other.m_obj; return m_controller->get() == other.m_controller->get();
} }
bool operator!=(T* other) const { bool operator!=(T* other) const {
return m_obj != other; return m_controller->get() != other;
} }
bool operator!=(WeakRef<T> const& other) const { bool operator!=(WeakRef<T> const& other) const {
return m_obj != other.m_obj; return m_controller->get() != other.m_controller->get();
} }
// for containers // for containers
bool operator<(WeakRef<T> const& other) const { bool operator<(WeakRef<T> const& other) const {
return m_obj < other.m_obj; return m_controller->get() < other.m_controller->get();
} }
bool operator<=(WeakRef<T> const& other) const { bool operator<=(WeakRef<T> const& other) const {
return m_obj <= other.m_obj; return m_controller->get() <= other.m_controller->get();
} }
bool operator>(WeakRef<T> const& other) const { bool operator>(WeakRef<T> const& other) const {
return m_obj > other.m_obj; return m_controller->get() > other.m_controller->get();
} }
bool operator>=(WeakRef<T> const& other) const { bool operator>=(WeakRef<T> const& other) const {
return m_obj >= other.m_obj; return m_controller->get() >= other.m_controller->get();
} }
}; };

View file

@ -795,7 +795,12 @@ CCSize AxisLayout::getSizeHint(CCNode* on) const {
for (auto& node : CCArrayExt<CCNode*>(nodes)) { for (auto& node : CCArrayExt<CCNode*>(nodes)) {
auto axis = nodeAxis(node, m_axis, 1.f); auto axis = nodeAxis(node, m_axis, 1.f);
length += axis.axisLength; length += axis.axisLength;
cross += axis.crossLength; if (axis.crossLength > cross) {
axis.crossLength = cross;
}
}
if (!m_allowCrossAxisOverflow) {
cross = nodeAxis(on, m_axis, 1.f).crossLength;
} }
if (m_axis == Axis::Row) { if (m_axis == Axis::Row) {
return { length, cross }; return { length, cross };

View file

@ -9,7 +9,8 @@ bool DefaultEventListenerPool::add(EventListenerProtocol* listener) {
m_toAdd.push_back(listener); m_toAdd.push_back(listener);
} }
else { else {
m_listeners.push_back(listener); // insert listeners at the start so new listeners get priority
m_listeners.insert(m_listeners.begin(), listener);
} }
return true; return true;
} }
@ -37,7 +38,7 @@ void DefaultEventListenerPool::handle(Event* event) {
if (m_locked == 0) { if (m_locked == 0) {
ranges::remove(m_listeners, nullptr); ranges::remove(m_listeners, nullptr);
for (auto listener : m_toAdd) { for (auto listener : m_toAdd) {
m_listeners.push_back(listener); m_listeners.insert(m_listeners.begin(), listener);
} }
m_toAdd.clear(); m_toAdd.clear();
} }

View file

@ -13,7 +13,7 @@ Mod* ModStateEvent::getMod() const {
} }
ListenerResult ModStateFilter::handle(utils::MiniFunction<Callback> fn, ModStateEvent* event) { ListenerResult ModStateFilter::handle(utils::MiniFunction<Callback> fn, ModStateEvent* event) {
if (event->getMod() == m_mod && event->getType() == m_type) { if (!m_mod || (event->getMod() == m_mod && event->getType() == m_type)) {
fn(event); fn(event);
} }
return ListenerResult::Propagate; return ListenerResult::Propagate;

View file

@ -17,9 +17,11 @@ bool DevProfilePopup::setup(std::string const& developer) {
// installed mods // installed mods
for (auto& mod : Loader::get()->getAllMods()) { for (auto& mod : Loader::get()->getAllMods()) {
if (mod->getDeveloper() == developer) { if (mod->getDeveloper() == developer) {
items->addObject(ModCell::create( auto cell = ModCell::create(
mod, nullptr, ModListDisplay::Concise, { 358.f, 40.f } mod, nullptr, ModListDisplay::Concise, { 358.f, 40.f }
)); );
cell->disableDeveloperButton();
items->addObject(cell);
} }
} }
@ -28,9 +30,11 @@ bool DevProfilePopup::setup(std::string const& developer) {
if (Loader::get()->isModInstalled(item->info.id())) { if (Loader::get()->isModInstalled(item->info.id())) {
continue; continue;
} }
items->addObject(IndexItemCell::create( auto cell = IndexItemCell::create(
item, nullptr, ModListDisplay::Concise, { 358.f, 40.f } item, nullptr, ModListDisplay::Concise, { 358.f, 40.f }
)); );
cell->disableDeveloperButton();
items->addObject(cell);
} }
// mods list // mods list

View file

@ -94,24 +94,24 @@ void ModListCell::setupInfo(
auto creatorLabel = CCLabelBMFont::create(creatorStr.c_str(), "goldFont.fnt"); auto creatorLabel = CCLabelBMFont::create(creatorStr.c_str(), "goldFont.fnt");
creatorLabel->setScale(.43f); creatorLabel->setScale(.43f);
auto creatorBtn = CCMenuItemSpriteExtra::create( m_developerBtn = CCMenuItemSpriteExtra::create(
creatorLabel, this, menu_selector(ModListCell::onViewDev) creatorLabel, this, menu_selector(ModListCell::onViewDev)
); );
creatorBtn->setPositionX( m_developerBtn->setPositionX(
m_height / 2 + logoSize / 2 + 13.f m_height / 2 + logoSize / 2 + 13.f
+ creatorLabel->getScaledContentSize().width / 2 + creatorLabel->getScaledContentSize().width / 2
- m_menu->getPositionX() - m_menu->getPositionX()
); );
if (hasDesc && spaceForTags) { if (hasDesc && spaceForTags) {
creatorBtn->setPositionY(+7.5f); m_developerBtn->setPositionY(+7.5f);
} }
else if (hasDesc || spaceForTags) { else if (hasDesc || spaceForTags) {
creatorBtn->setPositionY(0.f); m_developerBtn->setPositionY(0.f);
} }
else { else {
creatorBtn->setPositionY(-7.f); m_developerBtn->setPositionY(-7.f);
} }
m_menu->addChild(creatorBtn); m_menu->addChild(m_developerBtn);
if (hasDesc) { if (hasDesc) {
auto descBG = CCScale9Sprite::create("square02b_001.png", {0.0f, 0.0f, 80.0f, 80.0f}); auto descBG = CCScale9Sprite::create("square02b_001.png", {0.0f, 0.0f, 80.0f, 80.0f});
@ -150,6 +150,10 @@ bool ModListCell::init(ModListLayer* list, CCSize const& size) {
return true; return true;
} }
void ModListCell::disableDeveloperButton() {
m_developerBtn->setEnabled(false);
}
// ModCell // ModCell
ModCell* ModCell::create( ModCell* ModCell::create(

View file

@ -23,6 +23,7 @@ protected:
CCLabelBMFont* m_description; CCLabelBMFont* m_description;
CCMenuItemToggler* m_enableToggle = nullptr; CCMenuItemToggler* m_enableToggle = nullptr;
CCMenuItemSpriteExtra* m_unresolvedExMark; CCMenuItemSpriteExtra* m_unresolvedExMark;
CCMenuItemSpriteExtra* m_developerBtn;
bool init(ModListLayer* list, CCSize const& size); bool init(ModListLayer* list, CCSize const& size);
void setupInfo(ModInfo const& info, bool spaceForTags, ModListDisplay display); void setupInfo(ModInfo const& info, bool spaceForTags, ModListDisplay display);
@ -35,6 +36,8 @@ public:
virtual void updateState() = 0; virtual void updateState() = 0;
virtual CCNode* createLogo(CCSize const& size) = 0; virtual CCNode* createLogo(CCSize const& size) = 0;
virtual std::string getDeveloper() const = 0; virtual std::string getDeveloper() const = 0;
void disableDeveloperButton();
}; };
/** /**

View file

@ -233,6 +233,21 @@ std::string geode::cocos::cc4bToHexString(ccColor4B const& color) {
return output; return output;
} }
bool WeakRefController::isManaged() {
WeakRefPool::get()->check(m_obj);
return m_obj;
}
void WeakRefController::swap(CCObject* other) {
WeakRefPool::get()->check(m_obj);
m_obj = other;
WeakRefPool::get()->check(m_obj);
}
CCObject* WeakRefController::get() const {
return m_obj;
}
WeakRefPool* WeakRefPool::get() { WeakRefPool* WeakRefPool::get() {
static auto inst = new WeakRefPool(); static auto inst = new WeakRefPool();
return inst; return inst;
@ -241,22 +256,23 @@ WeakRefPool* WeakRefPool::get() {
void WeakRefPool::check(CCObject* obj) { void WeakRefPool::check(CCObject* obj) {
// if this object's only reference is the WeakRefPool aka only weak // if this object's only reference is the WeakRefPool aka only weak
// references exist to it, then release it // references exist to it, then release it
if (m_pool.contains(obj) && obj->retainCount() == 1) { if (obj && m_pool.contains(obj) && obj->retainCount() == 1) {
obj->release(); obj->release();
// log::info("nullify {}", m_pool.at(obj).get());
m_pool.at(obj)->m_obj = nullptr;
m_pool.erase(obj); m_pool.erase(obj);
} }
} }
bool WeakRefPool::isManaged(CCObject* obj) { std::shared_ptr<WeakRefController> WeakRefPool::manage(CCObject* obj) {
this->check(obj); if (!m_pool.contains(obj)) {
return m_pool.contains(obj); CC_SAFE_RETAIN(obj);
} auto controller = std::make_shared<WeakRefController>();
controller->m_obj = obj;
void WeakRefPool::manage(CCObject* obj) { m_pool.insert({ obj, controller });
if (obj && !m_pool.contains(obj)) {
obj->retain();
m_pool.insert(obj);
} }
// log::info("get {} for {}", m_pool.at(obj).get(), obj);
return m_pool.at(obj);
} }
CCRect geode::cocos::calculateNodeCoverage(std::vector<CCNode*> const& nodes) { CCRect geode::cocos::calculateNodeCoverage(std::vector<CCNode*> const& nodes) {

View file

@ -1,6 +1,7 @@
#include <Geode/Loader.hpp> #include <Geode/Loader.hpp>
#include <Geode/loader/ModJsonTest.hpp> #include <Geode/loader/ModJsonTest.hpp>
#include <Geode/loader/ModEvent.hpp> #include <Geode/loader/ModEvent.hpp>
#include <Geode/utils/cocos.hpp>
#include "../dependency/main.hpp" #include "../dependency/main.hpp"
using namespace geode::prelude; using namespace geode::prelude;
@ -31,6 +32,22 @@ $execute {
}); });
} }
#include <Geode/modify/MenuLayer.hpp>
struct $modify(MenuLayer) {
bool init() {
if (!MenuLayer::init())
return false;
auto node = CCNode::create();
auto ref = WeakRef(node);
log::info("ref: {}", ref.lock().data());
node->release();
log::info("ref: {}", ref.lock().data());
return true;
}
};
// Modify // Modify
#include <Geode/modify/GJGarageLayer.hpp> #include <Geode/modify/GJGarageLayer.hpp>