This commit is contained in:
altalk23 2023-04-05 23:10:23 +03:00
commit a5b1640556
58 changed files with 1212 additions and 352 deletions

1
.gitignore vendored
View file

@ -51,6 +51,7 @@ loader/src/internal/about.hpp
loader/src/internal/resources.hpp
loader/resources/mod.json
loader/resources/blanks/rename.js
loader/resources/changelog.md
fods-catgirl-hideout.txt
# krita files too because alk is funny

View file

@ -1,93 +1,131 @@
# Geode Changelog
## v1.0.0-beta.12
* Fix crash when installing mods
* FMOD is now linked on MacOS
* `GEODE_DEBUG` on longer needs to be defined for `log::debug` to work
* Make Geode changelog visible in-game
* Make the changelog only be loaded once the changelog button is pressed in ModInfoPopup
* Fix the scrollbar not working for the changelog in ModInfoPopup
* Fix visual issues with scrollbars
## v1.0.0-beta.11
* New `geode::prelude` namespace to replace the old `USE_GEODE_NAMESPACE()` macro
* Add `CCNode::removeChildByID`
* Add `CCNode::hasAncestor`
* Add `CCScene::get` and `CCScheduler::get`
* Add `geode::cocos::getMousePos`
* Add `GEODE_DONT_INSTALL_MODS` option to CMake
* Add logging `std::vector` with `log::` functions
* Add `EventListener::getFilter`
* Add `AttributeSetEvent` for detecting when attributes are added to nodes
* Add `CCNode::addEventListener` (along with other related functions) as convenience methods for adding event listeners that target nodes
* Add `WeakRef` as a weak pointer alternative to `Ref` (see [the docs](https://docs.geode-sdk.org/tutorials/memory#weakref) for a tutorial)
* Add option to ignore invisible children to `Layout`
* `CCNode` attributes now use `json::Value` over `std::any` for ABI compatability
* Implement file picker on Mac
* Define `CCNode::retainCount` inline
* `Layout` now inherits from `CCObject`, allowing you to share layouts across multiple nodes
* Update TulipHook version
* Make sure mod load/enable/etc. events are only ever posted in the GD thread
* `Mod::getResourcesDir` now returns `geode/unzipped/{mod.id}/resources/{mod.id}` in conjunction with [CLI v2.1.0](https://github.com/geode-sdk/cli/releases/tag/v2.1.1)
* Give a name to `ccTouchType`
* Fix `Scrollbar` being funky sometimes
* Fix mod resources not being loaded if the mod is enabled at runtime
* Fix `EditLevelLayer` description update button ID & layout
* Fix hooking functions with unpredictable calling conventions
* Fix `setup_geode_mod` not linking to Geode if CLI calls are disabled
* Fix code editors showing a ton of warnings with `$modify`
* Fix top sprite sizes of `CircleButtonSprite` and `EditorButtonSprite`
* Fix `Mod::enableHook` error message
* Lots of bindings on both Windows & Mac (50+ commits related to bindings)
## v1.0.0-beta.10
* Fix loader minimum mod version preventing the loader itself from loading
* Fix recursive comparison in VersionTag
* `geode/unzipped` is now deleted on startup if it exists
## v1.0.0-beta.9
* Fix multiple modifiers not being able to have fields on same class due to having same field index (7710fa9)
* Add `Result::ok` and `Result::err` for converting the `Result` into `std::optional` (4a15afc)
* Fix multiple modifiers not being able to have fields on same class due to having same field index
* Add `Result::ok` and `Result::err` for converting the `Result` into `std::optional`
## v1.0.0-beta.8
* Unload the mod even when first time warning pops up (63b4774)
* Error when address of a function returns nullptr when hooking (724a9d3)
* Add support for Geode CLI v2.0.0 (which has not been released yet) (088ac7b, deadb58)
* Logging no longer causes a crash on invalid formats, but instead just warns (6aba7cf)
* `file::pickFile` now uses the last item in the default path as the default filename to save/open (5c9ee08)
* Fix `EditorPauseLayer` crashing constantly due to some members being accidentally set to `nullptr` (33a91d6)
* Unload the mod even when first time warning pops up
* Error when address of a function returns nullptr when hooking
* Add support for Geode CLI v2.0.0 (which has not been released yet)
* Logging no longer causes a crash on invalid formats, but instead just warns
* `file::pickFile` now uses the last item in the default path as the default filename to save/open
* Fix `EditorPauseLayer` crashing constantly due to some members being accidentally set to `nullptr`
## v1.0.0-beta.7
* Add `Mod::getResourcesDir` for getting the mod resources directory (0055032)
* Deprecate `file::listFiles` for `file::readDirectory` (9f60091)
* Fix getting virtual function addresses for CCFileUtils (c183a35)
* Rename `BasedButtonSprite` sprite names to be more expressive (4d2daec)
* Fix `typeinfo_cast` causing a crash if passed a `nullptr` on MacOS (d024dbb)
* Fix settings not getting broadcasted (7089194)
* Make `Loader::getLoadedMod` and `Loader::isModLoaded` also check if mod is enabled (3222097)
* Display Geode commit hash in the bottom right of the mod info layer (1dfa907)
* Fix `EditorPauseLayer` info labels on the top left being too big (f5983a2)
* Add `Mod::getResourcesDir` for getting the mod resources directory
* Deprecate `file::listFiles` for `file::readDirectory`
* Fix getting virtual function addresses for CCFileUtils
* Rename `BasedButtonSprite` sprite names to be more expressive
* Fix `typeinfo_cast` causing a crash if passed a `nullptr` on MacOS
* Fix settings not getting broadcasted
* Make `Loader::getLoadedMod` and `Loader::isModLoaded` also check if mod is enabled
* Display Geode commit hash in the bottom right of the mod info layer
* Fix `EditorPauseLayer` info labels on the top left being too big
## v1.0.0-beta.6
* Reworked layouts from the ground up - see [the docs page](https://docs.geode-sdk.org/tutorials/layouts) for more information about how the new system works (#137)
* Update the IDs for a bunch of layers, aswell as adding some predefined layouts (3f64b98, ef9e741, a78bc0c, cb1a6f1, ea037a9, f7ddf0a, ...)
* Add IDs & layouts to `EditorPauseLayer` (12d88ae)
* Add `CCNode::insertBefore` and `CCNode::insertAfter` for adding children before/after existing ones (eb10eca, 4613af6)
* Add IDs & layouts to `EditorPauseLayer`
* Add `CCNode::insertBefore` and `CCNode::insertAfter` for adding children before/after existing ones
* Add `CCSize::aspect` convenience method
* Add `Mod::getResourcesDir` for getting a mod's runtime resources directory (where `[mod.json].resources.files` are placed) (0055032)
* Add `Mod::addCustomSetting` for convenience in registering custom settings (7089194)
* Add `file::readDirectory` as a sanely named alternative to `file::listFiles` (9f60091)
* Move `GEODE_DLL` to the structs themselves in `JsonValidation` (06bc6fd)
* Versions now support tag numbers & version tags are now used in comparisons. This change does not affect old betas, which still internally report their version as just `v1.0.0-beta`, but starting with this beta the version is correctly reported as `v1.0.0-beta.6` and correctly compared against other versions (bbbf332)
* `Loader::getLoadedMod` and `Loader::isModLoaded` now only return if the mod is also enabled (3222097)
* Geode's internal mod representation is now included in the loader's loaded mods list (4261e99)
* Fix settings value changes not being broadcast. This causes an API break relating to custom settings; `SettingValue` now requires the owner mod ID in its constructor (7089194)
* Fix some warnings (9c9706b, f7bfa21)
* Fix `CCNode::swapChildIndices` (ba0851e)
* Fix `typeinfo_cast` causing a crash if passed a `nullptr` (f4a3258)
* Fix `ranges::reverse` causing UB (ffd50eb)
* Other fixes & improvements (cb00c21)
* Add `Mod::getResourcesDir` for getting a mod's runtime resources directory (where `[mod.json].resources.files` are placed)
* Add `Mod::addCustomSetting` for convenience in registering custom settings
* Add `file::readDirectory` as a sanely named alternative to `file::listFiles`
* Move `GEODE_DLL` to the structs themselves in `JsonValidation`
* Versions now support tag numbers & version tags are now used in comparisons. This change does not affect old betas, which still internally report their version as just `v1.0.0-beta`, but starting with this beta the version is correctly reported as `v1.0.0-beta.6` and correctly compared against other versions
* `Loader::getLoadedMod` and `Loader::isModLoaded` now only return if the mod is also enabled
* Geode's internal mod representation is now included in the loader's loaded mods list
* Fix settings value changes not being broadcast. This causes an API break relating to custom settings; `SettingValue` now requires the owner mod ID in its constructor
* Fix some warnings
* Fix `CCNode::swapChildIndices`
* Fix `typeinfo_cast` causing a crash if passed a `nullptr`
* Fix `ranges::reverse` causing UB
* Other fixes & improvements
## v1.0.0-beta.5
- Make ModInfo Pimpl 51990ad89b25cecbabaf748a5bcb279227fce090
- Fix crash with event listeners 1f7d50a9b9140d02f6a9afb97734eb9761b6a0d4
- Some bindings 4a9f6ba52a3d756d9bc28c1809afc92479783673
- Make mods binaries not silently fail to load 0eb5f01ca81435cb90f2bc9d8d97a86405dadd1c
- Assume dependency version is >= comparison by default 41aef57758d7b858d5fa7cb22ab1ffe603ff365f
- Fix not following thunks 65f2cbb286cc1e5af57e43451862a2233d66453e
- Make ModInfo Pimpl
- Fix crash with event listeners
- Some bindings
- Make mods binaries not silently fail to load
- Assume dependency version is >= comparison by default
- Fix not following thunks
## v1.0.0-beta.4
- add some bindings
- fix macOS libzstd crash
- Add some bindings
- Fix macOS libzstd crash
## v1.0.0-beta.3
- Better support for dependencies with [Geode CLI v1.4.x](https://github.com/geode-sdk/cli/releases/latest): mod dependencies are now automatically installed from the mods index by simply declaring them in your `mod.json`. See [the tutorial page in docs](https://docs.geode-sdk.org/mods/dependencies/) for more (f32aaa8b124bdd040a453bc25d31a4e463cf1309)
- The `create_geode_file` CMake function has been replaced by `setup_geode_mod`. The old `create_geode_file` function is still available, but will be deprecated in the future (f32aaa8b124bdd040a453bc25d31a4e463cf1309)
- `Result::except` now works with non-copyable types (f32aaa8b124bdd040a453bc25d31a4e463cf1309)
- `Zip` and `Unzip` now support in-memory ZIP extraction and creation (f32aaa8b124bdd040a453bc25d31a4e463cf1309)
- `ComparableVersionInfo::compare` now always returns false if the major versions are different (f32aaa8b124bdd040a453bc25d31a4e463cf1309)
- `ComparableVersionInfo` parsing now expects equal to be marked with a single `=` instead of two (`==v1.2.3` => `=v1.2.3`) (6d3847d9e13fdf69cea0b9a69376ebcd88e71725)
- Fix `DS_Dictionary`-related `gd::string` Cocos2d functions not being linked (ab0030136ab8c20731fe768ef5f3b16ae4245583)
- `CC_DLL` no longer expands to dllexport/dllimport (ab0030136ab8c20731fe768ef5f3b16ae4245583)
- The JSON lib now default constructs to object, hopefully fixing uncaught bugs (30101fc0b5112317aac3c9eeea0aab888ca8b30d)
- Something related to codegen and addresser? I have no clue what it does, so you probably won't have either (ae1eb8bb7162342f598e047a459e3808801f731a, 1e9faac5aa5a32b0cb5e6ffaac0c5cbfc217e9cb, fea049cca069a08181ab66cacda6f2417c006a2f, ad261846919e503cc3faf75d777ab2f2882bc6bb, ...)
- MacOS minimum version bumped to 10.14 (916f54063008c6bdf892d02f8bcd92b58606817e)
- Better support for dependencies with [Geode CLI v1.4.x](https://github.com/geode-sdk/cli/releases/latest): mod dependencies are now automatically installed from the mods index by simply declaring them in your `mod.json`. See [the tutorial page in docs](https://docs.geode-sdk.org/mods/dependencies/) for more
- The `create_geode_file` CMake function has been replaced by `setup_geode_mod`. The old `create_geode_file` function is still available, but will be deprecated in the future
- `Result::except` now works with non-copyable types
- `Zip` and `Unzip` now support in-memory ZIP extraction and creation
- `ComparableVersionInfo::compare` now always returns false if the major versions are different
- `ComparableVersionInfo` parsing now expects equal to be marked with a single `=` instead of two (`==v1.2.3` => `=v1.2.3`)
- Fix `DS_Dictionary`-related `gd::string` Cocos2d functions not being linked
- `CC_DLL` no longer expands to dllexport/dllimport
- The JSON lib now default constructs to object, hopefully fixing uncaught bugs
- Something related to codegen and addresser? I have no clue what it does, so you probably won't have either
- MacOS minimum version bumped to 10.14
## v1.0.0-beta.2
* Fixed bug where `Mod::getSavedValue` would cause a crash due to trying operator on a null JSON value (5bbd34c)
* Fixed bug where loading would crash if one of the mods' binaries failed to load (ef86ae0)
* Fixed bug where `Mod::getSavedValue` would cause a crash due to trying operator on a null JSON value
* Fixed bug where loading would crash if one of the mods' binaries failed to load
## v1.0.0-beta.1
* Switched to [a new custom-built JSON library](https://github.com/geode-sdk/json) to replace `nlohmann::json` for compile-time improvements; if you were using the old JSON library, you can add & link to `nlohmann::json` in your own project, or update to use the new API. (deab672)
* Fix resources not being downloaded automatically by using a fallback to latest release (a418828)
* Add a new clear instruction popup in case downloading still fails (30dc9ad)
* String ID hooks now have higher priority, so they should always be applied regardless of if you call `NodeIDs::provideFor` or not (though it can still be called to absolutely ensure the IDs are there!) (b6a6e4d)
* Various internal bugfixes & improvements (ceb02e9, a90b3e1, b00ab40, c644b43)
* Switched to [a new custom-built JSON library](https://github.com/geode-sdk/json) to replace `nlohmann::json` for compile-time improvements; if you were using the old JSON library, you can add & link to `nlohmann::json` in your own project, or update to use the new API.
* Fix resources not being downloaded automatically by using a fallback to latest release
* Add a new clear instruction popup in case downloading still fails
* String ID hooks now have higher priority, so they should always be applied regardless of if you call `NodeIDs::provideFor` or not (though it can still be called to absolutely ensure the IDs are there!)
* Various internal bugfixes & improvements
## v1.0.0-alpha
- Major rework of the entire framework; most notable changes include switching to a whole new hooking framework (TulipHook), simplifying many parts of the framework, and making it production-ready.
## v0.7.0
@ -105,7 +143,6 @@
- Add more bindings
## v0.6.0
- Mod resource loading has been reworked again, with the intent of adding support for texture pack loaders
- Added `Loader::addTexturePath` and `Loader::removeTexturePath` to work with additional resource paths
- Mods that work with Cocos2d search paths directly should convert to using the above functions
@ -121,7 +158,6 @@
- Add IDs to `LevelSearchLayer` (thank you @Jollycistaken)
## v0.5.0
- Added `CCFileUtils::get`
- Fix crashes related to setting IDs in `MenuLayer`
- Remove `Loader::updateModResourcePaths` and `Loader::updateResourcePaths`. The minimum mod target version is still v0.4.0 however, as you should never have been using these functions.
@ -130,17 +166,14 @@
- Finally added a license to Geode! The framework is now licensed under BSL v1.0.
## v0.4.8
- CLI issues fixed in v1.0.6 so loader again verifies if loader resources are missing / corrupt on startup
- Resource download text is no longer a popup on top of the title but instead just replaces the loading text
- Add delegates to `EditLevelLayer`
## v0.4.7
- Loader resources check would always fail due to CLI issues, so for now loader just checks if the resources folder exists
## v0.4.6
- Automatically checks & downloads loader resources if they are missing / corrupt on startup
- CMake rework; `GeodeFile.cmake` now checks and verifies CLI version
- Add optional `DONT_INSTALL` parameter to `create_geode_file`
@ -150,7 +183,6 @@
- Change `CCArrayExt` to use `Ref`
## v0.4.5
- Rework bindings and codegen to improve compile times, now individual bindings can be included with `<Geode/binding/{ClassName}.hpp>`
- Modify has also been separated, you can now include individual modifiers with `<Geode/modify/{ClassName}.hpp>`
- Various other fixes to improve compile times
@ -175,25 +207,21 @@
- Remove automatic mod updating for now, however automatic update checking for mods is still there
## v0.4.4
- New `listenForSettingChanges` API for more ergonomically listening for setting changes
- Fixed bug where GD was unopenable through Steam
- Various other internal fixes
## v0.4.3
- Simplified the minimum and maximum loader versions, loader will now load any mod whose target version major and minor match. In practice, this means that for example mods whose target version is v0.4.8 can be loaded by loader of version v0.4.6.
- Add `Geode/ui/GeodeUI.hpp` header for exposing some access to internal Geode UI like opening a mod's settings popup
- Fix crash with settings that could have a slider control
## v0.4.2
- Moved SDK version to its own file so CLI can query it
- md4c is now linked statically on MacOS
- Fix log filenames
## v0.4.1
- Initial dev release of Geode.
---
@ -201,51 +229,39 @@
Note that from here on, changes to the framework were not tracked by versions as the framework was still considered to be in heavy development and not released. Instead, major changes are listed by dates.
## 2022/10/10
- Geode released for developers
## 2022/10/08
- `ui` branch merged to `main`
## 2022/10/03
- New CLI finished
- `ui` branch finished
## 2022/08/01
- CLI redesign started
## 2022/07/30
- `sdk`, `loader` and `api` repos all merged into one `geode` repo
## 2022/05/24
- Geode announced to be merging with Hyperdash, later on it turned out we were all pathological liars
## 2022/05/02
- Installer on Windows
## 2022/04/30
- Installing mods in-game works
## 2022/01/23
- CLI started
## 2022/01/19
- Lilac and Cacao merged and renamed to Geode
## 2021/07/30
- Lilac started by Mat, HJfod and Pie
## 2021/01/25
- CacaoSDK started by Camila, based on CappuccinoSDK

View file

@ -67,6 +67,7 @@ target_compile_definitions(${PROJECT_NAME} INTERFACE -DPROJECT_NAME=${CMAKE_PROJ
set(GEODE_CODEGEN_PATH ${CMAKE_CURRENT_BINARY_DIR}/codegenned)
set(GEODE_BIN_PATH ${CMAKE_CURRENT_SOURCE_DIR}/bin)
set(GEODE_LOADER_PATH ${CMAKE_CURRENT_SOURCE_DIR}/loader)
set(GEODE_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR})
include(cmake/GeodeFile.cmake)
include(cmake/Platform.cmake)
@ -146,7 +147,7 @@ endif()
add_library(GeodeCodegenSources ${GEODE_CODEGEN_PATH}/Geode/GeneratedSource.cpp ${GEODE_CODEGEN_PATH}/Geode/GeneratedAddress.cpp)
target_link_directories(GeodeCodegenSources PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/loader/include/link)
target_link_libraries(GeodeCodegenSources PRIVATE ghc_filesystem GeodeFilesystemImpl fmt TulipHookInclude)
target_link_libraries(GeodeCodegenSources PRIVATE ghc_filesystem GeodeFilesystemImpl fmt TulipHookInclude mat-json)
target_include_directories(GeodeCodegenSources PRIVATE
${GEODE_CODEGEN_PATH}
${GEODE_LOADER_PATH}/include

View file

@ -1 +1 @@
1.0.0-beta.10
1.0.0-beta.13

View file

@ -65,8 +65,18 @@ class cocos2d::CCCallFuncO {
}
class cocos2d::CCClippingNode {
CCClippingNode() {
m_pStencil = nullptr;
m_fAlphaThreshold = 1.f;
m_bInverted = false;
}
~CCClippingNode() = mac 0x4191e0;
static cocos2d::CCClippingNode* create() = mac 0x4192a0;
static cocos2d::CCClippingNode* create(cocos2d::CCNode*) = mac 0x419330;
// Inlined in create() call
auto init() = mac 0x4193e0;
// Inlined in create() call
auto init(cocos2d::CCNode* stencil) = mac 0x419400;
auto getAlphaThreshold() const = mac 0x419a10;
auto getStencil() const = mac 0x4199c0;
auto isInverted() const = mac 0x419a30;
@ -151,6 +161,31 @@ class cocos2d::CCDrawNode {
virtual ~CCDrawNode() = mac 0x378cc0;
}
class cocos2d::CCAction {
CCAction() = mac 0x35b610;
virtual ~CCAction() = mac 0x35b6b0;
auto stop() = mac 0x35b860;
auto copyWithZone(cocos2d::CCZone* zone) = mac 0x35b7a0;
auto startWithTarget(cocos2d::CCNode* target) = mac 0x35b850;
auto step(float dt) = mac 0x35b880;
auto isDone() = mac 0x35b870;
auto update(float time) = mac 0x35b890;
}
class cocos2d::CCFiniteTimeAction {
// same as CCActionInterval::reverse i think
auto reverse() = mac 0x1f2720;
}
class cocos2d::CCActionInterval {
auto copyWithZone(cocos2d::CCZone* zone) = mac 0x1f2550;
auto isDone() = mac 0x1f2640;
auto startWithTarget(cocos2d::CCNode* pTarget) = mac 0x1f2700;
auto step(float dt) = mac 0x1f2660;
auto reverse() = mac 0x1f2720;
bool initWithDuration(float d) = mac 0x1f2510;
}
class cocos2d::CCEaseBackIn {
static cocos2d::CCEaseBackIn* create(cocos2d::CCActionInterval*) = mac 0x2a41b0;
}
@ -176,6 +211,7 @@ class cocos2d::CCEGLView {
static cocos2d::CCEGLView* sharedOpenGLView() = mac 0x295320;
virtual void swapBuffers() = mac 0x295510;
void updateWindow(int width, int height);
void setupWindow(cocos2d::CCRect);
void toggleFullScreen(bool fullscreen);
void pollEvents();
void onGLFWCharCallback(GLFWwindow* window, unsigned int entered);
@ -407,12 +443,12 @@ class cocos2d::CCMenu {
virtual auto registerWithTouchDispatcher() = mac 0x438cd0, ios 0x131f8c;
virtual auto onExit() = mac 0x438bd0, ios 0x131ed4;
virtual auto removeChild(cocos2d::CCNode*, bool) = mac 0x438c20, ios 0x15e630;
auto initWithArray(cocos2d::CCArray*) = mac 0x4389f0, ios 0x131d04;
auto itemForTouch(cocos2d::CCTouch*) = mac 0x438dd0;
bool initWithArray(cocos2d::CCArray*) = mac 0x4389f0, ios 0x131d04;
cocos2d::CCMenuItem* itemForTouch(cocos2d::CCTouch*) = mac 0x438dd0;
}
class cocos2d::CCMenuItem {
auto initWithTarget(cocos2d::CCObject*, cocos2d::SEL_MenuHandler) = mac 0x1fb7f0;
bool initWithTarget(cocos2d::CCObject*, cocos2d::SEL_MenuHandler) = mac 0x1fb7f0;
virtual ~CCMenuItem() = mac 0x1fb8e0, ios 0x2cdf4;
virtual auto activate() = mac 0x1fba70, ios 0x2ceb0;
virtual auto selected() = mac 0x1fb9e0, ios 0x2ce2e;
@ -635,9 +671,6 @@ class cocos2d::CCObject {
auto isEqual(cocos2d::CCObject const*) = mac 0x250f20, ios 0x439e4;
auto release() = mac 0x250ea0, ios 0x43984;
auto retain() = mac 0x250ec0, ios 0x439a8;
unsigned int retainCount() const {
return m_uReference;
}
virtual auto setTag(int) = mac 0x250f60, ios 0x43a10;
~CCObject() = mac 0x250d20, ios 0x6ac0;
@ -701,6 +734,8 @@ class cocos2d::CCScheduler {
auto scheduleUpdateForTarget(cocos2d::CCObject*, int, bool) = mac 0x2438d0;
auto unscheduleAllForTarget(cocos2d::CCObject*) = mac 0x243e40;
auto unscheduleUpdateForTarget(cocos2d::CCObject const*) = mac 0x243c60;
auto resumeTargets(cocos2d::CCSet*) = mac 0x244680;
auto pauseAllTargets() = mac 0x244550;
virtual void update(float delta) = mac 0x2446d0;
}
@ -931,6 +966,7 @@ class cocos2d::CCTransitionFade {
class cocos2d::ZipUtils {
static auto compressString(gd::string, bool, int) = mac 0xe9a50;
static auto decompressString(gd::string, bool, int) = mac 0xea380;
static int ccDeflateMemory(unsigned char*, unsigned int, unsigned char**) = mac 0xe9cf0;
}
class cocos2d::extension::CCControl {
@ -1042,12 +1078,24 @@ class cocos2d {
static auto ccDrawSolidRect(cocos2d::CCPoint, cocos2d::CCPoint, cocos2d::_ccColor4F) = mac 0xecf00;
static auto ccGLEnableVertexAttribs(unsigned int) = mac 0x1ae740;
static auto ccGLBindTexture2D(GLuint) = mac 0x1ae610;
static float ccpDistance(cocos2d::CCPoint const&, cocos2d::CCPoint const&) = mac 0x1aaf90;
static void ccDrawPoly(cocos2d::CCPoint const*, unsigned int, bool) = mac 0xed0a0;
static void ccDrawColor4B(GLubyte, GLubyte, GLubyte, GLubyte) = mac 0xeddd0;
}
// class DS_Dictionary {
// DS_Dictionary() = mac 0xbe9a0;
// int getIntegerForKey(char const*) = mac 0xc1610;
// void setIntegerForKey(char const*, int) = mac 0xc26b0;
// }
class DS_Dictionary {
DS_Dictionary() = mac 0xbe9a0;
~DS_Dictionary() = mac 0x393c30;
bool saveRootSubDictToString() = mac 0xc09c0;
bool loadRootSubDictFromString(gd::string) = mac 0xbfd80;
bool stepIntoSubDictWithKey(char const*) = mac 0xc0cd0;
int getIntegerForKey(char const*) = mac 0xc1610;
void setIntegerForKey(char const*, int) = mac 0xc26b0;
}
class pugi::xml_document {
xml_document() = mac 0x393a80;
~xml_document() = mac 0x393b50;
}
// clang-format on

View file

@ -97,7 +97,10 @@ class AppDelegate : cocos2d::CCApplication, cocos2d::CCSceneDelegate {
void resumeSound() = win 0x3d4d0;
void setupGLView() = win 0x3c950;
PAD = win 0x4;
cocos2d::CCScene* m_runningScene;
bool m_loadingFinished;
// there's 0x18 more on Windows
}
class ArtistCell : TableViewCell {
@ -531,6 +534,10 @@ class CCScrollLayerExtDelegate {
virtual void scrollViewTouchEnd(CCScrollLayerExt*) {}
}
class CCSpritePart : CCSpritePlus {
}
class CCSpritePlus : cocos2d::CCSprite {
bool initWithSpriteFrameName(const char*) = mac 0x248670, win 0x1c1e0;
void setScaleX(float scale) = win 0x1c440;
@ -932,6 +939,7 @@ class CustomSongLayer : FLAlertLayer, FLAlertLayerProtocol, TextInputDelegate, G
}
class CustomSongWidget : cocos2d::CCNode, MusicDownloadDelegate, FLAlertLayerProtocol {
bool init(SongInfoObject*, LevelSettingsObject*, bool, bool, bool, bool, bool hideBackground) = mac 0x37be20, win 0x685b0;
void FLAlert_Clicked(FLAlertLayer*, bool) {}
void loadSongInfoFinished(SongInfoObject*) {}
@ -1111,6 +1119,12 @@ class EditButtonBar : cocos2d::CCNode {
}
void loadFromItems(cocos2d::CCArray* buttons, int rowCount, int columnCount, bool idk) = mac 0x351010, win 0x6e5e0, ios 0x2dd060;
static EditButtonBar* create(
cocos2d::CCArray* buttons,
cocos2d::CCPoint point,
int idk, bool idk0,
int rowCount, int columnCount
) = win 0x6e450;
cocos2d::CCPoint m_position;
int m_unknown;
@ -1235,10 +1249,11 @@ class EditorUI : cocos2d::CCLayer, FLAlertLayerProtocol, ColorSelectDelegate, GJ
virtual void scaleChanged(float) = mac 0x25490, win 0x88df0;
virtual void scaleChangeEnded() = win 0x88de0;
void scaleObjects(cocos2d::CCArray*, float, cocos2d::CCPoint) = mac 0x252e0, win 0x8f150;
void selectObjects(cocos2d::CCArray*, bool) = mac 0x23940, win 0x864a0;
void selectObjects(cocos2d::CCArray* objs, bool ignoreFilters) = mac 0x23940, win 0x864a0;
void setupCreateMenu() = mac 0xcb50, win 0x7caf0;
void undoLastAction(cocos2d::CCObject*) = mac 0xb830, win 0x87070;
void updateButtons() = mac 0x1a300, win 0x78280;
void updateEditMenu() = win 0x8b010;
void updateObjectInfoLabel() = mac 0x1cb10, win 0x793b0;
void updateSlider() = mac 0x18a90, win 0x78f10;
void updateZoom(float) = mac 0x248c0, win 0x878a0;
@ -1248,6 +1263,7 @@ class EditorUI : cocos2d::CCLayer, FLAlertLayerProtocol, ColorSelectDelegate, GJ
void selectAll() = win 0x86c40;
void selectAllWithDirection(bool left) = win 0x86d80;
cocos2d::CCPoint getTouchPoint(cocos2d::CCTouch* touch, cocos2d::CCEvent* event) = win 0x90620;
cocos2d::CCPoint getGridSnappedPos(cocos2d::CCPoint pos) = win 0x90550;
void onSelectBuildTab(cocos2d::CCObject* sender) = win 0x887f0;
void onCreateButton(cocos2d::CCObject* sender) = win 0x854f0;
CCMenuItemSpriteExtra* getSpriteButton(const char* sprite, cocos2d::SEL_MenuHandler callback, cocos2d::CCMenu* menu, float scale) = mac 0xb500, win 0x78bf0;
@ -1319,6 +1335,7 @@ class EditorUI : cocos2d::CCLayer, FLAlertLayerProtocol, ColorSelectDelegate, GJ
virtual void scrollWheel(float vertical, float horizontal) = win 0x921d0, mac 0x31370, ios 0x2c4884;
void createMoveMenu() = mac 0x275e0, win 0x8c0d0;
void sliderChanged(cocos2d::CCObject* slider) = win 0x78cc0;
void repositionObjectsToCenter(cocos2d::CCArray* objs, cocos2d::CCPoint center, bool ignoreGroupParent) = win 0x88410;
virtual void draw() = win 0x8fbe0;
bool m_isPlayingMusic;
@ -2342,7 +2359,12 @@ class GJGroundLayer : cocos2d::CCLayer {
void updateGroundWidth() = mac 0x356790, win 0x12dda0;
}
class GJItemIcon {
class GJItemIcon : cocos2d::CCSprite {
bool init(
UnlockType, int, cocos2d::ccColor3B, cocos2d::ccColor3B,
bool, bool, bool, cocos2d::ccColor3B
) = win 0x12ccf0;
GJItemIcon* createBrowserIcon(UnlockType _type, int _id) {
return GJItemIcon::create(_type, _id,
{ 0xaf, 0xaf, 0xaf }, { 0xff, 0xff, 0xff },
@ -2415,8 +2437,16 @@ class GJRobotSprite : CCAnimatedSprite {
void updateFrame(int) = mac 0x34bdd0, win 0x146700;
void hideGlow() = mac 0x34b860;
PAD = win 0x8;
cocos2d::CCArray* m_unk244;
bool m_unk248;
cocos2d::ccColor3B m_mainColor;
cocos2d::ccColor3B m_secondaryColor;
cocos2d::CCArray* m_unk250;
cocos2d::CCSprite* m_glowSprite;
cocos2d::CCSprite* m_unk258;
PAD = win 0x4;
cocos2d::CCSprite* m_flameSprite;
CCSpritePart* m_headSpritePart;
}
class GJRotationControl : cocos2d::CCLayer {
@ -3162,8 +3192,8 @@ class GameObject : CCSpritePlus {
bool m_isEffectObject;
bool m_randomisedAnimStart;
float m_animSpeed;
bool m_blackChild;
bool m_unkOutlineMaybe;
bool m_isBlackObject;
bool m_isBlackObjectWithOutline;
float m_blackChildOpacity;
bool field_21C;
bool m_editor;
@ -3367,6 +3397,7 @@ class GameStatsManager : cocos2d::CCNode {
void storePendingUserCoin(char const*) = mac 0x42940;
void storeSecretCoin(char const*) = mac 0x42a10;
void storeUserCoin(char const*) = mac 0x42890;
bool isItemUnlocked(UnlockType type, int id) = win 0xfbb80;
PAD = win 0x28;
cocos2d::CCDictionary* m_dailyChests;
@ -3407,6 +3438,7 @@ class GameToolbox {
static void alignItemsHorisontally(cocos2d::CCArray* array, bool idk, cocos2d::CCPoint start, float pad) = win 0x25b20;
static cocos2d::_ccHSVValue hsvFromString(gd::string, char const*) = mac 0x28cc30, win 0x26da0;
static gd::map<gd::string, gd::string> stringSetupToMap(gd::string, char const*) = mac 0x28d4c0;
static cocos2d::ccColor3B multipliedColorValue(cocos2d::ccColor3B color1, cocos2d::ccColor3B color2, float factor) = win 0x26CE0;
}
class GaragePage : cocos2d::CCLayer, ListButtonBarDelegate {
@ -3457,7 +3489,7 @@ class HardStreak : cocos2d::CCDrawNode {
// ~HardStreak() = mac 0x5bf00; inlined on windows
virtual bool init() = mac 0x5c090, win 0x14e430;
void addPoint(cocos2d::CCPoint) = mac 0x5c950, win 0x14ebc0;
void clearBehindXPos(float) = mac 0x5cb40;
void clearBehindXPos(float) = mac 0x5cb40, win 0x14ec00;
static HardStreak* create() = mac 0x5bfd0, win 0x14e390;
void firstSetup() = mac 0x5c160, win 0x14e490;
double normalizeAngle(double) = mac 0x5cbe0;
@ -3640,12 +3672,13 @@ class LevelEditorLayer : GJBaseGameLayer, LevelSettingsDelegate {
void getNextFreeBlockID(cocos2d::CCArray*) = mac 0x9a4e0;
int getNextFreeGroupID(cocos2d::CCArray*) = mac 0x9a1b0, win 0x164ae0;
void getNextFreeItemID(cocos2d::CCArray*) = mac 0x9a390;
void getObjectRect(GameObject*, bool) = mac 0x96240, win 0x1616b0;
cocos2d::CCRect getObjectRect(GameObject* obj, bool updateRect) = mac 0x96240, win 0x1616b0;
void getRelativeOffset(GameObject*) = mac 0x96840;
bool hasAction(bool) = mac 0x96ff0;
void handleAction(bool, cocos2d::CCArray*) = mac 0x97020, win 0x162010;
bool init(GJGameLevel*) = mac 0x91010, win 0x15EE00;
void objectAtPosition(cocos2d::CCPoint) = mac 0x960c0, win 0x161300;
GameObject* objectAtPosition(cocos2d::CCPoint position) = mac 0x960c0, win 0x161300;
cocos2d::CCArray* objectsAtPosition(cocos2d::CCPoint position) = win 0x1614d0;
void objectMoved(GameObject*) = mac 0x999f0, win 0x162d40;
cocos2d::CCArray* objectsInRect(cocos2d::CCRect rect, bool ignoreLayer) = mac 0x95e60, win 0x161ad0;
void onPlaytest() = mac 0xa06b0, win 0x1695A0;
@ -3692,7 +3725,7 @@ class LevelEditorLayer : GJBaseGameLayer, LevelSettingsDelegate {
}
void updateOptions() = mac 0x91ed0, win 0x15fcc0;
void updateToggledGroups() = mac 0x9bb10;
void updateVisibility(float) = mac 0x92c70, win 0x1632b0;
callback void updateVisibility(float delta) = mac 0x92c70, win 0x1632b0;
void groupStickyObjects(cocos2d::CCArray* objects) = mac 0x99dd0, win 0x164860;
void ungroupStickyObjects(cocos2d::CCArray* objects) = mac 0x99ee0, win 0x164950;
@ -4640,7 +4673,7 @@ class PlayerObject : GameObject, AnimatedSpriteDelegate {
void convertToClosestRotation(float) = mac 0x21c860, win 0x1e9ac0;
void copyAttributes(PlayerObject*) = mac 0x22dc70, win 0x1f93f0;
static PlayerObject* create(int, int, cocos2d::CCLayer*) = mac 0x217260, win 0x1e6cf0;
void deactivateParticle() = mac 0x21a540;
void deactivateParticle() = mac 0x21a540, win 0x1e8f50;
void deactivateStreak(bool) = mac 0x218b30;
void fadeOutStreak2(float) = mac 0x225890, win 0x1f9110;
void flashPlayer(float, float, cocos2d::_ccColor3B, cocos2d::_ccColor3B) = mac 0x221c80;
@ -4662,7 +4695,7 @@ class PlayerObject : GameObject, AnimatedSpriteDelegate {
void isSafeMode(float) = mac 0x2209b0;
void isSafeSpiderFlip(float) = mac 0x221be0;
void levelFlipFinished() = mac 0x21b060, win 0x1e8e70;
void levelFlipping() = mac 0x21a510, win 0x1f6820;
bool levelFlipping() = mac 0x21a510, win 0x1f6820;
void levelWillFlip() = mac 0x21b020;
void loadFromCheckpoint(PlayerCheckpoint*) = mac 0x22e420, win 0x1fa080;
void lockPlayer() = mac 0x22d680;
@ -4732,7 +4765,7 @@ class PlayerObject : GameObject, AnimatedSpriteDelegate {
void updateCollide(bool, int) = mac 0x220f10;
void updateCollideBottom(float, int) = mac 0x221790;
void updateCollideTop(float, int) = mac 0x221c20;
void updateDashAnimation() = mac 0x21a570;
void updateDashAnimation() = mac 0x21a570, win 0x1eea80;
void updateDashArt() = mac 0x222520, win 0x1ee3c0;
void updateGlowColor() = mac 0x22cf10;
void updateJump(float) = mac 0x219680, win 0x1e8f80;
@ -4845,6 +4878,8 @@ class PlayerObject : GameObject, AnimatedSpriteDelegate {
bool m_hasJustHeld;
bool m_isHolding2;
bool m_hasJustHeld2;
bool m_unk615;
bool m_unk616;
int m_unk618;
float m_unk61C;
int m_unk620;
@ -4860,6 +4895,7 @@ class PlayerObject : GameObject, AnimatedSpriteDelegate {
bool m_isRobot;
bool m_isSpider;
bool m_isUpsideDown;
bool m_unk63F;
bool m_isOnGround;
bool m_isDashing;
float m_vehicleSize;
@ -5417,7 +5453,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 scale, float width, cocos2d::CCPoint anchor, float height, bool disableColor) = mac 0x19eb40, win 0x33270;
static TextArea* create(gd::string str, char const* font, float scale, float width, cocos2d::CCPoint anchor, float lineHeight, bool disableColor) = mac 0x19eb40, win 0x33270;
void colorAllCharactersTo(cocos2d::ccColor3B color) = win 0x33830;
void setString(gd::string str) = mac 0x19eda0, win 0x33480;

View file

@ -1,13 +1,8 @@
set(GEODE_CLI_MINIMUM_VERSION 1.0.5)
# for passing CLI through CMake arguments
if (DEFINED CLI_PATH)
list(APPEND CMAKE_PROGRAM_PATH ${CLI_PATH})
endif()
# Find Geode CLI
if (NOT DEFINED GEODE_CLI)
find_program(GEODE_CLI NAMES geode.exe geode-cli.exe geode geode-cli)
find_program(GEODE_CLI NAMES geode.exe geode-cli.exe geode geode-cli PATHS ${CLI_PATH})
endif()
# Check if CLI was found
@ -53,6 +48,9 @@ function(setup_geode_mod proname)
set(multiValueArgs EXTERNALS)
cmake_parse_arguments(SETUP_GEODE_MOD "${options}" "" "${multiValueArgs}" ${ARGN})
# Link Geode to the mod
target_link_libraries(${proname} geode-sdk)
if (GEODE_DISABLE_CLI_CALLS)
message("Skipping setting up geode mod ${proname}")
return()
@ -109,7 +107,7 @@ function(setup_geode_mod proname)
endif()
# Check if --install should be passed
if (SETUP_GEODE_MOD_DONT_INSTALL)
if (SETUP_GEODE_MOD_DONT_INSTALL OR GEODE_DONT_INSTALL_MODS)
message(STATUS "Skipping installing ${proname}")
set(INSTALL_ARG "")
else()
@ -124,10 +122,6 @@ function(setup_geode_mod proname)
set(HAS_HEADERS Off)
endif()
# Add package target + make output name the mod id
set_target_properties(${proname} PROPERTIES PREFIX "")
set_target_properties(${proname} PROPERTIES OUTPUT_NAME ${MOD_ID})
# todo: figure out how to either not make cmake shit itself and print out --binary path/to/dll "" or
# make cli not shit itself when it sees that
if (HAS_HEADERS)
@ -199,8 +193,9 @@ function(setup_geode_mod proname)
endif()
# Link Geode to the mod
target_link_libraries(${proname} geode-sdk)
# Add package target + make output name the mod id
set_target_properties(${proname} PROPERTIES PREFIX "")
set_target_properties(${proname} PROPERTIES OUTPUT_NAME ${MOD_ID})
endfunction()

View file

@ -30,10 +30,16 @@ elseif (GEODE_TARGET_PLATFORM STREQUAL "MacOS")
APPLE_SILICON_PROCESSOR x86_64
)
# this should be set globally
set(CMAKE_OSX_ARCHITECTURES "x86_64")
# only exists as a global property
set(CMAKE_OSX_DEPLOYMENT_TARGET 10.14)
target_link_libraries(${PROJECT_NAME} INTERFACE curl "-framework Cocoa")
target_link_libraries(${PROJECT_NAME} INTERFACE
curl "-framework Cocoa"
${GEODE_LOADER_PATH}/include/link/libfmod.dylib
)
target_compile_options(${PROJECT_NAME} INTERFACE -fms-extensions #[[-Wno-deprecated]] -Wno-ignored-attributes -Os #[[-flto]] #[[-fvisibility=internal]])
set(GEODE_PLATFORM_BINARY "Geode.dylib")

View file

@ -103,7 +103,7 @@ std::string generateAddressHeader(Root& root) {
);
}
else if (codegen::getStatus(field) == BindStatus::NeedsBinding) {
if (field.parent.rfind("cocos2d::", 0) == 0 && codegen::platform == Platform::Windows) {
if (is_cocos_class(field.parent) && codegen::platform == Platform::Windows) {
address_str = fmt::format("base::getCocos() + 0x{:x}", codegen::platformNumber(fn->binds));
}
else {

View file

@ -144,7 +144,7 @@ std::string generateBindingHeader(Root& root, ghc::filesystem::path const& singl
std::string output;
for (auto& cls : root.classes) {
if (can_find(cls.name, "cocos2d"))
if (is_cocos_class(cls.name))
continue;
std::string filename = (codegen::getUnqualifiedClassName(cls.name) + ".hpp");
@ -168,7 +168,7 @@ std::string generateBindingHeader(Root& root, ghc::filesystem::path const& singl
}
std::string supers = str_if(
fmt::format(" : public {}", fmt::join(cls.superclasses, ", ")),
fmt::format(" : public {}", fmt::join(cls.superclasses, ", public ")),
!cls.superclasses.empty()
);
@ -180,7 +180,7 @@ std::string generateBindingHeader(Root& root, ghc::filesystem::path const& singl
// what.
if (!cls.superclasses.empty()) {
single_output += fmt::format(
can_find(cls.superclasses[0], "cocos2d")
is_cocos_class(cls.superclasses[0])
? format_strings::custom_constructor_cutoff
: format_strings::custom_constructor,
fmt::arg("class_name", cls.name),

View file

@ -25,7 +25,7 @@ int main(int argc, char** argv) try {
for (auto cls : root.classes) {
for (auto dep : cls.depends) {
if(!can_find(dep, "cocos2d::") && std::find(root.classes.begin(), root.classes.end(), dep) == root.classes.end()) {
if(!is_cocos_class(dep) && std::find(root.classes.begin(), root.classes.end(), dep) == root.classes.end()) {
throw codegen::error("Class {} depends on unknown class {}", cls.name, dep);
}
}

View file

@ -74,7 +74,7 @@ std::string generateModifyHeader(Root& root, ghc::filesystem::path const& single
if (c.name.find("cocos2d::extension") != std::string::npos) {
class_include = "#include <cocos-ext.h>";
}
else if (c.name.find("cocos2d") != std::string::npos) {
else if (is_cocos_class(c.name)) {
class_include = "#include <cocos2d.h>";
}
else {

View file

@ -10,7 +10,7 @@ std::string generatePredeclareHeader(Root& root) {
std::string output("#pragma once\n");
for (auto& cls : root.classes) {
if (can_find(cls.name, "cocos2d"))
if (is_cocos_class(cls.name))
continue;
output += fmt::format(::format_strings::class_predeclare,

View file

@ -51,6 +51,10 @@ inline bool can_find(std::string const& str, char const* text) {
return str.find(text) != std::string::npos;
}
inline bool is_cocos_class(std::string const& str) {
return can_find(str, "cocos2d") || can_find(str, "pugi::") || str == "DS_Dictionary";
}
enum class BindStatus {
Binded,
NeedsBinding,
@ -112,8 +116,10 @@ namespace codegen {
if (fb->type == FunctionType::Normal) {
if (field.parent.rfind("fmod::", 0) == 0) return BindStatus::Binded;
if (field.parent.rfind("cocos2d::", 0) == 0 && p == Platform::Windows)
return BindStatus::Binded;
if (
(field.parent.rfind("cocos2d::", 0) == 0 || field.parent == "DS_Dictionary") &&
p == Platform::Windows
) return BindStatus::Binded;
}
return BindStatus::Unbindable;
@ -153,48 +159,6 @@ namespace codegen {
return fmt::format("{}", fmt::join(parameters, ", "));
}
inline std::string getConvention(Field& f) {
if (codegen::platform != Platform::Windows) return "DefaultConv";
if (auto fn = f.get_fn()) {
auto status = getStatus(f);
if (fn->is_static) {
if (status == BindStatus::Binded) return "x86::Cdecl";
else return "x86::Optcall";
}
else if (fn->is_virtual || fn->is_callback) {
return "x86::Thiscall";
}
else {
if (status == BindStatus::Binded) return "x86::Thiscall";
else return "x86::Membercall";
}
}
else throw codegen::error("Tried to get convention of non-function");
}
inline std::string getModifyConvention(Field& f) {
if (codegen::platform != Platform::Windows) return "tulip::hook::DefaultConvention";
if (auto fn = f.get_fn()) {
auto status = getStatus(f);
if (fn->is_static) {
if (status == BindStatus::Binded) return "tulip::hook::CdeclConvention";
else return "tulip::hook::OptcallConvention";
}
else if (fn->is_virtual) {
return "tulip::hook::ThiscallConvention";
}
else {
if (status == BindStatus::Binded) return "tulip::hook::ThiscallConvention";
else return "tulip::hook::MembercallConvention";
}
}
else throw codegen::error("Tried to get convention of non-function");
}
inline std::string getModifyConventionName(Field& f) {
if (codegen::platform != Platform::Windows) return "Default";
@ -205,7 +169,7 @@ namespace codegen {
if (status == BindStatus::Binded) return "Cdecl";
else return "Optcall";
}
else if (fn->is_virtual) {
else if (fn->is_virtual || fn->is_callback) {
return "Thiscall";
}
else {
@ -216,6 +180,18 @@ namespace codegen {
else throw codegen::error("Tried to get convention of non-function");
}
inline std::string getConvention(Field& f) {
if (codegen::platform != Platform::Windows) return "DefaultConv";
return std::string("x86::") + getModifyConventionName(f);
}
inline std::string getModifyConvention(Field& f) {
if (codegen::platform != Platform::Windows) return "tulip::hook::DefaultConvention";
return std::string("tulip::hook::") + getModifyConventionName(f) + "Convention";
}
inline std::string getUnqualifiedClassName(std::string const& s) {
auto index = s.rfind("::");
if (index == std::string::npos) return s;

View file

@ -126,7 +126,7 @@ std::string generateBindingSource(Root& root) {
for (auto& f : c.fields) {
if (auto i = f.get_as<InlineField>()) {
if (codegen::platform == Platform::Mac || codegen::platform == Platform::iOS) {
if (can_find(c.name, "cocos2d"))
if (is_cocos_class(c.name))
output += i->inner + "\n";
}
} else if (auto fn = f.get_as<OutOfLineField>()) {
@ -134,7 +134,7 @@ std::string generateBindingSource(Root& root) {
continue;
// no cocos2d definitions on windows
if (codegen::platform == Platform::Windows && f.parent.rfind("cocos2d::", 0) == 0) {
if (codegen::platform == Platform::Windows && is_cocos_class(f.parent)) {
continue;
}
@ -166,7 +166,7 @@ std::string generateBindingSource(Root& root) {
continue;
// no cocos2d definitions on windows
if (codegen::platform == Platform::Windows && f.parent.rfind("cocos2d::", 0) == 0) {
if (codegen::platform == Platform::Windows && is_cocos_class(f.parent)) {
continue;
}

View file

@ -30,6 +30,7 @@ execute_process(
configure_file(resources/mod.json.in ${CMAKE_CURRENT_SOURCE_DIR}/resources/mod.json)
file(READ resources/mod.json LOADER_MOD_JSON)
file(READ resources/about.md LOADER_ABOUT_MD)
file(READ ${GEODE_ROOT_PATH}/CHANGELOG.md LOADER_CHANGELOG_MD)
configure_file(src/internal/about.hpp.in ${CMAKE_CURRENT_SOURCE_DIR}/src/internal/about.hpp)
# Source files

View file

@ -285,6 +285,12 @@ public:
*/
void resumeTargets(CCSet* targetsToResume);
/**
* Get the shared scheduler from CCDirector
* @note Geode addition
*/
static GEODE_DLL CCScheduler* get();
private:
void removeHashElement(struct _hashSelectorEntry *pElement);
void removeUpdateFromHash(struct _listEntry *entry);

View file

@ -54,6 +54,7 @@ public:
* @js ctor
*/
CCAction(void);
GEODE_CUSTOM_CONSTRUCTOR_COCOS(CCAction, CCObject);
/**
* @js NA
* @lua NA

View file

@ -38,7 +38,8 @@
#include "../script_support/CCScriptSupport.h"
#include "../include/CCProtocols.h"
#include "Layout.hpp"
#include <any>
#include "../../loader/Event.hpp"
#include <json.hpp>
NS_CC_BEGIN
@ -850,7 +851,11 @@ private:
friend class geode::modifier::FieldContainer;
GEODE_DLL geode::modifier::FieldContainer* getFieldContainer();
GEODE_DLL std::optional<std::any> getAttributeInternal(std::string const& attribute);
GEODE_DLL std::optional<json::Value> getAttributeInternal(std::string const& attribute);
GEODE_DLL void addEventListenerInternal(
std::string const& id,
geode::EventListenerProtocol* protocol
);
public:
/**
@ -892,6 +897,7 @@ public:
* @param before The child the node is added before of. If this is null or
* not a child of this node, the new child will be placed at the start of the
* child list
* @note Geode addition
*/
GEODE_DLL void insertBefore(CCNode* child, CCNode* before);
@ -902,9 +908,20 @@ public:
* @param after The child the node is added after of. If this is null or
* not a child of this node, the new child will be placed at the end of the
* child list
* @note Geode addition
*/
GEODE_DLL void insertAfter(CCNode* child, CCNode* after);
/**
* Check if this node's parent or its parents' parent is the given node
* @param ancestor The node whose child or subchild this node should be. If
* nullptr, returns true if the node is in the current scene, otherwise
* false.
* @returns True if ancestor is an ancestor of this node
* @note Geode addition
*/
GEODE_DLL bool hasAncestor(CCNode* ancestor);
/**
* Set an attribute on a node. Attributes are a system added by Geode,
* where a node may have any sort of extra data associated with it. Used
@ -917,7 +934,7 @@ public:
* @param value The value of the attribute
* @note Geode addition
*/
GEODE_DLL void setAttribute(std::string const& attribute, std::any value);
GEODE_DLL void setAttribute(std::string const& attribute, json::Value const& value);
/**
* Get an attribute from the node. Attributes may be anything
* @param attribute The attribute key
@ -929,7 +946,7 @@ public:
std::optional<T> getAttribute(std::string const& attribute) {
if (auto value = this->getAttributeInternal(attribute)) {
try {
return std::any_cast<T>(value.value());
return value.value().template as<T>();
} catch(...) {
return std::nullopt;
}
@ -986,6 +1003,32 @@ public:
* @note Geode addition
*/
GEODE_DLL void swapChildIndices(CCNode* first, CCNode* second);
template <class Filter, class... Args>
geode::EventListenerProtocol* addEventListener(
std::string const& id,
geode::utils::MiniFunction<typename Filter::Callback> callback,
Args&&... args
) {
auto listener = new geode::EventListener<Filter>(
callback, Filter(this, std::forward<Args>(args)...)
);
this->addEventListenerInternal(id, listener);
return listener;
}
template <class Filter, class... Args>
geode::EventListenerProtocol* addEventListener(
geode::utils::MiniFunction<typename Filter::Callback> callback,
Args&&... args
) {
return this->template addEventListener<Filter, Args...>(
"", callback, std::forward<Args>(args)...
);
}
GEODE_DLL void removeEventListener(geode::EventListenerProtocol* listener);
GEODE_DLL void removeEventListener(std::string const& id);
GEODE_DLL geode::EventListenerProtocol* getEventListener(std::string const& id);
GEODE_DLL size_t getEventListenerCount();
/// @{
/// @name Shader Program
@ -1690,4 +1733,27 @@ protected:
NS_CC_END
namespace geode {
struct GEODE_DLL AttributeSetEvent : public Event {
cocos2d::CCNode* node;
const std::string id;
json::Value& value;
AttributeSetEvent(cocos2d::CCNode* node, std::string const& id, json::Value& value);
};
class GEODE_DLL AttributeSetFilter : public EventFilter<AttributeSetEvent> {
public:
using Callback = void(AttributeSetEvent*);
protected:
std::string m_targetID;
public:
ListenerResult handle(utils::MiniFunction<Callback> fn, AttributeSetEvent* event);
AttributeSetFilter(std::string const& id);
};
}
#endif // __PLATFORM_CCNODE_H__

View file

@ -23,7 +23,9 @@ class CCNode;
*/
class GEODE_DLL Layout : public CCObject {
protected:
static CCArray* getNodesToPosition(CCNode* forNode);
CCArray* getNodesToPosition(CCNode* forNode) const;
bool m_ignoreInvisibleChildren = false;
public:
/**
@ -35,6 +37,14 @@ public:
*/
virtual void apply(CCNode* on) = 0;
/**
* Get how much space this layout would like to take up for a given target
*/
virtual CCSize getSizeHint(CCNode* on) const = 0;
void ignoreInvisibleChildren(bool ignore);
bool isIgnoreInvisibleChildren() const;
virtual ~Layout() = default;
};
@ -257,6 +267,7 @@ public:
static AxisLayout* create(Axis axis = Axis::Row);
void apply(CCNode* on) override;
CCSize getSizeHint(CCNode* on) const override;
Axis getAxis() const;
AxisAlignment getAxisAlignment() const;

View file

@ -0,0 +1,59 @@
#pragma once
#include "CCNode.h"
NS_CC_BEGIN
#pragma warning(push)
#pragma warning(disable: 4275)
/**
* A node for controlling spacing in Layouts. When a Layout is applied, if
* space is left over, the remaining space is divided among all SpacerNodes in
* the Layout. The space each node gets is the proprotion between its growth
* factor and the sum of all the SpacerNodes' growth factors in the Layout
* @example
* node->addChild(SpacerNode::create(1));
* node->addChild(SpacerNode::create(2));
* node->addChild(SpacerNode::create(1));
* node->updateLayout();
* // Total SpacerNode growth sum is 1 + 2 + 1 = 4
* // So s1 and s3 get 1/4 of the remaining space and s2 gets 2/4
* @note If you want to specify a minimum width for a SpacerNode, add
* AxisLayoutOptions for it and use setLength
*/
class GEODE_DLL SpacerNode : public CCNode {
protected:
size_t m_grow;
bool init(size_t grow);
public:
/**
* Create a new spacer node. When the layout is applied,
* if there is space left over the remaining space is distributed among
* all spacer nodes in proportion to the sum of all the spacers' grow
* factors (akin to CSS flew grow)
* @param grow The grow factor for this node. Default is 1
*/
static SpacerNode* create(size_t grow = 1);
/**
* Set the grow factor for this spacer node. When the layout is applied,
* if there is space left over the remaining space is distributed among
* all spacer nodes in proportion to the sum of all the spacers' grow
* factors (akin to CSS flew grow)
* @param grow The new grow factor for this node. Default is 1
* @note Make sure to call updateLayout on the spacer's parent afterwards
*/
void setGrow(size_t grow);
/**
* Get the grow factor for this spacer node
*/
size_t getGrow() const;
};
#pragma warning(pop)
NS_CC_END

View file

@ -177,12 +177,10 @@ public:
unsigned int indexOfObject(CCObject* object) const;
/** Returns an element with a certain index */
CCObject* objectAtIndex(unsigned int index);
RT_ADD(
/**
* Rob modification
* Returns an element with a certain index casted to CCString */
CCString* stringAtIndex(unsigned int index);
);
/**
* Rob modification
* Returns an element with a certain index casted to CCString */
CCString* stringAtIndex(unsigned int index);
/**
* Returns first element, or null if empty
@ -202,12 +200,10 @@ public:
/** Add a certain object */
void addObject(CCObject* object);
RT_ADD(
/**
* Rob modification
* Add a certain object */
void addObjectNew(CCObject* object);
);
/**
* Rob modification
* Add a certain object */
void addObjectNew(CCObject* object);
/** Add all elements of an existing array */
void addObjectsFromArray(CCArray* otherArray);
/** Insert a certain object at a certain index */

View file

@ -47,14 +47,13 @@ NS_CC_BEGIN
* @{
*/
RT_ADD(
// please someone tell we why in higher being(s)'s name rob did this
enum class CCObjectType {
PlayLayer = 5,
LevelEditorLayer = 6,
MenuLayer = 15,
};
)
// please someone tell we why in higher being(s)'s name rob did this
enum class CCObjectType {
PlayLayer = 5,
LevelEditorLayer = 6,
GameObject = 13,
MenuLayer = 15,
};
class CCZone;
class CCObject;
@ -101,16 +100,14 @@ public:
int m_nLuaID;
protected:
// the object's tag
RT_ADD( int m_nTag; )
int m_nTag;
// count of references
unsigned int m_uReference;
// count of autorelease
unsigned int m_uAutoReleaseCount;
RT_ADD(
CCObjectType m_eObjType;
int m_nUnknown;
)
CCObjectType m_eObjType;
int m_nUnknown;
public:
GEODE_CUSTOM_CONSTRUCTOR_BEGIN(CCObject)
CCObject(void);
@ -124,30 +121,32 @@ public:
CCObject* autorelease(void);
CCObject* copy(void);
bool isSingleReference(void) const;
inline unsigned int retainCount(void) const;
inline unsigned int retainCount(void) const {
return m_uReference;
}
virtual bool isEqual(const CCObject* pObject);
virtual void acceptVisitor(CCDataVisitor &visitor);
virtual void update(float dt) {CC_UNUSED_PARAM(dt);};
RT_ADD(
virtual void encodeWithCoder(DS_Dictionary*);
virtual void encodeWithCoder(DS_Dictionary*);
static CCObject* createWithCoder(DS_Dictionary*);
virtual bool canEncode();
static CCObject* createWithCoder(DS_Dictionary*);
virtual bool canEncode();
CCObjectType getObjType() const;
virtual int getTag() const;
inline CCObjectType getObjType() const {
return m_eObjType;
}
virtual int getTag() const;
virtual void setTag(int nTag);
inline void setObjType(CCObjectType type) {
m_eObjType = type;
}
)
virtual void setTag(int nTag);
inline void setObjType(CCObjectType type) {
m_eObjType = type;
}
friend class CCAutoreleasePool;
};

View file

@ -59,6 +59,7 @@ THE SOFTWARE.
// base_nodes
#include "../base_nodes/CCNode.h"
#include "../base_nodes/CCAtlasNode.h"
#include "../base_nodes/SpacerNode.hpp"
// cocoa
#include "../cocoa/CCAffineTransform.h"

View file

@ -71,17 +71,19 @@ public:
virtual ~CCScene();
bool init();
static CCScene *create(void);
static CCScene* create(void);
/**
* Get the running scene
* @note Geode addition
*/
static GEODE_DLL CCScene* get();
RT_ADD(
CCScene(const CCScene&);
CCScene& operator=(const CCScene&);
CCScene(const CCScene&);
CCScene& operator=(const CCScene&);
int getHighestChildZ(void);
int getHighestChildZ(void);
CCSceneDelegate* m_pDelegate;
)
CCSceneDelegate* m_pDelegate;
};
// end of scene group

View file

@ -115,6 +115,8 @@ public:
bool isInverted() const;
void setInverted(bool bInverted);
GEODE_CUSTOM_CONSTRUCTOR_COCOS(CCClippingNode, CCNode);
protected:
CCClippingNode();
};

View file

@ -42,14 +42,11 @@ public:
*/
virtual TargetPlatform getTargetPlatform();
RT_ADD(
virtual void openURL(const char* url);
virtual int run();
virtual void setupGLView();
virtual void platformShutdown();
void toggleVerticalSync(bool);
bool getVerticalSyncEnabled() const;
)
virtual void openURL(const char* url);
virtual int run();
virtual void setupGLView();
virtual void platformShutdown();
void toggleVerticalSync(bool);
/**
* Sets the Resource root path.
@ -65,8 +62,6 @@ public:
void setStartupScriptFilename(const gd::string& startupScriptFile);
bool getControllerConnected() const;
const gd::string& getStartupScriptFilename(void)
{
return m_startupScriptFilename;
@ -76,8 +71,24 @@ protected:
HINSTANCE m_hInstance;
HACCEL m_hAccelTable;
LARGE_INTEGER m_nAnimationInterval;
gd::string m_resourceRootPath;
gd::string m_startupScriptFilename;
PAD(4);
std::string m_resourceRootPath;
std::string m_startupScriptFilename;
void* m_pUnknown;
bool m_bUpdateController;
CC_SYNTHESIZE_NV(bool, m_bShutdownCalled, ShutdownCalled);
INPUT m_iInput;
CCPoint m_obUnknown1;
CCPoint m_obUnknown2;
bool m_bMouseControl;
float m_fOldAnimationInterval;
float m_fAnimationInterval;
CC_SYNTHESIZE_READONLY_NV(bool, m_bVerticalSyncEnabled, VerticalSyncEnabled);
CC_SYNTHESIZE_READONLY_NV(bool, m_bControllerConnected, ControllerConnected);
CC_SYNTHESIZE_NV(bool, m_bSleepMode, SleepMode);
CC_SYNTHESIZE_NV(bool, m_bForceTimer, ForceTimer);
CC_SYNTHESIZE_NV(bool, m_bSmoothFix, SmoothFix);
CC_SYNTHESIZE_NV(bool, m_bFullscreen, Fullscreen);
static CCApplication * sm_pSharedApplication;
};

View file

@ -63,6 +63,8 @@ public:
protected:
RT_REMOVE( virtual bool Create(); )
void setupWindow(cocos2d::CCRect rect);
public:
bool initGL();
void destroyGL();

View file

@ -21,6 +21,7 @@ public:
public:
DS_Dictionary();
~DS_Dictionary();
GEODE_CUSTOM_CONSTRUCTOR_BEGIN(DS_Dictionary)
static void copyFile(const char*, const char*);

View file

@ -371,6 +371,7 @@ namespace pugi
public:
// Default constructor. Constructs an empty node.
xml_node();
GEODE_CUSTOM_CONSTRUCTOR_BEGIN(xml_node);
// Constructs node from internal pointer
explicit xml_node(xml_node_struct* p);
@ -852,6 +853,7 @@ namespace pugi
{
private:
char_t* _buffer;
GEODE_CUSTOM_CONSTRUCTOR_COCOS(xml_document, xml_node);
char _memory[192];

View file

@ -47,13 +47,13 @@ typedef enum
} ccTouchSelectorFlag;
enum {
CCTOUCHBEGAN,
CCTOUCHMOVED,
CCTOUCHENDED,
CCTOUCHCANCELLED,
enum ccTouchType {
CCTOUCHBEGAN = 0,
CCTOUCHMOVED = 1,
CCTOUCHENDED = 2,
CCTOUCHCANCELLED = 3,
ccTouchMax,
ccTouchMax = 4,
};
class CCSet;

View file

@ -9,42 +9,41 @@
namespace geode {
// Mod interoperability
// todo: update to new event system
template <class... Args>
class DispatchEvent : public Event {
protected:
std::string m_id;
std::tuple<Args...> m_args;
public:
DispatchEvent(std::string const& id, Args&&... args)
: m_id(id), m_args(std::make_tuple(args...)) {}
std::tuple<Args...> getArgs() const {
return m_args;
}
// template <typename... Args>
// class DispatchEvent : public Event {
// std::string m_selector;
// std::tuple<Args...> m_args;
std::string getID() const {
return m_id;
}
};
// public:
// DispatchEvent(std::string const& name, Args... args) :
// m_selector(name), m_args(std::make_tuple(args...)) {}
template <class... Args>
class DispatchFilter : public EventFilter<DispatchEvent<Args...>> {
protected:
std::string m_id;
// std::string const& selector() {
// return m_selector;
// }
// };
public:
using Ev = DispatchEvent<Args...>;
using Callback = ListenerResult(Args...);
// template <typename... Args>
// class DispatchHandler : public EventHandler<DispatchEvent<Args...>> {
// std::string m_selector;
// utils::MiniFunction<void(Args...)> m_callback;
ListenerResult handle(utils::MiniFunction<Callback> fn, Ev* event) {
if (event->getID() == m_id) {
return std::apply(fn, event->getArgs());
}
return ListenerResult::Propagate;
}
// DispatchHandler(std::string const& name, utils::MiniFunction<void(Args...)> callback) :
// m_selector(name), m_callback(callback) {}
// public:
// bool handle(DispatchEvent<Args...>* ev) {
// if (ev->name() == m_selector) {
// std::apply(m_callback, ev->m_args);
// }
// return true;
// }
// static DispatchHandler* create(
// std::string const& name, utils::MiniFunction<void(Args...)> callback
// ) {
// return new DispatchHandler(name, callback);
// }
// };
DispatchFilter(std::string const& id) : m_id(id) {}
};
}

View file

@ -1,6 +1,7 @@
#pragma once
#include "../utils/casts.hpp"
#include "../utils/MiniFunction.hpp"
#include <Geode/DefaultInclude.hpp>
#include <type_traits>
@ -9,6 +10,7 @@
namespace geode {
class Mod;
class Event;
class EventListenerProtocol;
Mod* getMod();
@ -17,10 +19,41 @@ namespace geode {
Stop
};
struct GEODE_DLL EventListenerProtocol {
virtual void enable();
virtual void disable();
virtual ListenerResult passThrough(Event*) = 0;
struct GEODE_DLL EventListenerPool {
virtual bool add(EventListenerProtocol* listener) = 0;
virtual void remove(EventListenerProtocol* listener) = 0;
virtual void handle(Event* event) = 0;
virtual ~EventListenerPool() = default;
EventListenerPool() = default;
EventListenerPool(EventListenerPool const&) = delete;
EventListenerPool(EventListenerPool&&) = delete;
};
class GEODE_DLL DefaultEventListenerPool : public EventListenerPool {
protected:
std::atomic_size_t m_locked = 0;
std::vector<EventListenerProtocol*> m_listeners;
std::vector<EventListenerProtocol*> m_toAdd;
public:
bool add(EventListenerProtocol* listener) override;
void remove(EventListenerProtocol* listener) override;
void handle(Event* event) override;
static DefaultEventListenerPool* get();
};
class GEODE_DLL EventListenerProtocol {
private:
EventListenerPool* m_pool = nullptr;
public:
bool enable();
void disable();
virtual EventListenerPool* getPool() const;
virtual ListenerResult handle(Event*) = 0;
virtual ~EventListenerProtocol();
};
@ -44,6 +77,10 @@ namespace geode {
ListenerResult handle(utils::MiniFunction<Callback> fn, T* e) {
return fn(e);
}
EventListenerPool* getPool() const {
return DefaultEventListenerPool::get();
}
};
template <typename T>
@ -60,9 +97,8 @@ namespace geode {
requires std::is_class_v<C>
using MemberFn = typename to_member<C, Callback>::value;
ListenerResult passThrough(Event* e) override {
ListenerResult handle(Event* e) override {
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);
}
@ -70,6 +106,10 @@ namespace geode {
return ListenerResult::Propagate;
}
EventListenerPool* getPool() const override {
return m_filter.getPool();
}
EventListener(T filter = T()) {
this->enable();
}
@ -119,6 +159,14 @@ namespace geode {
m_filter = filter;
}
T getFilter() const {
return m_filter;
}
utils::MiniFunction<Callback>& getCallback() {
return m_callback;
}
protected:
utils::MiniFunction<Callback> m_callback = nullptr;
T m_filter;
@ -126,9 +174,11 @@ namespace geode {
class GEODE_DLL [[nodiscard]] Event {
private:
static std::unordered_set<EventListenerProtocol*>& listeners();
friend EventListenerProtocol;
protected:
virtual EventListenerPool* getPool() const;
public:
Mod* sender;
@ -137,7 +187,7 @@ namespace geode {
void post() {
postFrom(getMod());
}
virtual ~Event();
};
}

View file

@ -1,6 +1,7 @@
#pragma once
#include "Types.hpp"
#include "../utils/Result.hpp"
#include <Geode/DefaultInclude.hpp>
#include <Geode/utils/ranges.hpp>
@ -71,6 +72,24 @@ namespace geode {
return "nullopt";
}
template <class T>
requires requires(T t) {
parse(t);
}
std::string parse(std::vector<T> const& thing) {
std::string res = "[";
bool first = true;
for (auto& t : thing) {
if (!first) {
res += ", ";
}
first = false;
res += parse(t);
}
res += "]";
return res;
}
template <class A, class B>
requires requires(A a, B b) {
parse(a);
@ -187,9 +206,7 @@ namespace geode {
template <typename... Args>
void debug(Args... args) {
#ifdef GEODE_DEBUG
internalLog(Severity::Debug, getMod(), args...);
#endif
}
template <typename... Args>

View file

@ -203,6 +203,7 @@ namespace geode {
CircleBaseColor color = CircleBaseColor::Green,
CircleBaseSize size = CircleBaseSize::Medium
);
cocos2d::CCSize getMaxTopSize() const override;
};
/**

View file

@ -25,7 +25,7 @@ struct json::Serialize<cocos2d::ccColor4B> {
};
// operators for CC geometry
namespace geode {
namespace cocos2d {
static cocos2d::CCPoint& operator*=(cocos2d::CCPoint& pos, float mul) {
pos.x *= mul;
pos.y *= mul;
@ -356,6 +356,151 @@ namespace geode {
}
};
class GEODE_DLL WeakRefPool {
std::unordered_set<cocos2d::CCObject*> m_pool;
public:
static WeakRefPool* get();
bool isManaged(cocos2d::CCObject* obj);
void manage(cocos2d::CCObject* obj);
void check(cocos2d::CCObject* obj);
};
/**
* A smart pointer to a managed CCObject-deriving class. Like Ref, except
* only holds a weak reference to the targeted object. When all non-weak
* references (Refs, manual retain() calls) to the object are dropped, so
* are all weak references.
*
* In essence, WeakRef is like a raw pointer, except that you can know if
* the pointer is still valid or not, as WeakRef::lock() returns nullptr if
* the pointed-to-object has already been freed.
*
* Note that an object pointed to by WeakRef is only released once some
* WeakRef pointing to it checks for it after all other references to the
* object have been dropped. If you store WeakRefs in a global map, you may
* want to periodically lock all of them to make sure any memory that should
* be freed is freed.
*
* @tparam T A type that inherits from CCObject.
*/
template <class T>
class WeakRef final {
static_assert(
std::is_base_of_v<cocos2d::CCObject, T>,
"WeakRef can only be used with a CCObject-inheriting class!"
);
T* m_obj = nullptr;
public:
/**
* Construct a WeakRef of an object. A weak reference is one that will
* be valid as long as the object is referenced by other strong
* references (such as Ref or manual retain calls), but once all strong
* references are dropped, so are all weak references. The object is
* freed once no strong references exist to it, and any WeakRef pointing
* to it is freed or locked
* @param obj Object to construct the WeakRef from
*/
WeakRef(T* obj) : m_obj(obj) {
WeakRefPool::get()->manage(obj);
}
WeakRef(WeakRef<T> const& other) : WeakRef(other.m_obj) {}
WeakRef(WeakRef<T>&& other) : m_obj(other.m_obj) {
other.m_obj = nullptr;
}
/**
* Construct an empty WeakRef (the object will be null)
*/
WeakRef() = default;
~WeakRef() {
WeakRefPool::get()->check(m_obj);
}
/**
* Lock the WeakRef, returning a Ref if the pointed object is valid or
* a null Ref if the object has been freed
*/
Ref<T> lock() const {
if (WeakRefPool::get()->isManaged(m_obj)) {
return Ref(m_obj);
}
return Ref<T>(nullptr);
}
/**
* Check if the WeakRef points to a valid object
*/
bool valid() const {
return WeakRefPool::get()->isManaged(m_obj);
}
/**
* Swap the managed object with another object. The managed object
* will be released, and the new object retained
* @param other The new object to swap to
*/
void swap(T* other) {
WeakRefPool::get()->check(m_obj);
m_obj = other;
WeakRefPool::get()->manage(other);
}
Ref<T> operator=(T* obj) {
this->swap(obj);
return this->lock();
}
WeakRef<T>& operator=(WeakRef<T> const& other) {
this->swap(other.m_obj);
return *this;
}
WeakRef<T>& operator=(WeakRef<T>&& other) {
this->swap(other.m_obj);
return *this;
}
explicit operator bool() const noexcept {
return this->valid();
}
bool operator==(T* other) const {
return m_obj == other;
}
bool operator==(WeakRef<T> const& other) const {
return m_obj == other.m_obj;
}
bool operator!=(T* other) const {
return m_obj != other;
}
bool operator!=(WeakRef<T> const& other) const {
return m_obj != other.m_obj;
}
// for containers
bool operator<(WeakRef<T> const& other) const {
return m_obj < other.m_obj;
}
bool operator<=(WeakRef<T> const& other) const {
return m_obj <= other.m_obj;
}
bool operator>(WeakRef<T> const& other) const {
return m_obj > other.m_obj;
}
bool operator>=(WeakRef<T> const& other) const {
return m_obj >= other.m_obj;
}
};
template <class Filter>
class EventListenerNode : public cocos2d::CCNode {
protected:
@ -375,8 +520,8 @@ namespace geode {
return nullptr;
}
static EventListenerNode* create(typename Filter::Callback callback) {
auto ret = new EventListenerNode(EventListener<Filter>(callback));
static EventListenerNode* create(typename Filter::Callback callback, Filter filter = Filter()) {
auto ret = new EventListenerNode(EventListener<Filter>(callback, filter));
if (ret && ret->init()) {
ret->autorelease();
return ret;
@ -385,7 +530,6 @@ namespace geode {
return nullptr;
}
template <class C>
static EventListenerNode* create(
C* cls, typename EventListener<Filter>::template MemberFn<C> callback

View file

@ -65,7 +65,7 @@ namespace geode::utils::web {
* A handle to an in-progress sent asynchronous web request. Use this to
* cancel the request / query information about it
*/
class SentAsyncWebRequest {
class GEODE_DLL SentAsyncWebRequest {
private:
class Impl;
std::shared_ptr<Impl> m_impl;

View file

@ -37,4 +37,12 @@ CCTextureCache* CCTextureCache::get() {
return CCTextureCache::sharedTextureCache();
}
CCScene* CCScene::get() {
return CCDirector::get()->getRunningScene();
}
CCScheduler* CCScheduler::get() {
return CCDirector::get()->getScheduler();
}
#pragma warning(pop)

View file

@ -37,11 +37,35 @@ void CCNode::insertAfter(CCNode* child, CCNode* after) {
}
}
CCArray* Layout::getNodesToPosition(CCNode* on) {
if (!on->getChildren()) {
return CCArray::create();
bool CCNode::hasAncestor(CCNode* ancestor) {
if (!ancestor) {
ancestor = CCScene::get();
}
return on->getChildren()->shallowCopy();
if (m_pParent == ancestor) {
return true;
}
if (m_pParent) {
return m_pParent->hasAncestor(ancestor);
}
return false;
}
CCArray* Layout::getNodesToPosition(CCNode* on) const {
auto arr = CCArray::create();
for (auto child : CCArrayExt<CCNode>(on->getChildren())) {
if (!m_ignoreInvisibleChildren || child->isVisible()) {
arr->addObject(child);
}
}
return arr;
}
void Layout::ignoreInvisibleChildren(bool ignore) {
m_ignoreInvisibleChildren = ignore;
}
bool Layout::isIgnoreInvisibleChildren() const {
return m_ignoreInvisibleChildren;
}
static AxisLayoutOptions const* axisOpts(CCNode* node) {
@ -152,6 +176,32 @@ struct AxisLayout::Row : public CCObject {
{
this->autorelease();
}
void accountSpacers(Axis axis, float availableLength) {
std::vector<SpacerNode*> spacers;
for (auto& node : CCArrayExt<CCNode>(nodes)) {
if (auto spacer = typeinfo_cast<SpacerNode*>(node)) {
spacers.push_back(spacer);
}
}
if (spacers.size()) {
auto unusedSpace = availableLength - this->axisLength;
size_t sum = 0;
for (auto& spacer : spacers) {
sum += spacer->getGrow();
}
for (auto& spacer : spacers) {
auto size = unusedSpace * spacer->getGrow() / static_cast<float>(sum);
if (axis == Axis::Row) {
spacer->setContentSize({ size, this->crossLength });
}
else {
spacer->setContentSize({ this->crossLength, size });
}
}
this->axisLength = availableLength;
}
}
};
struct AxisPosition {
@ -171,6 +221,9 @@ static AxisPosition nodeAxis(CCNode* node, Axis axis, float scale) {
if (auto toggle = typeinfo_cast<CCMenuItemToggler*>(node)) {
scaledSize = toggle->m_offButton->getScaledContentSize();
}
if (auto spacer = typeinfo_cast<SpacerNode*>(node)) {
scaledSize = CCSizeZero;
}
auto anchor = node->getAnchorPoint();
if (axis == Axis::Row) {
return AxisPosition {
@ -587,6 +640,8 @@ void AxisLayout::tryFitLayout(
float rowEvenSpace = available.crossLength / rows->count();
for (auto row : CCArrayExt<Row*>(rows)) {
row->accountSpacers(m_axis, available.axisLength);
if (m_crossAlignment == AxisAlignment::Even) {
rowCrossPos -= rowEvenSpace / 2 + row->crossLength / 2;
}
@ -732,6 +787,24 @@ void AxisLayout::apply(CCNode* on) {
);
}
CCSize AxisLayout::getSizeHint(CCNode* on) const {
// Ideal is single row / column with no scaling
auto nodes = getNodesToPosition(on);
float length = 0.f;
float cross = 0.f;
for (auto& node : CCArrayExt<CCNode*>(nodes)) {
auto axis = nodeAxis(node, m_axis, 1.f);
length += axis.axisLength;
cross += axis.crossLength;
}
if (m_axis == Axis::Row) {
return { length, cross };
}
else {
return { cross, length };
}
}
AxisLayout::AxisLayout(Axis axis) : m_axis(axis) {}
Axis AxisLayout::getAxis() const {
@ -945,3 +1018,30 @@ AxisLayoutOptions* AxisLayoutOptions::setScalePriority(int priority) {
m_scalePriority = priority;
return this;
}
bool SpacerNode::init(size_t grow) {
if (!CCNode::init())
return false;
m_grow = grow;
return true;
}
SpacerNode* SpacerNode::create(size_t grow) {
auto ret = new SpacerNode;
if (ret && ret->init(grow)) {
ret->autorelease();
return ret;
}
CC_SAFE_DELETE(ret);
return nullptr;
}
void SpacerNode::setGrow(size_t grow) {
m_grow = grow;
}
size_t SpacerNode::getGrow() const {
return m_grow;
}

View file

@ -21,7 +21,9 @@ private:
std::string m_id = "";
Ref<Layout> m_layout = nullptr;
std::unique_ptr<LayoutOptions> m_layoutOptions = nullptr;
std::unordered_map<std::string, std::any> m_attributes;
std::unordered_map<std::string, json::Value> m_attributes;
std::unordered_set<std::unique_ptr<EventListenerProtocol>> m_eventListeners;
std::unordered_map<std::string, std::unique_ptr<EventListenerProtocol>> m_idEventListeners;
friend class ProxyCCNode;
friend class cocos2d::CCNode;
@ -165,11 +167,25 @@ void CCNode::updateLayout(bool updateChildOrder) {
}
}
void CCNode::setAttribute(std::string const& attr, std::any value) {
GeodeNodeMetadata::set(this)->m_attributes[attr] = value;
AttributeSetEvent::AttributeSetEvent(CCNode* node, std::string const& id, json::Value& value)
: node(node), id(id), value(value) {}
ListenerResult AttributeSetFilter::handle(MiniFunction<Callback> fn, AttributeSetEvent* event) {
if (event->id == m_targetID) {
fn(event);
}
return ListenerResult::Propagate;
}
std::optional<std::any> CCNode::getAttributeInternal(std::string const& attr) {
AttributeSetFilter::AttributeSetFilter(std::string const& id) : m_targetID(id) {}
void CCNode::setAttribute(std::string const& attr, json::Value const& value) {
auto meta = GeodeNodeMetadata::set(this);
meta->m_attributes[attr] = value;
AttributeSetEvent(this, attr, meta->m_attributes.at(attr)).post();
}
std::optional<json::Value> CCNode::getAttributeInternal(std::string const& attr) {
auto meta = GeodeNodeMetadata::set(this);
if (meta->m_attributes.count(attr)) {
return meta->m_attributes.at(attr);
@ -177,4 +193,49 @@ std::optional<std::any> CCNode::getAttributeInternal(std::string const& attr) {
return std::nullopt;
}
void CCNode::addEventListenerInternal(std::string const& id, EventListenerProtocol* listener) {
auto meta = GeodeNodeMetadata::set(this);
if (id.size()) {
if (meta->m_idEventListeners.contains(id)) {
meta->m_idEventListeners.at(id).reset(listener);
}
else {
meta->m_idEventListeners.emplace(id, listener);
}
}
else {
std::erase_if(meta->m_eventListeners, [=](auto& l) {
return l.get() == listener;
});
meta->m_eventListeners.emplace(listener);
}
}
void CCNode::removeEventListener(EventListenerProtocol* listener) {
auto meta = GeodeNodeMetadata::set(this);
std::erase_if(meta->m_eventListeners, [=](auto& l) {
return l.get() == listener;
});
std::erase_if(meta->m_idEventListeners, [=](auto& l) {
return l.second.get() == listener;
});
}
void CCNode::removeEventListener(std::string const& id) {
GeodeNodeMetadata::set(this)->m_idEventListeners.erase(id);
}
EventListenerProtocol* CCNode::getEventListener(std::string const& id) {
auto meta = GeodeNodeMetadata::set(this);
if (meta->m_idEventListeners.contains(id)) {
return meta->m_idEventListeners.at(id).get();
}
return nullptr;
}
size_t CCNode::getEventListenerCount() {
return GeodeNodeMetadata::set(this)->m_idEventListeners.size() +
GeodeNodeMetadata::set(this)->m_eventListeners.size();
}
#pragma warning(pop)

View file

@ -0,0 +1,40 @@
#include "AddIDs.hpp"
#include <Geode/Bindings.hpp>
#include <Geode/modify/CustomSongWidget.hpp>
#include <Geode/utils/cocos.hpp>
using namespace geode::prelude;
$register_ids(CustomSongWidget) {
setIDSafe<CCScale9Sprite>(this, 0, "bg");
setIDSafe<CCSprite>(this, 0, "loading-bar");
setIDSafe<CCLabelBMFont>(this, 0, "song-name-label");
setIDSafe<CCLabelBMFont>(this, 1, "author-name-label");
setIDSafe<CCLabelBMFont>(this, 2, "id-and-size-label");
setIDSafe<CCLabelBMFont>(this, 3, "error-label");
setIDSafe<CCMenu>(this, 0, "buttons-menu");
auto customSongWidgetMenu = this->getChildByID("buttons-menu");
setIDSafe<CCMenuItemSpriteExtra>(customSongWidgetMenu, 0, "download-button");
setIDSafe<CCMenuItemSpriteExtra>(customSongWidgetMenu, 1, "cancel-button");
setIDSafe<CCMenuItemSpriteExtra>(customSongWidgetMenu, 2, "use-button");
setIDSafe<CCMenuItemSpriteExtra>(customSongWidgetMenu, 3, "refresh-button");
setIDSafe<CCMenuItemSpriteExtra>(customSongWidgetMenu, 4, "play-song-button");
setIDSafe<CCMenuItemSpriteExtra>(customSongWidgetMenu, 5, "more-button");
};
struct CustomSongWidgetIDs : Modify<CustomSongWidgetIDs, CustomSongWidget> {
static void onModify(auto& self) {
if (!self.setHookPriority("CustomSongWidget::init", GEODE_ID_PRIORITY)) {
log::warn("Failed to set CustomSongWidget::init hook priority, node IDs may not work properly");
}
}
bool init(SongInfoObject* s, LevelSettingsObject* l, bool b1, bool b2, bool b3, bool b4, bool hideBackground) {
if (!CustomSongWidget::init(s, l, b1, b2, b3, b4, hideBackground)) return false;
NodeIDs::get()->provide(this);
return true;
}
};

View file

@ -31,9 +31,29 @@ $register_ids(EditLevelLayer) {
);
auto winSize = CCDirector::get()->getWinSize();
auto descBG = this->getChildByID("description-background");
auto descMenu = CCMenu::create();
descMenu->setID("description-menu");
descMenu->setLayout(ColumnLayout::create());
descMenu->setPosition(
descBG->getPositionX() - descBG->getScaledContentSize().width / 2 - 35.f,
descBG->getPositionY()
);
descMenu->setContentSize({ 40.f, 80.f });
this->addChild(descMenu);
if (auto menu = this->getChildByID("level-edit-menu")) {
setIDs(menu, 0, "edit-button", "play-button", "share-button");
if (menu->getChildrenCount() == 4) {
auto btn = static_cast<CCNode*>(menu->getChildren()->objectAtIndex(3));
btn->setID("update-desc-button");
btn->retain();
btn->removeFromParent();
descMenu->addChild(btn);
btn->release();
descMenu->updateLayout();
}
menu->setContentSize({ winSize.width - 160.f, 100.f });
menu->setLayout(RowLayout::create()->setGap(25.f));
}

View file

@ -3,6 +3,7 @@
#include <Geode/utils/VersionInfo.hpp>
static constexpr const char* LOADER_ABOUT_MD = R"MD_SEPARATOR(@LOADER_ABOUT_MD@)MD_SEPARATOR";
static constexpr const char* LOADER_CHANGELOG_MD = R"MD_SEPARATOR(@LOADER_CHANGELOG_MD@)MD_SEPARATOR";
static constexpr const char* LOADER_VERSION_STR = "@PROJECT_VERSION@@PROJECT_VERSION_SUFFIX@";
static constexpr int LOADER_VERSION_MAJOR = @PROJECT_VERSION_MAJOR@;
static constexpr int LOADER_VERSION_MINOR = @PROJECT_VERSION_MINOR@;

View file

@ -1,13 +1,74 @@
#include <Geode/loader/Event.hpp>
#include <Geode/utils/ranges.hpp>
#include <mutex>
using namespace geode::prelude;
void EventListenerProtocol::enable() {
Event::listeners().insert(this);
bool DefaultEventListenerPool::add(EventListenerProtocol* listener) {
if (m_locked) {
m_toAdd.push_back(listener);
}
else {
m_listeners.push_back(listener);
}
return true;
}
void DefaultEventListenerPool::remove(EventListenerProtocol* listener) {
for (size_t i = 0; i < m_listeners.size(); i++) {
if (m_listeners[i] == listener) {
m_listeners[i] = nullptr;
}
}
}
void DefaultEventListenerPool::handle(Event* event) {
m_locked += 1;
for (auto h : m_listeners) {
// if an event listener gets destroyed in the middle of this loop, it
// gets set to null
if (h && h->handle(event) == ListenerResult::Stop) {
break;
}
}
m_locked -= 1;
// only mutate listeners once nothing is iterating
// (if there are recursive handle calls)
if (m_locked == 0) {
ranges::remove(m_listeners, nullptr);
for (auto listener : m_toAdd) {
m_listeners.push_back(listener);
}
m_toAdd.clear();
}
}
DefaultEventListenerPool* DefaultEventListenerPool::get() {
static auto inst = new DefaultEventListenerPool();
return inst;
}
EventListenerPool* EventListenerProtocol::getPool() const {
return DefaultEventListenerPool::get();
}
bool EventListenerProtocol::enable() {
// virtual calls from destructors always call the base class so we gotta
// store the subclass' pool in a member to be able to access it in disable
// this is actually better because now regardless of what getPool() does
// we can always be assured that whatever pool it returns this listener
// will be removed from that pool and can't be in multiple pools at once
if (m_pool || !(m_pool = this->getPool())) {
return false;
}
return m_pool->add(this);
}
void EventListenerProtocol::disable() {
Event::listeners().erase(this);
if (m_pool) {
m_pool->remove(this);
m_pool = nullptr;
}
}
EventListenerProtocol::~EventListenerProtocol() {
@ -16,19 +77,11 @@ EventListenerProtocol::~EventListenerProtocol() {
Event::~Event() {}
EventListenerPool* Event::getPool() const {
return DefaultEventListenerPool::get();
}
void Event::postFrom(Mod* m) {
if (m) this->sender = m;
std::unordered_set<EventListenerProtocol*> listeners_copy = Event::listeners();
for (auto h : listeners_copy) {
if (h->passThrough(this) == ListenerResult::Stop) {
break;
}
}
}
std::unordered_set<EventListenerProtocol*>& Event::listeners() {
static std::unordered_set<EventListenerProtocol*> listeners;
return listeners;
this->getPool()->handle(this);
}

View file

@ -73,7 +73,7 @@ ghc::filesystem::path Mod::getBinaryPath() const {
}
ghc::filesystem::path Mod::getResourcesDir() const {
return dirs::getModRuntimeDir() / this->getID() / "resources";
return dirs::getModRuntimeDir() / this->getID() / "resources" / this->getID();
}
Result<> Mod::saveData() {

View file

@ -116,7 +116,9 @@ std::vector<Hook*> Mod::Impl::getHooks() const {
// Settings and saved values
Result<> Mod::Impl::loadData() {
ModStateEvent(m_self, ModEventType::DataLoaded).post();
Loader::get()->queueInGDThread([&]() {
ModStateEvent(m_self, ModEventType::DataLoaded).post();
});
// Settings
// Check if settings exist
@ -182,6 +184,7 @@ Result<> Mod::Impl::loadData() {
}
Result<> Mod::Impl::saveData() {
// saveData is expected to be synchronous, and always called from GD thread
ModStateEvent(m_self, ModEventType::DataSaved).post();
// Data saving should be fully fail-safe
@ -308,9 +311,12 @@ Result<> Mod::Impl::loadBinary() {
LoaderImpl::get()->releaseNextMod();
ModStateEvent(m_self, ModEventType::Loaded).post();
Loader::get()->queueInGDThread([&]() {
ModStateEvent(m_self, ModEventType::Loaded).post();
});
Loader::get()->updateAllDependencies();
Loader::get()->updateResources();
GEODE_UNWRAP(this->enable());
@ -329,7 +335,9 @@ Result<> Mod::Impl::unloadBinary() {
GEODE_UNWRAP(this->saveData());
GEODE_UNWRAP(this->disable());
ModStateEvent(m_self, ModEventType::Unloaded).post();
Loader::get()->queueInGDThread([&]() {
ModStateEvent(m_self, ModEventType::Unloaded).post();
});
// Disabling unhooks and unpatches already
for (auto const& hook : m_hooks) {
@ -367,7 +375,9 @@ Result<> Mod::Impl::enable() {
}
}
ModStateEvent(m_self, ModEventType::Enabled).post();
Loader::get()->queueInGDThread([&]() {
ModStateEvent(m_self, ModEventType::Enabled).post();
});
m_enabled = true;
return Ok();
@ -381,7 +391,9 @@ Result<> Mod::Impl::disable() {
return Err("Mod does not support disabling");
}
ModStateEvent(m_self, ModEventType::Disabled).post();
Loader::get()->queueInGDThread([&]() {
ModStateEvent(m_self, ModEventType::Disabled).post();
});
for (auto const& hook : m_hooks) {
GEODE_UNWRAP(this->disableHook(hook));
@ -682,6 +694,7 @@ static ModInfo getModImplInfo() {
}
auto info = infoRes.unwrap();
info.details() = LOADER_ABOUT_MD;
info.changelog() = LOADER_CHANGELOG_MD;
info.supportInfo() = SUPPORT_INFO;
info.supportsDisabling() = false;
return info;

View file

@ -112,12 +112,8 @@ bool ModInfoPopup::init(ModInfo const& info, ModListLayer* list) {
// changelog
if (info.changelog()) {
m_changelogArea = MDTextArea::create(info.changelog().value(), { 350.f, 137.5f });
m_changelogArea->setPosition(
-5000.f, winSize.height / 2 - m_changelogArea->getScaledContentSize().height / 2 - 20.f
);
m_changelogArea->setVisible(false);
m_mainLayer->addChild(m_changelogArea);
// m_changelogArea is only created if the changelog button is clicked
// because changelogs can get really long and take a while to load
auto changelogBtnOffSpr = ButtonSprite::create(
CCSprite::createWithSpriteFrameName("changelog.png"_spr),
@ -225,9 +221,19 @@ void ModInfoPopup::onInfo(CCObject*) {
}
void ModInfoPopup::onChangelog(CCObject* sender) {
auto toggle = static_cast<CCMenuItemToggler*>(sender);
auto winSize = CCDirector::get()->getWinSize();
if (!m_changelogArea) {
m_changelogArea = MDTextArea::create(Mod::get()->getModInfo().changelog().value(), { 350.f, 137.5f });
m_changelogArea->setPosition(
-5000.f, winSize.height / 2 - m_changelogArea->getScaledContentSize().height / 2 - 20.f
);
m_changelogArea->setVisible(false);
m_mainLayer->addChild(m_changelogArea);
}
auto toggle = static_cast<CCMenuItemToggler*>(sender);
m_detailsArea->setVisible(toggle->isToggled());
// as it turns out, cocos2d is stupid and still passes touch
// events to invisible nodes
@ -243,6 +249,12 @@ void ModInfoPopup::onChangelog(CCObject* sender) {
!toggle->isToggled() ? winSize.width / 2 - m_changelogArea->getScaledContentSize().width / 2 :
-5000.f
);
m_scrollbar->setTarget(
toggle->isToggled() ?
m_detailsArea->getScrollLayer() :
m_changelogArea->getScrollLayer()
);
}
void ModInfoPopup::keyDown(enumKeyCodes key) {
@ -559,7 +571,7 @@ bool IndexItemInfoPopup::init(IndexItemHandle item, ModListLayer* list) {
void IndexItemInfoPopup::onInstallProgress(ModInstallEvent* event) {
std::visit(makeVisitor {
[&](UpdateFinished) {
[&](UpdateFinished const&) {
this->setInstallStatus(std::nullopt);
FLAlertLayer::create(

View file

@ -35,7 +35,7 @@ protected:
CCMenuItemSpriteExtra* m_infoBtn;
CCLabelBMFont* m_updateVersionLabel = nullptr;
MDTextArea* m_detailsArea;
MDTextArea* m_changelogArea;
MDTextArea* m_changelogArea = nullptr;
Scrollbar* m_scrollbar;
void onChangelog(CCObject*);

View file

@ -303,6 +303,10 @@ DECL_BASED_CREATE_FUNS(Leaderboard);
DECL_BASED_CREATE_FUNS(Editor);
DECL_BASED_CREATE_FUNS(Category);
CCSize CircleButtonSprite::getMaxTopSize() const {
return m_obContentSize * .65f;
}
CCSize EditorButtonSprite::getMaxTopSize() const {
return m_obContentSize - CCSize { 8.f, 8.f };
}

View file

@ -6,8 +6,6 @@ using namespace geode::prelude;
bool Scrollbar::ccTouchBegan(CCTouch* touch, CCEvent* event) {
// hitbox
auto rect = this->boundingBox();
// since anchor point is 0.5, 0.5 it's offset
rect.origin -= this->getScaledContentSize() / 2;
if (!m_target || !rect.containsPoint(touch->getLocation())) return false;
// trigger scrollbar thumb move
@ -40,7 +38,7 @@ void Scrollbar::ccTouchMoved(CCTouch* touch, CCEvent*) {
auto thumbHeight = m_resizeThumb ? std::min(p, 1.f) * targetHeight / .4f : 0;
auto posY = h *
((-pos.y - targetHeight / 2 + thumbHeight / 4 - 5) / (targetHeight - thumbHeight / 2 + 10));
((-pos.y + thumbHeight / 4 - 5) / (targetHeight - thumbHeight / 2 + 10));
if (posY > 0.0f) posY = 0.0f;
if (posY < -h) posY = -h;
@ -73,7 +71,7 @@ void Scrollbar::draw() {
m_track->setContentSize({ m_width / m_track->getScale(),
targetHeight / m_track->getScale() });
}
m_track->setPosition(.0f, .0f);
m_track->setPosition(m_obContentSize / 2);
this->setContentSize({ m_width, targetHeight });
@ -101,6 +99,10 @@ void Scrollbar::draw() {
auto y = m_target->m_contentLayer->getPositionY();
auto thumbHeight = m_resizeThumb ? std::min(p, 1.f) * targetHeight / .4f : 0;
if (thumbHeight < 15.f) {
thumbHeight = 15.f;
}
auto thumbPosY = -targetHeight / 2 + thumbHeight / 4 - 5.0f +
(h ? (-y) / h : 1.f) * (targetHeight - thumbHeight / 2 + 10.0f);
@ -113,15 +115,21 @@ void Scrollbar::draw() {
if (fHeightTop() > 0.0f) {
thumbHeight -= fHeightTop();
if (thumbHeight < 15.f) {
thumbHeight = 15.f;
}
thumbPosY -= fHeightTop();
}
if (fHeightBottom() < 0.f) {
thumbHeight += fHeightBottom();
if (thumbHeight < 15.f) {
thumbHeight = 15.f;
}
thumbPosY -= fHeightBottom();
}
m_thumb->setPosition(0.f, thumbPosY);
m_thumb->setPosition(m_obContentSize / 2 + ccp(0.f, thumbPosY));
if (m_resizeThumb) {
m_thumb->setContentSize({ m_width, thumbHeight });
}
@ -134,6 +142,8 @@ void Scrollbar::setTarget(CCScrollLayerExt* target) {
bool Scrollbar::init(CCScrollLayerExt* target) {
if (!this->CCLayer::init()) return false;
this->ignoreAnchorPointForPosition(false);
m_target = target;
if (cocos::fileExistsInSearchPaths("scrollbar.png"_spr)) {

View file

@ -233,6 +233,32 @@ std::string geode::cocos::cc4bToHexString(ccColor4B const& color) {
return output;
}
WeakRefPool* WeakRefPool::get() {
static auto inst = new WeakRefPool();
return inst;
}
void WeakRefPool::check(CCObject* obj) {
// if this object's only reference is the WeakRefPool aka only weak
// references exist to it, then release it
if (m_pool.contains(obj) && obj->retainCount() == 1) {
obj->release();
m_pool.erase(obj);
}
}
bool WeakRefPool::isManaged(CCObject* obj) {
this->check(obj);
return m_pool.contains(obj);
}
void WeakRefPool::manage(CCObject* obj) {
if (obj && !m_pool.contains(obj)) {
obj->retain();
m_pool.insert(obj);
}
}
CCRect geode::cocos::calculateNodeCoverage(std::vector<CCNode*> const& nodes) {
CCRect coverage;
for (auto child : nodes) {
@ -302,8 +328,12 @@ void geode::cocos::limitNodeSize(cocos2d::CCNode* spr, cocos2d::CCSize const& si
}
bool geode::cocos::nodeIsVisible(cocos2d::CCNode* node) {
if (!node->isVisible()) return false;
if (node->getParent()) return nodeIsVisible(node->getParent());
if (!node->isVisible()) {
return false;
}
if (node->getParent()) {
return nodeIsVisible(node->getParent());
}
return true;
}

View file

@ -1,12 +1,25 @@
#include <Geode/Loader.hpp>
using namespace geode::prelude;
#include <Geode/modify/MenuLayer.hpp>
#include <Geode/loader/SettingNode.hpp>
#include <Geode/loader/ModJsonTest.hpp>
#include <Geode/binding/CCMenuItemSpriteExtra.hpp>
#include <Geode/binding/FLAlertLayer.hpp>
#include "main.hpp"
using namespace geode::prelude;
std::string TestEvent::getData() const {
return data;
}
TestEvent::TestEvent(std::string const& data) : data(data) {}
ListenerResult TestEventFilter::handle(utils::MiniFunction<Callback> fn, TestEvent* event) {
fn(event);
return ListenerResult::Propagate;
}
TestEventFilter::TestEventFilter() {}
enum class Icon {
Steve,
@ -137,6 +150,7 @@ SettingNode* MySettingValue::createNode(float width) {
struct MyMenuLayer : Modify<MyMenuLayer, MenuLayer> {
void onMoreGames(CCObject*) {
TestEvent("Event system works!").post();
if (Mod::get()->getSettingValue<bool>("its-raining-after-all")) {
FLAlertLayer::create("Damn", ":(", "OK")->show();
}

View file

@ -0,0 +1,32 @@
#pragma once
#include <Geode/loader/Event.hpp>
using namespace geode::prelude;
#ifdef GEODE_IS_WINDOWS
#ifdef EXPORTING_MOD
#define GEODE_TESTDEP_DLL __declspec(dllexport)
#else
#define GEODE_TESTDEP_DLL __declspec(dllimport)
#endif
#else
#define GEODE_TESTDEP_DLL
#endif
class GEODE_TESTDEP_DLL TestEvent : public Event {
protected:
std::string data;
public:
std::string getData() const;
TestEvent(std::string const& data);
};
class GEODE_TESTDEP_DLL TestEventFilter : public EventFilter<TestEvent> {
public:
using Callback = void(TestEvent*);
ListenerResult handle(utils::MiniFunction<Callback> fn, TestEvent* event);
TestEventFilter();
};

View file

@ -1,5 +1,5 @@
{
"geode": "0.6.1",
"geode": "1.0.0",
"version": "1.0.0",
"id": "geode.testdep",
"name": "Geode Test Dependency",

View file

@ -9,5 +9,6 @@ target_compile_features(${PROJECT_NAME} PUBLIC cxx_std_20)
set(GEODE_LINK_SOURCE ON)
set_target_properties(${PROJECT_NAME} PROPERTIES PREFIX "")
target_link_libraries(TestMod TestDependency)
setup_geode_mod(${PROJECT_NAME} DONT_INSTALL)

View file

@ -1,6 +1,7 @@
#include <Geode/Loader.hpp>
#include <Geode/loader/ModJsonTest.hpp>
#include <Geode/loader/ModEvent.hpp>
#include "../dependency/main.hpp"
using namespace geode::prelude;
@ -23,6 +24,13 @@ $on_mod(Unloaded) {
log::info("Unloaded");
}
// Events
$execute {
new EventListener<TestEventFilter>(+[](TestEvent* event) {
log::info("Received event: {}", event->getData());
});
}
// Modify
#include <Geode/modify/GJGarageLayer.hpp>

View file

@ -17,6 +17,6 @@ target_include_directories(${PROJECT_NAME} PRIVATE
target_compile_definitions(${PROJECT_NAME} PRIVATE -DGEODE_DONT_WARN_INCORRECT_MEMBERS)
target_link_libraries(${PROJECT_NAME} PRIVATE ghc_filesystem)
target_link_libraries(${PROJECT_NAME} PRIVATE ghc_filesystem mat-json)
add_dependencies(${PROJECT_NAME} CodegenRun)

View file

@ -4,10 +4,13 @@
GEODE_MEMBER_CHECK(GameObject, m_effectPlistName, 0x278);
GEODE_MEMBER_CHECK(GameObject, m_effectManager, 0x454);
GEODE_MEMBER_CHECK(GameObject, m_firstPosition, 0x424);
GEODE_MEMBER_CHECK(PlayerObject, m_unk484, 0x484);
GEODE_MEMBER_CHECK(PlayerObject, m_ghostTrail, 0x4E4);
GEODE_MEMBER_CHECK(PlayerObject, m_waveTrail, 0x514);
GEODE_MEMBER_CHECK(PlayerObject, m_isDart, 0x63b);
GEODE_MEMBER_CHECK(PlayerObject, m_isDashing, 0x641);
GEODE_MEMBER_CHECK(EditorUI, m_buttonBar, 0x134);
GEODE_MEMBER_CHECK(EditorUI, m_hideableUIElementArray, 0x13c);