diff --git a/CHANGELOG.md b/CHANGELOG.md index 27b007a2..180b2d85 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ # Geode Changelog +## v1.3.1 + * Fix TulipHook not relocating RIP relative operands on MacOS (6cad19d) + ## v1.3.0 * Completely remove runtime enabling & disabling of mods (d817749) * Patches auto enabling can be disabled (69821f3) diff --git a/CMakeLists.txt b/CMakeLists.txt index 62e9b72c..99b5201f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -96,7 +96,7 @@ if (PROJECT_IS_TOP_LEVEL AND NOT GEODE_BUILDING_DOCS) set(TULIP_LINK_SOURCE ON) endif() set(CMAKE_WARN_DEPRECATED OFF CACHE BOOL "" FORCE) -CPMAddPackage("gh:geode-sdk/TulipHook#2e4cb5a") +CPMAddPackage("gh:geode-sdk/TulipHook#3423a29") set(CMAKE_WARN_DEPRECATED ON CACHE BOOL "" FORCE) # Silence warnings from dependencies diff --git a/VERSION b/VERSION index f0bb29e7..e21e727f 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.3.0 +1.4.0 \ No newline at end of file diff --git a/bindings/Cocos2d.bro b/bindings/Cocos2d.bro index 692b6ce9..b26400b2 100644 --- a/bindings/Cocos2d.bro +++ b/bindings/Cocos2d.bro @@ -174,7 +174,7 @@ class cocos2d::CCDirector { auto convertToGL(cocos2d::CCPoint const&) = mac 0x24a210; auto convertToUI(cocos2d::CCPoint const&) = mac 0x24a340; auto drawScene() = mac 0x249690; - auto willSwitchToScene(cocos2d::CCScene* scene); + auto willSwitchToScene(cocos2d::CCScene* scene) = mac 0x24a520; auto setOpenGLView(cocos2d::CCEGLView *pobOpenGLView) = mac 0x249be0; auto updateScreenScale(cocos2d::CCSize) = mac 0x249f10; auto setContentScaleFactor(float); @@ -291,7 +291,7 @@ class cocos2d::CCEGLViewProtocol { auto getScaleX() const = mac 0x29e300; auto getScaleY() const = mac 0x29e310; virtual auto setDesignResolutionSize(float, float, ResolutionPolicy); - auto setFrameSize(float, float) = mac 0x29d960; + virtual void setFrameSize(float, float) = mac 0x29d960; } [[link(win)]] @@ -318,6 +318,7 @@ class cocos2d::CCFileUtils : cocos2d::TypeInfo { class cocos2d::CCGLProgram { auto setUniformsForBuiltins() = mac 0x232c70; auto use() = mac 0x231d70; + bool compileShader(unsigned int* shader, unsigned int type, const char* source); } [[link(win)]] @@ -891,6 +892,7 @@ class cocos2d::CCSet { class cocos2d::CCShaderCache { static auto sharedShaderCache() = mac 0xe6d10; auto programForKey(const char*) = mac 0xe7d40; + void reloadDefaultShaders(); } [[link(win)]] @@ -1277,4 +1279,4 @@ class pugi::xml_document { ~xml_document() = mac 0x393b50; } -// clang-format on \ No newline at end of file +// clang-format on diff --git a/bindings/GeometryDash.bro b/bindings/GeometryDash.bro index e6239966..ab336f38 100644 --- a/bindings/GeometryDash.bro +++ b/bindings/GeometryDash.bro @@ -3045,7 +3045,9 @@ class GameManager : GManager { void loadGround(int) = mac 0x1cc8e0, win 0xc9a50; void reloadAll(bool, bool, bool) = mac 0x1d08a0, win 0xce950; void reloadAllStep2() = mac 0x1d0940, win 0xce9e0, ios 0x23b1f4; - void reloadAllStep5() = mac 0x1d0b00; + void reloadAllStep3() = win 0xceb10; + void reloadAllStep4() = win 0xceb80; + void reloadAllStep5() = mac 0x1d0b00, win 0xcebf0; void reportPercentageForLevel(int, int, bool) = mac 0x1c5b00; void setGameVariable(const char*, bool) = mac 0x1cca80, win 0xc9b50; void setIntGameVariable(const char*, int) = mac 0x1cd0e0, win 0xca230; diff --git a/cmake/Platform.cmake b/cmake/Platform.cmake index 89f4fa89..22602b4b 100644 --- a/cmake/Platform.cmake +++ b/cmake/Platform.cmake @@ -46,6 +46,7 @@ elseif (GEODE_TARGET_PLATFORM STREQUAL "MacOS") target_link_libraries(${PROJECT_NAME} INTERFACE "-framework Cocoa" + "-framework OpenGL" ${CURL_LIBRARIES} ${GEODE_LOADER_PATH}/include/link/libfmod.dylib ) diff --git a/installer/windows/installer.nsi b/installer/windows/installer.nsi index dd9989ef..43d6c309 100644 --- a/installer/windows/installer.nsi +++ b/installer/windows/installer.nsi @@ -490,8 +490,10 @@ Function un.onInit FunctionEnd Section "Uninstall" DeleteRegKey /ifempty HKCU "Software\Geode" + Delete $INSTDIR\GeodeUninstaller.exe Delete $INSTDIR\Geode.dll Delete $INSTDIR\Geode.pdb + Delete $INSTDIR\Geode.lib Delete $INSTDIR\GeodeUpdater.exe Delete $INSTDIR\XInput9_1_0.dll diff --git a/loader/include/Geode/cocos/base_nodes/SpacerNode.hpp b/loader/include/Geode/cocos/base_nodes/SpacerNode.hpp index 2e153611..e600402a 100644 --- a/loader/include/Geode/cocos/base_nodes/SpacerNode.hpp +++ b/loader/include/Geode/cocos/base_nodes/SpacerNode.hpp @@ -54,6 +54,30 @@ public: size_t getGrow() const; }; +/** + * A spacer node that updates the content size of its child to match its own + * @note This is useful for making a spacer node that takes up the remaining + * space in a layout + */ +class GEODE_DLL SpacerNodeChild : public SpacerNode { +protected: + CCNode* m_child = nullptr; + + bool init(CCNode* child, 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 SpacerNodeChild* create(CCNode* child, size_t grow = 1); + + void setContentSize(CCSize const& size) override; +}; + #pragma warning(pop) NS_CC_END diff --git a/loader/include/Geode/cocos/shaders/CCGLProgram.h b/loader/include/Geode/cocos/shaders/CCGLProgram.h index 35412ec0..f1fa89fd 100644 --- a/loader/include/Geode/cocos/shaders/CCGLProgram.h +++ b/loader/include/Geode/cocos/shaders/CCGLProgram.h @@ -271,7 +271,7 @@ public: */ inline const GLuint getProgram() { return m_uProgram; } -protected: +private: bool updateUniformLocation(GLint location, GLvoid* data, unsigned int bytes); const char* description(); bool compileShader(GLuint * shader, GLenum type, const GLchar* source); diff --git a/loader/include/Geode/loader/Mod.hpp b/loader/include/Geode/loader/Mod.hpp index 4df6f9c2..bede828d 100644 --- a/loader/include/Geode/loader/Mod.hpp +++ b/loader/include/Geode/loader/Mod.hpp @@ -38,6 +38,7 @@ namespace geode { Enable, Disable, Uninstall, + UninstallWithSaveData }; GEODE_HIDDEN Mod* takeNextLoaderMod(); @@ -85,14 +86,14 @@ namespace geode { ghc::filesystem::path getPackagePath() const; VersionInfo getVersion() const; bool isEnabled() const; - bool isLoaded() const; + [[deprecated("use isEnabled instead")]] bool isLoaded() const; bool supportsDisabling() const; - bool canDisable() const; - bool canEnable() const; + [[deprecated("always true")]] bool canDisable() const; + [[deprecated("always true")]] bool canEnable() const; bool needsEarlyLoad() const; - [[deprecated]] bool supportsUnloading() const; - [[deprecated("use wasSuccessfullyLoaded instead")]] bool wasSuccesfullyLoaded() const; - bool wasSuccessfullyLoaded() const; + [[deprecated("always false")]] bool supportsUnloading() const; + [[deprecated("use isEnabled instead")]] bool wasSuccesfullyLoaded() const; + [[deprecated("use isEnabled instead")]] bool wasSuccessfullyLoaded() const; [[deprecated("use getMetadata instead")]] ModInfo getModInfo() const; ModMetadata getMetadata() const; ghc::filesystem::path getTempDir() const; @@ -336,12 +337,20 @@ namespace geode { */ Result<> disable(); + // TODO: in 2.0.0 make this use an optional arg instead /** - * Disable this mod (if supported), then delete the mod's .geode package. + * Delete the mod's .geode package. * @returns Successful result on success, * errorful result with info on error */ Result<> uninstall(); + /** + * Delete the mod's .geode package. + * @param deleteSaveData Whether should also delete the mod's save data + * @returns Successful result on success, + * errorful result with info on error + */ + Result<> uninstall(bool deleteSaveData); bool isUninstalled() const; ModRequestedAction getRequestedAction() const; diff --git a/loader/include/Geode/utils/general.hpp b/loader/include/Geode/utils/general.hpp index c4f5015f..b4209af2 100644 --- a/loader/include/Geode/utils/general.hpp +++ b/loader/include/Geode/utils/general.hpp @@ -129,5 +129,7 @@ namespace geode::utils::clipboard { } namespace geode::utils::game { + GEODE_DLL void exit(); GEODE_DLL void restart(); + GEODE_DLL void launchLoaderUninstaller(bool deleteSaveData); } diff --git a/loader/resources/blanks/baseAccount_Normal_Blue.png b/loader/resources/blanks/baseAccount_Normal_Blue.png index 559538ec..442fc794 100644 Binary files a/loader/resources/blanks/baseAccount_Normal_Blue.png and b/loader/resources/blanks/baseAccount_Normal_Blue.png differ diff --git a/loader/resources/blanks/baseAccount_Normal_Gray.png b/loader/resources/blanks/baseAccount_Normal_Gray.png index 74a96ca2..d09dbd52 100644 Binary files a/loader/resources/blanks/baseAccount_Normal_Gray.png and b/loader/resources/blanks/baseAccount_Normal_Gray.png differ diff --git a/loader/resources/blanks/baseAccount_Normal_Purple.png b/loader/resources/blanks/baseAccount_Normal_Purple.png index 97b90e4f..75239f60 100644 Binary files a/loader/resources/blanks/baseAccount_Normal_Purple.png and b/loader/resources/blanks/baseAccount_Normal_Purple.png differ diff --git a/loader/resources/blanks/baseCategory_Big_Green.png b/loader/resources/blanks/baseCategory_Big_Green.png index ae02fce5..ff67b43f 100644 Binary files a/loader/resources/blanks/baseCategory_Big_Green.png and b/loader/resources/blanks/baseCategory_Big_Green.png differ diff --git a/loader/resources/blanks/baseCircle_BigAlt_Blue.png b/loader/resources/blanks/baseCircle_BigAlt_Blue.png index 205e4fdd..ef69e647 100644 Binary files a/loader/resources/blanks/baseCircle_BigAlt_Blue.png and b/loader/resources/blanks/baseCircle_BigAlt_Blue.png differ diff --git a/loader/resources/blanks/baseCircle_BigAlt_Cyan.png b/loader/resources/blanks/baseCircle_BigAlt_Cyan.png index d5a0ad42..40130cba 100644 Binary files a/loader/resources/blanks/baseCircle_BigAlt_Cyan.png and b/loader/resources/blanks/baseCircle_BigAlt_Cyan.png differ diff --git a/loader/resources/blanks/baseCircle_BigAlt_Gray.png b/loader/resources/blanks/baseCircle_BigAlt_Gray.png index ea73faba..aa77f001 100644 Binary files a/loader/resources/blanks/baseCircle_BigAlt_Gray.png and b/loader/resources/blanks/baseCircle_BigAlt_Gray.png differ diff --git a/loader/resources/blanks/baseCircle_BigAlt_Green.png b/loader/resources/blanks/baseCircle_BigAlt_Green.png index 7c042572..d22bc68f 100644 Binary files a/loader/resources/blanks/baseCircle_BigAlt_Green.png and b/loader/resources/blanks/baseCircle_BigAlt_Green.png differ diff --git a/loader/resources/blanks/baseCircle_BigAlt_Pink.png b/loader/resources/blanks/baseCircle_BigAlt_Pink.png index 886024e3..4891b1f5 100644 Binary files a/loader/resources/blanks/baseCircle_BigAlt_Pink.png and b/loader/resources/blanks/baseCircle_BigAlt_Pink.png differ diff --git a/loader/resources/blanks/baseCircle_Big_Blue.png b/loader/resources/blanks/baseCircle_Big_Blue.png index 21957129..128d1331 100644 Binary files a/loader/resources/blanks/baseCircle_Big_Blue.png and b/loader/resources/blanks/baseCircle_Big_Blue.png differ diff --git a/loader/resources/blanks/baseCircle_Big_Cyan.png b/loader/resources/blanks/baseCircle_Big_Cyan.png index a06d3814..5b6a0bea 100644 Binary files a/loader/resources/blanks/baseCircle_Big_Cyan.png and b/loader/resources/blanks/baseCircle_Big_Cyan.png differ diff --git a/loader/resources/blanks/baseCircle_Big_Gray.png b/loader/resources/blanks/baseCircle_Big_Gray.png index 85ce3a8b..1cc9c125 100644 Binary files a/loader/resources/blanks/baseCircle_Big_Gray.png and b/loader/resources/blanks/baseCircle_Big_Gray.png differ diff --git a/loader/resources/blanks/baseCircle_Big_Green.png b/loader/resources/blanks/baseCircle_Big_Green.png index 4bec4026..ec3f451c 100644 Binary files a/loader/resources/blanks/baseCircle_Big_Green.png and b/loader/resources/blanks/baseCircle_Big_Green.png differ diff --git a/loader/resources/blanks/baseCircle_Big_Pink.png b/loader/resources/blanks/baseCircle_Big_Pink.png index eade39db..889eeee1 100644 Binary files a/loader/resources/blanks/baseCircle_Big_Pink.png and b/loader/resources/blanks/baseCircle_Big_Pink.png differ diff --git a/loader/resources/blanks/baseCircle_Large_Blue.png b/loader/resources/blanks/baseCircle_Large_Blue.png index fa5d25f6..3a42af72 100644 Binary files a/loader/resources/blanks/baseCircle_Large_Blue.png and b/loader/resources/blanks/baseCircle_Large_Blue.png differ diff --git a/loader/resources/blanks/baseCircle_Large_Cyan.png b/loader/resources/blanks/baseCircle_Large_Cyan.png index 50d04df2..cea77e2f 100644 Binary files a/loader/resources/blanks/baseCircle_Large_Cyan.png and b/loader/resources/blanks/baseCircle_Large_Cyan.png differ diff --git a/loader/resources/blanks/baseCircle_Large_Gray.png b/loader/resources/blanks/baseCircle_Large_Gray.png index 0cd467cc..3cecc3ab 100644 Binary files a/loader/resources/blanks/baseCircle_Large_Gray.png and b/loader/resources/blanks/baseCircle_Large_Gray.png differ diff --git a/loader/resources/blanks/baseCircle_Large_Green.png b/loader/resources/blanks/baseCircle_Large_Green.png index b8c12356..6100ff10 100644 Binary files a/loader/resources/blanks/baseCircle_Large_Green.png and b/loader/resources/blanks/baseCircle_Large_Green.png differ diff --git a/loader/resources/blanks/baseCircle_Large_Pink.png b/loader/resources/blanks/baseCircle_Large_Pink.png index efd3874a..97b3d246 100644 Binary files a/loader/resources/blanks/baseCircle_Large_Pink.png and b/loader/resources/blanks/baseCircle_Large_Pink.png differ diff --git a/loader/resources/blanks/baseCircle_MediumAlt_Blue.png b/loader/resources/blanks/baseCircle_MediumAlt_Blue.png index 667a1f59..b5cdf449 100644 Binary files a/loader/resources/blanks/baseCircle_MediumAlt_Blue.png and b/loader/resources/blanks/baseCircle_MediumAlt_Blue.png differ diff --git a/loader/resources/blanks/baseCircle_MediumAlt_Cyan.png b/loader/resources/blanks/baseCircle_MediumAlt_Cyan.png index 61ffcd7d..daed179f 100644 Binary files a/loader/resources/blanks/baseCircle_MediumAlt_Cyan.png and b/loader/resources/blanks/baseCircle_MediumAlt_Cyan.png differ diff --git a/loader/resources/blanks/baseCircle_MediumAlt_Gray.png b/loader/resources/blanks/baseCircle_MediumAlt_Gray.png index ceb93598..aa7a993f 100644 Binary files a/loader/resources/blanks/baseCircle_MediumAlt_Gray.png and b/loader/resources/blanks/baseCircle_MediumAlt_Gray.png differ diff --git a/loader/resources/blanks/baseCircle_MediumAlt_Green.png b/loader/resources/blanks/baseCircle_MediumAlt_Green.png index de9bcacd..d1afa1f0 100644 Binary files a/loader/resources/blanks/baseCircle_MediumAlt_Green.png and b/loader/resources/blanks/baseCircle_MediumAlt_Green.png differ diff --git a/loader/resources/blanks/baseCircle_MediumAlt_Pink.png b/loader/resources/blanks/baseCircle_MediumAlt_Pink.png index 2734e40b..9717f081 100644 Binary files a/loader/resources/blanks/baseCircle_MediumAlt_Pink.png and b/loader/resources/blanks/baseCircle_MediumAlt_Pink.png differ diff --git a/loader/resources/blanks/baseCircle_Medium_Blue.png b/loader/resources/blanks/baseCircle_Medium_Blue.png index fb174e72..964b312d 100644 Binary files a/loader/resources/blanks/baseCircle_Medium_Blue.png and b/loader/resources/blanks/baseCircle_Medium_Blue.png differ diff --git a/loader/resources/blanks/baseCircle_Medium_Cyan.png b/loader/resources/blanks/baseCircle_Medium_Cyan.png index 374fef8f..01589f7a 100644 Binary files a/loader/resources/blanks/baseCircle_Medium_Cyan.png and b/loader/resources/blanks/baseCircle_Medium_Cyan.png differ diff --git a/loader/resources/blanks/baseCircle_Medium_Gray.png b/loader/resources/blanks/baseCircle_Medium_Gray.png index e2b59434..663f92ce 100644 Binary files a/loader/resources/blanks/baseCircle_Medium_Gray.png and b/loader/resources/blanks/baseCircle_Medium_Gray.png differ diff --git a/loader/resources/blanks/baseCircle_Medium_Green.png b/loader/resources/blanks/baseCircle_Medium_Green.png index b50d38f7..ecf70e53 100644 Binary files a/loader/resources/blanks/baseCircle_Medium_Green.png and b/loader/resources/blanks/baseCircle_Medium_Green.png differ diff --git a/loader/resources/blanks/baseCircle_Medium_Pink.png b/loader/resources/blanks/baseCircle_Medium_Pink.png index cf609277..8ca18621 100644 Binary files a/loader/resources/blanks/baseCircle_Medium_Pink.png and b/loader/resources/blanks/baseCircle_Medium_Pink.png differ diff --git a/loader/resources/blanks/baseCircle_SmallAlt_Blue.png b/loader/resources/blanks/baseCircle_SmallAlt_Blue.png index 958f148e..a72d42c8 100644 Binary files a/loader/resources/blanks/baseCircle_SmallAlt_Blue.png and b/loader/resources/blanks/baseCircle_SmallAlt_Blue.png differ diff --git a/loader/resources/blanks/baseCircle_SmallAlt_Cyan.png b/loader/resources/blanks/baseCircle_SmallAlt_Cyan.png index c777f6ab..1e1583bd 100644 Binary files a/loader/resources/blanks/baseCircle_SmallAlt_Cyan.png and b/loader/resources/blanks/baseCircle_SmallAlt_Cyan.png differ diff --git a/loader/resources/blanks/baseCircle_SmallAlt_Gray.png b/loader/resources/blanks/baseCircle_SmallAlt_Gray.png index dcc712c6..5319559a 100644 Binary files a/loader/resources/blanks/baseCircle_SmallAlt_Gray.png and b/loader/resources/blanks/baseCircle_SmallAlt_Gray.png differ diff --git a/loader/resources/blanks/baseCircle_SmallAlt_Green.png b/loader/resources/blanks/baseCircle_SmallAlt_Green.png index 831acf9b..811f2fc3 100644 Binary files a/loader/resources/blanks/baseCircle_SmallAlt_Green.png and b/loader/resources/blanks/baseCircle_SmallAlt_Green.png differ diff --git a/loader/resources/blanks/baseCircle_SmallAlt_Pink.png b/loader/resources/blanks/baseCircle_SmallAlt_Pink.png index d139fd9c..41c6334b 100644 Binary files a/loader/resources/blanks/baseCircle_SmallAlt_Pink.png and b/loader/resources/blanks/baseCircle_SmallAlt_Pink.png differ diff --git a/loader/resources/blanks/baseCircle_Small_Blue.png b/loader/resources/blanks/baseCircle_Small_Blue.png index 00e86d9e..49eed3bc 100644 Binary files a/loader/resources/blanks/baseCircle_Small_Blue.png and b/loader/resources/blanks/baseCircle_Small_Blue.png differ diff --git a/loader/resources/blanks/baseCircle_Small_Cyan.png b/loader/resources/blanks/baseCircle_Small_Cyan.png index 83c21119..3f539bc0 100644 Binary files a/loader/resources/blanks/baseCircle_Small_Cyan.png and b/loader/resources/blanks/baseCircle_Small_Cyan.png differ diff --git a/loader/resources/blanks/baseCircle_Small_Gray.png b/loader/resources/blanks/baseCircle_Small_Gray.png index de8b71dc..be7b3608 100644 Binary files a/loader/resources/blanks/baseCircle_Small_Gray.png and b/loader/resources/blanks/baseCircle_Small_Gray.png differ diff --git a/loader/resources/blanks/baseCircle_Small_Green.png b/loader/resources/blanks/baseCircle_Small_Green.png index 8f1690ec..34830d5d 100644 Binary files a/loader/resources/blanks/baseCircle_Small_Green.png and b/loader/resources/blanks/baseCircle_Small_Green.png differ diff --git a/loader/resources/blanks/baseCircle_Small_Pink.png b/loader/resources/blanks/baseCircle_Small_Pink.png index fd80a292..ecbf9d99 100644 Binary files a/loader/resources/blanks/baseCircle_Small_Pink.png and b/loader/resources/blanks/baseCircle_Small_Pink.png differ diff --git a/loader/resources/blanks/baseCircle_Tiny_Blue.png b/loader/resources/blanks/baseCircle_Tiny_Blue.png index f1d70b2b..f6f5e734 100644 Binary files a/loader/resources/blanks/baseCircle_Tiny_Blue.png and b/loader/resources/blanks/baseCircle_Tiny_Blue.png differ diff --git a/loader/resources/blanks/baseCircle_Tiny_Cyan.png b/loader/resources/blanks/baseCircle_Tiny_Cyan.png index f953b2fc..ea69dd86 100644 Binary files a/loader/resources/blanks/baseCircle_Tiny_Cyan.png and b/loader/resources/blanks/baseCircle_Tiny_Cyan.png differ diff --git a/loader/resources/blanks/baseCircle_Tiny_Gray.png b/loader/resources/blanks/baseCircle_Tiny_Gray.png index 1bd93285..4d3d9d53 100644 Binary files a/loader/resources/blanks/baseCircle_Tiny_Gray.png and b/loader/resources/blanks/baseCircle_Tiny_Gray.png differ diff --git a/loader/resources/blanks/baseCircle_Tiny_Green.png b/loader/resources/blanks/baseCircle_Tiny_Green.png index 1d0a28d7..8be15fca 100644 Binary files a/loader/resources/blanks/baseCircle_Tiny_Green.png and b/loader/resources/blanks/baseCircle_Tiny_Green.png differ diff --git a/loader/resources/blanks/baseCircle_Tiny_Pink.png b/loader/resources/blanks/baseCircle_Tiny_Pink.png index 02f86ec8..afa3f94a 100644 Binary files a/loader/resources/blanks/baseCircle_Tiny_Pink.png and b/loader/resources/blanks/baseCircle_Tiny_Pink.png differ diff --git a/loader/resources/blanks/baseCross_Huge_Green.png b/loader/resources/blanks/baseCross_Huge_Green.png index 619eb833..15411545 100644 Binary files a/loader/resources/blanks/baseCross_Huge_Green.png and b/loader/resources/blanks/baseCross_Huge_Green.png differ diff --git a/loader/resources/blanks/baseCross_Small_Green.png b/loader/resources/blanks/baseCross_Small_Green.png index 1d44a8d3..350c5f0c 100644 Binary files a/loader/resources/blanks/baseCross_Small_Green.png and b/loader/resources/blanks/baseCross_Small_Green.png differ diff --git a/loader/resources/blanks/baseEditor_Normal_Aqua.png b/loader/resources/blanks/baseEditor_Normal_Aqua.png index 1feb76a2..908e0920 100644 Binary files a/loader/resources/blanks/baseEditor_Normal_Aqua.png and b/loader/resources/blanks/baseEditor_Normal_Aqua.png differ diff --git a/loader/resources/blanks/baseEditor_Normal_Cyan.png b/loader/resources/blanks/baseEditor_Normal_Cyan.png index c726ca74..0943c41c 100644 Binary files a/loader/resources/blanks/baseEditor_Normal_Cyan.png and b/loader/resources/blanks/baseEditor_Normal_Cyan.png differ diff --git a/loader/resources/blanks/baseEditor_Normal_DarkGray.png b/loader/resources/blanks/baseEditor_Normal_DarkGray.png index a8ed3f93..56ba0849 100644 Binary files a/loader/resources/blanks/baseEditor_Normal_DarkGray.png and b/loader/resources/blanks/baseEditor_Normal_DarkGray.png differ diff --git a/loader/resources/blanks/baseEditor_Normal_Gray.png b/loader/resources/blanks/baseEditor_Normal_Gray.png index faeeb5a7..3f7f0023 100644 Binary files a/loader/resources/blanks/baseEditor_Normal_Gray.png and b/loader/resources/blanks/baseEditor_Normal_Gray.png differ diff --git a/loader/resources/blanks/baseEditor_Normal_Green.png b/loader/resources/blanks/baseEditor_Normal_Green.png index 9162a71f..db9c9250 100644 Binary files a/loader/resources/blanks/baseEditor_Normal_Green.png and b/loader/resources/blanks/baseEditor_Normal_Green.png differ diff --git a/loader/resources/blanks/baseEditor_Normal_LightBlue.png b/loader/resources/blanks/baseEditor_Normal_LightBlue.png index 00ccfe5d..f9f798e7 100644 Binary files a/loader/resources/blanks/baseEditor_Normal_LightBlue.png and b/loader/resources/blanks/baseEditor_Normal_LightBlue.png differ diff --git a/loader/resources/blanks/baseEditor_Normal_Orange.png b/loader/resources/blanks/baseEditor_Normal_Orange.png index b2ddde8d..55633876 100644 Binary files a/loader/resources/blanks/baseEditor_Normal_Orange.png and b/loader/resources/blanks/baseEditor_Normal_Orange.png differ diff --git a/loader/resources/blanks/baseEditor_Normal_Pink.png b/loader/resources/blanks/baseEditor_Normal_Pink.png index 8a5abb5c..9f67c3c9 100644 Binary files a/loader/resources/blanks/baseEditor_Normal_Pink.png and b/loader/resources/blanks/baseEditor_Normal_Pink.png differ diff --git a/loader/resources/blanks/baseEditor_Normal_Teal.png b/loader/resources/blanks/baseEditor_Normal_Teal.png index 4b492b41..bb31bce2 100644 Binary files a/loader/resources/blanks/baseEditor_Normal_Teal.png and b/loader/resources/blanks/baseEditor_Normal_Teal.png differ diff --git a/loader/resources/blanks/baseIconSelect_Normal_Selected.png b/loader/resources/blanks/baseIconSelect_Normal_Selected.png index 02d2ac37..b74ae53c 100644 Binary files a/loader/resources/blanks/baseIconSelect_Normal_Selected.png and b/loader/resources/blanks/baseIconSelect_Normal_Selected.png differ diff --git a/loader/resources/blanks/baseIconSelect_Normal_Unselected.png b/loader/resources/blanks/baseIconSelect_Normal_Unselected.png index 5dd3b93c..08c74ea1 100644 Binary files a/loader/resources/blanks/baseIconSelect_Normal_Unselected.png and b/loader/resources/blanks/baseIconSelect_Normal_Unselected.png differ diff --git a/loader/resources/blanks/baseLeaderboard_Normal_Blue.png b/loader/resources/blanks/baseLeaderboard_Normal_Blue.png index ded180c0..e628ba7d 100644 Binary files a/loader/resources/blanks/baseLeaderboard_Normal_Blue.png and b/loader/resources/blanks/baseLeaderboard_Normal_Blue.png differ diff --git a/loader/resources/blanks/baseTab_Normal_Selected.png b/loader/resources/blanks/baseTab_Normal_Selected.png index 49e45255..ec0b74b6 100644 Binary files a/loader/resources/blanks/baseTab_Normal_Selected.png and b/loader/resources/blanks/baseTab_Normal_Selected.png differ diff --git a/loader/resources/blanks/baseTab_Normal_Unselected.png b/loader/resources/blanks/baseTab_Normal_Unselected.png index e71e5450..16961c5f 100644 Binary files a/loader/resources/blanks/baseTab_Normal_Unselected.png and b/loader/resources/blanks/baseTab_Normal_Unselected.png differ diff --git a/loader/resources/blanks/baseTab_Normal_UnselectedDark.png b/loader/resources/blanks/baseTab_Normal_UnselectedDark.png index e063f2e0..a7da7b67 100644 Binary files a/loader/resources/blanks/baseTab_Normal_UnselectedDark.png and b/loader/resources/blanks/baseTab_Normal_UnselectedDark.png differ diff --git a/loader/resources/category-bg.png b/loader/resources/category-bg.png index e9df8e44..b75dd8e6 100644 Binary files a/loader/resources/category-bg.png and b/loader/resources/category-bg.png differ diff --git a/loader/resources/category-dot.png b/loader/resources/category-dot.png index 07d604fd..fdabdad9 100644 Binary files a/loader/resources/category-dot.png and b/loader/resources/category-dot.png differ diff --git a/loader/resources/filters.png b/loader/resources/filters.png index f4b718ce..54c0389c 100644 Binary files a/loader/resources/filters.png and b/loader/resources/filters.png differ diff --git a/loader/resources/gift.png b/loader/resources/gift.png index ad89c950..9015fd28 100644 Binary files a/loader/resources/gift.png and b/loader/resources/gift.png differ diff --git a/loader/resources/github.png b/loader/resources/github.png index a0ef002d..4e6d1a62 100644 Binary files a/loader/resources/github.png and b/loader/resources/github.png differ diff --git a/loader/resources/images/tab-gradient-mask.png b/loader/resources/images/tab-gradient-mask.png index d9a06ce7..32326688 100644 Binary files a/loader/resources/images/tab-gradient-mask.png and b/loader/resources/images/tab-gradient-mask.png differ diff --git a/loader/resources/info-warning.png b/loader/resources/info-warning.png index 64fab516..a986e6d6 100644 Binary files a/loader/resources/info-warning.png and b/loader/resources/info-warning.png differ diff --git a/loader/resources/pencil.png b/loader/resources/pencil.png index b94c2732..2ef2de73 100644 Binary files a/loader/resources/pencil.png and b/loader/resources/pencil.png differ diff --git a/loader/src/cocos2d-ext/Layout.cpp b/loader/src/cocos2d-ext/Layout.cpp index 53b2976c..06bdd3b9 100644 --- a/loader/src/cocos2d-ext/Layout.cpp +++ b/loader/src/cocos2d-ext/Layout.cpp @@ -177,7 +177,7 @@ struct AxisLayout::Row : public CCObject { this->autorelease(); } - void accountSpacers(Axis axis, float availableLength) { + void accountSpacers(Axis axis, float availableLength, float crossLength) { std::vector spacers; for (auto& node : CCArrayExt(nodes)) { if (auto spacer = typeinfo_cast(node)) { @@ -193,10 +193,10 @@ struct AxisLayout::Row : public CCObject { for (auto& spacer : spacers) { auto size = unusedSpace * spacer->getGrow() / static_cast(sum); if (axis == Axis::Row) { - spacer->setContentSize({ size, this->crossLength }); + spacer->setContentSize({ size, crossLength }); } else { - spacer->setContentSize({ this->crossLength, size }); + spacer->setContentSize({ crossLength, size }); } } this->axisLength = availableLength; @@ -376,16 +376,16 @@ AxisLayout::Row* AxisLayout::fitInRow( } // todo: make this calculation more smart to avoid so much unnecessary recursion - auto scaleDownFactor = scale - .0125f; + auto scaleDownFactor = scale - .002f; auto squishFactor = available.axisLength / (axisUnsquishedLength + .01f) * squish; // calculate row scale, squish, and prio int tries = 1000; while (axisLength > available.axisLength) { if (this->canTryScalingDown( - res, prio, scale, scale - .0125f, minMaxPrios + res, prio, scale, scale - .002f, minMaxPrios )) { - scale -= .0125f; + scale -= .002f; } else { squish = available.axisLength / (axisUnsquishedLength + .01f) * squish; @@ -643,7 +643,7 @@ void AxisLayout::tryFitLayout( float rowEvenSpace = available.crossLength / rows->count(); for (auto row : CCArrayExt(rows)) { - row->accountSpacers(m_axis, available.axisLength); + row->accountSpacers(m_axis, available.axisLength, available.crossLength); if (m_crossAlignment == AxisAlignment::Even) { rowCrossPos -= rowEvenSpace / 2 + row->crossLength / 2; @@ -1053,3 +1053,34 @@ void SpacerNode::setGrow(size_t grow) { size_t SpacerNode::getGrow() const { return m_grow; } + +bool SpacerNodeChild::init(CCNode* child, size_t grow) { + if (!SpacerNode::init(grow)) + return false; + + if (child) { + this->addChild(child); + m_child = child; + } + + return true; +} + +SpacerNodeChild* SpacerNodeChild::create(CCNode* child, size_t grow) { + auto ret = new SpacerNodeChild; + if (ret && ret->init(child, grow)) { + ret->autorelease(); + return ret; + } + CC_SAFE_DELETE(ret); + return nullptr; +} + +void SpacerNodeChild::setContentSize(CCSize const& size) { + CCNode::setContentSize(size); + if (m_child) { + m_child->setPosition(CCPointZero); + m_child->setContentSize(size); + m_child->setAnchorPoint(CCPointZero); + } +} \ No newline at end of file diff --git a/loader/src/hooks/GeodeNodeMetadata.cpp b/loader/src/hooks/GeodeNodeMetadata.cpp index 3daa411b..d856922d 100644 --- a/loader/src/hooks/GeodeNodeMetadata.cpp +++ b/loader/src/hooks/GeodeNodeMetadata.cpp @@ -69,15 +69,18 @@ public: #include struct ProxyCCNode : Modify { virtual CCObject* getUserObject() { - if (static_cast(this) == static_cast(CCDirector::get())) { - // apparently this function is the same as - // CCDirector::getNextScene so yeah - return m_pUserObject; + if (typeinfo_cast(this)) { + return GeodeNodeMetadata::set(this)->m_userObject; } - return GeodeNodeMetadata::set(this)->m_userObject; + // apparently this function is the same as + // CCDirector::getNextScene so yeah + return m_pUserObject; } virtual void setUserObject(CCObject* obj) { - GeodeNodeMetadata::set(this)->m_userObject = obj; + if (typeinfo_cast(this)) { + GeodeNodeMetadata::set(this)->m_userObject = obj; + } + m_pUserObject = obj; } }; diff --git a/loader/src/hooks/LoadingLayer.cpp b/loader/src/hooks/LoadingLayer.cpp index 9307998c..b3a24bd0 100644 --- a/loader/src/hooks/LoadingLayer.cpp +++ b/loader/src/hooks/LoadingLayer.cpp @@ -16,7 +16,7 @@ struct CustomLoadingLayer : Modify { void updateLoadedModsLabel() { auto allMods = Loader::get()->getAllMods(); auto count = std::count_if(allMods.begin(), allMods.end(), [&](auto& item) { - return item->isLoaded(); + return item->isEnabled(); }); auto str = fmt::format("Geode: Loaded {}/{} mods", count, allMods.size()); m_fields->m_loadedModsLabel->setCString(str.c_str()); diff --git a/loader/src/hooks/MenuLayer.cpp b/loader/src/hooks/MenuLayer.cpp index 4942a0ec..869d1bce 100644 --- a/loader/src/hooks/MenuLayer.cpp +++ b/loader/src/hooks/MenuLayer.cpp @@ -208,14 +208,14 @@ struct CustomMenuLayer : Modify { #ifdef GEODE_IS_DESKTOP - (void) utils::file::createDirectoryAll(dirs::getGeodeDir() / "update" / "resources"); + (void) utils::file::createDirectoryAll(dirs::getGeodeDir() / "update" / "resources" / "geode.loader"); createQuickPopup( "Missing Textures", "You appear to be missing textures, and the automatic texture fixer " "hasn't fixed the issue.\n" "Download resources.zip from the latest release on GitHub, " - "and unzip its contents into geode/update/resources.\n" + "and unzip its contents into geode/update/resources/geode.loader.\n" "Afterwards, restart the game.\n" "You may also continue without installing resources, but be aware that " "you won't be able to open the Geode menu.", diff --git a/loader/src/internal/crashlog.cpp b/loader/src/internal/crashlog.cpp index 486ed09f..7f698616 100644 --- a/loader/src/internal/crashlog.cpp +++ b/loader/src/internal/crashlog.cpp @@ -24,14 +24,13 @@ static void printGeodeInfo(std::stringstream& stream) { static void printMods(std::stringstream& stream) { auto mods = Loader::get()->getAllMods(); - if (!mods.size()) { + if (mods.empty()) { stream << "\n"; } using namespace std::string_view_literals; for (auto& mod : mods) { - stream << fmt::format("{:>8} | {:>8} | [{}] {}\n", - mod->isLoaded() ? "Loaded"sv : "Unloaded"sv, - mod->isEnabled() ? "Enabled"sv : "Disabled"sv, + stream << fmt::format("{} | [{}] {}\n", + mod->isEnabled() ? "x"sv : " "sv, mod->getVersion().toString(), mod->getID() ); } diff --git a/loader/src/loader/Index.cpp b/loader/src/loader/Index.cpp index d223ae0c..acb95064 100644 --- a/loader/src/loader/Index.cpp +++ b/loader/src/loader/Index.cpp @@ -8,6 +8,8 @@ #include #include +#include + #ifdef GEODE_IS_WINDOWS #include #endif @@ -318,19 +320,25 @@ void Index::Impl::downloadIndex() { return; } - // unzip new index - auto unzip = file::Unzip::intoDir(targetFile, targetDir, true) - .expect("Unable to unzip new index"); - if (!unzip) { - IndexUpdateEvent(UpdateFailed(unzip.unwrapErr())).post(); - return; - } + std::thread([=, this]() { + // unzip new index + auto unzip = file::Unzip::intoDir(targetFile, targetDir, true) + .expect("Unable to unzip new index"); + if (!unzip) { + Loader::get()->queueInMainThread([unzip] { + IndexUpdateEvent(UpdateFailed(unzip.unwrapErr())).post(); + }); + return; + } - // remove the directory github adds to the root of the zip - (void)flattenGithubRepo(targetDir); + // remove the directory github adds to the root of the zip + (void)flattenGithubRepo(targetDir); - // update index - this->updateFromLocalTree(); + Loader::get()->queueInMainThread([this] { + // update index + this->updateFromLocalTree(); + }); + }).detach(); }) .expect([](std::string const& err) { IndexUpdateEvent(UpdateFailed(fmt::format("Error downloading: {}", err))).post(); @@ -448,7 +456,8 @@ void Index::update(bool force) { // update sources if (force) { m_impl->downloadIndex(); - } else { + } + else { m_impl->checkForUpdates(); } } diff --git a/loader/src/loader/LoaderImpl.cpp b/loader/src/loader/LoaderImpl.cpp index 61afcb13..197676f5 100644 --- a/loader/src/loader/LoaderImpl.cpp +++ b/loader/src/loader/LoaderImpl.cpp @@ -201,13 +201,13 @@ Mod* Loader::Impl::getInstalledMod(std::string const& id) const { } bool Loader::Impl::isModLoaded(std::string const& id) const { - return m_mods.count(id) && m_mods.at(id)->isLoaded(); + return m_mods.count(id) && m_mods.at(id)->isEnabled(); } Mod* Loader::Impl::getLoadedMod(std::string const& id) const { if (m_mods.count(id)) { auto mod = m_mods.at(id); - if (mod->isLoaded()) { + if (mod->isEnabled()) { return mod; } } @@ -225,8 +225,6 @@ void Loader::Impl::updateModResources(Mod* mod) { if (mod->getMetadata().getSpritesheets().empty()) return; - auto searchPath = mod->getResourcesDir(); - log::debug("Adding resources for {}", mod->getID()); // add spritesheets @@ -402,7 +400,7 @@ void Loader::Impl::loadModGraph(Mod* node, bool early) { log::debug("{} {}", node->getID(), node->getVersion()); log::pushNest(); - if (node->isLoaded()) { + if (node->isEnabled()) { for (auto const& dep : node->m_impl->m_dependants) { this->loadModGraph(dep, early); } @@ -438,7 +436,7 @@ void Loader::Impl::findProblems() { log::pushNest(); for (auto const& dep : mod->getMetadata().getDependencies()) { - if (dep.mod && dep.mod->isLoaded() && dep.version.compare(dep.mod->getVersion())) + if (dep.mod && dep.mod->isEnabled() && dep.version.compare(dep.mod->getVersion())) continue; switch(dep.importance) { case ModMetadata::Dependency::Importance::Suggested: @@ -493,19 +491,21 @@ void Loader::Impl::findProblems() { Mod* myEpicMod = mod; // clang fix // if the mod is not loaded but there are no problems related to it - // if (!mod->isLoaded() && !std::any_of(m_problems.begin(), m_problems.end(), [myEpicMod](auto& item) { - // return std::holds_alternative(item.cause) && - // std::get(item.cause).getID() == myEpicMod->getID() || - // std::holds_alternative(item.cause) && - // std::get(item.cause) == myEpicMod; - // })) { - // m_problems.push_back({ - // LoadProblem::Type::Unknown, - // mod, - // "" - // }); - // log::error("{} failed to load for an unknown reason", id); - // } + if (!mod->isEnabled() && + Mod::get()->getSavedValue("should-load-" + mod->getID(), true) && + !std::any_of(m_problems.begin(), m_problems.end(), [myEpicMod](auto& item) { + return std::holds_alternative(item.cause) && + std::get(item.cause).getID() == myEpicMod->getID() || + std::holds_alternative(item.cause) && + std::get(item.cause) == myEpicMod; + })) { + m_problems.push_back({ + LoadProblem::Type::Unknown, + mod, + "" + }); + log::error("{} failed to load for an unknown reason", id); + } log::popNest(); } diff --git a/loader/src/loader/Mod.cpp b/loader/src/loader/Mod.cpp index 100e39ee..77b551a4 100644 --- a/loader/src/loader/Mod.cpp +++ b/loader/src/loader/Mod.cpp @@ -47,7 +47,7 @@ bool Mod::isEnabled() const { } bool Mod::isLoaded() const { - return m_impl->isLoaded(); + return this->isEnabled(); } bool Mod::supportsDisabling() const { @@ -55,11 +55,11 @@ bool Mod::supportsDisabling() const { } bool Mod::canDisable() const { - return m_impl->canDisable(); + return true; } bool Mod::canEnable() const { - return m_impl->canEnable(); + return true; } bool Mod::needsEarlyLoad() const { @@ -71,10 +71,10 @@ bool Mod::supportsUnloading() const { } bool Mod::wasSuccesfullyLoaded() const { - return this->wasSuccessfullyLoaded(); + return this->isEnabled(); } bool Mod::wasSuccessfullyLoaded() const { - return m_impl->wasSuccessfullyLoaded(); + return this->isEnabled(); } ModInfo Mod::getModInfo() const { @@ -191,7 +191,10 @@ Result<> Mod::disable() { } Result<> Mod::uninstall() { - return m_impl->uninstall(); + return m_impl->uninstall(false); +} +Result<> Mod::uninstall(bool deleteSaveData) { + return m_impl->uninstall(deleteSaveData); } bool Mod::isUninstalled() const { diff --git a/loader/src/loader/ModImpl.cpp b/loader/src/loader/ModImpl.cpp index f932af84..510693d1 100644 --- a/loader/src/loader/ModImpl.cpp +++ b/loader/src/loader/ModImpl.cpp @@ -42,8 +42,9 @@ Result<> Mod::Impl::setup() { if (!loadRes) { log::warn("Unable to load data for \"{}\": {}", m_metadata.getID(), loadRes.unwrapErr()); } - if (LoaderImpl::get()->m_isSetup) { - Loader::get()->updateResources(false); + if (!m_resourcesLoaded) { + LoaderImpl::get()->updateModResources(m_self); + m_resourcesLoaded = true; } return Ok(); @@ -112,22 +113,10 @@ bool Mod::Impl::isEnabled() const { return m_enabled; } -bool Mod::Impl::isLoaded() const { - return m_binaryLoaded; -} - bool Mod::Impl::supportsDisabling() const { return m_metadata.getID() != "geode.loader"; } -bool Mod::Impl::canDisable() const { - return true; -} - -bool Mod::Impl::canEnable() const { - return true; -} - bool Mod::Impl::needsEarlyLoad() const { auto deps = m_dependants; return getMetadata().needsEarlyLoad() || @@ -136,10 +125,6 @@ bool Mod::Impl::needsEarlyLoad() const { }); } -bool Mod::Impl::wasSuccessfullyLoaded() const { - return !this->isEnabled() || this->isLoaded(); -} - std::vector Mod::Impl::getHooks() const { return m_hooks; } @@ -321,7 +306,7 @@ bool Mod::Impl::hasSetting(std::string const& key) const { Result<> Mod::Impl::loadBinary() { log::debug("Loading binary for mod {}", m_metadata.getID()); - if (m_binaryLoaded) + if (m_enabled) return Ok(); LoaderImpl::get()->provideNextMod(m_self); @@ -333,7 +318,6 @@ Result<> Mod::Impl::loadBinary() { log::error("Failed to load binary for mod {}: {}", m_metadata.getID(), res.unwrapErr()); return res; } - m_binaryLoaded = true; LoaderImpl::get()->releaseNextMod(); @@ -393,12 +377,20 @@ Result<> Mod::Impl::disable() { return Ok(); } -Result<> Mod::Impl::uninstall() { +Result<> Mod::Impl::uninstall(bool deleteSaveData) { if (m_requestedAction != ModRequestedAction::None) { return Err("Mod already has a requested action"); } - m_requestedAction = ModRequestedAction::Uninstall; + if (this->getID() == "geode.loader") { + utils::game::launchLoaderUninstaller(deleteSaveData); + utils::game::exit(); + return Ok(); + } + + m_requestedAction = deleteSaveData ? + ModRequestedAction::UninstallWithSaveData : + ModRequestedAction::Uninstall; std::error_code ec; ghc::filesystem::remove(m_metadata.getPath(), ec); @@ -408,11 +400,21 @@ Result<> Mod::Impl::uninstall() { ); } + if (deleteSaveData) { + ghc::filesystem::remove_all(this->getSaveDir(), ec); + if (ec) { + return Err( + "Unable to delete mod's save directory: " + ec.message() + ); + } + } + return Ok(); } bool Mod::Impl::isUninstalled() const { - return m_requestedAction == ModRequestedAction::Uninstall; + return m_requestedAction == ModRequestedAction::Uninstall || + m_requestedAction == ModRequestedAction::UninstallWithSaveData; } ModRequestedAction Mod::Impl::getRequestedAction() const { @@ -612,8 +614,9 @@ ModJson Mod::Impl::getRuntimeInfo() const { for (auto patch : m_patches) { obj["patches"].as_array().push_back(ModJson(patch->getRuntimeInfo())); } + // TODO: so which one is it // obj["enabled"] = m_enabled; - obj["loaded"] = m_binaryLoaded; + obj["loaded"] = m_enabled; obj["temp-dir"] = this->getTempDir(); obj["save-dir"] = this->getSaveDir(); obj["config-dir"] = this->getConfigDir(false); @@ -660,7 +663,6 @@ Mod* Loader::Impl::createInternalMod() { else { mod = new Mod(infoRes.unwrap()); } - mod->m_impl->m_binaryLoaded = true; mod->m_impl->m_enabled = true; m_mods.insert({ mod->getID(), mod }); return mod; diff --git a/loader/src/loader/ModImpl.hpp b/loader/src/loader/ModImpl.hpp index e79dc8ae..c7a82dfe 100644 --- a/loader/src/loader/ModImpl.hpp +++ b/loader/src/loader/ModImpl.hpp @@ -26,10 +26,6 @@ namespace geode { * Whether the mod is enabled or not */ bool m_enabled = false; - /** - * Whether the mod binary is loaded or not - */ - bool m_binaryLoaded = false; /** * Mod temp directory name */ @@ -66,7 +62,6 @@ namespace geode { bool m_loggingEnabled = true; - ModRequestedAction m_requestedAction = ModRequestedAction::None; Impl(Mod* self, ModMetadata const& metadata); @@ -88,12 +83,8 @@ namespace geode { ghc::filesystem::path getPackagePath() const; VersionInfo getVersion() const; bool isEnabled() const; - bool isLoaded() const; bool supportsDisabling() const; - bool canDisable() const; - bool canEnable() const; bool needsEarlyLoad() const; - bool wasSuccessfullyLoaded() const; ModMetadata getMetadata() const; ghc::filesystem::path getTempDir() const; ghc::filesystem::path getBinaryPath() const; @@ -127,7 +118,7 @@ namespace geode { Result<> unpatch(Patch* patch); Result<> enable(); Result<> disable(); - Result<> uninstall(); + Result<> uninstall(bool deleteSaveData); bool isUninstalled() const; // 1.3.0 additions diff --git a/loader/src/loader/ModInfoImpl.cpp b/loader/src/loader/ModInfoImpl.cpp index c482b7b6..f10f6f8c 100644 --- a/loader/src/loader/ModInfoImpl.cpp +++ b/loader/src/loader/ModInfoImpl.cpp @@ -15,7 +15,7 @@ ModInfo::Impl& ModInfoImpl::getImpl(ModInfo& info) { bool Dependency::isResolved() const { return !this->required || - (this->mod && this->mod->isLoaded() && this->mod->isEnabled() && + (this->mod && this->mod->isEnabled() && this->version.compare(this->mod->getVersion())); } diff --git a/loader/src/loader/ModMetadataImpl.cpp b/loader/src/loader/ModMetadataImpl.cpp index 884ace85..0265f8af 100644 --- a/loader/src/loader/ModMetadataImpl.cpp +++ b/loader/src/loader/ModMetadataImpl.cpp @@ -18,7 +18,7 @@ ModMetadata::Impl& ModMetadataImpl::getImpl(ModMetadata& info) { bool ModMetadata::Dependency::isResolved() const { return this->importance != Importance::Required || - this->mod && this->mod->isLoaded() && this->version.compare(this->mod->getVersion()); + this->mod && this->mod->isEnabled() && this->version.compare(this->mod->getVersion()); } bool ModMetadata::Incompatibility::isResolved() const { diff --git a/loader/src/platform/mac/LoaderImpl.mm b/loader/src/platform/mac/LoaderImpl.mm index f48d949e..93251846 100644 --- a/loader/src/platform/mac/LoaderImpl.mm +++ b/loader/src/platform/mac/LoaderImpl.mm @@ -48,16 +48,17 @@ void Loader::Impl::openPlatformConsole() { auto script = outFile + ".command"; auto scriptContent = fmt::format(R"( #!/bin/sh - echo -n -e "\033]0;Geode Console\007" + echo -n -e "\033]0;Geode Console {}\007" tail -f {} & trap "" SIGINT - while [ $(lsof -t {} 2>/dev/null | wc -l) -gt 1 ]; do :; done + lsof -p {} +r 1 &>/dev/null + pkill -P $$ osascript -e 'tell application "Terminal" - close (every window whose name contains "Geode Console") + close (every window whose name contains "Geode Console {}") if (count windows) is 0 then quit end tell' & exit - )", outFile, outFile); + )", getpid(), outFile, getpid(), getpid()); if (file::writeString(script, scriptContent)) { chmod(script.c_str(), 0777); diff --git a/loader/src/platform/mac/crashlog.mm b/loader/src/platform/mac/crashlog.mm index 1c7704f8..7a104e86 100644 --- a/loader/src/platform/mac/crashlog.mm +++ b/loader/src/platform/mac/crashlog.mm @@ -151,7 +151,7 @@ static Mod* modFromAddress(void const* addr) { } for (auto& mod : Loader::get()->getAllMods()) { - if (!mod->isLoaded() || !ghc::filesystem::exists(mod->getBinaryPath())) { + if (!mod->isEnabled() || !ghc::filesystem::exists(mod->getBinaryPath())) { continue; } if (ghc::filesystem::equivalent(imagePath, mod->getBinaryPath())) { diff --git a/loader/src/platform/mac/util.mm b/loader/src/platform/mac/util.mm index 4807ebca..6a100715 100644 --- a/loader/src/platform/mac/util.mm +++ b/loader/src/platform/mac/util.mm @@ -200,6 +200,30 @@ ghc::filesystem::path dirs::getSaveDir() { return path; } +void geode::utils::game::exit() { + if (CCApplication::sharedApplication() && + (GameManager::get()->m_playLayer || GameManager::get()->m_levelEditorLayer)) { + log::error("Cannot restart in PlayLayer or LevelEditorLayer!"); + return; + } + + class Exit : public CCObject { + public: + void shutdown() { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wobjc-method-access" + [[[NSClassFromString(@"AppControllerManager") sharedInstance] controller] shutdownGame]; +#pragma clang diagnostic pop + } + }; + + CCDirector::get()->getActionManager()->addAction(CCSequence::create( + CCDelayTime::create(0.5f), + CCCallFunc::create(nullptr, callfunc_selector(Exit::shutdown)), + nullptr + ), CCDirector::get()->getRunningScene(), false); +} + void geode::utils::game::restart() { if (CCApplication::sharedApplication() && (GameManager::get()->m_playLayer || GameManager::get()->m_levelEditorLayer)) { @@ -216,22 +240,12 @@ void geode::utils::game::restart() { [task launch]; }; - class Exit : public CCObject { - public: - void shutdown() { -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wobjc-method-access" - [[[NSClassFromString(@"AppControllerManager") sharedInstance] controller] shutdownGame]; -#pragma clang diagnostic pop - } - }; - std::atexit(restart); - CCDirector::get()->getActionManager()->addAction(CCSequence::create( - CCDelayTime::create(0.5f), - CCCallFunc::create(nullptr, callfunc_selector(Exit::shutdown)), - nullptr - ), CCDirector::get()->getRunningScene(), false); + exit(); +} + +void geode::utils::game::launchLoaderUninstaller(bool deleteSaveData) { + log::error("Launching Geode uninstaller is not supported on macOS"); } Result<> geode::hook::addObjcMethod(std::string const& className, std::string const& selectorName, void* imp) { diff --git a/loader/src/platform/windows/LoaderImpl.cpp b/loader/src/platform/windows/LoaderImpl.cpp index a3f632eb..df77a0a9 100644 --- a/loader/src/platform/windows/LoaderImpl.cpp +++ b/loader/src/platform/windows/LoaderImpl.cpp @@ -126,7 +126,7 @@ void Loader::Impl::setupIPC() { if (ConnectNamedPipe(pipe, nullptr)) { // log::debug("Got connection, creating thread"); std::thread pipeThread(&ipcPipeThread, pipe); - SetThreadDescription(pipeThread.native_handle(), L"Geode IPC Pipe"); + // SetThreadDescription(pipeThread.native_handle(), L"Geode IPC Pipe"); pipeThread.detach(); } else { @@ -135,7 +135,7 @@ void Loader::Impl::setupIPC() { } } }); - SetThreadDescription(ipcThread.native_handle(), L"Geode Main IPC"); + // SetThreadDescription(ipcThread.native_handle(), L"Geode Main IPC"); ipcThread.detach(); log::debug("IPC set up"); diff --git a/loader/src/platform/windows/util.cpp b/loader/src/platform/windows/util.cpp index 74291a38..ee26861f 100644 --- a/loader/src/platform/windows/util.cpp +++ b/loader/src/platform/windows/util.cpp @@ -158,6 +158,22 @@ ghc::filesystem::path dirs::getSaveDir() { return path; } +void geode::utils::game::exit() { + if (CCApplication::sharedApplication() && + (GameManager::get()->m_playLayer || GameManager::get()->m_levelEditorLayer)) { + log::error("Cannot exit in PlayLayer or LevelEditorLayer!"); + return; + } + + if (CCApplication::sharedApplication()) + // please forgive me.. + // manually set the closed flag + // TODO: actually call glfwSetWindowShouldClose + *reinterpret_cast(reinterpret_cast(CCEGLView::sharedOpenGLView()->getWindow()) + 0xa) = true; + else + std::exit(0); +} + void geode::utils::game::restart() { if (CCApplication::sharedApplication() && (GameManager::get()->m_playLayer || GameManager::get()->m_levelEditorLayer)) { @@ -175,13 +191,25 @@ void geode::utils::game::restart() { const auto updaterPath = (workingDir / "GeodeUpdater.exe").string(); ShellExecuteA(nullptr, "open", updaterPath.c_str(), gdName.c_str(), workingDir.string().c_str(), false); - if (CCApplication::sharedApplication()) - // please forgive me.. - // manually set the closed flag - // TODO: actually call glfwSetWindowShouldClose - *reinterpret_cast(reinterpret_cast(CCEGLView::sharedOpenGLView()->getWindow()) + 0xa) = true; - else - exit(0); + exit(); +} + +void geode::utils::game::launchLoaderUninstaller(bool deleteSaveData) { + const auto workingDir = dirs::getGameDir(); + + if (!exists((workingDir / "GeodeUninstaller.exe"))) { + log::error("Uninstaller not found! Not launching."); + return; + } + + std::string params; + if (deleteSaveData) { + params = "\"/DATA=" + dirs::getSaveDir().string() + "\""; + } + + // launch uninstaller + const auto uninstallerPath = (workingDir / "GeodeUninstaller.exe").string(); + ShellExecuteA(nullptr, "open", uninstallerPath.c_str(), params.c_str(), workingDir.string().c_str(), false); } Result<> geode::hook::addObjcMethod(std::string const& className, std::string const& selectorName, void* imp) { diff --git a/loader/src/ui/internal/GeodeUI.cpp b/loader/src/ui/internal/GeodeUI.cpp index a575a737..5bc6705a 100644 --- a/loader/src/ui/internal/GeodeUI.cpp +++ b/loader/src/ui/internal/GeodeUI.cpp @@ -79,7 +79,13 @@ CCNode* geode::createModLogo(Mod* mod, CCSize const& size) { if (!spr) spr = CCSprite::createWithSpriteFrameName("no-logo.png"_spr); if (!spr) spr = CCLabelBMFont::create("N/A", "goldFont.fnt"); limitNodeSize(spr, size, 1.f, .1f); - return spr; + spr->setPosition(size/2); + spr->setAnchorPoint({.5f, .5f}); + + auto node = CCNode::create(); + node->addChild(spr); + node->setContentSize(size); + return node; } CCNode* geode::createIndexItemLogo(IndexItemHandle item, CCSize const& size) { @@ -112,5 +118,11 @@ CCNode* geode::createIndexItemLogo(IndexItemHandle item, CCSize const& size) { else { limitNodeSize(spr, size, 1.f, .1f); } - return spr; + spr->setPosition(size/2); + spr->setAnchorPoint({.5f, .5f}); + + auto node = CCNode::create(); + node->addChild(spr); + node->setContentSize(size); + return node; } diff --git a/loader/src/ui/internal/info/ModInfoPopup.cpp b/loader/src/ui/internal/info/ModInfoPopup.cpp index 4e071c46..ebe9ac9b 100644 --- a/loader/src/ui/internal/info/ModInfoPopup.cpp +++ b/loader/src/ui/internal/info/ModInfoPopup.cpp @@ -24,7 +24,7 @@ static constexpr int const TAG_CONFIRM_UNINSTALL = 5; static constexpr int const TAG_CONFIRM_UPDATE = 6; -static constexpr int const TAG_DELETE_SAVEDATA = 7; +static constexpr int const TAG_CONFIRM_UNINSTALL_WITH_SAVEDATA = 7; static const CCSize LAYER_SIZE = {440.f, 290.f}; bool ModInfoPopup::init(ModMetadata const& metadata, ModListLayer* list) { @@ -434,7 +434,12 @@ bool LocalModInfoPopup::init(Mod* mod, ModListLayer* list) { disableBtnSpr->setColor({150, 150, 150}); } - if (mod != Mod::get()) { + bool shouldShowUninstall = mod != Mod::get(); +#if defined(GEODE_IS_WINDOWS) + shouldShowUninstall = shouldShowUninstall || + exists((dirs::getGameDir() / "GeodeUninstaller.exe")); +#endif + if (shouldShowUninstall) { auto uninstallBtnSpr = ButtonSprite::create("Uninstall", "bigFont.fnt", "GJ_button_05.png", .6f); uninstallBtnSpr->setScale(.6f); @@ -550,12 +555,11 @@ void LocalModInfoPopup::onUpdateProgress(ModInstallEvent* event) { std::visit(makeVisitor { [&](UpdateFinished const&) { this->setInstallStatus(std::nullopt); - + FLAlertLayer::create( - "Update complete", - "Mod successfully updated! :) " - "(You have to restart the game " - "for the mod to take effect)", + "Update Complete", + "Mod updated successfully!\n" + "Restart the game to apply changes.", "OK" )->show(); @@ -571,7 +575,7 @@ void LocalModInfoPopup::onUpdateProgress(ModInstallEvent* event) { this->setInstallStatus(std::nullopt); FLAlertLayer::create( - "Update failed :(", info, "OK" + "Update Failed", info, "OK" )->show(); m_installBtn->setEnabled(true); @@ -592,7 +596,10 @@ void LocalModInfoPopup::onUninstall(CCObject*) { auto layer = FLAlertLayer::create( this, "Confirm Uninstall", - fmt::format("Are you sure you want to uninstall {}?", m_mod->getName()), + fmt::format("Are you sure you want to uninstall {}?{}", m_mod->getName(), + m_mod == Mod::get() ? + "\nThis will close the game and launch the Geode Uninstaller. " + "You will have to restart the game manually after that." : ""), "Cancel", "OK" ); @@ -601,13 +608,11 @@ void LocalModInfoPopup::onUninstall(CCObject*) { } void LocalModInfoPopup::onEnableMod(CCObject* sender) { - if (!Mod::get()->setSavedValue("shown-disable-vs-unload-info", true)) { + if (!Mod::get()->setSavedValue("shown-mod-toggle-info", true)) { FLAlertLayer::create( "Notice", - "Disabling a mod removes its hooks & patches and " - "calls its user-defined disable function if one exists. You may " - "still see some effects of the mod left however, and you may " - "need to restart the game to have it fully unloaded.", + "Toggling a mod requires you to restart the game.\n" + "When a mod is disabled, it will not get loaded at all.", "OK" )->show(); } @@ -651,18 +656,23 @@ void LocalModInfoPopup::FLAlert_Clicked(FLAlertLayer* layer, bool btn2) { switch (layer->getTag()) { case TAG_CONFIRM_UNINSTALL: { if (btn2) { - this->doUninstall(); + auto layer2 = FLAlertLayer::create( + this, + "Confirm Uninstall", + "Would you also like to delete the mod's save data?", + "Keep", + "Delete", + 350.f + ); + layer2->setTag(TAG_CONFIRM_UNINSTALL_WITH_SAVEDATA); + layer2->show(); } } break; - case TAG_DELETE_SAVEDATA: { - if (btn2) { - if (ghc::filesystem::remove_all(m_mod->getSaveDir())) { - FLAlertLayer::create("Deleted", "The mod's save data was deleted.", "OK")->show(); - } - else { - FLAlertLayer::create("Error", "Unable to delete mod's save directory!", "OK")->show(); - } + case TAG_CONFIRM_UNINSTALL_WITH_SAVEDATA: { + auto res = m_mod->uninstall(btn2); + if (!res) { + return FLAlertLayer::create("Uninstall Failed", res.unwrapErr(), "OK")->show(); } if (m_layer) { m_layer->reloadList(); @@ -672,27 +682,6 @@ void LocalModInfoPopup::FLAlert_Clicked(FLAlertLayer* layer, bool btn2) { } } -void LocalModInfoPopup::doUninstall() { - auto res = m_mod->uninstall(); - if (!res) { - return FLAlertLayer::create("Uninstall failed :(", res.unwrapErr(), "OK")->show(); - } - auto layer = FLAlertLayer::create( - this, - "Uninstall complete", - "Mod was successfully uninstalled! :) " - "(You have to restart the game " - "for the mod to take effect). " - "Would you also like to delete the mod's " - "save data?", - "Keep", - "Delete", - 350.f - ); - layer->setTag(TAG_DELETE_SAVEDATA); - layer->show(); -} - LocalModInfoPopup* LocalModInfoPopup::create(Mod* mod, ModListLayer* list) { auto ret = new LocalModInfoPopup; if (ret && ret->init(mod, list)) { @@ -748,12 +737,11 @@ void IndexItemInfoPopup::onInstallProgress(ModInstallEvent* event) { std::visit(makeVisitor { [&](UpdateFinished const&) { this->setInstallStatus(std::nullopt); - + FLAlertLayer::create( - "Install complete", - "Mod successfully installed! :) " - "(You have to restart the game " - "for the mod to take effect)", + "Install Complete", + "Mod installed successfully!\n" + "Restart the game to apply changes.", "OK" )->show(); @@ -769,7 +757,7 @@ void IndexItemInfoPopup::onInstallProgress(ModInstallEvent* event) { this->setInstallStatus(std::nullopt); FLAlertLayer::create( - "Installation failed :(", info, "OK" + "Installation Failed", info, "OK" )->show(); m_installBtn->setEnabled(true); diff --git a/loader/src/ui/internal/info/ModInfoPopup.hpp b/loader/src/ui/internal/info/ModInfoPopup.hpp index 7870dc9c..808cd918 100644 --- a/loader/src/ui/internal/info/ModInfoPopup.hpp +++ b/loader/src/ui/internal/info/ModInfoPopup.hpp @@ -74,7 +74,6 @@ protected: void onEnableMod(CCObject*); void onUninstall(CCObject*); void onOpenConfigDir(CCObject*); - void doUninstall(); void onUpdateProgress(ModInstallEvent* event); void onUpdate(CCObject*); diff --git a/loader/src/ui/internal/list/ModListCell.cpp b/loader/src/ui/internal/list/ModListCell.cpp index 3c9c199d..5b883959 100644 --- a/loader/src/ui/internal/list/ModListCell.cpp +++ b/loader/src/ui/internal/list/ModListCell.cpp @@ -35,94 +35,107 @@ void ModListCell::setupInfo( bool inactive ) { m_menu = CCMenu::create(); - m_menu->setPosition(m_width - 40.f, m_height / 2); + m_menu->setPosition({m_width / 2, m_height / 2}); + m_menu->setContentSize({m_width - 20, m_height}); + m_menu->setAnchorPoint({.5f, .5f}); + m_menu->setLayout( + RowLayout::create() + ->setAxisAlignment(AxisAlignment::Start) + ->setAutoScale(false) + ->setCrossAxisOverflow(false) + ); + this->addChild(m_menu); auto logoSize = this->getLogoSize(); auto logoSpr = this->createLogo({ logoSize, logoSize }); - logoSpr->setPosition({ logoSize / 2 + 12.f, m_height / 2 }); auto logoSprColor = typeinfo_cast(logoSpr); if (inactive && logoSprColor) { logoSprColor->setColor({ 163, 163, 163 }); } - this->addChild(logoSpr); + m_menu->addChild(logoSpr); + + m_columnMenu = CCMenu::create(); + m_columnMenu->setContentSize({m_width, m_height}); + m_columnMenu->setLayout( + ColumnLayout::create() + ->setAxisAlignment(AxisAlignment::Center) + ->setCrossAxisLineAlignment(AxisAlignment::Start) + ->setAxisReverse(true) + ->setAutoScale(false) + ->setGap(spaceForTags ? 3.f : 5.f) + ); + m_menu->addChild(SpacerNodeChild::create(m_columnMenu)); + + m_labelMenu = CCMenu::create(); + m_labelMenu->setContentSize({m_width, m_height / 2}); + m_labelMenu->setLayout( + RowLayout::create() + ->setAxisAlignment(AxisAlignment::Start) + ->setAutoScale(true) + ); + m_labelMenu->setLayoutOptions( + AxisLayoutOptions::create() + ->setNextGap(spaceForTags ? -2.f : 0.f) + ->setPrevGap(0.f) + ); + m_columnMenu->addChild(m_labelMenu); bool hasDesc = display == ModListDisplay::Expanded && metadata.getDescription().has_value(); auto titleLabel = CCLabelBMFont::create(metadata.getName().c_str(), "bigFont.fnt"); - titleLabel->setAnchorPoint({ .0f, .5f }); - titleLabel->setPositionX(m_height / 2 + logoSize / 2 + 13.f); - if (hasDesc && spaceForTags) { - titleLabel->setPositionY(m_height / 2 + 20.f); - } - else if (spaceForTags) { - titleLabel->setPositionY(m_height / 2 + 12.f); - } - else if (hasDesc) { - titleLabel->setPositionY(m_height / 2 + 15.f); - } - else { - titleLabel->setPositionY(m_height / 2 + 7.f); - } titleLabel->limitLabelWidth(m_width / 2 - 40.f, .5f, .1f); + titleLabel->setLayoutOptions( + AxisLayoutOptions::create() + ->setScalePriority(1) + ->setMaxScale(0.5f) + ->setMinScale(0.1f) + ); if (inactive) { titleLabel->setColor({ 163, 163, 163 }); } - this->addChild(titleLabel); + m_labelMenu->addChild(titleLabel); auto versionLabel = CCLabelBMFont::create( metadata.getVersion().toString(false).c_str(), "bigFont.fnt" ); - versionLabel->setAnchorPoint({ .0f, .5f }); - versionLabel->setScale(.3f); - versionLabel->setPosition( - titleLabel->getPositionX() + titleLabel->getScaledContentSize().width + 5.f, - titleLabel->getPositionY() - 1.f - ); versionLabel->setColor({ 0, 255, 0 }); if (inactive) { versionLabel->setColor({ 0, 163, 0 }); } - this->addChild(versionLabel); + versionLabel->setLayoutOptions( + AxisLayoutOptions::create() + ->setMaxScale(0.3) + ->setMinScale(0.1) + ); + m_labelMenu->addChild(versionLabel); TagNode* apiLabel = nullptr; - if (metadata.isAPI()) { - apiLabel = TagNode::create("API"); - apiLabel->setAnchorPoint({ .0f, .5f }); - apiLabel->setScale(.3f); - apiLabel->setPosition( - versionLabel->getPositionX() + - versionLabel->getScaledContentSize().width + 5.f, - versionLabel->getPositionY() - ); - } + if (auto tag = metadata.getVersion().getTag()) { auto tagLabel = TagNode::create(tag.value().toString().c_str()); - tagLabel->setAnchorPoint({ .0f, .5f }); - tagLabel->setScale(.3f); - tagLabel->setPosition( - versionLabel->getPositionX() + - versionLabel->getScaledContentSize().width + 5.f, - versionLabel->getPositionY() + tagLabel->setLayoutOptions( + AxisLayoutOptions::create() + ->setMaxScale(0.3) + ->setMinScale(0.1) ); - this->addChild(tagLabel); - - if (apiLabel) { - apiLabel->setPosition( - tagLabel->getPositionX() + - tagLabel->getScaledContentSize().width + 5.f, - tagLabel->getPositionY() - ); - } + m_labelMenu->addChild(tagLabel); } - if (apiLabel) - this->addChild(apiLabel); + if (metadata.isAPI()) { + apiLabel = TagNode::create("API"); + apiLabel->setLayoutOptions( + AxisLayoutOptions::create() + ->setMaxScale(0.3) + ->setMinScale(0.1) + ); + m_labelMenu->addChild(apiLabel); + } auto creatorStr = "by " + metadata.getDeveloper(); auto creatorLabel = CCLabelBMFont::create(creatorStr.c_str(), "goldFont.fnt"); @@ -134,47 +147,42 @@ void ModListCell::setupInfo( m_developerBtn = CCMenuItemSpriteExtra::create( creatorLabel, this, menu_selector(ModListCell::onViewDev) ); - m_developerBtn->setPositionX( - m_height / 2 + logoSize / 2 + 13.f - + creatorLabel->getScaledContentSize().width / 2 - - m_menu->getPositionX() - ); - if (hasDesc && spaceForTags) { - m_developerBtn->setPositionY(+7.5f); - } - else if (hasDesc || spaceForTags) { - m_developerBtn->setPositionY(0.f); - } - else { - m_developerBtn->setPositionY(-7.f); - } - m_menu->addChild(m_developerBtn); + m_columnMenu->addChild(m_developerBtn); if (hasDesc) { auto descBG = CCScale9Sprite::create("square02b_001.png", {0.0f, 0.0f, 80.0f, 80.0f}); descBG->setColor({0, 0, 0}); descBG->setOpacity(90); descBG->setContentSize({m_width * 2, 60.f}); - descBG->setAnchorPoint({.0f, .5f}); - descBG->setPositionX(m_height / 2 + logoSize / 2 + 13.f); - if (spaceForTags) { - descBG->setPositionY(m_height / 2 - 7.5f); - } - else { - descBG->setPositionY(m_height / 2 - 17.f); - } descBG->setScale(.25f); - this->addChild(descBG); + m_columnMenu->addChild(descBG); + + // limitLabelWidth defaults to 1.0 even though we give a bigger scale + auto node = CCNode::create(); + node->setContentSize(descBG->getContentSize() / 4); + node->setAnchorPoint({ .5f, .5f }); + node->setScale(4.f); + node->setPosition(descBG->getContentSize() / 2); + descBG->addChild(node); m_description = CCLabelBMFont::create(metadata.getDescription().value().c_str(), "chatFont.fnt"); - m_description->setAnchorPoint({ .0f, .5f }); - m_description->setPosition(m_height / 2 + logoSize / 2 + 18.f, descBG->getPositionY()); - m_description->limitLabelWidth(m_width / 2 - 10.f, .5f, .1f); + m_description->setAnchorPoint({ .5f, .5f }); + m_description->setPosition(node->getContentSize() / 2); + m_description->limitLabelWidth(node->getContentSize().width - 5.f, 0.5f, .1f); if (inactive) { m_description->setColor({ 163, 163, 163 }); } - this->addChild(m_description); + node->addChild(m_description); } + + this->updateCellLayout(); +} + +void ModListCell::updateCellLayout() { + m_menu->updateLayout(); + m_labelMenu->setContentSize(m_columnMenu->getContentSize()); + m_labelMenu->updateLayout(); + m_columnMenu->updateLayout(); } void ModListCell::onViewDev(CCObject*) { @@ -211,13 +219,11 @@ ModCell* ModCell::create( } void ModCell::onEnable(CCObject* sender) { - if (!Mod::get()->setSavedValue("shown-disable-vs-unload-info", true)) { + if (!Mod::get()->getSavedValue("shown-mod-toggle-info", true)) { FLAlertLayer::create( "Notice", - "Disabling a mod removes its hooks & patches and " - "calls its user-defined disable function if one exists. You may " - "still see some effects of the mod left however, and you may " - "need to restart the game to have it fully unloaded.", + "Toggling a mod requires you to restart the game.\n" + "When a mod is disabled, it will not get loaded at all.", "OK" )->show(); } @@ -262,6 +268,8 @@ void ModCell::updateState() { break; } m_unresolvedExMark->setVisible(hasProblems); + + this->updateCellLayout(); } bool ModCell::init( @@ -276,22 +284,35 @@ bool ModCell::init( this->setupInfo(mod->getMetadata(), false, display, mod->getRequestedAction() != ModRequestedAction::None); + auto exMark = CCSprite::createWithSpriteFrameName("exMark_001.png"); + exMark->setScale(.5f); + + m_unresolvedExMark = + CCMenuItemSpriteExtra::create(exMark, this, menu_selector(ModCell::onUnresolvedInfo)); + m_unresolvedExMark->setVisible(false); + m_menu->addChild(m_unresolvedExMark); + if (mod->getRequestedAction() != ModRequestedAction::None) { auto restartSpr = ButtonSprite::create("Restart", "bigFont.fnt", "GJ_button_03.png", .8f); restartSpr->setScale(.65f); auto restartBtn = CCMenuItemSpriteExtra::create(restartSpr, this, menu_selector(ModCell::onRestart)); - restartBtn->setPositionX(-16.f); m_menu->addChild(restartBtn); } else { + if (m_mod->getMetadata().getID() != "geode.loader") { + m_enableToggle = + CCMenuItemToggler::createWithStandardSprites(this, menu_selector(ModCell::onEnable), .7f); + m_menu->addChild(m_enableToggle); + } + auto viewSpr = ButtonSprite::create("View", "bigFont.fnt", "GJ_button_01.png", .8f); viewSpr->setScale(.65f); auto viewBtn = CCMenuItemSpriteExtra::create(viewSpr, this, menu_selector(ModCell::onInfo)); m_menu->addChild(viewBtn); - if (m_mod->wasSuccessfullyLoaded()) { + if (m_mod->isEnabled()) { auto latestIndexItem = Index::get()->getMajorItem( mod->getMetadata().getID() ); @@ -306,33 +327,14 @@ bool ModCell::init( if (latestIndexItem->getMetadata().getVersion().getMajor() > minorIndexItem->getMetadata().getVersion().getMajor()) { auto updateIcon = CCSprite::createWithSpriteFrameName("updates-available.png"_spr); - updateIcon->setPosition(viewSpr->getContentSize() - CCSize { 2.f, 2.f }); updateIcon->setZOrder(99); updateIcon->setScale(.5f); viewSpr->addChild(updateIcon); } } } - - if (m_mod->wasSuccessfullyLoaded() && m_mod->getMetadata().getID() != "geode.loader") { - m_enableToggle = - CCMenuItemToggler::createWithStandardSprites(this, menu_selector(ModCell::onEnable), .7f); - m_enableToggle->setPosition(-45.f, 0.f); - m_menu->addChild(m_enableToggle); - } } - - - auto exMark = CCSprite::createWithSpriteFrameName("exMark_001.png"); - exMark->setScale(.5f); - - m_unresolvedExMark = - CCMenuItemSpriteExtra::create(exMark, this, menu_selector(ModCell::onUnresolvedInfo)); - m_unresolvedExMark->setPosition(-80.f, 0.f); - m_unresolvedExMark->setVisible(false); - m_menu->addChild(m_unresolvedExMark); - this->updateState(); return true; @@ -390,7 +392,6 @@ bool IndexItemCell::init( restartSpr->setScale(.65f); auto restartBtn = CCMenuItemSpriteExtra::create(restartSpr, this, menu_selector(IndexItemCell::onRestart)); - restartBtn->setPositionX(-16.f); m_menu->addChild(restartBtn); } else { @@ -403,22 +404,21 @@ bool IndexItemCell::init( } if (item->getTags().size()) { - float x = m_height / 2 + this->getLogoSize() / 2 + 13.f; + auto tagRow = CCNode::create(); + tagRow->setContentSize({m_width, m_height}); + tagRow->setLayout( + RowLayout::create() + ->setAxisAlignment(AxisAlignment::Start) + ->setAutoScale(false) + ->setGap(3.f) + ); + m_columnMenu->insertAfter(tagRow, m_developerBtn); for (auto& category : item->getTags()) { auto node = TagNode::create(category); - node->setAnchorPoint({ .0f, .5f }); - node->setPositionX(x); node->setScale(.3f); - if (m_description) { - node->setPositionY(m_height / 2 - 23.f); - } - else { - node->setPositionY(m_height / 2 - 12.f); - } - this->addChild(node); - - x += node->getScaledContentSize().width + 5.f; + tagRow->addChild(node); } + tagRow->updateLayout(); } this->updateState(); @@ -426,7 +426,9 @@ bool IndexItemCell::init( return true; } -void IndexItemCell::updateState() {} +void IndexItemCell::updateState() { + this->updateCellLayout(); +} std::string IndexItemCell::getDeveloper() const { return m_item->getMetadata().getDeveloper(); @@ -451,19 +453,19 @@ void InvalidGeodeFileCell::FLAlert_Clicked(FLAlertLayer*, bool btn2) { try { if (ghc::filesystem::remove(m_info.path)) { FLAlertLayer::create( - "File removed", "Removed " + m_info.path.string() + "", "OK" + "File Removed", "Removed " + m_info.path.string() + "", "OK" )->show(); } else { FLAlertLayer::create( - "Unable to remove file", + "Unable to Remove File", "Unable to remove " + m_info.path.string() + "", "OK" )->show(); } } catch (std::exception& e) { FLAlertLayer::create( - "Unable to remove file", + "Unable to Remove File", "Unable to remove " + m_info.path.string() + ": " + std::string(e.what()) + "", "OK" @@ -595,7 +597,15 @@ bool ProblemsCell::init( } m_menu = CCMenu::create(); - m_menu->setPosition(m_width - 40.f, m_height / 2); + m_menu->setPosition({m_width / 2, m_height / 2}); + m_menu->setContentSize({m_width - 20, m_height}); + m_menu->setAnchorPoint({.5f, .5f}); + m_menu->setLayout( + RowLayout::create() + ->setAxisAlignment(AxisAlignment::Start) + // ->setAutoScale(false) + ->setCrossAxisOverflow(false) + ); this->addChild(m_menu); auto logoSize = this->getLogoSize(); @@ -603,15 +613,19 @@ bool ProblemsCell::init( if (!icon.empty()) { auto logoSpr = CCSprite::createWithSpriteFrameName(icon.c_str()); limitNodeSize(logoSpr, size, 1.f, .1f); - logoSpr->setPosition({logoSize / 2 + 12.f, m_height / 2}); - this->addChild(logoSpr); + m_menu->addChild(logoSpr); } auto titleLabel = CCLabelBMFont::create(title.c_str(), "bigFont.fnt"); - titleLabel->setAnchorPoint({ .0f, .5f }); - titleLabel->setPosition(m_height / 2 + logoSize / 2 + 13.f, m_height / 2); - titleLabel->limitLabelWidth(m_width - 120.f, 1.f, .1f); - this->addChild(titleLabel); + titleLabel->setLayoutOptions( + AxisLayoutOptions::create() + ->setScalePriority(1) + ->setMaxScale(1.f) + ->setMinScale(0.1f) + ); + m_menu->addChild(titleLabel); + + // m_menu->addChild(SpacerNode::create()); auto viewSpr = ButtonSprite::create("View", "bigFont.fnt", "GJ_button_01.png", .8f); viewSpr->setScale(.65f); @@ -620,6 +634,8 @@ bool ProblemsCell::init( CCMenuItemSpriteExtra::create(viewSpr, this, menu_selector(ProblemsCell::onInfo)); m_menu->addChild(viewBtn); + m_menu->updateLayout(); + return true; } diff --git a/loader/src/ui/internal/list/ModListCell.hpp b/loader/src/ui/internal/list/ModListCell.hpp index 0944d54a..28e253d9 100644 --- a/loader/src/ui/internal/list/ModListCell.hpp +++ b/loader/src/ui/internal/list/ModListCell.hpp @@ -24,6 +24,9 @@ protected: CCMenuItemToggler* m_enableToggle = nullptr; CCMenuItemSpriteExtra* m_unresolvedExMark; CCMenuItemSpriteExtra* m_developerBtn; + SpacerNode* m_spacer = nullptr; + CCMenu* m_columnMenu = nullptr; + CCMenu* m_labelMenu = nullptr; bool init(ModListLayer* list, CCSize const& size); void setupInfo(ModMetadata const& metadata, bool spaceForTags, ModListDisplay display, bool inactive); @@ -32,6 +35,8 @@ protected: float getLogoSize() const; void onViewDev(CCObject*); + void updateCellLayout(); + public: virtual void updateState() = 0; virtual CCNode* createLogo(CCSize const& size) = 0; diff --git a/loader/src/ui/internal/list/ModListLayer.cpp b/loader/src/ui/internal/list/ModListLayer.cpp index df4fbecc..a06e745c 100644 --- a/loader/src/ui/internal/list/ModListLayer.cpp +++ b/loader/src/ui/internal/list/ModListLayer.cpp @@ -349,7 +349,7 @@ bool ModListLayer::init() { m_tabsGradientNode->ignoreAnchorPointForPosition(true); m_tabsGradientNode->setZOrder(9); m_tabsGradientNode->setInverted(false); - m_tabsGradientNode->setAlphaThreshold(0.f); + m_tabsGradientNode->setAlphaThreshold(0.7f); m_tabsGradientSprite = CCSprite::create("tab-gradient.png"_spr); m_tabsGradientNode->addChild(m_tabsGradientSprite); @@ -646,7 +646,7 @@ void ModListLayer::onExpand(CCObject* sender) { m_display = static_cast(sender)->isToggled() ? ModListDisplay::Concise : ModListDisplay::Expanded; - this->reloadList(); + this->reloadList(false); } void ModListLayer::onFilters(CCObject*) { @@ -682,7 +682,7 @@ void ModListLayer::onTab(CCObject* pSender) { member->release(); } if (isSelected && m_tabsGradientStencil) - m_tabsGradientStencil->setPosition(member->m_onButton->convertToWorldSpace({0.f, -1.f})); + m_tabsGradientStencil->setPosition(member->m_onButton->convertToWorldSpace({0.f, 0.f})); }; toggleTab(m_downloadTabBtn);