Merge branch 'main' into copy-mods

This commit is contained in:
Justin 2024-11-18 19:28:08 -05:00 committed by GitHub
commit 00ab705154
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
184 changed files with 3674 additions and 11624 deletions

View file

@ -1,5 +1,56 @@
# Geode Changelog # Geode Changelog
## v4.0.0-beta.1
* Button to manually install mods from files (e881dc5)
* Add `ModRequestedAction::Update` (e881dc5)
* Add `ModMetadata::checkGeodeVersion` and `ModMetadata::checkTargetVersions` (e881dc5)
* Add `geode::createModLogo` for creating a logo from a `.geode` package (e881dc5)
* Tags now use names provided by the server (893b03e)
* Add web support for multiple request headers with same name (#1150)
* Fix `Task::chain` using the wrong type in the impl (22a11b9)
* Fix installing mods not checking the current version (#1148)
* Fix searching for mods ignoring geode and gd version (#1153)
* Fix crash when checking tags (01807fe)
* Fix 'Outdated' label being visible while updating (6679a69)
* Fix log nesting issue (0e8d4c6)
* Remove forward compat message box as it confuses users (5592ef6)
* Fix crash on opening mod changelogs (9834cb2)
* Make `ColorPickPopup` pimpl (1a201e1)
* Fix lag issue in `ColorPickPopup` (3081164)
* Change return type of `ModSettingsManager::save` (da92090)
* Fix every misspelling of successfully (#1151)
* Allow building geode itself in debug mode (5645399)
## v4.0.0-alpha.1
* Support for the 2.2074 update
* Developers, see [this page for a migration guide](https://docs.geode-sdk.org/tutorials/migrate-v4)
* Major API breaks:
* Remove everything previously marked deprecated
* `utils::MiniFunction` removed
* Rewritten `geode::Result` class
* Rewritten matjson library
* Settings V2 completely removed, use V3 now
* `JsonChecker` removed
* Add new system for ordered hook priority, [see docs](https://docs.geode-sdk.org/tutorials/hookpriority) (673317d, 6db3084)
* C++20 coroutine support for `geode::Task`, [see docs](https://docs.geode-sdk.org/tutorials/tasks#coroutines) (e61b2c0, ab196b9)
* Add `Task::chain`, [see docs](https://docs.geode-sdk.org/tutorials/tasks#chaining-tasks) (3248831)
* Single page local mods list (efb1fbf)
* Split mod problems into load and outdated (12e8bbb, 09fa872, df2528c)
* This means mods made for outdated gd or geode versions no longer count as actual errors, resulting in less clutter in the ui
* Fix safe mode popup on windows showing up when not supposed to (038788b)
* WebRequest::ignoreContentLength (#1126)
* Lots of smaller fixes to the geode ui (c9afa75, f5f3365, 2d66279, 02845d9, 9b95301, 6d13f78, 123b3ab, 0b2fc66, f96ea5e, cad670f)
* Fix CCArrayExt::pop_back() return type (#1130)
* Add missing spanish translations to installer (#1145)
* Add hashtag symbol to CommonFilter::Any (#1131)
* Disable forward compat on android (c9e97af)
## v3.9.2
* Fix searching for mods returning unavailable mods (#1149)
## v3.9.1
* Fix mod downloads not checking version (f575187)
## v3.9.0 ## v3.9.0
* Many changes to the settings ui (#1108) * Many changes to the settings ui (#1108)
* Fuzzy search is now more reasonable * Fuzzy search is now more reasonable

View file

@ -189,8 +189,8 @@ include(cmake/Platform.cmake)
include(cmake/GeodeFile.cmake) include(cmake/GeodeFile.cmake)
if (NOT DEFINED GEODE_GD_VERSION) if (NOT DEFINED GEODE_GD_VERSION)
set(GEODE_GD_VERSION 2.206) set(GEODE_GD_VERSION 2.2074)
set(GEODE_COMP_GD_VERSION 22060) set(GEODE_COMP_GD_VERSION 22074)
endif() endif()
target_compile_definitions( target_compile_definitions(
@ -236,8 +236,9 @@ if (ANDROID)
endif() endif()
set(MAT_JSON_AS_INTERFACE ON) set(MAT_JSON_AS_INTERFACE ON)
CPMAddPackage("gh:geode-sdk/json#cda9807") CPMAddPackage("gh:geode-sdk/result@1.2.1")
CPMAddPackage("gh:fmtlib/fmt#10.2.1") CPMAddPackage("gh:geode-sdk/json@3.1.0")
CPMAddPackage("gh:fmtlib/fmt#11.0.2")
target_compile_definitions(${PROJECT_NAME} INTERFACE MAT_JSON_DYNAMIC=1) target_compile_definitions(${PROJECT_NAME} INTERFACE MAT_JSON_DYNAMIC=1)
@ -266,7 +267,7 @@ if (DEFINED GEODE_TULIPHOOK_REPO_PATH)
message(STATUS "Using ${GEODE_TULIPHOOK_REPO_PATH} for TulipHook") message(STATUS "Using ${GEODE_TULIPHOOK_REPO_PATH} for TulipHook")
add_subdirectory(${GEODE_TULIPHOOK_REPO_PATH} ${GEODE_TULIPHOOK_REPO_PATH}/build) add_subdirectory(${GEODE_TULIPHOOK_REPO_PATH} ${GEODE_TULIPHOOK_REPO_PATH}/build)
else() else()
CPMAddPackage("gh:geode-sdk/TulipHook#d1d9559") CPMAddPackage("gh:geode-sdk/TulipHook@2.4.1")
endif() endif()
set(CMAKE_WARN_DEPRECATED ON CACHE BOOL "" FORCE) set(CMAKE_WARN_DEPRECATED ON CACHE BOOL "" FORCE)
@ -296,7 +297,7 @@ target_include_directories(GeodeBindings PUBLIC
${GEODE_LOADER_PATH}/include/Geode/fmod ${GEODE_LOADER_PATH}/include/Geode/fmod
) )
target_link_directories(GeodeBindings PUBLIC ${GEODE_LOADER_PATH}/include/link) target_link_directories(GeodeBindings PUBLIC ${GEODE_LOADER_PATH}/include/link)
target_link_libraries(GeodeBindings PUBLIC fmt TulipHookInclude mat-json) target_link_libraries(GeodeBindings PUBLIC fmt TulipHookInclude mat-json GeodeResult)
target_link_libraries(${PROJECT_NAME} INTERFACE GeodeBindings) target_link_libraries(${PROJECT_NAME} INTERFACE GeodeBindings)
if (NOT EXISTS ${GEODE_BIN_PATH}) if (NOT EXISTS ${GEODE_BIN_PATH})

View file

@ -1 +1 @@
3.9.0 4.0.0-beta.1

View file

@ -2,8 +2,8 @@
# #
# SPDX-FileCopyrightText: Copyright (c) 2019-2023 Lars Melchior and contributors # SPDX-FileCopyrightText: Copyright (c) 2019-2023 Lars Melchior and contributors
set(CPM_DOWNLOAD_VERSION 0.39.0) set(CPM_DOWNLOAD_VERSION 0.40.2)
set(CPM_HASH_SUM "66639bcac9dd2907b2918de466783554c1334446b9874e90d38e3778d404c2ef") set(CPM_HASH_SUM "c8cdc32c03816538ce22781ed72964dc864b2a34a310d3b7104812a5ca2d835d")
if(CPM_SOURCE_CACHE) if(CPM_SOURCE_CACHE)
set(CPM_DOWNLOAD_LOCATION "${CPM_SOURCE_CACHE}/cpm/CPM_${CPM_DOWNLOAD_VERSION}.cmake") set(CPM_DOWNLOAD_LOCATION "${CPM_SOURCE_CACHE}/cpm/CPM_${CPM_DOWNLOAD_VERSION}.cmake")

View file

@ -72,19 +72,27 @@ elseif (GEODE_TARGET_PLATFORM STREQUAL "Win64")
target_link_libraries(${PROJECT_NAME} INTERFACE target_link_libraries(${PROJECT_NAME} INTERFACE
${GEODE_LOADER_PATH}/include/link/win64/libcocos2d.lib ${GEODE_LOADER_PATH}/include/link/win64/libcocos2d.lib
${GEODE_LOADER_PATH}/include/link/win64/libExtensions.lib ${GEODE_LOADER_PATH}/include/link/win64/libExtensions.lib
${GEODE_LOADER_PATH}/include/link/win64/ssl.lib
${GEODE_LOADER_PATH}/include/link/win64/crypto.lib
${GEODE_LOADER_PATH}/include/link/win64/nghttp2.lib
${GEODE_LOADER_PATH}/include/link/win64/ngtcp2.lib
${GEODE_LOADER_PATH}/include/link/win64/nghttp3.lib
${GEODE_LOADER_PATH}/include/link/win64/ngtcp2_crypto_boringssl.lib
${GEODE_LOADER_PATH}/include/link/win64/libcurl.lib
${GEODE_LOADER_PATH}/include/link/win64/glew32.lib ${GEODE_LOADER_PATH}/include/link/win64/glew32.lib
${GEODE_LOADER_PATH}/include/link/win64/gdstring.lib
${GEODE_LOADER_PATH}/include/link/win64/fmod.lib ${GEODE_LOADER_PATH}/include/link/win64/fmod.lib
opengl32 opengl32
) )
if (PROJECT_IS_TOP_LEVEL AND CMAKE_BUILD_TYPE STREQUAL "Debug")
target_link_libraries(${PROJECT_NAME} INTERFACE
${GEODE_LOADER_PATH}/include/link/win64/gd-libcurl.lib
)
else()
target_link_libraries(${PROJECT_NAME} INTERFACE
${GEODE_LOADER_PATH}/include/link/win64/ssl.lib
${GEODE_LOADER_PATH}/include/link/win64/crypto.lib
${GEODE_LOADER_PATH}/include/link/win64/nghttp2.lib
${GEODE_LOADER_PATH}/include/link/win64/ngtcp2.lib
${GEODE_LOADER_PATH}/include/link/win64/nghttp3.lib
${GEODE_LOADER_PATH}/include/link/win64/ngtcp2_crypto_boringssl.lib
${GEODE_LOADER_PATH}/include/link/win64/libcurl.lib
)
endif()
# Windows links against .lib and not .dll # Windows links against .lib and not .dll
set(GEODE_OUTPUT_NAME "Geode") set(GEODE_OUTPUT_NAME "Geode")
set(GEODE_PLATFORM_BINARY "Geode.lib") set(GEODE_PLATFORM_BINARY "Geode.lib")

View file

@ -0,0 +1,16 @@
!insertmacro LANGFILE_EXT Portuguese
!pragma warning disable 6030
${LangFileString} MUI_TEXT_WELCOME_INFO_TEXT "O instalador irá guiá-lo através da instalação de $(^NameDA).$\r$\n$\r$\nAntes de iniciar a instalação, certifique-se de que o Geometry Dash não está aberto.$\r$\n$\r$\n$_CLICK"
${LangFileString} MUI_UNTEXT_WELCOME_INFO_TEXT "O instalador irá guiá-lo através da desinstalação de $(^NameDA).$\r$\n$\r$\nAntes de iniciar a desinstalação, certifique-se de que o Geometry Dash não está aberto.$\r$\n$\r$\n$_CLICK"
!pragma warning default 6030
; installer
${LangFileString} GEODE_TEXT_GD_MISSING "$\r$\n$\r$\nEsse caminho não tem o Geometry Dash instalado!"
${LangFileString} GEODE_TEXT_GD_OLD "$\r$\n$\r$\nA sua versão do Geometry Dash é demasiado antiga para esta versão do Geode!"
${LangFileString} GEODE_TEXT_MOD_LOADER_ALREADY_INSTALLED "Esse caminho já tem outros mods instalados!$\r$\nEles serão substituídos pelo Geode. (the dll trademark)"
; uninstaller
${LangFileString} GEODE_UNTEXT_GEODE_MISSING "Esse caminho não tem o Geode instalado!"

View file

@ -1,15 +1,15 @@
!insertmacro LANGFILE_EXT SpanishInternational !insertmacro LANGFILE_EXT SpanishInternational
!pragma warning disable 6030 !pragma warning disable 6030
${LangFileString} MUI_TEXT_WELCOME_INFO_TEXT "Este asistente le guiará durante la instalación de $(^NameDA) en su computadora.$\r$\n$\r$\nAntes de iniciar la instalación, asegúrese de que Geometry Dash no se está ejecutando$\r$\n$\r$\n$_CLICK" ${LangFileString} MUI_TEXT_WELCOME_INFO_TEXT "Este asistente le guiará durante la instalación de Geode en su computadora.$\r$\n$\r$\nAntes de iniciar la instalación, asegúrese de que Geometry Dash no se está ejecutando$\r$\n$\r$\n$_CLICK"
${LangFileString} MUI_UNTEXT_WELCOME_INFO_TEXT "Este asistente le guiará durante la desinstalación de $(^NameDA).$\r$\n$\r$\nAntes de iniciar la desinstalación, asegúrese de que Geometry Dash no se está ejecutando.$\r$\n$\r$\n$_CLICK" ${LangFileString} MUI_UNTEXT_WELCOME_INFO_TEXT "Este asistente le guiará durante la desinstalación de Geode.$\r$\n$\r$\nAntes de iniciar la desinstalación, asegúrese de que Geometry Dash no se está ejecutando.$\r$\n$\r$\n$_CLICK"
!pragma warning default 6030 !pragma warning default 6030
; installer ; installer
${LangFileString} GEODE_TEXT_GD_MISSING "$\r$\n$\r$\n¡Geometry Dash no está instalado en esta ruta!" ${LangFileString} GEODE_TEXT_GD_MISSING "$\r$\n$\r$\n¡Geometry Dash no está instalado en esta ruta!"
${LangFileString} GEODE_TEXT_GD_OLD "$\r$\n$\r$\nYour version of Geometry Dash is too old for this version of Geode!" ${LangFileString} GEODE_TEXT_GD_OLD "$\r$\n$\r$\n¡Su versión de Geometry Dash es demasiado antigua para esta versión de Geode!"
${LangFileString} GEODE_TEXT_MOD_LOADER_ALREADY_INSTALLED "This path has other mods installed!$\r$\nThey will be overwritten by Geode. (the dll trademark)" ${LangFileString} GEODE_TEXT_MOD_LOADER_ALREADY_INSTALLED "¡Esta ruta ya tiene otros mods instalados!$\r$\nVan a ser sobreescritos por Geode. (the dll trademark)"
; uninstaller ; uninstaller

View file

@ -58,6 +58,7 @@
!insertmacro GEODE_LANGUAGE "Polish" !insertmacro GEODE_LANGUAGE "Polish"
!insertmacro GEODE_LANGUAGE "Russian" !insertmacro GEODE_LANGUAGE "Russian"
!insertmacro GEODE_LANGUAGE "PortugueseBR" !insertmacro GEODE_LANGUAGE "PortugueseBR"
!insertmacro GEODE_LANGUAGE "Portuguese"
!insertmacro GEODE_LANGUAGE "Ukrainian" !insertmacro GEODE_LANGUAGE "Ukrainian"
!insertmacro GEODE_LANGUAGE "Czech" !insertmacro GEODE_LANGUAGE "Czech"
!insertmacro GEODE_LANGUAGE "Turkish" !insertmacro GEODE_LANGUAGE "Turkish"

View file

@ -5,21 +5,10 @@
#include <Geode/platform/platform.hpp> #include <Geode/platform/platform.hpp>
#include <variant> #include <variant>
#define GEODE_STATIC_PTR(type, name) \ #if !defined(GEODE_CONCAT)
static type* s_##name; \ #define GEODE_WRAPPER_CONCAT(x, y) x##y
inline type* name() { \ #define GEODE_CONCAT(x, y) GEODE_WRAPPER_CONCAT(x, y)
if (!s_##name) s_##name = new type(); \ #endif
return s_##name; \
}
#define GEODE_STATIC_VAR(type, name) \
inline type& name() { \
static type s_##name; \
return s_##name; \
}
#define GEODE_WRAPPER_CONCAT(x, y) x##y
#define GEODE_CONCAT(x, y) GEODE_WRAPPER_CONCAT(x, y)
#define GEODE_WRAPPER_STR(...) #__VA_ARGS__ #define GEODE_WRAPPER_STR(...) #__VA_ARGS__
#define GEODE_STR(...) GEODE_WRAPPER_STR(__VA_ARGS__) #define GEODE_STR(...) GEODE_WRAPPER_STR(__VA_ARGS__)

View file

@ -6,7 +6,8 @@
#include "ui/EnterLayerEvent.hpp" #include "ui/EnterLayerEvent.hpp"
#include "ui/BasedButtonSprite.hpp" #include "ui/BasedButtonSprite.hpp"
#include "ui/IconButtonSprite.hpp" #include "ui/IconButtonSprite.hpp"
#include "ui/InputNode.hpp" #include "ui/Layout.hpp"
#include "ui/SpacerNode.hpp"
#include "ui/General.hpp" #include "ui/General.hpp"
#include "ui/ListView.hpp" #include "ui/ListView.hpp"
#include "ui/MDPopup.hpp" #include "ui/MDPopup.hpp"

View file

@ -1,7 +1,6 @@
#pragma once #pragma once
#include "DefaultInclude.hpp" #include "DefaultInclude.hpp"
#include "utils/Result.hpp"
#include "utils/VersionInfo.hpp" #include "utils/VersionInfo.hpp"
#include "utils/ranges.hpp" #include "utils/ranges.hpp"
#include "utils/casts.hpp" #include "utils/casts.hpp"
@ -12,5 +11,4 @@
#include "utils/permission.hpp" #include "utils/permission.hpp"
#include "utils/general.hpp" #include "utils/general.hpp"
#include "utils/timer.hpp" #include "utils/timer.hpp"
#include "utils/MiniFunction.hpp"
#include "utils/ObjcHook.hpp" #include "utils/ObjcHook.hpp"

View file

@ -93,7 +93,7 @@ namespace gd {
bool empty() const; bool empty() const;
bool operator==(string const& other) const; bool operator==(string const& other) const;
bool operator==(std::string_view const other) const; bool operator==(std::string_view other) const;
bool operator==(char const* other) const { bool operator==(char const* other) const {
return *this == std::string_view(other); return *this == std::string_view(other);
} }
@ -101,7 +101,7 @@ namespace gd {
return *this == std::string_view(other); return *this == std::string_view(other);
} }
std::strong_ordering operator<=>(string const& other) const; std::strong_ordering operator<=>(string const& other) const;
std::strong_ordering operator<=>(std::string_view const other) const; std::strong_ordering operator<=>(std::string_view other) const;
std::strong_ordering operator<=>(char const* other) const { std::strong_ordering operator<=>(char const* other) const {
return *this <=> std::string_view(other); return *this <=> std::string_view(other);
} }

View file

@ -37,7 +37,6 @@
#include "../kazmath/include/kazmath/kazmath.h" #include "../kazmath/include/kazmath/kazmath.h"
#include "../script_support/CCScriptSupport.h" #include "../script_support/CCScriptSupport.h"
#include "../include/CCProtocols.h" #include "../include/CCProtocols.h"
#include "Layout.hpp"
#include "../../loader/Event.hpp" #include "../../loader/Event.hpp"
#include <Geode/utils/casts.hpp> #include <Geode/utils/casts.hpp>
@ -45,6 +44,12 @@
#include <matjson.hpp> #include <matjson.hpp>
#endif #endif
namespace geode {
class Layout;
class LayoutOptions;
enum class Anchor;
}
NS_CC_BEGIN NS_CC_BEGIN
class CCCamera; class CCCamera;
@ -871,31 +876,19 @@ public:
private: private:
friend class geode::modifier::FieldContainer; friend class geode::modifier::FieldContainer;
[[deprecated("Will be removed, it's an ABI break")]]
GEODE_DLL geode::modifier::FieldContainer* getFieldContainer();
GEODE_DLL geode::modifier::FieldContainer* getFieldContainer(char const* forClass); GEODE_DLL geode::modifier::FieldContainer* getFieldContainer(char const* forClass);
GEODE_DLL void addEventListenerInternal( GEODE_DLL void addEventListenerInternal(
std::string const& id, std::string const& id,
geode::EventListenerProtocol* protocol geode::EventListenerProtocol* protocol
); );
#ifdef GEODE_EXPORTING
[[deprecated("Will be removed, it's an ABI break")]]
GEODE_DLL std::optional<matjson::Value> getAttributeInternal(std::string const& attribute);
#endif
public: public:
#ifdef GEODE_EXPORTING
[[deprecated("Will be removed, it's an ABI break")]]
GEODE_DLL void setAttribute(std::string const& attribute, matjson::Value const& value);
#endif
/** /**
* Get the string ID of this node * Get the string ID of this node
* @returns The ID, or an empty string if the node has no ID. * @returns The ID, or an empty string if the node has no ID.
* @note Geode addition * @note Geode addition
*/ */
GEODE_DLL std::string getID(); GEODE_DLL const std::string& getID();
/** /**
* Set the string ID of this node. String IDs are a Geode addition * Set the string ID of this node. String IDs are a Geode addition
* that are much safer to use to get nodes than absolute indexes * that are much safer to use to get nodes than absolute indexes
@ -906,13 +899,23 @@ public:
*/ */
GEODE_DLL void setID(std::string const& id); GEODE_DLL void setID(std::string const& id);
/**
* Set the string ID of this node. String IDs are a Geode addition
* that are much safer to use to get nodes than absolute indexes
* @param id The ID of the node, recommended to be in kebab case
* without any spaces or uppercase letters. If the node is added
* by a mod, use the _spr literal to append the mod ID to it
* @note Geode addition
*/
GEODE_DLL void setID(std::string&& id);
/** /**
* Get a child by its string ID * Get a child by its string ID
* @param id ID of the child * @param id ID of the child
* @returns The child, or nullptr if none was found * @returns The child, or nullptr if none was found
* @note Geode addition * @note Geode addition
*/ */
GEODE_DLL CCNode* getChildByID(std::string const& id); GEODE_DLL CCNode* getChildByID(std::string_view id);
/** /**
* Get a child by its string ID. Recursively searches all the children * Get a child by its string ID. Recursively searches all the children
@ -920,7 +923,7 @@ public:
* @returns The child, or nullptr if none was found * @returns The child, or nullptr if none was found
* @note Geode addition * @note Geode addition
*/ */
GEODE_DLL CCNode* getChildByIDRecursive(std::string const& id); GEODE_DLL CCNode* getChildByIDRecursive(std::string_view id);
/** /**
* Get a child based on a query. Searches the child tree for a matching * Get a child based on a query. Searches the child tree for a matching
@ -936,14 +939,14 @@ public:
* ->getChildByID("mod.id/epic-button")` * ->getChildByID("mod.id/epic-button")`
* @returns The first matching node, or nullptr if none was found * @returns The first matching node, or nullptr if none was found
*/ */
GEODE_DLL CCNode* querySelector(std::string const& query); GEODE_DLL CCNode* querySelector(std::string_view query);
/** /**
* Removes a child from the container by its ID. * Removes a child from the container by its ID.
* @param id The ID of the node * @param id The ID of the node
* @note Geode addition * @note Geode addition
*/ */
GEODE_DLL void removeChildByID(std::string const& id); GEODE_DLL void removeChildByID(std::string_view id);
/** /**
* Add a child before a specified existing child * Add a child before a specified existing child
@ -990,13 +993,13 @@ public:
* CCLayers / CCMenus, this will change where the children are located * CCLayers / CCMenus, this will change where the children are located
* @note Geode addition * @note Geode addition
*/ */
GEODE_DLL void setLayout(Layout* layout, bool apply = true, bool respectAnchor = true); GEODE_DLL void setLayout(geode::Layout* layout, bool apply = true, bool respectAnchor = true);
/** /**
* Get the Layout for this node * Get the Layout for this node
* @returns The current layout, or nullptr if no layout is set * @returns The current layout, or nullptr if no layout is set
* @note Geode addition * @note Geode addition
*/ */
GEODE_DLL Layout* getLayout(); GEODE_DLL geode::Layout* getLayout();
/** /**
* Update the layout of this node using the current Layout. If no layout is * Update the layout of this node using the current Layout. If no layout is
* set, nothing happens * set, nothing happens
@ -1011,13 +1014,13 @@ public:
* @param apply Whether to update the layout of the parent node * @param apply Whether to update the layout of the parent node
* @note Geode addition * @note Geode addition
*/ */
GEODE_DLL void setLayoutOptions(LayoutOptions* options, bool apply = true); GEODE_DLL void setLayoutOptions(geode::LayoutOptions* options, bool apply = true);
/** /**
* Get the layout options for this node * Get the layout options for this node
* @returns The current layout options, or nullptr if no options are set * @returns The current layout options, or nullptr if no options are set
* @note Geode addition * @note Geode addition
*/ */
GEODE_DLL LayoutOptions* getLayoutOptions(); GEODE_DLL geode::LayoutOptions* getLayoutOptions();
/** /**
* Adds a child at an anchored position with an offset. The node is placed * Adds a child at an anchored position with an offset. The node is placed
* in its parent where the anchor specifies, and then the offset is used to * in its parent where the anchor specifies, and then the offset is used to
@ -1029,7 +1032,7 @@ public:
* if no other layout is already specified * if no other layout is already specified
* @note Geode addition * @note Geode addition
*/ */
GEODE_DLL void addChildAtPosition(CCNode* child, Anchor anchor, CCPoint const& offset = CCPointZero, bool useAnchorLayout = true); GEODE_DLL void addChildAtPosition(CCNode* child, geode::Anchor anchor, CCPoint const& offset = CCPointZero, bool useAnchorLayout = true);
/** /**
* Adds a child at an anchored position with an offset. The node is placed * Adds a child at an anchored position with an offset. The node is placed
* in its parent where the anchor specifies, and then the offset is used to * in its parent where the anchor specifies, and then the offset is used to
@ -1044,7 +1047,7 @@ public:
*/ */
GEODE_DLL void addChildAtPosition( GEODE_DLL void addChildAtPosition(
CCNode* child, CCNode* child,
Anchor anchor, geode::Anchor anchor,
CCPoint const& offset, CCPoint const& offset,
CCPoint const& nodeAnchor, CCPoint const& nodeAnchor,
bool useAnchorLayout = true bool useAnchorLayout = true
@ -1057,7 +1060,7 @@ public:
* @param offset Where to place the child relative to the anchor * @param offset Where to place the child relative to the anchor
* @note Geode addition * @note Geode addition
*/ */
GEODE_DLL void updateAnchoredPosition(Anchor anchor, CCPoint const& offset = CCPointZero); GEODE_DLL void updateAnchoredPosition(geode::Anchor anchor, CCPoint const& offset = CCPointZero);
/** /**
* Updates the anchored position of a child. Requires the child to already * Updates the anchored position of a child. Requires the child to already
* have a parent; if the child already has AnchorLayoutOptions set, those * have a parent; if the child already has AnchorLayoutOptions set, those
@ -1068,7 +1071,7 @@ public:
* @note Geode addition * @note Geode addition
*/ */
GEODE_DLL void updateAnchoredPosition( GEODE_DLL void updateAnchoredPosition(
Anchor anchor, geode::Anchor anchor,
CCPoint const& offset, CCPoint const& offset,
CCPoint const& nodeAnchor CCPoint const& nodeAnchor
); );
@ -1102,7 +1105,7 @@ public:
template <class Filter, class... Args> template <class Filter, class... Args>
geode::EventListenerProtocol* addEventListener( geode::EventListenerProtocol* addEventListener(
std::string const& id, std::string const& id,
geode::utils::MiniFunction<typename Filter::Callback> callback, std::function<typename Filter::Callback> callback,
Args&&... args Args&&... args
) { ) {
auto listener = new geode::EventListener<Filter>( auto listener = new geode::EventListener<Filter>(
@ -1113,7 +1116,7 @@ public:
} }
template <class Filter, class... Args> template <class Filter, class... Args>
geode::EventListenerProtocol* addEventListener( geode::EventListenerProtocol* addEventListener(
geode::utils::MiniFunction<typename Filter::Callback> callback, std::function<typename Filter::Callback> callback,
Args&&... args Args&&... args
) { ) {
return this->addEventListener<Filter, Args...>( return this->addEventListener<Filter, Args...>(
@ -1923,7 +1926,7 @@ namespace geode {
std::string m_targetID; std::string m_targetID;
public: public:
ListenerResult handle(utils::MiniFunction<Callback> fn, UserObjectSetEvent* event); ListenerResult handle(std::function<Callback> fn, UserObjectSetEvent* event);
AttributeSetFilter(std::string const& id); AttributeSetFilter(std::string const& id);
}; };

View file

@ -28,6 +28,8 @@
#include "../../include/cocos2d.h" #include "../../include/cocos2d.h"
#include "../ExtensionMacros.h" #include "../ExtensionMacros.h"
enum class GJHttpType;
NS_CC_EXT_BEGIN NS_CC_EXT_BEGIN
class CC_DLL CCHttpClient; class CC_DLL CCHttpClient;
@ -216,6 +218,50 @@ public:
return _headers; return _headers;
} }
inline int getType() {
return _type;
}
inline void setType(int type) {
_type = type;
}
// @note Geode addition
inline void setType(GJHttpType type) {
_type = static_cast<int>(type);
}
inline bool getShouldCancel() {
return _shouldCancel;
}
inline void setShouldCancel(bool shouldCancel) {
_shouldCancel = shouldCancel;
}
inline int getDownloadProgress() {
return _downloadProgress;
}
inline void setDownloadProgress(int downloadProgress) {
_downloadProgress = downloadProgress;
}
inline int getReadTimeout() {
return _readTimeout;
}
inline void setReadTimeout(int readTimeout) {
_readTimeout = readTimeout;
}
inline int getConnectTimeout() {
return _connectTimeout;
}
inline void setConnectTimeout(int connectTimeout) {
_connectTimeout = connectTimeout;
}
protected: protected:
// properties // properties
@ -229,13 +275,15 @@ protected:
gd::vector<gd::string> _headers; /// custom http headers gd::vector<gd::string> _headers; /// custom http headers
// @note RobTop Addition // @note RobTop Addition
int _requestTypeGJ; int _type;
// @note RobTop Addition // @note RobTop Addition
bool _shouldCancel; bool _shouldCancel;
// @note RobTop Addition // @note RobTop Addition
int _downloadProgress; int _downloadProgress;
// @note RobTop Addition // @note RobTop Addition
int _readTimeout; int _readTimeout;
// @note RobTop Addition
int _connectTimeout;
}; };
NS_CC_EXT_END NS_CC_EXT_END

View file

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

View file

@ -142,7 +142,7 @@ public:
/** /**
* @note RobTop addition * @note RobTop addition
*/ */
void toggleFullScreen(bool fullscreen, bool borderless); void toggleFullScreen(bool fullscreen, bool borderless, bool fix);
/** /**
* @note RobTop addition * @note RobTop addition
@ -185,6 +185,8 @@ public:
protected: protected:
static CCEGLView* s_pEglView; static CCEGLView* s_pEglView;
// @note unknown members here
uint8_t m_unkPad[8];
bool m_bCaptured; bool m_bCaptured;
// Robtop Removal // Robtop Removal
// HWND m_hWnd; // HWND m_hWnd;
@ -203,6 +205,8 @@ protected:
int m_nRetinaFactor; int m_nRetinaFactor;
// @note RobTop Addition // @note RobTop Addition
bool m_bCursorHidden; bool m_bCursorHidden;
// @note may be before m_bCursorHidden
int m_unkSize4;
// Robtop Removal // Robtop Removal
// LPCWSTR m_menu; // LPCWSTR m_menu;
// Robtop Removal // Robtop Removal
@ -225,6 +229,8 @@ public:
// @note RobTop Addition // @note RobTop Addition
bool m_bIsBorderless; bool m_bIsBorderless;
// @note RobTop Addition // @note RobTop Addition
bool m_bIsFix;
// @note RobTop Addition
bool m_bShouldHideCursor; bool m_bShouldHideCursor;
// @note RobTop Addition // @note RobTop Addition
bool m_bCursorLocked; bool m_bCursorLocked;

View file

@ -208,9 +208,9 @@ namespace cocos2d
/** /**
* Custom function added for geode; returns if the * Custom function added for geode; returns if the
* zip file was succesfully decoded. * zip file was successfully decoded.
* *
* @return true if the zip was succesfully loaded, * @return true if the zip was successfully loaded,
* false otherwise. * false otherwise.
* *
* @since geode v1.0.0 * @since geode v1.0.0

File diff suppressed because it is too large Load diff

View file

@ -53,7 +53,7 @@ namespace geode {
return dispatchPools()[m_id]; return dispatchPools()[m_id];
} }
ListenerResult handle(utils::MiniFunction<Callback> fn, Ev* event) { ListenerResult handle(std::function<Callback> fn, Ev* event) {
if (event->getID() == m_id) { if (event->getID() == m_id) {
return std::apply(fn, event->getArgs()); return std::apply(fn, event->getArgs());
} }

View file

@ -1,7 +1,6 @@
#pragma once #pragma once
#include "../utils/casts.hpp" #include "../utils/casts.hpp"
#include "../utils/MiniFunction.hpp"
#include <Geode/DefaultInclude.hpp> #include <Geode/DefaultInclude.hpp>
#include <type_traits> #include <type_traits>
@ -52,6 +51,7 @@ namespace geode {
private: private:
static DefaultEventListenerPool* create(); static DefaultEventListenerPool* create();
DefaultEventListenerPool();
public: public:
bool add(EventListenerProtocol* listener) override; bool add(EventListenerProtocol* listener) override;
@ -65,9 +65,6 @@ namespace geode {
template <class... Args> template <class... Args>
friend class DispatchFilter; friend class DispatchFilter;
// todo: make this private in Geode 4.0.0
DefaultEventListenerPool();
}; };
class GEODE_DLL EventListenerProtocol { class GEODE_DLL EventListenerProtocol {
@ -103,7 +100,7 @@ namespace geode {
using Callback = ListenerResult(T*); using Callback = ListenerResult(T*);
using Event = T; using Event = T;
ListenerResult handle(utils::MiniFunction<Callback> fn, T* e) { ListenerResult handle(std::function<Callback> fn, T* e) {
return fn(e); return fn(e);
} }
@ -156,7 +153,7 @@ namespace geode {
this->enable(); this->enable();
} }
EventListener(utils::MiniFunction<Callback> fn, T filter = T()) EventListener(std::function<Callback> fn, T filter = T())
: m_callback(fn), m_filter(filter) : m_callback(fn), m_filter(filter)
{ {
m_filter.setListener(this); m_filter.setListener(this);
@ -193,10 +190,7 @@ namespace geode {
this->enable(); this->enable();
} }
void bind(utils::MiniFunction<Callback> const& fn) { void bind(std::function<Callback> fn) {
m_callback = fn;
}
void bind(utils::MiniFunction<Callback>&& fn) {
m_callback = fn; m_callback = fn;
} }
@ -218,12 +212,12 @@ namespace geode {
return m_filter; return m_filter;
} }
utils::MiniFunction<Callback>& getCallback() { std::function<Callback>& getCallback() {
return m_callback; return m_callback;
} }
protected: protected:
utils::MiniFunction<Callback> m_callback = nullptr; std::function<Callback> m_callback = nullptr;
T m_filter; T m_filter;
}; };

View file

@ -57,7 +57,7 @@ namespace geode::ipc {
std::string m_messageID; std::string m_messageID;
public: public:
ListenerResult handle(utils::MiniFunction<Callback> fn, IPCEvent* event); ListenerResult handle(std::function<Callback> fn, IPCEvent* event);
IPCFilter( IPCFilter(
std::string const& modID, std::string const& modID,
std::string const& messageID std::string const& messageID

View file

@ -1,8 +1,7 @@
#pragma once #pragma once
#include <filesystem> #include <filesystem>
#include "../utils/Result.hpp" #include <Geode/Result.hpp>
#include "../utils/MiniFunction.hpp"
#include "Log.hpp" #include "Log.hpp"
#include "ModEvent.hpp" #include "ModEvent.hpp"
#include "ModMetadata.hpp" #include "ModMetadata.hpp"
@ -15,7 +14,7 @@
#include <string_view> #include <string_view>
namespace geode { namespace geode {
using ScheduledFunction = utils::MiniFunction<void()>; using ScheduledFunction = std::function<void()>;
struct InvalidGeodeFile { struct InvalidGeodeFile {
std::filesystem::path path; std::filesystem::path path;
@ -47,6 +46,21 @@ namespace geode {
Type type; Type type;
std::variant<std::filesystem::path, ModMetadata, Mod*> cause; std::variant<std::filesystem::path, ModMetadata, Mod*> cause;
std::string message; std::string message;
bool isSuggestion() const {
return
type == LoadProblem::Type::Recommendation ||
type == LoadProblem::Type::Suggestion;
}
bool isOutdated() const {
return
type == LoadProblem::Type::UnsupportedVersion ||
type == LoadProblem::Type::NeedsNewerGeodeVersion ||
type == LoadProblem::Type::UnsupportedGeodeVersion;
}
bool isProblem() const {
return !isSuggestion() && !isOutdated();
}
}; };
class LoaderImpl; class LoaderImpl;
@ -92,7 +106,8 @@ namespace geode {
Mod* getLoadedMod(std::string const& id) const; Mod* getLoadedMod(std::string const& id) const;
std::vector<Mod*> getAllMods(); std::vector<Mod*> getAllMods();
std::vector<LoadProblem> getAllProblems() const; std::vector<LoadProblem> getAllProblems() const;
std::vector<LoadProblem> getProblems() const; std::vector<LoadProblem> getLoadProblems() const;
std::vector<LoadProblem> getOutdated() const;
std::vector<LoadProblem> getRecommendations() const; std::vector<LoadProblem> getRecommendations() const;
/** /**
@ -103,41 +118,36 @@ namespace geode {
* Returns whether the specified launch argument was passed in via the command line. * Returns whether the specified launch argument was passed in via the command line.
* @param name The argument name * @param name The argument name
*/ */
bool hasLaunchArgument(std::string_view const name) const; bool hasLaunchArgument(std::string_view name) const;
/** /**
* Get a launch argument. These are passed into the game as command-line arguments * Get a launch argument. These are passed into the game as command-line arguments
* with the format `--geode:arg-name=value`. * with the format `--geode:arg-name=value`.
* @param name The argument name * @param name The argument name
* @return The value, if present * @return The value, if present
*/ */
std::optional<std::string> getLaunchArgument(std::string_view const name) const; std::optional<std::string> getLaunchArgument(std::string_view name) const;
/** /**
* Get a launch argument flag. Returns whether the argument is present and its * Get a launch argument flag. Returns whether the argument is present and its
* value is exactly `true`. * value is exactly `true`.
* @param name The argument name * @param name The argument name
*/ */
bool getLaunchFlag(std::string_view const name) const; bool getLaunchFlag(std::string_view name) const;
/** /**
* Get and parse a launch argument value using the setting value system. * Get and parse a launch argument value using the setting value system.
* @param name The argument name * @param name The argument name
*/ */
template <class T> template <class T>
std::optional<T> parseLaunchArgument(std::string_view const name) const { Result<T> parseLaunchArgument(std::string_view name) const {
auto str = this->getLaunchArgument(name); auto str = this->getLaunchArgument(name);
if (!str.has_value()) { if (!str.has_value()) {
return std::nullopt; return Err(fmt::format("Launch argument '{}' not found", name));
} }
std::string parseError; auto jsonOpt = matjson::Value::parse(str.value());
auto jsonOpt = matjson::parse(str.value(), parseError); if (jsonOpt.isErr()) {
if (!jsonOpt.has_value()) { return Err(fmt::format("Parsing launch argument '{}' failed: {}", name, jsonOpt.unwrapErr()));
log::debug("Parsing launch argument '{}' failed: {}", name, parseError);
return std::nullopt;
} }
auto value = jsonOpt.value(); auto value = jsonOpt.unwrap();
if (!value.is<T>()) { return value.template as<T>();
return std::nullopt;
}
return value.as<T>();
} }
void queueInMainThread(ScheduledFunction&& func); void queueInMainThread(ScheduledFunction&& func);

View file

@ -65,12 +65,6 @@ namespace std::filesystem {
} }
} }
namespace matjson {
GEODE_INLINE GEODE_HIDDEN std::string format_as(matjson::Value const& value) {
return value.dump(matjson::NO_INDENTATION);
}
}
namespace geode { namespace geode {
class Mod; class Mod;
@ -120,6 +114,37 @@ namespace geode {
popNest(getMod()); popNest(getMod());
} }
struct NestScope {
private:
bool m_active = true;
public:
NestScope() {
pushNest();
}
NestScope(NestScope const&) {
pushNest();
}
NestScope(NestScope&& other) {
other.m_active = false;
}
NestScope& operator=(NestScope const&) {
pushNest();
return *this;
}
NestScope& operator=(NestScope&& other) {
other.m_active = false;
return *this;
}
~NestScope() {
if (m_active) popNest();
}
};
class Nest final { class Nest final {
private: private:
class Impl; class Impl;

View file

@ -2,7 +2,7 @@
#include "../DefaultInclude.hpp" #include "../DefaultInclude.hpp"
#include "../cocos/support/zip_support/ZipUtils.h" #include "../cocos/support/zip_support/ZipUtils.h"
#include "../utils/Result.hpp" #include <Geode/Result.hpp>
#include "../utils/VersionInfo.hpp" #include "../utils/VersionInfo.hpp"
#include "../utils/general.hpp" #include "../utils/general.hpp"
@ -10,7 +10,6 @@
#include "Hook.hpp" #include "Hook.hpp"
#include "ModMetadata.hpp" #include "ModMetadata.hpp"
#include "Setting.hpp" #include "Setting.hpp"
#include "SettingV3.hpp"
#include "Types.hpp" #include "Types.hpp"
#include "Loader.hpp" #include "Loader.hpp"
@ -24,8 +23,6 @@
#include <vector> #include <vector>
namespace geode { namespace geode {
class SettingV3;
template <class T> template <class T>
struct HandleToSaved : public T { struct HandleToSaved : public T {
Mod* m_mod; Mod* m_mod;
@ -44,7 +41,8 @@ namespace geode {
Enable, Enable,
Disable, Disable,
Uninstall, Uninstall,
UninstallWithSaveData UninstallWithSaveData,
Update
}; };
static constexpr bool modRequestedActionIsToggle(ModRequestedAction action) { static constexpr bool modRequestedActionIsToggle(ModRequestedAction action) {
@ -54,22 +52,6 @@ namespace geode {
return action == ModRequestedAction::Uninstall || action == ModRequestedAction::UninstallWithSaveData; return action == ModRequestedAction::Uninstall || action == ModRequestedAction::UninstallWithSaveData;
} }
template <class T>
static consteval bool typeImplementsIsJSON() {
using namespace matjson;
if constexpr (requires(const Value& json) { Serialize<std::decay_t<T>>::is_json(json); })
return true;
if constexpr (std::is_same_v<T, Value>) return true;
if constexpr (std::is_same_v<T, Array>) return true;
if constexpr (std::is_same_v<T, Object>) return true;
if constexpr (std::is_constructible_v<std::string, T>) return true;
if constexpr (std::is_integral_v<T> || std::is_floating_point_v<T>) return true;
if constexpr (std::is_same_v<T, bool>) return true;
if constexpr (std::is_same_v<T, std::nullptr_t>) return true;
return false;
}
GEODE_HIDDEN Mod* takeNextLoaderMod(); GEODE_HIDDEN Mod* takeNextLoaderMod();
class ModImpl; class ModImpl;
@ -108,8 +90,6 @@ namespace geode {
std::string getID() const; std::string getID() const;
std::string getName() const; std::string getName() const;
[[deprecated("Use Mod::getDevelopers instead")]]
std::string getDeveloper() const;
std::vector<std::string> getDevelopers() const; std::vector<std::string> getDevelopers() const;
std::optional<std::string> getDescription() const; std::optional<std::string> getDescription() const;
std::optional<std::string> getDetails() const; std::optional<std::string> getDetails() const;
@ -137,17 +117,6 @@ namespace geode {
std::vector<Mod*> getDependants() const; std::vector<Mod*> getDependants() const;
#endif #endif
/**
* Check if this Mod has updates available on the mods index. If
* you're using this for automatic update checking, use
* `openInfoPopup` or `openIndexPopup` from the `ui/GeodeUI.hpp`
* header to open the Mod's page to let the user install the update
* @returns The latest available version on the index if there are
* updates for this mod
*/
[[deprecated("Use Mod::checkUpdates instead; this function always returns nullopt")]]
std::optional<VersionInfo> hasAvailableUpdate() const;
using CheckUpdatesTask = Task<Result<std::optional<VersionInfo>, std::string>>; using CheckUpdatesTask = Task<Result<std::optional<VersionInfo>, std::string>>;
/** /**
* Check if this Mod has updates available on the mods index. If * Check if this Mod has updates available on the mods index. If
@ -187,52 +156,15 @@ namespace geode {
* declared in `mod.json`) * declared in `mod.json`)
*/ */
std::vector<std::string> getSettingKeys() const; std::vector<std::string> getSettingKeys() const;
bool hasSetting(std::string_view const key) const; bool hasSetting(std::string_view key) const;
// todo in v4: remove these
[[deprecated("Use Mod::getSettingV3")]]
std::optional<Setting> getSettingDefinition(std::string_view const key) const;
[[deprecated("Use Mod::getSettingV3")]]
SettingValue* getSetting(std::string_view const key) const;
// todo in v4: possibly rename this to getSetting?
/** /**
* Get the definition of a setting, or null if the setting was not found, * Get the definition of a setting, or null if the setting was not found,
* or if it's a custom setting that has not yet been registered using * or if it's a custom setting that has not yet been registered using
* `Mod::registerCustomSettingType` * `Mod::registerCustomSettingType`
* @param key The key of the setting as defined in `mod.json` * @param key The key of the setting as defined in `mod.json`
*/ */
std::shared_ptr<SettingV3> getSettingV3(std::string_view const key) const; std::shared_ptr<Setting> getSetting(std::string_view key) const;
/**
* Register a custom setting's value class. See Mod::addCustomSetting
* for a convenience wrapper that creates the value in-place to avoid
* code duplication. Also see
* [the tutorial page](https://docs.geode-sdk.org/mods/settings) for
* more information about custom settings
* @param key The setting's key
* @param value The SettingValue class that shall handle this setting
* @see addCustomSetting
*/
[[deprecated("Use Mod::registerCustomSettingType")]]
void registerCustomSetting(std::string_view const key, std::unique_ptr<SettingValue> value);
/**
* Register a custom setting's value class. The new SettingValue class
* will be created in-place using `std::make_unique`. See
* [the tutorial page](https://docs.geode-sdk.org/mods/settings) for
* more information about custom settings
* @param key The setting's key
* @param value The value of the custom setting
* @example
* $on_mod(Loaded) {
* Mod::get()->addCustomSetting<MySettingValue>("setting-key", DEFAULT_VALUE);
* }
*/
template <class T, class V>
[[deprecated("Use Mod::registerCustomSettingType")]]
void addCustomSetting(std::string_view const key, V const& value) {
this->registerCustomSetting(key, std::make_unique<T>(std::string(key), this->getID(), value));
}
/** /**
* Register a custom setting type. See * Register a custom setting type. See
@ -248,7 +180,7 @@ namespace geode {
* Returns a prefixed launch argument name. See `Mod::getLaunchArgument` * Returns a prefixed launch argument name. See `Mod::getLaunchArgument`
* for details about mod-specific launch arguments. * for details about mod-specific launch arguments.
*/ */
std::string getLaunchArgumentName(std::string_view const name) const; std::string getLaunchArgumentName(std::string_view name) const;
/** /**
* Returns the names of the available mod-specific launch arguments. * Returns the names of the available mod-specific launch arguments.
*/ */
@ -258,7 +190,7 @@ namespace geode {
* for details about mod-specific launch arguments. * for details about mod-specific launch arguments.
* @param name The argument name * @param name The argument name
*/ */
bool hasLaunchArgument(std::string_view const name) const; bool hasLaunchArgument(std::string_view name) const;
/** /**
* Get a mod-specific launch argument. This is equivalent to `Loader::getLaunchArgument` * Get a mod-specific launch argument. This is equivalent to `Loader::getLaunchArgument`
* with the argument name prefixed by the mod ID. For example, a launch argument named * with the argument name prefixed by the mod ID. For example, a launch argument named
@ -266,20 +198,20 @@ namespace geode {
* @param name The argument name * @param name The argument name
* @return The value, if present * @return The value, if present
*/ */
std::optional<std::string> getLaunchArgument(std::string_view const name) const; std::optional<std::string> getLaunchArgument(std::string_view name) const;
/** /**
* Equivalent to a prefixed `Loader::getLaunchFlag` call. See `Mod::getLaunchArgument` * Equivalent to a prefixed `Loader::getLaunchFlag` call. See `Mod::getLaunchArgument`
* for details about mod-specific launch arguments. * for details about mod-specific launch arguments.
* @param name The argument name * @param name The argument name
*/ */
bool getLaunchFlag(std::string_view const name) const; bool getLaunchFlag(std::string_view name) const;
/** /**
* Equivalent to a prefixed `Loader::parseLaunchArgument` call. See `Mod::getLaunchArgument` * Equivalent to a prefixed `Loader::parseLaunchArgument` call. See `Mod::getLaunchArgument`
* for details about mod-specific launch arguments. * for details about mod-specific launch arguments.
* @param name The argument name * @param name The argument name
*/ */
template <class T> template <class T>
std::optional<T> parseLaunchArgument(std::string_view const name) const { std::optional<T> parseLaunchArgument(std::string_view name) const {
return Loader::get()->parseLaunchArgument<T>(this->getLaunchArgumentName(name)); return Loader::get()->parseLaunchArgument<T>(this->getLaunchArgumentName(name));
} }
@ -293,18 +225,18 @@ namespace geode {
* setting type has a `getValue` function which returns the value * setting type has a `getValue` function which returns the value
*/ */
template <class T> template <class T>
T getSettingValue(std::string_view const key) const { T getSettingValue(std::string_view key) const {
using S = typename SettingTypeForValueType<T>::SettingType; using S = typename SettingTypeForValueType<T>::SettingType;
if (auto sett = cast::typeinfo_pointer_cast<S>(this->getSettingV3(key))) { if (auto sett = cast::typeinfo_pointer_cast<S>(this->getSetting(key))) {
return sett->getValue(); return sett->getValue();
} }
return T(); return T();
} }
template <class T> template <class T>
T setSettingValue(std::string_view const key, T const& value) { T setSettingValue(std::string_view key, T const& value) {
using S = typename SettingTypeForValueType<T>::SettingType; using S = typename SettingTypeForValueType<T>::SettingType;
if (auto sett = cast::typeinfo_pointer_cast<S>(this->getSettingV3(key))) { if (auto sett = cast::typeinfo_pointer_cast<S>(this->getSetting(key))) {
auto old = sett->getValue(); auto old = sett->getValue();
sett->setValue(value); sett->setValue(value);
return old; return old;
@ -312,30 +244,28 @@ namespace geode {
return T(); return T();
} }
bool hasSavedValue(std::string_view const key); bool hasSavedValue(std::string_view key);
template <class T> template <class T>
T getSavedValue(std::string_view const key) { T getSavedValue(std::string_view key) {
static_assert(geode::typeImplementsIsJSON<T>(), "T must implement is_json in matjson::Serialize<T>, otherwise this always returns default value.");
auto& saved = this->getSaveContainer(); auto& saved = this->getSaveContainer();
if (saved.contains(key)) { if (auto res = saved.get(key).andThen([](auto&& v) {
if (auto value = saved.try_get<T>(key)) { return v.template as<T>();
return *value; }); res.isOk()) {
} return res.unwrap();
} }
return T(); return T();
} }
template <class T> template <class T>
T getSavedValue(std::string_view const key, T const& defaultValue) { T getSavedValue(std::string_view key, T const& defaultValue) {
static_assert(geode::typeImplementsIsJSON<T>(), "T must implement is_json in matjson::Serialize<T>, otherwise this always returns default value.");
auto& saved = this->getSaveContainer(); auto& saved = this->getSaveContainer();
if (saved.contains(key)) { if (auto res = saved.get(key).andThen([](auto&& v) {
if (auto value = saved.try_get<T>(key)) { return v.template as<T>();
return *value; }); res.isOk()) {
} return res.unwrap();
} }
saved[key] = defaultValue; saved[key] = matjson::Value(defaultValue);
return defaultValue; return defaultValue;
} }
@ -347,7 +277,7 @@ namespace geode {
* @returns The old value * @returns The old value
*/ */
template <class T> template <class T>
T setSavedValue(std::string_view const key, T const& value) { T setSavedValue(std::string_view key, T const& value) {
auto& saved = this->getSaveContainer(); auto& saved = this->getSaveContainer();
auto old = this->getSavedValue<T>(key); auto old = this->getSavedValue<T>(key);
saved[key] = value; saved[key] = value;
@ -488,7 +418,7 @@ namespace geode {
* Check whether or not this Mod * Check whether or not this Mod
* depends on another mod * depends on another mod
*/ */
bool depends(std::string_view const id) const; bool depends(std::string_view id) const;
/** /**
* Check whether all the required * Check whether all the required
@ -517,7 +447,17 @@ namespace geode {
bool isLoggingEnabled() const; bool isLoggingEnabled() const;
void setLoggingEnabled(bool enabled); void setLoggingEnabled(bool enabled);
bool hasProblems() const; /**
* If this mod is built for an outdated GD or Geode version, returns the
* `LoadProblem` describing the situation. Otherwise `nullopt` if the
* mod is made for the correct version of the game and Geode
*/
std::optional<LoadProblem> targetsOutdatedVersion() const;
/**
* @note Make sure to also call `targetsOutdatedVersion` if you want to
* make sure the mod is actually loadable
*/
bool hasLoadProblems() const;
std::vector<LoadProblem> getAllProblems() const; std::vector<LoadProblem> getAllProblems() const;
std::vector<LoadProblem> getProblems() const; std::vector<LoadProblem> getProblems() const;
std::vector<LoadProblem> getRecommendations() const; std::vector<LoadProblem> getRecommendations() const;

View file

@ -10,9 +10,6 @@ namespace geode {
enum class ModEventType { enum class ModEventType {
Loaded, Loaded,
Unloaded,
Enabled,
Disabled,
DataLoaded, DataLoaded,
DataSaved, DataSaved,
}; };
@ -43,7 +40,7 @@ namespace geode {
Mod* m_mod; Mod* m_mod;
public: public:
ListenerResult handle(utils::MiniFunction<Callback> fn, ModStateEvent* event); ListenerResult handle(std::function<Callback> fn, ModStateEvent* event);
/** /**
* Create a mod state listener * Create a mod state listener

View file

@ -1,8 +1,7 @@
#pragma once #pragma once
#include "../utils/Result.hpp" #include <Geode/Result.hpp>
#include "../utils/VersionInfo.hpp" #include "../utils/VersionInfo.hpp"
#include "Setting.hpp"
#include "Types.hpp" #include "Types.hpp"
#include <matjson.hpp> #include <matjson.hpp>
@ -123,13 +122,6 @@ namespace geode {
* character set. * character set.
*/ */
[[nodiscard]] std::string getName() const; [[nodiscard]] std::string getName() const;
/**
* The name of the head developer.
* If the mod has multiple * developers, this will return the first
* developer in the list.
*/
[[nodiscard, deprecated("Use ModMetadata::getDevelopers() instead")]]
std::string getDeveloper() const;
/** /**
* The developers of this mod * The developers of this mod
*/ */
@ -155,11 +147,6 @@ namespace geode {
* (see MDTextArea for more info) * (see MDTextArea for more info)
*/ */
[[nodiscard]] std::optional<std::string> getSupportInfo() const; [[nodiscard]] std::optional<std::string> getSupportInfo() const;
/**
* Git Repository of the mod
*/
[[nodiscard, deprecated("Use ModMetadata::getLinks instead")]]
std::optional<std::string> getRepository() const;
/** /**
* Get the links (related websites / servers / etc.) for this mod * Get the links (related websites / servers / etc.) for this mod
*/ */
@ -184,12 +171,7 @@ namespace geode {
* Mod settings * Mod settings
* @note Not a map because insertion order must be preserved * @note Not a map because insertion order must be preserved
*/ */
[[nodiscard, deprecated("Use getSettingsV3")]] std::vector<std::pair<std::string, Setting>> getSettings() const; [[nodiscard]] std::vector<std::pair<std::string, matjson::Value>> getSettings() const;
/**
* Mod settings
* @note Not a map because insertion order must be preserved
*/
[[nodiscard]] std::vector<std::pair<std::string, matjson::Value>> getSettingsV3() const;
/** /**
* Get the tags for this mod * Get the tags for this mod
*/ */
@ -216,9 +198,19 @@ namespace geode {
/** /**
* Checks if mod can be installed on the current GD version. * Checks if mod can be installed on the current GD version.
* Returns Ok() if it can, Err otherwise. * Returns Ok() if it can, Err explaining why not otherwise.
*/ */
Result<> checkGameVersion() const; Result<> checkGameVersion() const;
/**
* Checks if mod can be installed on the current Geode version.
* Returns Ok() if it can, Err explaining why not otherwise.
*/
Result<> checkGeodeVersion() const;
/**
* Checks if mod can be installed on the current GD & Geode version.
* Returns Ok() if it can, Err explaining why not otherwise.
*/
Result<> checkTargetVersions() const;
#if defined(GEODE_EXPOSE_SECRET_INTERNALS_IN_HEADERS_DO_NOT_DEFINE_PLEASE) #if defined(GEODE_EXPOSE_SECRET_INTERNALS_IN_HEADERS_DO_NOT_DEFINE_PLEASE)
void setPath(std::filesystem::path const& value); void setPath(std::filesystem::path const& value);
@ -237,8 +229,6 @@ namespace geode {
void setDependencies(std::vector<Dependency> const& value); void setDependencies(std::vector<Dependency> const& value);
void setIncompatibilities(std::vector<Incompatibility> const& value); void setIncompatibilities(std::vector<Incompatibility> const& value);
void setSpritesheets(std::vector<std::string> const& value); void setSpritesheets(std::vector<std::string> const& value);
[[deprecated("This function does NOTHING")]]
void setSettings(std::vector<std::pair<std::string, Setting>> const& value);
void setSettings(std::vector<std::pair<std::string, matjson::Value>> const& value); void setSettings(std::vector<std::pair<std::string, matjson::Value>> const& value);
void setTags(std::unordered_set<std::string> const& value); void setTags(std::unordered_set<std::string> const& value);
void setNeedsEarlyLoad(bool const& value); void setNeedsEarlyLoad(bool const& value);
@ -310,7 +300,8 @@ namespace geode {
template <> template <>
struct matjson::Serialize<geode::ModMetadata> { struct matjson::Serialize<geode::ModMetadata> {
static matjson::Value to_json(geode::ModMetadata const& info) { static Value toJson(geode::ModMetadata const& value)
return info.toJSON(); {
return Value(value.toJSON());
} }
}; };

View file

@ -1,7 +1,7 @@
#pragma once #pragma once
#include <Geode/DefaultInclude.hpp> #include <Geode/DefaultInclude.hpp>
#include "SettingV3.hpp" #include "Setting.hpp"
namespace geode { namespace geode {
class Mod; class Mod;
@ -40,12 +40,8 @@ namespace geode {
* The format of the savedata will be an object with the keys being * The format of the savedata will be an object with the keys being
* setting IDs and then the values the values of the saved settings * setting IDs and then the values the values of the saved settings
* @note If saving a setting fails, it will log a warning to the console * @note If saving a setting fails, it will log a warning to the console
* @warning This will overwrite the whole `json` parameter - be sure to
* pass the full settings savedata to `load()` so you can be sure that
* unregistered custom settings' saved values don't disappear!
* @todo in v4: make this return the value instead lol
*/ */
void save(matjson::Value& json); matjson::Value save();
/** /**
* Get the savedata for settings, aka the JSON object that contains all * Get the savedata for settings, aka the JSON object that contains all
@ -57,12 +53,8 @@ namespace geode {
matjson::Value& getSaveData(); matjson::Value& getSaveData();
Result<> registerCustomSettingType(std::string_view type, SettingGenerator generator); Result<> registerCustomSettingType(std::string_view type, SettingGenerator generator);
// todo in v4: remove this
Result<> registerLegacyCustomSetting(std::string_view key, std::unique_ptr<SettingValue>&& ptr);
std::shared_ptr<SettingV3> get(std::string_view key); std::shared_ptr<Setting> get(std::string_view key);
std::shared_ptr<SettingValue> getLegacy(std::string_view key);
std::optional<Setting> getLegacyDefinition(std::string_view key);
/** /**
* Returns true if any setting with the `"restart-required"` attribute * Returns true if any setting with the `"restart-required"` attribute

View file

@ -1,324 +1,46 @@
#pragma once #pragma once
#include "Types.hpp" #include "SettingV3.hpp"
#include "../DefaultInclude.hpp"
#include "../utils/Result.hpp"
#include "../utils/file.hpp"
#include <matjson.hpp>
#include <optional>
#include <unordered_set>
#include <cocos2d.h>
#pragma warning(push)
#pragma warning(disable : 4275)
namespace geode { namespace geode {
class SettingNode; using Setting = SettingV3;
class SettingValue; using SettingGenerator = SettingGeneratorV3;
struct JsonMaybeObject; template <class T, class V = T>
struct JsonMaybeValue; using SettingBaseValue = SettingBaseValueV3<T, V>;
/** using TitleSetting = TitleSettingV3;
* A Setting for a boolean value. Represented in-game as a simple toggle using BoolSetting = BoolSettingV3;
*/ using IntSetting = IntSettingV3;
struct GEODE_DLL BoolSetting final { using FloatSetting = FloatSettingV3;
using ValueType = bool; using StringSetting = StringSettingV3;
using FileSetting = FileSettingV3;
using Color3BSetting = Color3BSettingV3;
using Color4BSetting = Color4BSettingV3;
std::optional<std::string> name; using SettingNode = SettingNodeV3;
std::optional<std::string> description; template <class S>
bool defaultValue; using SettingValueNode = SettingValueNodeV3<S>;
static Result<BoolSetting> parse(JsonMaybeObject& obj); using SettingChangedEvent = SettingChangedEventV3;
}; using SettingChangedFilter = SettingChangedFilterV3;
using SettingNodeSizeChangeEvent = SettingNodeSizeChangeEventV3;
using SettingNodeValueChangeEvent = SettingNodeValueChangeEventV3;
/** template <class T, class Lambda>
* A Setting for an integer value. The value can be limited using the min EventListener<SettingChangedFilter>* listenForSettingChanges(std::string_view settingKey, Lambda&& callback, Mod* mod = getMod()) {
* and max options return listenForSettingChangesV3<T>(settingKey, std::forward<Lambda>(callback), mod);
*/
struct GEODE_DLL IntSetting final {
using ValueType = int64_t;
std::optional<std::string> name;
std::optional<std::string> description;
ValueType defaultValue;
std::optional<ValueType> min;
std::optional<ValueType> max;
struct {
bool arrows = true;
bool bigArrows = false;
size_t arrowStep = 1;
size_t bigArrowStep = 5;
bool slider = true;
std::optional<ValueType> sliderStep = std::nullopt;
bool input = true;
} controls;
static Result<IntSetting> parse(JsonMaybeObject& obj);
};
/**
* A Setting for a float value. The value can be limited using the min
* and max options
*/
struct GEODE_DLL FloatSetting final {
using ValueType = double;
std::optional<std::string> name;
std::optional<std::string> description;
ValueType defaultValue;
std::optional<ValueType> min;
std::optional<ValueType> max;
struct {
bool arrows = true;
bool bigArrows = false;
size_t arrowStep = 1;
size_t bigArrowStep = 5;
bool slider = true;
std::optional<ValueType> sliderStep = std::nullopt;
bool input = true;
} controls;
static Result<FloatSetting> parse(JsonMaybeObject& obj);
};
/**
* A Setting for a string value
*/
struct GEODE_DLL StringSetting final {
using ValueType = std::string;
std::optional<std::string> name;
std::optional<std::string> description;
ValueType defaultValue;
struct Data {
/**
* A regex the string must successfully match against
*/
std::optional<std::string> match;
/**
* The CCTextInputNode's allowed character filter
*/
std::optional<std::string> filter;
/**
* A list of options the user can choose from
*/
std::optional<std::vector<std::string>> options;
};
std::unique_ptr<Data> controls;
std::array<uint8_t, sizeof(Data::match) + sizeof(Data::filter) - sizeof(controls)> m_padding;
StringSetting();
StringSetting(StringSetting const& other);
StringSetting(StringSetting&& other) noexcept;
StringSetting& operator=(StringSetting const& other);
StringSetting& operator=(StringSetting&& other) noexcept;
~StringSetting();
static Result<StringSetting> parse(JsonMaybeObject& obj);
};
/**
* A Setting for a file input. Lets the user select a file from their
* local device
*/
struct GEODE_DLL FileSetting final {
using ValueType = std::filesystem::path;
using Filter = utils::file::FilePickOptions::Filter;
std::optional<std::string> name;
std::optional<std::string> description;
ValueType defaultValue;
struct {
std::vector<Filter> filters;
} controls;
static Result<FileSetting> parse(JsonMaybeObject& obj);
};
/**
* A Setting for an RGB color. See ColorAlphaSetting for a setting that
* also allows customizing alpha
*/
struct GEODE_DLL ColorSetting final {
using ValueType = cocos2d::ccColor3B;
std::optional<std::string> name;
std::optional<std::string> description;
ValueType defaultValue;
static Result<ColorSetting> parse(JsonMaybeObject& obj);
};
/**
* A Setting for an RGBA color. See ColorSetting for a setting that doesn't
* have alpha
*/
struct GEODE_DLL ColorAlphaSetting final {
using ValueType = cocos2d::ccColor4B;
std::optional<std::string> name;
std::optional<std::string> description;
ValueType defaultValue;
static Result<ColorAlphaSetting> parse(JsonMaybeObject& obj);
};
/**
* A custom setting, defined by the mod. See
* [the tutorial page](https://docs.geode-sdk.org/mods/settings) for more
* information about how to create custom settings
*/
struct GEODE_DLL CustomSetting final {
std::shared_ptr<ModJson> json;
};
using SettingKind = std::variant<
BoolSetting,
IntSetting,
FloatSetting,
StringSetting,
FileSetting,
ColorSetting,
ColorAlphaSetting,
CustomSetting
>;
/**
* Represents a saved value for a mod that can be customized by the user
* through an in-game UI. This class is for modeling the setting's
* definition - what values are accepted, its name etc.
* See [the tutorial page](https://docs.geode-sdk.org/mods/settings)
* for more information about how settings work
* @see SettingValue
* @see SettingNode
*/
struct GEODE_DLL Setting final {
private:
std::string m_key;
std::string m_modID;
SettingKind m_kind;
Setting() = default;
public:
static Result<Setting> parse(
std::string const& key,
std::string const& mod,
JsonMaybeValue& obj
);
Setting(
std::string const& key,
std::string const& mod,
SettingKind const& kind
);
template<class T>
std::optional<T> get() {
if (std::holds_alternative<T>(m_kind)) {
return std::get<T>(m_kind);
}
return std::nullopt;
}
std::unique_ptr<SettingValue> createDefaultValue() const;
bool isCustom() const;
std::string getDisplayName() const;
std::optional<std::string> getDescription() const;
std::string getModID() const;
};
/**
* Stores the actual, current value of a Setting. See
* [the tutorial page](https://docs.geode-sdk.org/mods/settings) for more
* information, and how to create custom settings
*/
class GEODE_DLL SettingValue {
protected:
std::string m_key;
std::string m_modID;
SettingValue(std::string const& key, std::string const& mod);
void valueChanged();
public:
virtual ~SettingValue() = default;
virtual bool load(matjson::Value const& json) = 0;
virtual bool save(matjson::Value& json) const = 0;
virtual SettingNode* createNode(float width) = 0;
std::string getKey() const;
std::string getModID() const;
};
template<class T>
class GeodeSettingValue final : public SettingValue {
public:
using ValueType = typename T::ValueType;
protected:
ValueType m_value;
T m_definition;
using Valid = std::pair<ValueType, std::optional<std::string>>;
GEODE_DLL Valid toValid(ValueType const& value) const;
public:
GeodeSettingValue(std::string const& key, std::string const& modID, T const& definition)
: SettingValue(key, modID),
m_definition(definition),
m_value(definition.defaultValue) {}
bool load(matjson::Value const& json) override;
bool save(matjson::Value& json) const;
GEODE_DLL SettingNode* createNode(float width) override;
T castDefinition() const {
return m_definition;
}
Setting getDefinition() const {
return Setting(m_key, m_modID, m_definition);
}
GEODE_DLL ValueType getValue() const;
GEODE_DLL void setValue(ValueType const& value);
GEODE_DLL Result<> validate(ValueType const& value) const;
};
using BoolSettingValue = GeodeSettingValue<BoolSetting>;
using IntSettingValue = GeodeSettingValue<IntSetting>;
using FloatSettingValue = GeodeSettingValue<FloatSetting>;
using StringSettingValue = GeodeSettingValue<StringSetting>;
using FileSettingValue = GeodeSettingValue<FileSetting>;
using ColorSettingValue = GeodeSettingValue<ColorSetting>;
using ColorAlphaSettingValue = GeodeSettingValue<ColorAlphaSetting>;
// todo: remove in v3
template<class T>
struct [[deprecated("Use SettingTypeForValueType from SettingV3 instead")]] GEODE_DLL SettingValueSetter {
static T get(SettingValue* setting);
static void set(SettingValue* setting, T const& value);
};
template<class T>
bool GeodeSettingValue<T>::load(matjson::Value const& json) {
if (!json.is<ValueType>()) return false;
m_value = json.as<ValueType>();
return true;
} }
template<class T> template <class Lambda>
bool GeodeSettingValue<T>::save(matjson::Value& json) const { EventListener<SettingChangedFilter>* listenForSettingChanges(std::string_view settingKey, Lambda&& callback, Mod* mod = getMod()) {
json = m_value; return listenForSettingChangesV3(settingKey, std::forward<Lambda>(callback), mod);
return true; }
inline EventListener<SettingChangedFilter>* listenForAllSettingChanges(
std::function<void(std::shared_ptr<SettingV3>)> const& callback,
Mod* mod = getMod()
) {
return listenForAllSettingChangesV3(callback, mod);
} }
} }
#pragma warning(pop)

View file

@ -1,63 +0,0 @@
#pragma once
#include "Event.hpp"
#include "Loader.hpp"
#include "Setting.hpp"
#include "Mod.hpp"
#include "SettingV3.hpp"
#include <optional>
namespace geode {
struct GEODE_DLL [[deprecated("Use SettingChangedEventV3 from SettingV3.hpp instead")]] SettingChangedEvent : public Event {
Mod* mod;
SettingValue* value;
SettingChangedEvent(Mod* mod, SettingValue* value);
};
class GEODE_DLL [[deprecated("Use SettingChangedEventV3 from SettingV3.hpp instead")]] SettingChangedFilter : public EventFilter<SettingChangedEvent> {
protected:
std::string m_modID;
std::optional<std::string> m_targetKey;
public:
using Callback = void(SettingValue*);
ListenerResult handle(utils::MiniFunction<Callback> fn, SettingChangedEvent* event);
/**
* Listen to changes on a setting, or all settings
* @param modID Mod whose settings to listen to
* @param settingID Setting to listen to, or all settings if nullopt
*/
SettingChangedFilter(
std::string const& modID,
std::optional<std::string> const& settingKey
);
SettingChangedFilter(SettingChangedFilter const&) = default;
};
/**
* Listen for built-in setting changes
*/
template<class T>
class [[deprecated("Use SettingChangedEventV3 from SettingV3.hpp instead")]] GeodeSettingChangedFilter : public SettingChangedFilter {
public:
using Callback = void(T);
ListenerResult handle(utils::MiniFunction<Callback> fn, SettingChangedEvent* event) {
if (
m_modID == event->mod->getID() &&
(!m_targetKey || m_targetKey.value() == event->value->getKey())
) {
fn(SettingValueSetter<T>::get(event->value));
}
return ListenerResult::Propagate;
}
GeodeSettingChangedFilter(
std::string const& modID,
std::string const& settingID
) : SettingChangedFilter(modID, settingID) {}
GeodeSettingChangedFilter(GeodeSettingChangedFilter const&) = default;
};
}

View file

@ -1,32 +0,0 @@
#pragma once
#include "Setting.hpp"
#include <cocos2d.h>
namespace geode {
class SettingNode;
struct SettingNodeDelegate {
virtual void settingValueChanged(SettingNode* node) {}
virtual void settingValueCommitted(SettingNode* node) {}
};
class GEODE_DLL SettingNode : public cocos2d::CCNode {
protected:
SettingValue* m_value;
SettingNodeDelegate* m_delegate = nullptr;
bool init(SettingValue* value);
void dispatchChanged();
void dispatchCommitted();
public:
void setDelegate(SettingNodeDelegate* delegate);
virtual void commit() = 0;
virtual bool hasUncommittedChanges() = 0;
virtual bool hasNonDefaultValue() = 0;
virtual void resetToDefault() = 0;
};
}

View file

@ -3,22 +3,17 @@
#include "../DefaultInclude.hpp" #include "../DefaultInclude.hpp"
#include <optional> #include <optional>
#include <cocos2d.h> #include <cocos2d.h>
// todo: remove this header in 4.0.0
#include "Setting.hpp"
#include "../utils/cocos.hpp" #include "../utils/cocos.hpp"
#include "../utils/file.hpp"
// this unfortunately has to be included because of C++ templates // this unfortunately has to be included because of C++ templates
#include "../utils/JsonValidation.hpp" #include "../utils/JsonValidation.hpp"
#include "../utils/function.hpp" #include "../utils/function.hpp"
// todo in v4: this can be removed as well as the friend decl in LegacyCustomSettingV3
class LegacyCustomSettingToV3Node;
class ModSettingsPopup; class ModSettingsPopup;
namespace geode { namespace geode {
class ModSettingsManager; class ModSettingsManager;
class SettingNodeV3; class SettingNodeV3;
// todo in v4: remove this
class SettingValue;
class GEODE_DLL SettingV3 : public std::enable_shared_from_this<SettingV3> { class GEODE_DLL SettingV3 : public std::enable_shared_from_this<SettingV3> {
private: private:
@ -125,8 +120,6 @@ namespace geode {
*/ */
void markChanged(); void markChanged();
friend class ::geode::SettingValue;
public: public:
SettingV3(); SettingV3();
virtual ~SettingV3(); virtual ~SettingV3();
@ -183,20 +176,9 @@ namespace geode {
* Reset this setting's value back to its original value * Reset this setting's value back to its original value
*/ */
virtual void reset() = 0; virtual void reset() = 0;
[[deprecated(
"This function will be removed alongside legacy settings in 4.0.0! "
"You should NOT be implementing it for your own custom setting classes"
)]]
virtual std::optional<Setting> convertToLegacy() const;
[[deprecated(
"This function will be removed alongside legacy settings in 4.0.0! "
"You should NOT be implementing it for your own custom setting classes"
)]]
virtual std::optional<std::shared_ptr<SettingValue>> convertToLegacyValue() const;
}; };
using SettingGenerator = std::function<Result<std::shared_ptr<SettingV3>>( using SettingGeneratorV3 = std::function<Result<std::shared_ptr<SettingV3>>(
std::string const& key, std::string const& key,
std::string const& modID, std::string const& modID,
matjson::Value const& json matjson::Value const& json
@ -324,14 +306,15 @@ namespace geode {
} }
bool load(matjson::Value const& json) override { bool load(matjson::Value const& json) override {
if (json.is<T>()) { auto res = json.as<T>();
m_impl->value = json.as<T>(); if (res.isErr()) {
return true; return false;
} }
return false; m_impl->value = res.unwrap();
return true;
} }
bool save(matjson::Value& json) const override { bool save(matjson::Value& json) const override {
json = m_impl->value; json = matjson::Value(m_impl->value);
return true; return true;
} }
}; };
@ -357,37 +340,6 @@ namespace geode {
void reset() override; void reset() override;
}; };
// todo in v4: remove this class completely
class GEODE_DLL LegacyCustomSettingV3 final : public SettingV3 {
private:
class Impl;
std::shared_ptr<Impl> m_impl;
friend class ::geode::ModSettingsManager;
friend class ::LegacyCustomSettingToV3Node;
private:
class PrivateMarker {};
friend class SettingV3;
public:
LegacyCustomSettingV3(PrivateMarker);
static Result<std::shared_ptr<LegacyCustomSettingV3>> parse(std::string const& key, std::string const& modID, matjson::Value const& json);
std::shared_ptr<SettingValue> getValue() const;
void setValue(std::shared_ptr<SettingValue> value);
bool load(matjson::Value const& json) override;
bool save(matjson::Value& json) const override;
SettingNodeV3* createNode(float width) override;
bool isDefaultValue() const override;
void reset() override;
std::optional<Setting> convertToLegacy() const override;
std::optional<std::shared_ptr<SettingValue>> convertToLegacyValue() const override;
};
class GEODE_DLL BoolSettingV3 final : public SettingBaseValueV3<bool> { class GEODE_DLL BoolSettingV3 final : public SettingBaseValueV3<bool> {
private: private:
class Impl; class Impl;
@ -404,9 +356,6 @@ namespace geode {
Result<> isValid(bool value) const override; Result<> isValid(bool value) const override;
SettingNodeV3* createNode(float width) override; SettingNodeV3* createNode(float width) override;
std::optional<Setting> convertToLegacy() const override;
std::optional<std::shared_ptr<SettingValue>> convertToLegacyValue() const override;
}; };
class GEODE_DLL IntSettingV3 final : public SettingBaseValueV3<int64_t> { class GEODE_DLL IntSettingV3 final : public SettingBaseValueV3<int64_t> {
@ -436,9 +385,6 @@ namespace geode {
bool isInputEnabled() const; bool isInputEnabled() const;
SettingNodeV3* createNode(float width) override; SettingNodeV3* createNode(float width) override;
std::optional<Setting> convertToLegacy() const override;
std::optional<std::shared_ptr<SettingValue>> convertToLegacyValue() const override;
}; };
class GEODE_DLL FloatSettingV3 final : public SettingBaseValueV3<double> { class GEODE_DLL FloatSettingV3 final : public SettingBaseValueV3<double> {
@ -468,9 +414,6 @@ namespace geode {
bool isInputEnabled() const; bool isInputEnabled() const;
SettingNodeV3* createNode(float width) override; SettingNodeV3* createNode(float width) override;
std::optional<Setting> convertToLegacy() const override;
std::optional<std::shared_ptr<SettingValue>> convertToLegacyValue() const override;
}; };
class GEODE_DLL StringSettingV3 final : public SettingBaseValueV3<std::string, std::string_view> { class GEODE_DLL StringSettingV3 final : public SettingBaseValueV3<std::string, std::string_view> {
@ -493,9 +436,6 @@ namespace geode {
std::optional<std::vector<std::string>> getEnumOptions() const; std::optional<std::vector<std::string>> getEnumOptions() const;
SettingNodeV3* createNode(float width) override; SettingNodeV3* createNode(float width) override;
std::optional<Setting> convertToLegacy() const override;
std::optional<std::shared_ptr<SettingValue>> convertToLegacyValue() const override;
}; };
class GEODE_DLL FileSettingV3 final : public SettingBaseValueV3<std::filesystem::path, std::filesystem::path const&> { class GEODE_DLL FileSettingV3 final : public SettingBaseValueV3<std::filesystem::path, std::filesystem::path const&> {
@ -519,9 +459,6 @@ namespace geode {
std::optional<std::vector<utils::file::FilePickOptions::Filter>> getFilters() const; std::optional<std::vector<utils::file::FilePickOptions::Filter>> getFilters() const;
SettingNodeV3* createNode(float width) override; SettingNodeV3* createNode(float width) override;
std::optional<Setting> convertToLegacy() const override;
std::optional<std::shared_ptr<SettingValue>> convertToLegacyValue() const override;
}; };
class GEODE_DLL Color3BSettingV3 final : public SettingBaseValueV3<cocos2d::ccColor3B> { class GEODE_DLL Color3BSettingV3 final : public SettingBaseValueV3<cocos2d::ccColor3B> {
@ -540,9 +477,6 @@ namespace geode {
Result<> isValid(cocos2d::ccColor3B value) const override; Result<> isValid(cocos2d::ccColor3B value) const override;
SettingNodeV3* createNode(float width) override; SettingNodeV3* createNode(float width) override;
std::optional<Setting> convertToLegacy() const override;
std::optional<std::shared_ptr<SettingValue>> convertToLegacyValue() const override;
}; };
class GEODE_DLL Color4BSettingV3 final : public SettingBaseValueV3<cocos2d::ccColor4B> { class GEODE_DLL Color4BSettingV3 final : public SettingBaseValueV3<cocos2d::ccColor4B> {
@ -561,9 +495,6 @@ namespace geode {
Result<> isValid(cocos2d::ccColor4B value) const override; Result<> isValid(cocos2d::ccColor4B value) const override;
SettingNodeV3* createNode(float width) override; SettingNodeV3* createNode(float width) override;
std::optional<Setting> convertToLegacy() const override;
std::optional<std::shared_ptr<SettingValue>> convertToLegacyValue() const override;
}; };
class GEODE_DLL SettingNodeV3 : public cocos2d::CCNode { class GEODE_DLL SettingNodeV3 : public cocos2d::CCNode {
@ -724,7 +655,7 @@ namespace geode {
public: public:
using Callback = void(std::shared_ptr<SettingV3>); using Callback = void(std::shared_ptr<SettingV3>);
ListenerResult handle(utils::MiniFunction<Callback> fn, SettingChangedEventV3* event); ListenerResult handle(std::function<Callback> fn, SettingChangedEventV3* event);
/** /**
* Listen to changes on a setting, or all settings * Listen to changes on a setting, or all settings
* @param modID Mod whose settings to listen to * @param modID Mod whose settings to listen to
@ -800,7 +731,7 @@ namespace geode {
}; };
template <class T> template <class T>
EventListener<SettingChangedFilterV3>* listenForSettingChanges(std::string_view settingKey, auto&& callback, Mod* mod = getMod()) { EventListener<SettingChangedFilterV3>* listenForSettingChangesV3(std::string_view settingKey, auto&& callback, Mod* mod = getMod()) {
using Ty = typename SettingTypeForValueType<T>::SettingType; using Ty = typename SettingTypeForValueType<T>::SettingType;
return new EventListener( return new EventListener(
[callback = std::move(callback)](std::shared_ptr<SettingV3> setting) { [callback = std::move(callback)](std::shared_ptr<SettingV3> setting) {
@ -811,11 +742,11 @@ namespace geode {
SettingChangedFilterV3(mod, std::string(settingKey)) SettingChangedFilterV3(mod, std::string(settingKey))
); );
} }
EventListener<SettingChangedFilterV3>* listenForSettingChanges(std::string_view settingKey, auto&& callback, Mod* mod = getMod()) { EventListener<SettingChangedFilterV3>* listenForSettingChangesV3(std::string_view settingKey, auto&& callback, Mod* mod = getMod()) {
using T = std::remove_cvref_t<utils::function::Arg<0, decltype(callback)>>; using T = std::remove_cvref_t<utils::function::Arg<0, decltype(callback)>>;
return listenForSettingChanges<T>(settingKey, std::move(callback), mod); return listenForSettingChangesV3<T>(settingKey, std::move(callback), mod);
} }
GEODE_DLL EventListener<SettingChangedFilterV3>* listenForAllSettingChanges( GEODE_DLL EventListener<SettingChangedFilterV3>* listenForAllSettingChangesV3(
std::function<void(std::shared_ptr<SettingV3>)> const& callback, std::function<void(std::shared_ptr<SettingV3>)> const& callback,
Mod* mod = getMod() Mod* mod = getMod()
); );

View file

@ -2,12 +2,13 @@
#include <Geode/platform/platform.hpp> #include <Geode/platform/platform.hpp>
#include <tulip/TulipHook.hpp> #include <tulip/TulipHook.hpp>
#include "../Prelude.hpp"
namespace geode::hook { namespace geode::hook {
/** /**
* Create a calling convention wrapper for a function. * Create a calling convention wrapper for a function.
*/ */
GEODE_DLL tulip::hook::Result<void*> createWrapper( GEODE_DLL Result<void*> createWrapper(
void* address, void* address,
tulip::hook::WrapperMetadata const& metadata tulip::hook::WrapperMetadata const& metadata
) noexcept; ) noexcept;

View file

@ -89,14 +89,11 @@ namespace geode {
constexpr std::string_view GEODE_MOD_EXTENSION = ".geode"; constexpr std::string_view GEODE_MOD_EXTENSION = ".geode";
class Mod; class Mod;
class Setting;
class Loader; class Loader;
class Hook; class Hook;
class VersionInfo; class VersionInfo;
class Unknown; class Unknown;
using unknownmemfn_t = void (Unknown::*)();
using unknownfn_t = void (*)();
namespace modifier { namespace modifier {
template <class, class> template <class, class>

View file

@ -1,6 +1,5 @@
#pragma once #pragma once
#include "../utils/MiniFunction.hpp"
#include "Traits.hpp" #include "Traits.hpp"
#include <Geode/loader/Loader.hpp> #include <Geode/loader/Loader.hpp>
@ -20,7 +19,7 @@ namespace geode::modifier {
class FieldContainer { class FieldContainer {
private: private:
std::vector<void*> m_containedFields; std::vector<void*> m_containedFields;
std::vector<utils::MiniFunction<void(void*)>> m_destructorFunctions; std::vector<std::function<void(void*)>> m_destructorFunctions;
public: public:
~FieldContainer() { ~FieldContainer() {
@ -40,9 +39,9 @@ namespace geode::modifier {
return m_containedFields.at(index); return m_containedFields.at(index);
} }
void* setField(size_t index, size_t size, utils::MiniFunction<void(void*)> destructor) { void* setField(size_t index, size_t size, std::function<void(void*)> destructor) {
m_containedFields.at(index) = operator new(size); m_containedFields.at(index) = operator new(size);
m_destructorFunctions.at(index) = destructor; m_destructorFunctions.at(index) = std::move(destructor);
return m_containedFields.at(index); return m_containedFields.at(index);
} }

View file

@ -104,8 +104,36 @@
} \ } \
} while (0); } while (0);
namespace geode::modifier { namespace geode {
class Priority {
public:
static inline constexpr int32_t First = -3000;
static inline constexpr int32_t VeryEarly = -2000;
static inline constexpr int32_t Early = -1000;
static inline constexpr int32_t Normal = 0;
static inline constexpr int32_t Late = 1000;
static inline constexpr int32_t VeryLate = 2000;
static inline constexpr int32_t Last = 3000;
static inline constexpr int32_t FirstPre = First;
static inline constexpr int32_t VeryEarlyPre = VeryEarly;
static inline constexpr int32_t EarlyPre = Early;
static inline constexpr int32_t NormalPre = Normal;
static inline constexpr int32_t LatePre = Late;
static inline constexpr int32_t VeryLatePre = VeryLate;
static inline constexpr int32_t LastPre = Last;
static inline constexpr int32_t FirstPost = Last;
static inline constexpr int32_t VeryEarlyPost = VeryLate;
static inline constexpr int32_t EarlyPost = Late;
static inline constexpr int32_t NormalPost = Normal;
static inline constexpr int32_t LatePost = Early;
static inline constexpr int32_t VeryLatePost = VeryEarly;
static inline constexpr int32_t LastPost = First;
};
}
namespace geode::modifier {
template <class Derived, class Base> template <class Derived, class Base>
class ModifyDerive; class ModifyDerive;
@ -114,22 +142,179 @@ namespace geode::modifier {
public: public:
std::map<std::string, std::shared_ptr<Hook>> m_hooks; std::map<std::string, std::shared_ptr<Hook>> m_hooks;
Result<Hook*> getHook(std::string const& name) { /// @brief Get a hook by name
if (m_hooks.find(name) == m_hooks.end()) { /// @param name The name of the hook to get
/// @returns Ok if the hook was found, Err if the hook was not found
Result<Hook*> getHook(std::string_view name) {
auto key = std::string(name);
if (m_hooks.find(key) == m_hooks.end()) {
return Err("Hook not in this modify"); return Err("Hook not in this modify");
} }
return Ok(m_hooks[name].get()); return Ok(m_hooks[key].get());
} }
Result<> setHookPriority(std::string const& name, int32_t priority) { /// @brief Set the priority of a hook
auto res = this->getHook(name); /// @param name The name of the hook to set the priority of
if (!res) { /// @param priority The priority to set the hook to
return Err(res.unwrapErr()); /// @returns Ok if the hook was found and the priority was set, Err if the hook was not found
} Result<> setHookPriority(std::string_view name, int32_t priority = Priority::Normal) {
res.unwrap()->setPriority(priority); GEODE_UNWRAP_INTO(auto hook, this->getHook(name));
hook->setPriority(priority);
return Ok(); return Ok();
} }
/// @brief Set the priority of a hook
/// @param name The name of the hook to set the priority of
/// @param priority The priority to set the hook to
/// @returns Ok if the hook was found and the priority was set, Err if the hook was not found
Result<> setHookPriorityPre(std::string_view name, int32_t priority = Priority::Normal) {
return this->setHookPriority(name, priority);
}
/// @brief Set the priority of a hook
/// @param name The name of the hook to set the priority of
/// @param priority The priority to set the hook to
/// @returns Ok if the hook was found and the priority was set, Err if the hook was not found
Result<> setHookPriorityPost(std::string_view name, int32_t priority = Priority::Normal) {
return this->setHookPriority(name, -priority);
}
/// @brief Set the priority of a hook to be after another hook in different mods
/// @param name The name of the hook to set the priority of
/// @param after The mod to set the priority after
/// @returns Ok if the hook was found and the priority was set, Err if the hook was not found
Result<> setHookPriorityAfter(std::string_view name, Mod* mod) {
GEODE_UNWRAP_INTO(auto hook, this->getHook(name));
auto func = [=](ModStateEvent* event){
auto hooks = mod->getHooks();
for (auto modHook : hooks) {
if (modHook->getAddress() != hook->getAddress()) continue;
auto priority = modHook->getPriority();
if (hook->getPriority() <= priority) {
hook->setPriority(priority + 1);
}
}
return ListenerResult::Propagate;
};
if (mod->isEnabled()) {
func(nullptr);
}
else {
new EventListener(func, ModStateFilter(mod, ModEventType::Loaded));
}
return Ok();
}
/// @brief Set the priority of a hook to be after another hook in different mods
/// @param name The name of the hook to set the priority of
/// @param after The mod id of the mod to set the priority after
/// @returns Ok if the hook was found and the priority was set, Err if the hook was not found
Result<> setHookPriorityAfter(std::string_view name, std::string_view after) {
auto mod = Loader::get()->getInstalledMod(std::string(after));
if (!mod) return Err("Mod not found");
return this->setHookPriorityAfter(name, mod);
}
/// @brief Set the priority of a hook to be before another hook in different mods
/// @param name The name of the hook to set the priority of
/// @param before The mod to set the priority before
/// @returns Ok if the hook was found and the priority was set, Err if the hook was not found
Result<> setHookPriorityBefore(std::string_view name, Mod* mod) {
GEODE_UNWRAP_INTO(auto hook, this->getHook(name));
auto func = [=](ModStateEvent* event){
auto hooks = mod->getHooks();
for (auto modHook : hooks) {
if (modHook->getAddress() != hook->getAddress()) continue;
auto priority = modHook->getPriority();
if (hook->getPriority() >= priority) {
hook->setPriority(priority - 1);
}
}
return ListenerResult::Propagate;
};
if (mod->isEnabled()) {
func(nullptr);
}
else {
new EventListener(func, ModStateFilter(mod, ModEventType::Loaded));
}
return Ok();
}
/// @brief Set the priority of a hook to be before another hook in different mods
/// @param name The name of the hook to set the priority of
/// @param before The mod id of the mod to set the priority before
/// @returns Ok if the hook was found and the priority was set, Err if the hook was not found
Result<> setHookPriorityBefore(std::string_view name, std::string_view before) {
auto mod = Loader::get()->getInstalledMod(std::string(before));
if (!mod) return Err("Mod not found");
return this->setHookPriorityBefore(name, mod);
}
/// @brief Set the priority of a hook to be after another hook in different mods
/// @param name The name of the hook to set the priority of
/// @param after The mod id of the mod to set the priority after
/// @returns Ok if the hook was found and the priority was set, Err if the hook was not found
Result<> setHookPriorityAfterPre(std::string_view name, std::string_view after) {
return this->setHookPriorityAfter(name, after);
}
/// @brief Set the priority of a hook to be after another hook in different mods
/// @param name The name of the hook to set the priority of
/// @param before The mod to set the priority after
/// @returns Ok if the hook was found and the priority was set, Err if the hook was not found
Result<> setHookPriorityAfterPre(std::string_view name, Mod* mod) {
return this->setHookPriorityAfter(name, mod);
}
/// @brief Set the priority of a hook to be before another hook in different mods
/// @param name The name of the hook to set the priority of
/// @param before The mod id of the mod to set the priority before
/// @returns Ok if the hook was found and the priority was set, Err if the hook was not found
Result<> setHookPriorityBeforePre(std::string_view name, std::string_view before) {
return this->setHookPriorityBefore(name, before);
}
/// @brief Set the priority of a hook to be before another hook in different mods
/// @param name The name of the hook to set the priority of
/// @param before The mod to set the priority before
/// @returns Ok if the hook was found and the priority was set, Err if the hook was not found
Result<> setHookPriorityBeforePre(std::string_view name, Mod* mod) {
return this->setHookPriorityBefore(name, mod);
}
/// @brief Set the priority of a hook to be after another hook in different mods
/// @param name The name of the hook to set the priority of
/// @param after The mod id of the mod to set the priority after
/// @returns Ok if the hook was found and the priority was set, Err if the hook was not found
Result<> setHookPriorityAfterPost(std::string_view name, std::string_view after) {
return this->setHookPriorityBefore(name, after);
}
/// @brief Set the priority of a hook to be after another hook in different mods
/// @param name The name of the hook to set the priority of
/// @param before The mod to set the priority after
/// @returns Ok if the hook was found and the priority was set, Err if the hook was not found
Result<> setHookPriorityAfterPost(std::string_view name, Mod* mod) {
return this->setHookPriorityBefore(name, mod);
}
/// @brief Set the priority of a hook to be before another hook in different mods
/// @param name The name of the hook to set the priority of
/// @param before The mod id of the mod to set the priority before
/// @returns Ok if the hook was found and the priority was set, Err if the hook was not found
Result<> setHookPriorityBeforePost(std::string_view name, std::string_view before) {
return this->setHookPriorityAfter(name, before);
}
/// @brief Set the priority of a hook to be before another hook in different mods
/// @param name The name of the hook to set the priority of
/// @param before The mod to set the priority before
/// @returns Ok if the hook was found and the priority was set, Err if the hook was not found
Result<> setHookPriorityBeforePost(std::string_view name, Mod* mod) {
return this->setHookPriorityAfter(name, mod);
}
// unordered_map<handles> idea // unordered_map<handles> idea
ModifyBase() { ModifyBase() {
struct EboCheck : ModifyDerived::Base { struct EboCheck : ModifyDerived::Base {
@ -158,7 +343,7 @@ namespace geode::modifier {
for (auto& [uuid, hook] : m_hooks) { for (auto& [uuid, hook] : m_hooks) {
auto res = Mod::get()->claimHook(hook); auto res = Mod::get()->claimHook(hook);
if (!res) { if (!res) {
log::error("Failed to claim hook {}: {}", hook->getDisplayName(), res.error()); log::error("Failed to claim hook {}: {}", hook->getDisplayName(), res.unwrapErr());
} }
else { else {
added.push_back(uuid); added.push_back(uuid);

View file

@ -11,6 +11,8 @@
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) && !defined(__CYGWIN__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) && !defined(__CYGWIN__) #if defined(WIN32) || defined(_WIN32) || defined(__WIN32) && !defined(__CYGWIN__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) && !defined(__CYGWIN__)
#define GEODE_WINDOWS(...) __VA_ARGS__ #define GEODE_WINDOWS(...) __VA_ARGS__
#define GEODE_DESKTOP(...) __VA_ARGS__
#define GEODE_MOBILE(...)
#define GEODE_IS_WINDOWS #define GEODE_IS_WINDOWS
#define GEODE_IS_DESKTOP #define GEODE_IS_DESKTOP
#define GEODE_PLATFORM_NAME "Windows" #define GEODE_PLATFORM_NAME "Windows"
@ -43,6 +45,8 @@
#define GEODE_INTEL_MAC(...) #define GEODE_INTEL_MAC(...)
#define GEODE_ARM_MAC(...) #define GEODE_ARM_MAC(...)
#define GEODE_IOS(...) __VA_ARGS__ #define GEODE_IOS(...) __VA_ARGS__
#define GEODE_DESKTOP(...)
#define GEODE_MOBILE(...) __VA_ARGS__
#define GEODE_IS_IOS #define GEODE_IS_IOS
#define GEODE_IS_MOBILE #define GEODE_IS_MOBILE
#define GEODE_PLATFORM_NAME "iOS" #define GEODE_PLATFORM_NAME "iOS"
@ -53,6 +57,8 @@
#else #else
#define GEODE_IOS(...) #define GEODE_IOS(...)
#define GEODE_MACOS(...) __VA_ARGS__ #define GEODE_MACOS(...) __VA_ARGS__
#define GEODE_DESKTOP(...) __VA_ARGS__
#define GEODE_MOBILE(...)
#define GEODE_IS_MACOS #define GEODE_IS_MACOS
#define GEODE_IS_DESKTOP #define GEODE_IS_DESKTOP
#define GEODE_PLATFORM_EXTENSION ".dylib" #define GEODE_PLATFORM_EXTENSION ".dylib"
@ -84,6 +90,8 @@
// Android // Android
#if defined(__ANDROID__) #if defined(__ANDROID__)
#define GEODE_ANDROID(...) __VA_ARGS__ #define GEODE_ANDROID(...) __VA_ARGS__
#define GEODE_MOBILE(...) __VA_ARGS__
#define GEODE_DESKTOP(...)
#define GEODE_IS_ANDROID #define GEODE_IS_ANDROID
#define GEODE_IS_MOBILE #define GEODE_IS_MOBILE
#define GEODE_CALL #define GEODE_CALL

View file

@ -116,14 +116,22 @@ namespace geode {
public: public:
// todo in v4: make these flags and add archless Mac and Android as well as Desktop and Mobile and remove Linux // todo in v4: make these flags and add archless Mac and Android as well as Desktop and Mobile and remove Linux
enum { enum {
Unknown = -1, Unknown = 0b000000,
Windows = 0, Windows = 0b000001,
MacIntel = 1, Android32 = 0b000010,
MacArm = 2, Android64 = 0b000100,
iOS = 3, MacIntel = 0b001000,
Android32 = 4, MacArm = 0b010000,
Android64 = 5, iOS = 0b100000,
Linux = 6, Android = Android32 | Android64,
Mac = MacIntel | MacArm,
Apple = Mac | iOS,
X64 = MacIntel | Windows,
X86 = Unknown,
ArmV7 = Android32,
ArmV8 = Android64 | MacArm | iOS,
Desktop = Windows | Mac,
Mobile = Android | iOS,
}; };
using Type = decltype(Unknown); using Type = decltype(Unknown);
@ -190,7 +198,6 @@ namespace geode {
case iOS: return "iOS"; case iOS: return "iOS";
case Android32: return "Android32"; case Android32: return "Android32";
case Android64: return "Android64"; case Android64: return "Android64";
case Linux: return "Linux";
default: break; default: break;
} }
return "Undefined"; return "Undefined";
@ -206,7 +213,6 @@ namespace geode {
case iOS: return "ios"; case iOS: return "ios";
case Android32: return ignoreArch ? "android" : "android32"; case Android32: return ignoreArch ? "android" : "android32";
case Android64: return ignoreArch ? "android" : "android64"; case Android64: return ignoreArch ? "android" : "android64";
case Linux: return "linux";
default: break; default: break;
} }
return "undefined"; return "undefined";

View file

@ -13,7 +13,7 @@ namespace geode {
virtual void updateColor(cocos2d::ccColor4B const& color) {} virtual void updateColor(cocos2d::ccColor4B const& color) {}
}; };
// todo in v4: make this pimpl and maybe use events over the delegate? // todo in v4: maybe use events over the delegate?
// thing with events is that if you just filter via ColorPickPopup* it // thing with events is that if you just filter via ColorPickPopup* it
// won't work unless you automatically detach the filter when closing the // won't work unless you automatically detach the filter when closing the
// popup (otherwise opening another popup really quickly will just be // popup (otherwise opening another popup really quickly will just be
@ -24,18 +24,8 @@ namespace geode {
public cocos2d::extension::ColorPickerDelegate, public cocos2d::extension::ColorPickerDelegate,
public TextInputDelegate { public TextInputDelegate {
protected: protected:
cocos2d::ccColor4B m_color; class Impl;
cocos2d::ccColor4B m_originalColor; std::unique_ptr<Impl> m_impl;
cocos2d::extension::CCControlColourPicker* m_picker;
Slider* m_opacitySlider = nullptr;
TextInput* m_rInput;
TextInput* m_gInput;
TextInput* m_bInput;
TextInput* m_hexInput;
TextInput* m_opacityInput = nullptr;
ColorPickPopupDelegate* m_delegate = nullptr;
cocos2d::CCSprite* m_newColorSpr;
CCMenuItemSpriteExtra* m_resetBtn;
static constexpr auto TAG_OPACITY_INPUT = 0; static constexpr auto TAG_OPACITY_INPUT = 0;
static constexpr auto TAG_R_INPUT = 1; static constexpr auto TAG_R_INPUT = 1;
@ -43,10 +33,13 @@ namespace geode {
static constexpr auto TAG_B_INPUT = 3; static constexpr auto TAG_B_INPUT = 3;
static constexpr auto TAG_HEX_INPUT = 4; static constexpr auto TAG_HEX_INPUT = 4;
ColorPickPopup();
~ColorPickPopup();
bool setup(cocos2d::ccColor4B const& color, bool isRGBA) override; bool setup(cocos2d::ccColor4B const& color, bool isRGBA) override;
void onOpacitySlider(cocos2d::CCObject* sender); void onOpacitySlider(cocos2d::CCObject* sender);
void onReset(cocos2d::CCObject* sender); void onReset(cocos2d::CCObject* sender);
void onClose(cocos2d::CCObject* sender) override;
void textChanged(CCTextInputNode* input) override; void textChanged(CCTextInputNode* input) override;
void colorValueChanged(cocos2d::ccColor3B color) override; void colorValueChanged(cocos2d::ccColor3B color) override;

View file

@ -30,7 +30,7 @@ namespace geode {
std::optional<std::string> m_targetID; std::optional<std::string> m_targetID;
public: public:
ListenerResult handle(utils::MiniFunction<Callback> fn, AEnterLayerEvent* event); ListenerResult handle(std::function<Callback> fn, AEnterLayerEvent* event);
AEnterLayerFilter( AEnterLayerFilter(
std::optional<std::string> const& id std::optional<std::string> const& id
@ -63,7 +63,7 @@ namespace geode {
std::optional<std::string> m_targetID; std::optional<std::string> m_targetID;
public: public:
ListenerResult handle(utils::MiniFunction<Callback> fn, EnterLayerEvent<N>* event) { ListenerResult handle(std::function<Callback> fn, EnterLayerEvent<N>* event) {
if (m_targetID == event->getID()) { if (m_targetID == event->getID()) {
fn(static_cast<T*>(event)); fn(static_cast<T*>(event));
} }

View file

@ -133,11 +133,6 @@ namespace geode {
*/ */
GEODE_DLL void openSupportPopup(Mod* mod); GEODE_DLL void openSupportPopup(Mod* mod);
GEODE_DLL void openSupportPopup(ModMetadata const& metadata); GEODE_DLL void openSupportPopup(ModMetadata const& metadata);
/**
* Open the store page for a mod (if it exists)
*/
[[deprecated("Will be removed, use openInfoPopup instead")]]
GEODE_DLL void openIndexPopup(Mod* mod);
/** /**
* Open the settings popup for a mod (if it has any settings) * Open the settings popup for a mod (if it has any settings)
*/ */
@ -160,6 +155,10 @@ namespace geode {
* Create a logo sprite for a mod * Create a logo sprite for a mod
*/ */
GEODE_DLL cocos2d::CCNode* createModLogo(Mod* mod); GEODE_DLL cocos2d::CCNode* createModLogo(Mod* mod);
/**
* Create a logo sprite for a mod from a .geode file
*/
GEODE_DLL cocos2d::CCNode* createModLogo(std::filesystem::path const& geodePackage);
/** /**
* Create a logo sprite for a mod downloaded from the Geode servers. The * Create a logo sprite for a mod downloaded from the Geode servers. The
* logo is initially a loading circle, with the actual sprite downloaded * logo is initially a loading circle, with the actual sprite downloaded

View file

@ -1,41 +0,0 @@
#pragma once
#include <Geode/DefaultInclude.hpp>
#include <Geode/binding/CCTextInputNode.hpp>
#include <cocos2d.h>
namespace geode {
class GEODE_DLL InputNode : public cocos2d::CCMenuItem {
protected:
cocos2d::extension::CCScale9Sprite* m_bgSprite;
CCTextInputNode* m_input;
bool init(float, float, char const*, char const*, std::string const&, int);
bool init(float, char const*, char const*, std::string const&, int);
public:
[[deprecated("Use geode::TextInput from the ui/TextInput.hpp header instead")]]
static InputNode* create(
float width, char const* placeholder, char const* fontFile, std::string const& filter,
int limit
);
[[deprecated("Use geode::TextInput from the ui/TextInput.hpp header instead")]]
static InputNode* create(
float width, char const* placeholder, std::string const& filter, int limit
);
[[deprecated("Use geode::TextInput from the ui/TextInput.hpp header instead")]]
static InputNode* create(float width, char const* placeholder, std::string const& filter);
[[deprecated("Use geode::TextInput from the ui/TextInput.hpp header instead")]]
static InputNode* create(float width, char const* placeholder, char const* fontFile);
[[deprecated("Use geode::TextInput from the ui/TextInput.hpp header instead")]]
static InputNode* create(float width, char const* placeholder);
CCTextInputNode* getInput() const;
cocos2d::extension::CCScale9Sprite* getBG() const;
void setEnabled(bool enabled) override;
void setString(std::string const&);
std::string getString();
};
}

View file

@ -1,15 +1,11 @@
#pragma once #pragma once
#include "../include/ccMacros.h" #include <cocos2d.h>
#include "../cocoa/CCAffineTransform.h"
#include "../cocoa/CCArray.h"
#include <Geode/platform/platform.hpp> #include <Geode/platform/platform.hpp>
#include <optional> #include <optional>
#include <memory> #include <memory>
NS_CC_BEGIN namespace geode {
class CCNode;
#pragma warning(push) #pragma warning(push)
#pragma warning(disable: 4275) #pragma warning(disable: 4275)
@ -21,9 +17,9 @@ class CCNode;
* RowLayout, ColumnLayout, and GridLayout, but if you need a different kind * RowLayout, ColumnLayout, and GridLayout, but if you need a different kind
* of layout you can inherit from the Layout class. * of layout you can inherit from the Layout class.
*/ */
class GEODE_DLL Layout : public CCObject { class GEODE_DLL Layout : public cocos2d::CCObject {
protected: protected:
CCArray* getNodesToPosition(CCNode* forNode) const; cocos2d::CCArray* getNodesToPosition(cocos2d::CCNode* forNode) const;
bool m_ignoreInvisibleChildren = false; bool m_ignoreInvisibleChildren = false;
@ -35,12 +31,12 @@ public:
* respected as a boundary the layout shouldn't overflow. The node may be * respected as a boundary the layout shouldn't overflow. The node may be
* rescaled to better fit its contents * rescaled to better fit its contents
*/ */
virtual void apply(CCNode* on) = 0; virtual void apply(cocos2d::CCNode* on) = 0;
/** /**
* Get how much space this layout would like to take up for a given target * Get how much space this layout would like to take up for a given target
*/ */
virtual CCSize getSizeHint(CCNode* on) const = 0; virtual cocos2d::CCSize getSizeHint(cocos2d::CCNode* on) const = 0;
void ignoreInvisibleChildren(bool ignore); void ignoreInvisibleChildren(bool ignore);
bool isIgnoreInvisibleChildren() const; bool isIgnoreInvisibleChildren() const;
@ -48,7 +44,7 @@ public:
virtual ~Layout() = default; virtual ~Layout() = default;
}; };
class GEODE_DLL LayoutOptions : public CCObject { class GEODE_DLL LayoutOptions : public cocos2d::CCObject {
public: public:
virtual ~LayoutOptions() = default; virtual ~LayoutOptions() = default;
}; };
@ -125,20 +121,6 @@ public:
int getScalePriority() const; int getScalePriority() const;
std::optional<AxisAlignment> getCrossAxisAlignment() const; std::optional<AxisAlignment> getCrossAxisAlignment() const;
/**
* Set the maximum scale this node can be if it's contained in an
* auto-scaled layout. Default is 1
*/
[[deprecated("Use AxisLayoutOptions::setScaleLimits")]]
AxisLayoutOptions* setMaxScale(float scale);
/**
* Set the minimum scale this node can be if it's contained in an
* auto-scaled layout. Default is AXISLAYOUT_DEFAULT_MIN_SCALE
*/
[[deprecated("Use AxisLayoutOptions::setScaleLimits")]]
AxisLayoutOptions* setMinScale(float scale);
/** /**
* Set the limits to what the node can be scaled to. Passing `std::nullopt` * Set the limits to what the node can be scaled to. Passing `std::nullopt`
* uses the parent layout's default min / max scales * uses the parent layout's default min / max scales
@ -253,8 +235,8 @@ public:
virtual ~AxisLayout(); virtual ~AxisLayout();
void apply(CCNode* on) override; void apply(cocos2d::CCNode* on) override;
CCSize getSizeHint(CCNode* on) const override; cocos2d::CCSize getSizeHint(cocos2d::CCNode* on) const override;
Axis getAxis() const; Axis getAxis() const;
AxisAlignment getAxisAlignment() const; AxisAlignment getAxisAlignment() const;
@ -380,16 +362,16 @@ enum class Anchor {
class GEODE_DLL AnchorLayoutOptions : public LayoutOptions { class GEODE_DLL AnchorLayoutOptions : public LayoutOptions {
protected: protected:
Anchor m_anchor = Anchor::Center; Anchor m_anchor = Anchor::Center;
CCPoint m_offset = CCPointZero; cocos2d::CCPoint m_offset = cocos2d::CCPointZero;
public: public:
static AnchorLayoutOptions* create(); static AnchorLayoutOptions* create();
Anchor getAnchor() const; Anchor getAnchor() const;
CCPoint getOffset() const; cocos2d::CCPoint getOffset() const;
AnchorLayoutOptions* setAnchor(Anchor anchor); AnchorLayoutOptions* setAnchor(Anchor anchor);
AnchorLayoutOptions* setOffset(CCPoint const& offset); AnchorLayoutOptions* setOffset(cocos2d::CCPoint const& offset);
}; };
/** /**
@ -402,8 +384,8 @@ class GEODE_DLL AnchorLayout : public Layout {
public: public:
static AnchorLayout* create(); static AnchorLayout* create();
void apply(CCNode* on) override; void apply(cocos2d::CCNode* on) override;
CCSize getSizeHint(CCNode* on) const override; cocos2d::CCSize getSizeHint(cocos2d::CCNode* on) const override;
/** /**
* Get a position according to anchoring rules, with the same algorithm as * Get a position according to anchoring rules, with the same algorithm as
@ -413,7 +395,7 @@ public:
* @param offset Offset from the anchor * @param offset Offset from the anchor
* @returns A position in `in` for the anchored and offsetted location * @returns A position in `in` for the anchored and offsetted location
*/ */
static CCPoint getAnchoredPosition(CCNode* in, Anchor anchor, CCPoint const& offset); static cocos2d::CCPoint getAnchoredPosition(cocos2d::CCNode* in, Anchor anchor, cocos2d::CCPoint const& offset);
}; };
/** /**
@ -421,7 +403,7 @@ public:
* Basically main use case is for FLAlertLayers (setting the size of the * Basically main use case is for FLAlertLayers (setting the size of the
* background and `m_buttonMenu` based on `m_mainLayer`) * background and `m_buttonMenu` based on `m_mainLayer`)
*/ */
class GEODE_DLL CopySizeLayout : public cocos2d::AnchorLayout { class GEODE_DLL CopySizeLayout : public AnchorLayout {
protected: protected:
cocos2d::CCArray* m_targets; cocos2d::CCArray* m_targets;
@ -445,4 +427,4 @@ public:
#pragma warning(pop) #pragma warning(pop)
NS_CC_END }

View file

@ -11,13 +11,13 @@ namespace geode {
class GEODE_DLL MDPopup : class GEODE_DLL MDPopup :
public Popup< public Popup<
std::string const&, std::string const&, char const*, char const*, std::string const&, std::string const&, char const*, char const*,
utils::MiniFunction<void(bool)>> { std::function<void(bool)>> {
protected: protected:
utils::MiniFunction<void(bool)> m_onClick = nullptr; std::function<void(bool)> m_onClick = nullptr;
bool setup( bool setup(
std::string const& title, std::string const& info, char const* btn1, char const* btn2, std::string const& title, std::string const& info, char const* btn1, char const* btn2,
utils::MiniFunction<void(bool)> onClick std::function<void(bool)> onClick
) override; ) override;
void onBtn(CCObject*); void onBtn(CCObject*);
@ -27,7 +27,7 @@ namespace geode {
public: public:
static MDPopup* create( static MDPopup* create(
std::string const& title, std::string const& content, char const* btn1, std::string const& title, std::string const& content, char const* btn1,
char const* btn2 = nullptr, utils::MiniFunction<void(bool)> onClick = nullptr char const* btn2 = nullptr, std::function<void(bool)> onClick = nullptr
); );
}; };
} }

View file

@ -2,8 +2,8 @@
#include <Geode/binding/CCMenuItemSpriteExtra.hpp> #include <Geode/binding/CCMenuItemSpriteExtra.hpp>
#include <Geode/binding/FLAlertLayer.hpp> #include <Geode/binding/FLAlertLayer.hpp>
#include <Geode/utils/MiniFunction.hpp>
#include <Geode/utils/cocos.hpp> #include <Geode/utils/cocos.hpp>
#include <Geode/ui/Layout.hpp>
namespace geode { namespace geode {
template <class... InitArgs> template <class... InitArgs>
@ -51,7 +51,7 @@ namespace geode {
} }
public: public:
ListenerResult handle(utils::MiniFunction<Callback> fn, CloseEvent* event) { ListenerResult handle(std::function<Callback> fn, CloseEvent* event) {
if (event->getPopup() == m_impl->popup) { if (event->getPopup() == m_impl->popup) {
fn(event); fn(event);
} }
@ -104,7 +104,7 @@ namespace geode {
m_mainLayer->setPosition(winSize / 2); m_mainLayer->setPosition(winSize / 2);
m_mainLayer->setContentSize(m_size); m_mainLayer->setContentSize(m_size);
m_mainLayer->setLayout( m_mainLayer->setLayout(
cocos2d::CopySizeLayout::create() geode::CopySizeLayout::create()
->add(m_buttonMenu) ->add(m_buttonMenu)
->add(m_bgSprite) ->add(m_bgSprite)
); );
@ -119,7 +119,7 @@ namespace geode {
closeSpr, this, (cocos2d::SEL_MenuHandler)(&Popup::onClose) closeSpr, this, (cocos2d::SEL_MenuHandler)(&Popup::onClose)
); );
if (dynamic) { if (dynamic) {
m_buttonMenu->addChildAtPosition(m_closeBtn, cocos2d::Anchor::TopLeft, { 3.f, -3.f }); m_buttonMenu->addChildAtPosition(m_closeBtn, geode::Anchor::TopLeft, { 3.f, -3.f });
} }
else { else {
m_closeBtn->setPosition(-m_size.width / 2 + 3.f, m_size.height / 2 - 3.f); m_closeBtn->setPosition(-m_size.width / 2 + 3.f, m_size.height / 2 - 3.f);
@ -137,14 +137,6 @@ namespace geode {
} }
protected: protected:
[[deprecated("Use Popup::initAnchored instead, as it has more reasonable menu and layer content sizes")]]
bool init(
float width, float height, InitArgs... args, char const* bg = "GJ_square01.png",
cocos2d::CCRect bgRect = { 0, 0, 80, 80 }
) {
return this->initBase(width, height, std::forward<InitArgs>(args)..., bg, bgRect, false);
}
/** /**
* Init with AnchorLayout and the content size of `m_buttonMenu` and * Init with AnchorLayout and the content size of `m_buttonMenu` and
* `m_bgSprite` being tied to the size of `m_mainLayer` (rather than * `m_bgSprite` being tied to the size of `m_mainLayer` (rather than
@ -185,7 +177,7 @@ namespace geode {
m_title = cocos2d::CCLabelBMFont::create(title.c_str(), font); m_title = cocos2d::CCLabelBMFont::create(title.c_str(), font);
m_title->setZOrder(2); m_title->setZOrder(2);
if (m_dynamic) { if (m_dynamic) {
m_mainLayer->addChildAtPosition(m_title, cocos2d::Anchor::Top, ccp(0, -offset)); m_mainLayer->addChildAtPosition(m_title, geode::Anchor::Top, ccp(0, -offset));
} }
else { else {
auto winSize = cocos2d::CCDirector::get()->getWinSize(); auto winSize = cocos2d::CCDirector::get()->getWinSize();
@ -221,21 +213,21 @@ namespace geode {
GEODE_DLL FLAlertLayer* createQuickPopup( GEODE_DLL FLAlertLayer* createQuickPopup(
char const* title, std::string const& content, char const* btn1, char const* btn2, char const* title, std::string const& content, char const* btn1, char const* btn2,
utils::MiniFunction<void(FLAlertLayer*, bool)> selected, bool doShow = true std::function<void(FLAlertLayer*, bool)> selected, bool doShow = true
); );
GEODE_DLL FLAlertLayer* createQuickPopup( GEODE_DLL FLAlertLayer* createQuickPopup(
char const* title, std::string const& content, char const* btn1, char const* btn2, char const* title, std::string const& content, char const* btn1, char const* btn2,
float width, utils::MiniFunction<void(FLAlertLayer*, bool)> selected, bool doShow = true float width, std::function<void(FLAlertLayer*, bool)> selected, bool doShow = true
); );
GEODE_DLL FLAlertLayer* createQuickPopup( GEODE_DLL FLAlertLayer* createQuickPopup(
char const* title, std::string const& content, char const* btn1, char const* btn2, char const* title, std::string const& content, char const* btn1, char const* btn2,
utils::MiniFunction<void(FLAlertLayer*, bool)> selected, bool doShow, bool cancelledByEscape std::function<void(FLAlertLayer*, bool)> selected, bool doShow, bool cancelledByEscape
); );
GEODE_DLL FLAlertLayer* createQuickPopup( GEODE_DLL FLAlertLayer* createQuickPopup(
char const* title, std::string const& content, char const* btn1, char const* btn2, char const* title, std::string const& content, char const* btn1, char const* btn2,
float width, utils::MiniFunction<void(FLAlertLayer*, bool)> selected, bool doShow, bool cancelledByEscape float width, std::function<void(FLAlertLayer*, bool)> selected, bool doShow, bool cancelledByEscape
); );
} }

View file

@ -8,6 +8,8 @@
#include <Geode/utils/cocos.hpp> #include <Geode/utils/cocos.hpp>
namespace geode { namespace geode {
struct SceneSwitch;
class GEODE_DLL SceneManager final { class GEODE_DLL SceneManager final {
protected: protected:
std::vector<Ref<cocos2d::CCNode>> m_persistedNodes; std::vector<Ref<cocos2d::CCNode>> m_persistedNodes;
@ -15,6 +17,10 @@ namespace geode {
virtual ~SceneManager(); virtual ~SceneManager();
void willSwitchToScene(cocos2d::CCScene* scene);
friend struct SceneSwitch;
public: public:
static SceneManager* get(); static SceneManager* get();
@ -34,9 +40,5 @@ namespace geode {
* Gets a span of the persisted nodes. To add new nodes to the list, use keepAcrossScenes. * Gets a span of the persisted nodes. To add new nodes to the list, use keepAcrossScenes.
*/ */
std::span<Ref<cocos2d::CCNode> const> getPersistedNodes(); std::span<Ref<cocos2d::CCNode> const> getPersistedNodes();
// This method should only be called by geode itself
// TODO(v4): hide this
void willSwitchToScene(cocos2d::CCScene* scene);
}; };
} }

View file

@ -14,13 +14,13 @@ namespace geode {
protected: protected:
std::vector<T> m_list; std::vector<T> m_list;
size_t m_index = 0; size_t m_index = 0;
utils::MiniFunction<void(T const&, size_t)> m_onChange; std::function<void(T const&, size_t)> m_onChange;
cocos2d::CCLabelBMFont* m_label; cocos2d::CCLabelBMFont* m_label;
CCMenuItemSpriteExtra* m_prevBtn; CCMenuItemSpriteExtra* m_prevBtn;
CCMenuItemSpriteExtra* m_nextBtn; CCMenuItemSpriteExtra* m_nextBtn;
bool init( bool init(
float width, std::vector<T> const& list, utils::MiniFunction<void(T const&, size_t)> onChange float width, std::vector<T> const& list, std::function<void(T const&, size_t)> onChange
) { ) {
if (!cocos2d::CCMenu::init()) return false; if (!cocos2d::CCMenu::init()) return false;
@ -94,7 +94,7 @@ namespace geode {
public: public:
static SelectList* create( static SelectList* create(
float width, std::vector<T> const& list, utils::MiniFunction<void(T const&, size_t)> onChange float width, std::vector<T> const& list, std::function<void(T const&, size_t)> onChange
) { ) {
auto ret = new SelectList(); auto ret = new SelectList();
if (ret->init(width, list, onChange)) { if (ret->init(width, list, onChange)) {

View file

@ -1,8 +1,8 @@
#pragma once #pragma once
#include "CCNode.h" #include <cocos2d.h>
NS_CC_BEGIN namespace geode {
#pragma warning(push) #pragma warning(push)
#pragma warning(disable: 4275) #pragma warning(disable: 4275)
@ -22,7 +22,7 @@ NS_CC_BEGIN
* @note If you want to specify a minimum width for a SpacerNode, add * @note If you want to specify a minimum width for a SpacerNode, add
* AxisLayoutOptions for it and use setLength * AxisLayoutOptions for it and use setLength
*/ */
class GEODE_DLL SpacerNode : public CCNode { class GEODE_DLL SpacerNode : public cocos2d::CCNode {
protected: protected:
size_t m_grow; size_t m_grow;
@ -61,9 +61,9 @@ public:
*/ */
class GEODE_DLL SpacerNodeChild : public SpacerNode { class GEODE_DLL SpacerNodeChild : public SpacerNode {
protected: protected:
CCNode* m_child = nullptr; cocos2d::CCNode* m_child = nullptr;
bool init(CCNode* child, size_t grow); bool init(cocos2d::CCNode* child, size_t grow);
public: public:
/** /**
@ -73,11 +73,11 @@ public:
* factors (akin to CSS flew grow) * factors (akin to CSS flew grow)
* @param grow The grow factor for this node. Default is 1 * @param grow The grow factor for this node. Default is 1
*/ */
static SpacerNodeChild* create(CCNode* child, size_t grow = 1); static SpacerNodeChild* create(cocos2d::CCNode* child, size_t grow = 1);
void setContentSize(CCSize const& size) override; void setContentSize(cocos2d::CCSize const& size) override;
}; };
#pragma warning(pop) #pragma warning(pop)
NS_CC_END }

View file

@ -24,34 +24,34 @@ namespace geode {
*/ */
class GEODE_DLL SimpleTextArea : public cocos2d::CCNode { class GEODE_DLL SimpleTextArea : public cocos2d::CCNode {
public: public:
static SimpleTextArea* create(const std::string& text, const std::string& font = "chatFont.fnt", const float scale = 1); static SimpleTextArea* create(const std::string& text, const std::string& font = "chatFont.fnt", float scale = 1.0f);
static SimpleTextArea* create(const std::string& text, const std::string& font, const float scale, const float width); static SimpleTextArea* create(const std::string& text, const std::string& font, float scale, float width);
void setFont(const std::string& font); void setFont(const std::string& font);
std::string getFont(); std::string getFont();
void setColor(const cocos2d::ccColor4B& color); void setColor(const cocos2d::ccColor4B& color);
cocos2d::ccColor4B getColor(); cocos2d::ccColor4B getColor();
void setAlignment(const cocos2d::CCTextAlignment alignment); void setAlignment(cocos2d::CCTextAlignment alignment);
cocos2d::CCTextAlignment getAlignment(); cocos2d::CCTextAlignment getAlignment();
void setWrappingMode(const WrappingMode mode); void setWrappingMode(WrappingMode mode);
WrappingMode getWrappingMode(); WrappingMode getWrappingMode();
void setText(const std::string& text); void setText(const std::string& text);
std::string getText(); std::string getText();
void setMaxLines(const size_t maxLines); void setMaxLines(size_t maxLines);
size_t getMaxLines(); size_t getMaxLines();
void setWidth(const float width); void setWidth(float width);
float getWidth(); float getWidth();
void setScale(const float scale) override; void setScale(float scale) override;
float getScale() override; float getScale() override;
void setLinePadding(const float padding); void setLinePadding(float padding);
float getLinePadding(); float getLinePadding();
std::vector<cocos2d::CCLabelBMFont*> getLines(); std::vector<cocos2d::CCLabelBMFont*> getLines();
float getHeight(); float getHeight();
float getLineHeight(); float getLineHeight();
private: private:
static SimpleTextArea* create(const std::string& font, const std::string& text, const float scale, const float width, const bool artificialWidth); static SimpleTextArea* create(const std::string& font, const std::string& text, float scale, float width, const bool artificialWidth);
bool init(const std::string& font, const std::string& text, const float scale, const float width, const bool artificialWidth); bool init(const std::string& font, const std::string& text, float scale, float width, const bool artificialWidth);
bool m_shouldUpdate = false; bool m_shouldUpdate = false;
bool m_artificialWidth = false; bool m_artificialWidth = false;
@ -67,9 +67,9 @@ namespace geode {
float m_lineHeight = 0.f; float m_lineHeight = 0.f;
float m_linePadding = 0.f; float m_linePadding = 0.f;
cocos2d::CCLabelBMFont* createLabel(const std::string& text, const float top); cocos2d::CCLabelBMFont* createLabel(const std::string& text, float top);
float calculateOffset(cocos2d::CCLabelBMFont* label); float calculateOffset(cocos2d::CCLabelBMFont* label);
void charIteration(const std::function<cocos2d::CCLabelBMFont*(cocos2d::CCLabelBMFont* line, const char c, const float top)>& overflowHandling); void charIteration(const std::function<cocos2d::CCLabelBMFont*(cocos2d::CCLabelBMFont* line, char c, float top)>& overflowHandling);
void updateLinesNoWrap(); void updateLinesNoWrap();
void updateLinesWordWrap(bool spaceWrap); void updateLinesWordWrap(bool spaceWrap);
void updateLinesCutoffWrap(); void updateLinesCutoffWrap();

View file

@ -122,7 +122,7 @@ namespace geode {
* to distinguish between bold, italic and * to distinguish between bold, italic and
* regular text. * regular text.
*/ */
using Font = utils::MiniFunction<Label(int)>; using Font = std::function<Label(int)>;
protected: protected:
cocos2d::CCPoint m_origin = cocos2d::CCPointZero; cocos2d::CCPoint m_origin = cocos2d::CCPointZero;

View file

@ -21,7 +21,7 @@ namespace geode {
std::string m_id; std::string m_id;
public: public:
ListenerResult handle(utils::MiniFunction<Callback> fn, ColorProvidedEvent* event); ListenerResult handle(std::function<Callback> fn, ColorProvidedEvent* event);
ColorProvidedFilter(std::string const& id); ColorProvidedFilter(std::string const& id);
}; };

View file

@ -4,8 +4,7 @@
#include "../loader/Log.hpp" #include "../loader/Log.hpp"
#include <set> #include <set>
#include <variant> #include <variant>
#include <Geode/utils/MiniFunction.hpp> #include <Geode/Result.hpp>
#include <Geode/utils/Result.hpp>
namespace geode { namespace geode {
struct JsonChecker; struct JsonChecker;
@ -73,231 +72,11 @@ namespace geode {
} }
template <class T> template <class T>
using JsonValueValidator = utils::MiniFunction<bool(T const&)>; using JsonValueValidator = std::function<bool(T const&)>;
struct JsonMaybeObject; struct JsonMaybeObject;
struct JsonMaybeValue; struct JsonMaybeValue;
struct GEODE_DLL
[[deprecated("Use JsonExpectedValue via the checkJson function instead")]]
JsonMaybeSomething {
protected:
JsonChecker& m_checker;
matjson::Value& m_json;
std::string m_hierarchy;
bool m_hasValue;
friend struct JsonMaybeObject;
friend struct JsonMaybeValue;
void setError(std::string const& error);
public:
matjson::Value& json();
JsonMaybeSomething(
JsonChecker& checker, matjson::Value& json, std::string const& hierarchy, bool hasValue
);
bool isError() const;
std::string getError() const;
operator bool() const;
};
struct GEODE_DLL
[[deprecated("Use JsonExpectedValue via the checkJson function instead")]]
JsonMaybeValue : public JsonMaybeSomething {
bool m_inferType = true;
JsonMaybeValue(
JsonChecker& checker, matjson::Value& json, std::string const& hierarchy, bool hasValue
);
JsonMaybeSomething& self();
template <matjson::Type T>
JsonMaybeValue& as() {
if (this->isError()) return *this;
if (!jsonConvertibleTo(self().m_json.type(), T)) {
this->setError(
self().m_hierarchy + ": Invalid type \"" + jsonValueTypeToString(self().m_json.type()) +
"\", expected \"" + jsonValueTypeToString(T) + "\""
);
}
m_inferType = false;
return *this;
}
JsonMaybeValue& array();
template <matjson::Type... T>
JsonMaybeValue& asOneOf() {
if (this->isError()) return *this;
bool isOneOf = (... || jsonConvertibleTo(self().m_json.type(), T));
if (!isOneOf) {
this->setError(
self().m_hierarchy + ": Invalid type \"" + jsonValueTypeToString(self().m_json.type()) +
"\", expected one of \"" + (jsonValueTypeToString(T), ...) + "\""
);
}
m_inferType = false;
return *this;
}
template <class T>
bool is() {
if (this->isError()) return false;
return self().m_json.is<T>();
}
template <class T>
JsonMaybeValue& validate(JsonValueValidator<T> validator) {
if (this->isError()) return *this;
if (self().m_json.is<T>()) {
if (!validator(self().m_json.as<T>())) {
this->setError(self().m_hierarchy + ": Invalid value format");
}
}
else {
this->setError(
self().m_hierarchy + ": Invalid type \"" +
std::string(jsonValueTypeToString(self().m_json.type())) + "\""
);
}
return *this;
}
template <class T>
JsonMaybeValue& inferType() {
if (this->isError() || !m_inferType) return *this;
return this->as<getJsonType<T>()>();
}
template <class T>
JsonMaybeValue& intoRaw(T& target) {
if (this->isError()) return *this;
target = self().m_json;
return *this;
}
template <class T>
JsonMaybeValue& into(T& target) {
return this->intoAs<T, T>(target);
}
template <class T>
JsonMaybeValue& into(std::optional<T>& target) {
return this->intoAs<T, std::optional<T>>(target);
}
template <class A, class T>
JsonMaybeValue& intoAs(T& target) {
this->inferType<A>();
if (this->isError()) return *this;
if (self().m_json.is<A>()) {
try {
target = self().m_json.as<A>();
}
catch(matjson::JsonException const& e) {
this->setError(
self().m_hierarchy + ": Error parsing JSON: " + std::string(e.what())
);
}
}
else {
this->setError(
self().m_hierarchy + ": Invalid type \"" +
std::string(jsonValueTypeToString(self().m_json.type())) + "\""
);
}
return *this;
}
template <class T>
T get() {
this->inferType<T>();
if (this->isError()) return T();
if (self().m_json.is<T>()) {
return self().m_json.as<T>();
}
return T();
}
JsonMaybeObject obj();
template <class T>
struct Iterator {
std::vector<T> m_values;
using iterator = typename std::vector<T>::iterator;
using const_iterator = typename std::vector<T>::const_iterator;
iterator begin() {
return m_values.begin();
}
iterator end() {
return m_values.end();
}
const_iterator begin() const {
return m_values.begin();
}
const_iterator end() const {
return m_values.end();
}
};
JsonMaybeValue at(size_t i);
Iterator<JsonMaybeValue> iterate();
Iterator<std::pair<std::string, JsonMaybeValue>> items();
};
struct
[[deprecated("Use JsonExpectedValue via the checkJson function instead")]]
GEODE_DLL JsonMaybeObject : JsonMaybeSomething {
std::set<std::string> m_knownKeys;
JsonMaybeObject(
JsonChecker& checker, matjson::Value& json, std::string const& hierarchy, bool hasValue
);
JsonMaybeSomething& self();
void addKnownKey(std::string const& key);
matjson::Value& json();
JsonMaybeValue emptyValue();
JsonMaybeValue has(std::string const& key);
JsonMaybeValue needs(std::string const& key);
void checkUnknownKeys();
};
struct
[[deprecated("Use JsonExpectedValue via the checkJson function instead")]]
GEODE_DLL JsonChecker {
std::variant<std::monostate, std::string> m_result;
matjson::Value& m_json;
JsonChecker(matjson::Value& json);
bool isError() const;
std::string getError() const;
JsonMaybeValue root(std::string const& hierarchy);
};
class GEODE_DLL JsonExpectedValue final { class GEODE_DLL JsonExpectedValue final {
protected: protected:
class Impl; class Impl;
@ -325,21 +104,14 @@ namespace geode {
return this->getJSONRef(); return this->getJSONRef();
} }
else { else {
try { auto res = this->getJSONRef().as<T>();
if (this->getJSONRef().is<T>()) { if (res) {
return this->getJSONRef().as<T>(); return res.unwrap();
}
else {
this->setError(
"unexpected type {}",
this->matJsonTypeToString(this->getJSONRef().type())
);
}
}
// matjson can throw variant exceptions too so you need to do this
catch(std::exception const& e) {
this->setError("unable to parse json: {}", e);
} }
this->setError(
"unexpected type {}",
this->matJsonTypeToString(this->getJSONRef().type())
);
} }
return std::nullopt; return std::nullopt;
} }
@ -451,6 +223,13 @@ namespace geode {
* @returns The key, which is a no-op value if it didn't exist * @returns The key, which is a no-op value if it didn't exist
*/ */
JsonExpectedValue has(std::string_view key); JsonExpectedValue has(std::string_view key);
/**
* Check if this object has an optional key. Asserts that this JSON
* value is an object. If the key doesn't exist, or the value is null, returns a
* `JsonExpectValue` that does nothing
* @returns The key, which is a no-op value if it didn't exist, or was null
*/
JsonExpectedValue hasNullable(std::string_view key);
/** /**
* Check if this object has an optional key. Asserts that this JSON * Check if this object has an optional key. Asserts that this JSON
* value is an object. If the key doesn't exist, sets an error and * value is an object. If the key doesn't exist, sets an error and

View file

@ -1,141 +0,0 @@
#pragma once
#include <Geode/DefaultInclude.hpp>
#include <memory>
#include <concepts>
#include "terminate.hpp"
namespace geode::utils {
template <class FunctionType>
class MiniFunction;
template <class Ret, class... Args>
class MiniFunctionStateBase {
public:
virtual ~MiniFunctionStateBase() = default;
virtual Ret call(Args... args) const = 0;
virtual MiniFunctionStateBase* clone() const = 0;
};
template <class Type, class Ret, class... Args>
class MiniFunctionState final : public MiniFunctionStateBase<Ret, Args...> {
public:
Type m_func;
explicit MiniFunctionState(Type func) : m_func(func) {}
Ret call(Args... args) const override {
return const_cast<Type&>(m_func)(std::forward<Args>(args)...);
}
MiniFunctionStateBase<Ret, Args...>* clone() const override {
return new MiniFunctionState(*this);
}
};
template <class Type, class Ret, class... Args>
class MiniFunctionStatePointer final : public MiniFunctionStateBase<Ret, Args...> {
public:
Type m_func;
explicit MiniFunctionStatePointer(Type func) : m_func(func) {}
Ret call(Args... args) const override {
return const_cast<Type&>(*m_func)(std::forward<Args>(args)...);
}
MiniFunctionStateBase<Ret, Args...>* clone() const override {
return new MiniFunctionStatePointer(*this);
}
};
template <class Type, class Ret, class Class, class... Args>
class MiniFunctionStateMemberPointer final : public MiniFunctionStateBase<Ret, Class, Args...> {
public:
Type m_func;
explicit MiniFunctionStateMemberPointer(Type func) : m_func(func) {}
Ret call(Class self, Args... args) const override {
return const_cast<Type&>(self->*m_func)(std::forward<Args>(args)...);
}
MiniFunctionStateBase<Ret, Class, Args...>* clone() const override {
return new MiniFunctionStateMemberPointer(*this);
}
};
template <class Callable, class Ret, class... Args>
concept MiniFunctionCallable = requires(Callable&& func, Args... args) {
{ func(std::forward<Args>(args)...) } -> std::same_as<Ret>;
};
template <class Ret, class... Args>
class MiniFunction<Ret(Args...)> {
public:
using FunctionType = Ret(Args...);
using StateType = MiniFunctionStateBase<Ret, Args...>;
private:
StateType* m_state;
public:
MiniFunction() : m_state(nullptr) {}
MiniFunction(std::nullptr_t) : MiniFunction() {}
MiniFunction(MiniFunction const& other) :
m_state(other.m_state ? other.m_state->clone() : nullptr) {}
MiniFunction(MiniFunction&& other) : m_state(other.m_state) {
other.m_state = nullptr;
}
~MiniFunction() {
if (m_state) delete m_state;
}
template <class Callable>
requires(MiniFunctionCallable<Callable, Ret, Args...> && !std::is_same_v<std::decay_t<Callable>, MiniFunction<FunctionType>>)
MiniFunction(Callable&& func) :
m_state(new MiniFunctionState<std::decay_t<Callable>, Ret, Args...>(std::forward<Callable>(func))) {}
template <class FunctionPointer>
requires(!MiniFunctionCallable<FunctionPointer, Ret, Args...> && std::is_pointer_v<FunctionPointer> && std::is_function_v<std::remove_pointer_t<FunctionPointer>>)
MiniFunction(FunctionPointer func) :
m_state(new MiniFunctionStatePointer<FunctionPointer, Ret, Args...>(func)) {}
template <class MemberFunctionPointer>
requires(std::is_member_function_pointer_v<MemberFunctionPointer>)
MiniFunction(MemberFunctionPointer func) :
m_state(new MiniFunctionStateMemberPointer<MemberFunctionPointer, Ret, Args...>(func)) {}
MiniFunction& operator=(MiniFunction const& other) {
if (m_state) delete m_state;
m_state = other.m_state ? other.m_state->clone() : nullptr;
return *this;
}
MiniFunction& operator=(MiniFunction&& other) {
if (m_state) delete m_state;
m_state = other.m_state;
other.m_state = nullptr;
return *this;
}
Ret operator()(Args... args) const {
if (!m_state) {
utils::terminate(
"Attempted to call a MiniFunction that was never assigned "
"any function, or one that has been moved"
);
}
return m_state->call(std::forward<Args>(args)...);
}
explicit operator bool() const {
return m_state;
}
};
}

View file

@ -1,9 +1,11 @@
#pragma once
#include <Geode/modify/Modify.hpp>
#include "cocos.hpp" #include "cocos.hpp"
namespace geode::node_ids { namespace geode::node_ids {
using namespace cocos2d; using namespace cocos2d;
static constexpr int32_t GEODE_ID_PRIORITY = 0x100000; static constexpr int32_t GEODE_ID_PRIORITY = Priority::VeryEarlyPost;
template <class T = CCNode> template <class T = CCNode>
requires std::is_base_of_v<CCNode, T> requires std::is_base_of_v<CCNode, T>

View file

@ -1,7 +1,7 @@
#pragma once #pragma once
#include "../loader/Hook.hpp" #include "../loader/Hook.hpp"
#include "Result.hpp" #include <Geode/Result.hpp>
namespace geode { namespace geode {
namespace hook { namespace hook {

View file

@ -1,305 +0,0 @@
#pragma once
#include "../DefaultInclude.hpp"
#include "../external/result/result.hpp"
#include <fmt/format.h>
#include <string>
#include <string_view>
#include <type_traits>
#include <variant>
#include <optional>
namespace geode {
namespace impl {
using DefaultValue = std::monostate;
using DefaultError = std::string;
template <class T>
using WrappedResult = std::conditional_t<
std::is_lvalue_reference_v<T>, std::reference_wrapper<std::remove_reference_t<T>>,
std::remove_const_t<T>>;
template <class E = impl::DefaultError>
class [[nodiscard]] Failure {
public:
WrappedResult<E> m_error;
Failure() = default;
template <class E2>
requires(std::is_constructible_v<E, E2 const&>)
explicit constexpr Failure(E2 const& e) : m_error(e) {}
template <class E2>
requires(std::is_constructible_v<E, E2 &&>)
explicit constexpr Failure(E2&& e) : m_error(std::move(e)) {}
E& error() & noexcept {
return m_error;
}
E const& error() const& noexcept {
return m_error;
}
E&& error() && noexcept {
return static_cast<E&&>(m_error);
}
E const&& error() const&& noexcept {
return static_cast<E&&>(m_error);
}
};
template <class T = impl::DefaultValue>
class [[nodiscard]] Success {
public:
WrappedResult<T> m_value;
Success() = default;
template <class T2>
requires(std::is_constructible_v<T, T2 const&>)
explicit constexpr Success(T2 const& v) : m_value(v) {}
template <class T2>
requires(std::is_constructible_v<T, T2 &&>)
explicit constexpr Success(T2&& v) : m_value(std::forward<T2>(v)) {}
T& value() & noexcept {
return m_value;
}
T const& value() const& noexcept {
return m_value;
}
T&& value() && noexcept {
return static_cast<T&&>(m_value);
}
T const&& value() const&& noexcept {
return static_cast<T&&>(m_value);
}
};
}
template <class T = impl::DefaultValue, class E = impl::DefaultError>
class [[nodiscard]] Result : public cpp::result<T, E> {
public:
using Base = cpp::result<T, E>;
using ValueType = typename Base::value_type;
using ErrorType = typename Base::error_type;
using Base::result;
template <class U>
requires(cpp::detail::result_is_implicit_value_convertible<T, U>::value)
constexpr Result(U&& value) = delete;
template <class E2>
requires(std::is_constructible_v<E, E2 const&>)
constexpr Result(impl::Failure<E2> const& e) : Base(cpp::failure<E>(e.error())) {}
template <class E2>
requires(std::is_constructible_v<E, E2 &&>)
constexpr Result(impl::Failure<E2>&& e) : Base(cpp::failure<E>(std::move(e.error()))) {}
template <class T2>
requires(std::is_constructible_v<T, T2 const&>)
constexpr Result(impl::Success<T2> const& s) : Base(s.value()) {}
template <class T2>
requires(std::is_constructible_v<T, T2 &&>)
constexpr Result(impl::Success<T2>&& s) : Base(std::move(s.value())) {}
[[nodiscard]] constexpr explicit operator bool() const noexcept {
return this->Base::operator bool();
}
[[nodiscard]] constexpr bool isOk() const noexcept {
return this->Base::has_value();
}
[[nodiscard]] constexpr bool isErr() const noexcept {
return this->Base::has_error();
}
[[nodiscard]] constexpr decltype(auto) unwrap() & {
return this->Base::value();
}
[[nodiscard]] constexpr decltype(auto) unwrap() const& {
return this->Base::value();
}
[[nodiscard]] constexpr decltype(auto) unwrap() && {
return this->Base::value();
}
[[nodiscard]] constexpr decltype(auto) unwrap() const&& {
return this->Base::value();
}
[[nodiscard]] constexpr decltype(auto) unwrapErr() & {
return this->Base::error();
}
[[nodiscard]] constexpr decltype(auto) unwrapErr() const& {
return this->Base::error();
}
[[nodiscard]] constexpr decltype(auto) unwrapErr() && {
return this->Base::error();
}
[[nodiscard]] constexpr decltype(auto) unwrapErr() const&& {
return this->Base::error();
}
template <class... Args>
requires(std::is_constructible_v<T, T &&>)
[[nodiscard]] Result<T, std::string> expect(std::string const& format, Args&&... args) {
if (this->isErr()) {
return impl::Failure<std::string>(fmt::format(
fmt::runtime(format), std::forward<Args>(args)...,
fmt::arg("error", this->unwrapErr())
));
}
else {
return std::move(*this);
}
}
template <class... Args>
requires(std::is_constructible_v<T, T const&>)
[[nodiscard]] Result<T, std::string> expect(std::string const& format, Args&&... args)
const {
if (this->isErr()) {
return impl::Failure<std::string>(fmt::format(
fmt::runtime(format), std::forward<Args>(args)...,
fmt::arg("error", this->unwrapErr())
));
}
else {
return *this;
}
}
template <class U>
[[nodiscard]] constexpr decltype(auto) unwrapOr(U&& val) && {
return this->Base::value_or(std::forward<U>(val));
}
template <class U>
[[nodiscard]] constexpr decltype(auto) unwrapOr(U&& val) const& {
return this->Base::value_or(std::forward<U>(val));
}
[[nodiscard]] constexpr decltype(auto) unwrapOrDefault() && requires std::is_default_constructible_v<T> {
return this->Base::value_or(T());
}
[[nodiscard]] constexpr decltype(auto) unwrapOrDefault() const& requires std::is_default_constructible_v<T> {
return this->Base::value_or(T());
}
template <class U>
[[nodiscard]] constexpr decltype(auto) errorOr(U&& val) && {
return this->Base::error_or(std::forward<U>(val));
}
template <class U>
[[nodiscard]] constexpr decltype(auto) errorOr(U&& val) const& {
return this->Base::error_or(std::forward<U>(val));
}
/**
* Convert the result into an optional containing the value if Ok, and
* nullopt if Err
*/
[[nodiscard]] constexpr std::optional<T> ok() const& {
if (this->isOk()) {
return this->unwrap();
}
return std::nullopt;
}
/**
* Convert the result into an optional containing the value if Ok, and
* nullopt if Err
*/
[[nodiscard]] constexpr std::optional<T> ok() && {
if (this->isOk()) {
return this->unwrap();
}
return std::nullopt;
}
/**
* Convert the result into an optional containing the error if Err, and
* nullopt if Ok
*/
[[nodiscard]] constexpr std::optional<E> err() const& {
if (this->isErr()) {
return this->unwrapErr();
}
return std::nullopt;
}
/**
* Convert the result into an optional containing the error if Err, and
* nullopt if Ok
*/
[[nodiscard]] constexpr std::optional<E> err() && {
if (this->isErr()) {
return this->unwrapErr();
}
return std::nullopt;
}
/**
* Completely disregard the result. Only recommended if the result is
* inconsequential
*/
constexpr void disregard() && {}
};
template <class T = impl::DefaultValue>
constexpr impl::Success<T> Ok() {
return impl::Success<T>();
}
template <class T>
constexpr impl::Success<T> Ok(T value) {
// DO NOT MAKE THE PARAMETER T&&!!!! THAT WILL CAUSE C++ TO DO UNEXPECTED
// IMPLICIT MOVES FOR EXAMPLE WHEN DOING `Ok(unordered_map.at(value))`
return impl::Success<T>(std::forward<T>(value));
}
template <class E>
constexpr impl::Failure<E> Err(E error) {
return impl::Failure<E>(std::forward<E>(error));
}
template <class... Args>
inline impl::Failure<std::string> Err(std::string const& format, Args&&... args) {
return impl::Failure<std::string>(
fmt::format(fmt::runtime(format), std::forward<Args>(args)...)
);
}
#define GEODE_UNWRAP_INTO(into, ...) \
auto GEODE_CONCAT(unwrap_res_, __LINE__) = (__VA_ARGS__); \
if (GEODE_CONCAT(unwrap_res_, __LINE__).isErr()) { \
return geode::Err(std::move(GEODE_CONCAT(unwrap_res_, __LINE__).unwrapErr())); \
} \
into = std::move(GEODE_CONCAT(unwrap_res_, __LINE__).unwrap())
#define GEODE_UNWRAP(...) \
do { \
auto GEODE_CONCAT(unwrap_res_, __LINE__) = (__VA_ARGS__); \
if (GEODE_CONCAT(unwrap_res_, __LINE__).isErr()) { \
return geode::Err(std::move(GEODE_CONCAT(unwrap_res_, __LINE__).unwrapErr())); \
} \
} while(false)
}

View file

@ -1,13 +1,21 @@
#pragma once #pragma once
#include "general.hpp" #include "general.hpp"
#include "MiniFunction.hpp"
#include "../loader/Event.hpp" #include "../loader/Event.hpp"
#include "../loader/Loader.hpp" #include "../loader/Loader.hpp"
#include <mutex> #include <mutex>
#include <string_view> #include <string_view>
#include <coroutine>
namespace geode { namespace geode {
namespace geode_internal {
template <class T, class P>
struct TaskPromise;
template <class T, class P>
struct TaskAwaiter;
}
/** /**
* Tasks represent an asynchronous operation that will be finished at some * Tasks represent an asynchronous operation that will be finished at some
* unknown point in the future. Tasks can report their progress, and will * unknown point in the future. Tasks can report their progress, and will
@ -87,7 +95,7 @@ namespace geode {
enum class Status { enum class Status {
/// The task is still running or waiting to start /// The task is still running or waiting to start
Pending, Pending,
/// The task has succesfully finished /// The task has successfully finished
Finished, Finished,
/// The task has been cancelled /// The task has been cancelled
Cancelled, Cancelled,
@ -141,7 +149,7 @@ namespace geode {
class PrivateMarker final {}; class PrivateMarker final {};
static std::shared_ptr<Handle> create(std::string_view const name) { static std::shared_ptr<Handle> create(std::string_view name) {
return std::make_shared<Handle>(PrivateMarker(), name); return std::make_shared<Handle>(PrivateMarker(), name);
} }
@ -153,8 +161,14 @@ namespace geode {
template <std::move_constructible T2, std::move_constructible P2> template <std::move_constructible T2, std::move_constructible P2>
friend class Task; friend class Task;
template <class, class>
friend struct geode_internal::TaskPromise;
template <class, class>
friend struct geode_internal::TaskAwaiter;
public: public:
Handle(PrivateMarker, std::string_view const name) : m_name(name) {} Handle(PrivateMarker, std::string_view name) : m_name(name) {}
~Handle() { ~Handle() {
// If this Task was still pending when the Handle was destroyed, // If this Task was still pending when the Handle was destroyed,
// it can no longer be listened to so just cancel and cleanup // it can no longer be listened to so just cancel and cleanup
@ -249,11 +263,11 @@ namespace geode {
using Value = T; using Value = T;
using Progress = P; using Progress = P;
using PostResult = utils::MiniFunction<void(Result)>; using PostResult = std::function<void(Result&&)>;
using PostProgress = utils::MiniFunction<void(P)>; using PostProgress = std::function<void(P)>;
using HasBeenCancelled = utils::MiniFunction<bool()>; using HasBeenCancelled = std::function<bool()>;
using Run = utils::MiniFunction<Result(PostProgress, HasBeenCancelled)>; using Run = std::function<Result(PostProgress, HasBeenCancelled)>;
using RunWithCallback = utils::MiniFunction<void(PostResult, PostProgress, HasBeenCancelled)>; using RunWithCallback = std::function<void(PostResult, PostProgress, HasBeenCancelled)>;
using Callback = void(Event*); using Callback = void(Event*);
@ -308,6 +322,12 @@ namespace geode {
template <std::move_constructible T2, std::move_constructible P2> template <std::move_constructible T2, std::move_constructible P2>
friend class Task; friend class Task;
template <class, class>
friend struct geode_internal::TaskPromise;
template <class, class>
friend struct geode_internal::TaskAwaiter;
public: public:
// Allow default-construction // Allow default-construction
Task() : m_handle(nullptr) {} Task() : m_handle(nullptr) {}
@ -394,7 +414,7 @@ namespace geode {
* Create a new Task that is immediately cancelled * Create a new Task that is immediately cancelled
* @param name The name of the Task; used for debugging * @param name The name of the Task; used for debugging
*/ */
static Task cancelled(std::string_view const name = "<Cancelled Task>") { static Task cancelled(std::string_view name = "<Cancelled Task>") {
auto task = Task(Handle::create(name)); auto task = Task(Handle::create(name));
Task::cancel(task.m_handle); Task::cancel(task.m_handle);
return task; return task;
@ -405,7 +425,7 @@ namespace geode {
* @param value The value the Task shall be finished with * @param value The value the Task shall be finished with
* @param name The name of the Task; used for debugging * @param name The name of the Task; used for debugging
*/ */
static Task immediate(T value, std::string_view const name = "<Immediate Task>") { static Task immediate(T value, std::string_view name = "<Immediate Task>") {
auto task = Task(Handle::create(name)); auto task = Task(Handle::create(name));
Task::finish(task.m_handle, std::move(value)); Task::finish(task.m_handle, std::move(value));
return task; return task;
@ -417,7 +437,7 @@ namespace geode {
* function MUST be synchronous - Task creates the thread for you! * function MUST be synchronous - Task creates the thread for you!
* @param name The name of the Task; used for debugging * @param name The name of the Task; used for debugging
*/ */
static Task run(Run&& body, std::string_view const name = "<Task>") { static Task run(Run&& body, std::string_view name = "<Task>") {
auto task = Task(Handle::create(name)); auto task = Task(Handle::create(name));
std::thread([handle = std::weak_ptr(task.m_handle), name = std::string(name), body = std::move(body)] { std::thread([handle = std::weak_ptr(task.m_handle), name = std::string(name), body = std::move(body)] {
utils::thread::setName(fmt::format("Task '{}'", name)); utils::thread::setName(fmt::format("Task '{}'", name));
@ -450,7 +470,7 @@ namespace geode {
* calls will always be ignored * calls will always be ignored
* @param name The name of the Task; used for debugging * @param name The name of the Task; used for debugging
*/ */
static Task runWithCallback(RunWithCallback&& body, std::string_view const name = "<Callback Task>") { static Task runWithCallback(RunWithCallback&& body, std::string_view name = "<Callback Task>") {
auto task = Task(Handle::create(name)); auto task = Task(Handle::create(name));
std::thread([handle = std::weak_ptr(task.m_handle), name = std::string(name), body = std::move(body)] { std::thread([handle = std::weak_ptr(task.m_handle), name = std::string(name), body = std::move(body)] {
utils::thread::setName(fmt::format("Task '{}'", name)); utils::thread::setName(fmt::format("Task '{}'", name));
@ -485,7 +505,7 @@ namespace geode {
* were cancelled! * were cancelled!
*/ */
template <std::move_constructible NP> template <std::move_constructible NP>
static Task<std::vector<T*>, std::monostate> all(std::vector<Task<T, NP>>&& tasks, std::string_view const name = "<Multiple Tasks>") { static Task<std::vector<T*>, std::monostate> all(std::vector<Task<T, NP>>&& tasks, std::string_view name = "<Multiple Tasks>") {
using AllTask = Task<std::vector<T*>, std::monostate>; using AllTask = Task<std::vector<T*>, std::monostate>;
// If there are no tasks, return an immediate task that does nothing // If there are no tasks, return an immediate task that does nothing
@ -582,7 +602,7 @@ namespace geode {
* the mapped task is appended to the end * the mapped task is appended to the end
*/ */
template <class ResultMapper, class ProgressMapper, class OnCancelled> template <class ResultMapper, class ProgressMapper, class OnCancelled>
auto map(ResultMapper&& resultMapper, ProgressMapper&& progressMapper, OnCancelled&& onCancelled, std::string_view const name = "<Mapping Task>") const { auto map(ResultMapper&& resultMapper, ProgressMapper&& progressMapper, OnCancelled&& onCancelled, std::string_view name = "<Mapping Task>") const {
using T2 = decltype(resultMapper(std::declval<T*>())); using T2 = decltype(resultMapper(std::declval<T*>()));
using P2 = decltype(progressMapper(std::declval<P*>())); using P2 = decltype(progressMapper(std::declval<P*>()));
@ -652,7 +672,7 @@ namespace geode {
* @param name The name of the Task; used for debugging. The name of * @param name The name of the Task; used for debugging. The name of
* the mapped task is appended to the end * the mapped task is appended to the end
*/ template <class ResultMapper, class ProgressMapper> */ template <class ResultMapper, class ProgressMapper>
auto map(ResultMapper&& resultMapper, ProgressMapper&& progressMapper, std::string_view const name = "<Mapping Task>") const { auto map(ResultMapper&& resultMapper, ProgressMapper&& progressMapper, std::string_view name = "<Mapping Task>") const {
return this->map(std::move(resultMapper), std::move(progressMapper), +[]() {}, name); return this->map(std::move(resultMapper), std::move(progressMapper), +[]() {}, name);
} }
@ -670,7 +690,7 @@ namespace geode {
* the mapped task is appended to the end * the mapped task is appended to the end
*/ template <class ResultMapper> */ template <class ResultMapper>
requires std::copy_constructible<P> requires std::copy_constructible<P>
auto map(ResultMapper&& resultMapper, std::string_view const name = "<Mapping Task>") const { auto map(ResultMapper&& resultMapper, std::string_view name = "<Mapping Task>") const {
return this->map(std::move(resultMapper), +[](P* p) -> P { return *p; }, name); return this->map(std::move(resultMapper), +[](P* p) -> P { return *p; }, name);
} }
@ -752,7 +772,94 @@ namespace geode {
this->listen(std::move(onResult), [](auto const&) {}, [] {}); this->listen(std::move(onResult), [](auto const&) {}, [] {});
} }
ListenerResult handle(utils::MiniFunction<Callback> fn, Event* e) { /**
* Create a new Task that listens to this Task and maps the values using
* the provided function. The new Task will only start when this Task finishes.
* @param mapper Function that makes a new task given the finished value of this task.
* The function signature should be `Task<NewType, NewProgress>(T*)`, and it will be executed
* on the main thread.
* @param name The name of the Task; used for debugging.
* @return The new Task that will run when this Task finishes.
* @note Progress from this task is not sent through, only progress from the new task is.
*/
template <std::invocable<T*> Mapper>
auto chain(Mapper mapper, std::string_view name = "<Chained Task>") const -> decltype(mapper(std::declval<T*>())) {
using NewTask = decltype(mapper(std::declval<T*>()));
using NewType = typename NewTask::Value;
using NewProgress = typename NewTask::Progress;
std::unique_lock<std::recursive_mutex> lock(m_handle->m_mutex);
if (m_handle->m_status == Status::Cancelled) {
// if the current task has been cancelled already, make an immediate cancelled task
return NewTask::cancelled();
}
else if (m_handle->m_status == Status::Finished) {
// if the current task is already done, we can just call the mapper directly
return mapper(&*m_handle->m_resultValue);
}
// otherwise, make a wrapper task that waits for the current task to finish,
// and then runs the mapper on the result. this new task will also wait for the task
// created by the mapper to finish, and will just forward the values through.
// do this because we cant really change the handle of the task we already returned
NewTask task = NewTask::Handle::create(fmt::format("{} <- {}", name, m_handle->m_name));
task.m_handle->m_extraData = std::make_unique<typename NewTask::Handle::ExtraData>(
// make the first event listener that waits for the current task
static_cast<void*>(new EventListener<Task>(
[handle = std::weak_ptr(task.m_handle), mapper = std::move(mapper)](Event* event) mutable {
if (auto v = event->getValue()) {
auto newInnerTask = mapper(v);
// this is scary.. but it doesn't seem to crash lol
handle.lock()->m_extraData = std::make_unique<typename NewTask::Handle::ExtraData>(
// make the second event listener that waits for the mapper's task
// and just forwards everything through
static_cast<void*>(new EventListener<NewTask>(
[handle](typename NewTask::Event* event) mutable {
if (auto v = event->getValue()) {
NewTask::finish(handle.lock(), std::move(*v));
}
else if (auto p = event->getProgress()) {
NewTask::progress(handle.lock(), std::move(*p));
}
else if (event->isCancelled()) {
NewTask::cancel(handle.lock());
}
},
std::move(newInnerTask)
)),
+[](void* ptr) {
delete static_cast<EventListener<NewTask>*>(ptr);
},
+[](void* ptr) {
static_cast<EventListener<NewTask>*>(ptr)->getFilter().cancel();
}
);
}
else if (auto p = event->getProgress()) {
// no guarantee P and NewProgress are compatible
// nor does it seem like the intended behavior?
// TODO: maybe add a mapper for progress?
}
else if (event->isCancelled()) {
NewTask::cancel(handle.lock());
}
},
*this
)),
+[](void* ptr) {
delete static_cast<EventListener<Task>*>(ptr);
},
+[](void* ptr) {
static_cast<EventListener<Task>*>(ptr)->getFilter().cancel();
}
);
return task;
}
ListenerResult handle(std::function<Callback> fn, Event* e) {
if (e->m_handle == m_handle && (!e->m_for || e->m_for == m_listener)) { if (e->m_handle == m_handle && (!e->m_for || e->m_for == m_listener)) {
fn(e); fn(e);
} }
@ -797,3 +904,132 @@ namespace geode {
static_assert(is_filter<Task<int>>, "The Task class must be a valid event filter!"); static_assert(is_filter<Task<int>>, "The Task class must be a valid event filter!");
} }
// - C++20 coroutine support for Task - //
// Example usage (function must return a Task):
// ```
// Task<int> someTask() {
// auto response = co_await web::WebRequest().get("https://example.com");
// co_return response.code();
// }
// ```
// This will create a Task that will finish with the response code of the
// web request.
//
// Note: If the Task the coroutine is waiting on is cancelled, the coroutine
// will be destroyed and the Task will be cancelled as well. If the Task returned
// by the coroutine is cancelled, the coroutine will be destroyed as well and execution
// stops as soon as possible.
//
// The body of the coroutine is ran in whatever thread it got called in.
// TODO: maybe guarantee main thread?
//
// The coroutine can also yield progress values using `co_yield`:
// ```
// Task<std::string, int> someTask() {
// for (int i = 0; i < 10; i++) {
// co_yield i;
// }
// co_return "done!";
// }
// ```
namespace geode {
namespace geode_internal {
template <class T, class P>
struct TaskPromise {
using MyTask = Task<T, P>;
std::weak_ptr<typename MyTask::Handle> m_handle;
~TaskPromise() {
// does nothing if its not pending
MyTask::cancel(m_handle.lock());
}
std::suspend_never initial_suspend() noexcept { return {}; }
std::suspend_never final_suspend() noexcept { return {}; }
// TODO: do something here?
void unhandled_exception() {}
MyTask get_return_object() {
auto handle = MyTask::Handle::create("<Coroutine Task>");
m_handle = handle;
return handle;
}
void return_value(T x) {
MyTask::finish(m_handle.lock(), std::move(x));
}
std::suspend_never yield_value(P value) {
MyTask::progress(m_handle.lock(), std::move(value));
return {};
}
bool isCancelled() {
if (auto p = m_handle.lock()) {
return p->is(MyTask::Status::Cancelled);
}
return true;
}
};
template <class T, class P>
struct TaskAwaiter {
Task<T, P> task;
bool await_ready() {
return task.isFinished();
}
template <class U, class V>
void await_suspend(std::coroutine_handle<TaskPromise<U, V>> handle) {
if (handle.promise().isCancelled()) {
handle.destroy();
return;
}
// this should be fine because the parent task can only have
// one pending task at a time
auto parentHandle = handle.promise().m_handle.lock();
if (!parentHandle) {
handle.destroy();
return;
}
parentHandle->m_extraData = std::make_unique<typename Task<U, V>::Handle::ExtraData>(
static_cast<void*>(new EventListener<Task<T, P>>(
[handle](auto* event) {
if (event->getValue()) {
handle.resume();
}
if (event->isCancelled()) {
handle.destroy();
}
},
task
)),
+[](void* ptr) {
delete static_cast<EventListener<Task<T, P>>*>(ptr);
},
+[](void* ptr) {
static_cast<EventListener<Task<T, P>>*>(ptr)->getFilter().cancel();
}
);
}
T await_resume() {
return std::move(*task.getFinishedValue());
}
};
}
}
template <class T, class P>
auto operator co_await(geode::Task<T, P> task) {
return geode::geode_internal::TaskAwaiter<T, P>{task};
}
template <class T, class P, class... Args>
struct std::coroutine_traits<geode::Task<T, P>, Args...> {
using promise_type = geode::geode_internal::TaskPromise<T, P>;
};

View file

@ -4,7 +4,8 @@
#include <string_view> #include <string_view>
#include <matjson.hpp> #include <matjson.hpp>
#include <tuple> #include <tuple>
#include "../utils/Result.hpp" #include <Geode/Result.hpp>
#include <fmt/format.h>
namespace geode { namespace geode {
enum class VersionCompare { enum class VersionCompare {
@ -186,8 +187,6 @@ namespace geode {
std::tie(other.m_major, other.m_minor, other.m_patch, other.m_tag); std::tie(other.m_major, other.m_minor, other.m_patch, other.m_tag);
} }
[[deprecated("Use toNonVString or toVString instead")]]
std::string toString(bool includeTag = true) const;
std::string toVString(bool includeTag = true) const; std::string toVString(bool includeTag = true) const;
std::string toNonVString(bool includeTag = true) const; std::string toNonVString(bool includeTag = true) const;
@ -258,25 +257,15 @@ namespace geode {
template <class V> template <class V>
requires std::is_same_v<V, geode::VersionInfo> || std::is_same_v<V, geode::ComparableVersionInfo> requires std::is_same_v<V, geode::VersionInfo> || std::is_same_v<V, geode::ComparableVersionInfo>
struct matjson::Serialize<V> { struct matjson::Serialize<V> {
static matjson::Value to_json(V const& info) { static geode::Result<V, std::string> fromJson(Value const& value) {
return info.toString(); GEODE_UNWRAP_INTO(auto str, value.asString());
GEODE_UNWRAP_INTO(auto version, V::parse(str).mapErr([](auto&& err) {
return fmt::format("Invalid version format: {}", err);
}));
return geode::Ok(version);
} }
static bool is_json(matjson::Value const& json) { static Value toJson(V const& value) {
if (json.is_string()) { return Value(value.toNonVString());
auto ver = V::parse(json.as_string());
return !ver.isErr();
}
return false;
}
static V from_json(matjson::Value const& json) {
auto ver = V::parse(json.as_string());
if (!ver) {
throw matjson::JsonException(
"Invalid version format: " + ver.unwrapErr()
);
}
return ver.unwrap();
} }
}; };

View file

@ -10,22 +10,21 @@
#include "../loader/Event.hpp" #include "../loader/Event.hpp"
#include <Geode/binding/CCMenuItemSpriteExtra.hpp> #include <Geode/binding/CCMenuItemSpriteExtra.hpp>
#include <Geode/binding/CCMenuItemToggler.hpp> #include <Geode/binding/CCMenuItemToggler.hpp>
#include "MiniFunction.hpp" #include "../ui/Layout.hpp"
#include "../ui/SpacerNode.hpp"
// support converting ccColor3B / ccColor4B to / from json // support converting ccColor3B / ccColor4B to / from json
template <> template <>
struct matjson::Serialize<cocos2d::ccColor3B> { struct matjson::Serialize<cocos2d::ccColor3B> {
static matjson::Value GEODE_DLL to_json(cocos2d::ccColor3B const& color); static geode::Result<cocos2d::ccColor3B> GEODE_DLL fromJson(Value const& value);
static cocos2d::ccColor3B GEODE_DLL from_json(matjson::Value const& color); static Value GEODE_DLL toJson(cocos2d::ccColor3B const& value);
static bool GEODE_DLL is_json(matjson::Value const& json);
}; };
template <> template <>
struct matjson::Serialize<cocos2d::ccColor4B> { struct matjson::Serialize<cocos2d::ccColor4B> {
static matjson::Value GEODE_DLL to_json(cocos2d::ccColor4B const& color); static geode::Result<cocos2d::ccColor4B> GEODE_DLL fromJson(Value const& value);
static cocos2d::ccColor4B GEODE_DLL from_json(matjson::Value const& color); static Value GEODE_DLL toJson(cocos2d::ccColor4B const& value);
static bool GEODE_DLL is_json(matjson::Value const& json);
}; };
// operators for CC geometry // operators for CC geometry
@ -615,18 +614,6 @@ namespace geode::cocos {
return static_cast<T*>(x->getChildren()->objectAtIndex(i)); return static_cast<T*>(x->getChildren()->objectAtIndex(i));
} }
/**
* Get nth child that is a given type. Checks bounds.
* @returns Child at index cast to the given type,
* or nullptr if index exceeds bounds
*/
template <class Type = cocos2d::CCNode>
[[deprecated("Use CCNode::getChildByType instead")]]
static Type* getChildOfType(cocos2d::CCNode* node, int index) {
return node->getChildByType<Type>(index);
}
/** /**
* Return a node, or create a default one if it's * Return a node, or create a default one if it's
* nullptr. Syntactic sugar function * nullptr. Syntactic sugar function
@ -670,7 +657,7 @@ namespace geode::cocos {
*/ */
GEODE_DLL cocos2d::CCScene* switchToScene(cocos2d::CCLayer* layer); GEODE_DLL cocos2d::CCScene* switchToScene(cocos2d::CCLayer* layer);
using CreateLayerFunc = utils::MiniFunction<cocos2d::CCLayer*()>; using CreateLayerFunc = std::function<cocos2d::CCLayer*()>;
/** /**
* Reload textures, overwriting the scene to return to after the loading * Reload textures, overwriting the scene to return to after the loading
@ -738,7 +725,7 @@ namespace geode::cocos {
* there is none * there is none
*/ */
template <class Type = cocos2d::CCNode> template <class Type = cocos2d::CCNode>
Type* findFirstChildRecursive(cocos2d::CCNode* node, utils::MiniFunction<bool(Type*)> predicate) { Type* findFirstChildRecursive(cocos2d::CCNode* node, std::function<bool(Type*)> predicate) {
if (cast::typeinfo_cast<Type*>(node) && predicate(static_cast<Type*>(node))) if (cast::typeinfo_cast<Type*>(node) && predicate(static_cast<Type*>(node)))
return static_cast<Type*>(node); return static_cast<Type*>(node);
@ -856,30 +843,6 @@ namespace geode::cocos {
return {color.r / 255.f, color.g / 255.f, color.b / 255.f, color.a / 255.f}; return {color.r / 255.f, color.g / 255.f, color.b / 255.f, color.a / 255.f};
} }
[[deprecated("This function may have unintended behavior, use cc3bFromHexString or manually expand the color instead")]]
constexpr cocos2d::ccColor3B cc3x(int hexValue) {
if (hexValue <= 0xf)
return cocos2d::ccColor3B{
static_cast<GLubyte>(hexValue * 17),
static_cast<GLubyte>(hexValue * 17),
static_cast<GLubyte>(hexValue * 17)};
if (hexValue <= 0xff)
return cocos2d::ccColor3B{
static_cast<GLubyte>(hexValue),
static_cast<GLubyte>(hexValue),
static_cast<GLubyte>(hexValue)};
if (hexValue <= 0xfff)
return cocos2d::ccColor3B{
static_cast<GLubyte>((hexValue >> 8 & 0xf) * 17),
static_cast<GLubyte>((hexValue >> 4 & 0xf) * 17),
static_cast<GLubyte>((hexValue >> 0 & 0xf) * 17)};
else
return cocos2d::ccColor3B{
static_cast<GLubyte>(hexValue >> 16 & 0xff),
static_cast<GLubyte>(hexValue >> 8 & 0xff),
static_cast<GLubyte>(hexValue >> 0 & 0xff)};
}
/** /**
* Parse a ccColor3B from a hexadecimal string. The string may contain * Parse a ccColor3B from a hexadecimal string. The string may contain
* a leading '#' * a leading '#'
@ -887,7 +850,7 @@ namespace geode::cocos {
* @param permissive If true, strings like "f" are considered valid * @param permissive If true, strings like "f" are considered valid
* representations of the color white. Useful for UIs that allow entering * representations of the color white. Useful for UIs that allow entering
* a hex color. Empty strings evaluate to pure white * a hex color. Empty strings evaluate to pure white
* @returns A ccColor3B if it could be succesfully parsed, or an error * @returns A ccColor3B if it could be successfully parsed, or an error
* indicating the failure reason * indicating the failure reason
*/ */
GEODE_DLL Result<cocos2d::ccColor3B> cc3bFromHexString(std::string const& hexValue, bool permissive = false); GEODE_DLL Result<cocos2d::ccColor3B> cc3bFromHexString(std::string const& hexValue, bool permissive = false);
@ -900,7 +863,7 @@ namespace geode::cocos {
* @param permissive If true, strings like "f" are considered valid * @param permissive If true, strings like "f" are considered valid
* representations of the color white. Useful for UIs that allow entering * representations of the color white. Useful for UIs that allow entering
* a hex color. Empty strings evaluate to pure white * a hex color. Empty strings evaluate to pure white
* @returns A ccColor4B if it could be succesfully parsed, or an error * @returns A ccColor4B if it could be successfully parsed, or an error
* indicating the failure reason * indicating the failure reason
*/ */
GEODE_DLL Result<cocos2d::ccColor4B> cc4bFromHexString(std::string const& hexValue, bool requireAlpha = false, bool permissive = false); GEODE_DLL Result<cocos2d::ccColor4B> cc4bFromHexString(std::string const& hexValue, bool requireAlpha = false, bool permissive = false);
@ -916,7 +879,7 @@ namespace geode::cocos {
} }
template <typename T, typename C, typename = std::enable_if_t<std::is_pointer_v<C>>> template <typename T, typename C, typename = std::enable_if_t<std::is_pointer_v<C>>>
static cocos2d::CCArray* vectorToCCArray(std::vector<T> const& vec, utils::MiniFunction<C(T)> convFunc) { static cocos2d::CCArray* vectorToCCArray(std::vector<T> const& vec, std::function<C(T)> convFunc) {
auto res = cocos2d::CCArray::createWithCapacity(vec.size()); auto res = cocos2d::CCArray::createWithCapacity(vec.size());
for (auto const& item : vec) for (auto const& item : vec)
res->addObject(convFunc(item)); res->addObject(convFunc(item));
@ -943,7 +906,7 @@ namespace geode::cocos {
template < template <
typename K, typename V, typename C, typename K, typename V, typename C,
typename = std::enable_if_t<std::is_same_v<C, std::string> || std::is_same_v<C, intptr_t>>> typename = std::enable_if_t<std::is_same_v<C, std::string> || std::is_same_v<C, intptr_t>>>
static cocos2d::CCDictionary* mapToCCDict(std::map<K, V> const& map, utils::MiniFunction<C(K)> convFunc) { static cocos2d::CCDictionary* mapToCCDict(std::map<K, V> const& map, std::function<C(K)> convFunc) {
auto res = cocos2d::CCDictionary::create(); auto res = cocos2d::CCDictionary::create();
for (auto const& [key, value] : map) for (auto const& [key, value] : map)
res->setObject(value, convFunc(key)); res->setObject(value, convFunc(key));
@ -969,10 +932,10 @@ namespace std {
}; };
template <typename T> template <typename T>
struct std::hash<geode::WeakRef<T>> { struct hash<geode::WeakRef<T>> {
size_t operator()(geode::WeakRef<T> const& ref) const { size_t operator()(geode::WeakRef<T> const& ref) const {
// the explicit template argument is needed here because it would otherwise cast to WeakRef and recurse // the explicit template argument is needed here because it would otherwise cast to WeakRef and recurse
return hash<std::shared_ptr<geode::WeakRefController>>{}(ref.m_controller); return std::hash<std::shared_ptr<geode::WeakRefController>>{}(ref.m_controller);
} }
}; };
} }
@ -1216,11 +1179,11 @@ namespace geode::cocos {
template <class Node> template <class Node>
class LambdaCallback : public cocos2d::CCObject { class LambdaCallback : public cocos2d::CCObject {
public: public:
utils::MiniFunction<void(Node*)> m_callback; std::function<void(Node*)> m_callback;
static LambdaCallback* create(utils::MiniFunction<void(Node*)>&& callback) { static LambdaCallback* create(std::function<void(Node*)> callback) {
auto ret = new (std::nothrow) LambdaCallback(); auto ret = new (std::nothrow) LambdaCallback();
if (ret->init(std::forward<std::remove_reference_t<decltype(callback)>>(callback))) { if (ret->init(std::move(callback))) {
ret->autorelease(); ret->autorelease();
return ret; return ret;
} }
@ -1228,8 +1191,8 @@ namespace geode::cocos {
return nullptr; return nullptr;
} }
bool init(utils::MiniFunction<void(Node*)>&& callback) { bool init(std::function<void(Node*)> callback) {
m_callback = std::forward<std::remove_reference_t<decltype(callback)>>(callback); m_callback = std::move(callback);
return true; return true;
} }
@ -1240,20 +1203,20 @@ namespace geode::cocos {
public: public:
static cocos2d::CCMenuItem* create( static cocos2d::CCMenuItem* create(
utils::MiniFunction<void(cocos2d::CCMenuItem*)>&& callback std::function<void(cocos2d::CCMenuItem*)> callback
) { ) {
auto item = cocos2d::CCMenuItem::create(); auto item = cocos2d::CCMenuItem::create();
assignCallback(item, std::forward<std::remove_reference_t<decltype(callback)>>(callback)); assignCallback(item, std::move(callback));
return item; return item;
} }
static cocos2d::CCMenuItemSprite* createSprite( static cocos2d::CCMenuItemSprite* createSprite(
cocos2d::CCNode* normalSprite, cocos2d::CCNode* normalSprite,
cocos2d::CCNode* selectedSprite, cocos2d::CCNode* selectedSprite,
utils::MiniFunction<void(cocos2d::CCMenuItemSprite*)>&& callback std::function<void(cocos2d::CCMenuItemSprite*)> callback
) { ) {
auto item = cocos2d::CCMenuItemSprite::create(normalSprite, selectedSprite); auto item = cocos2d::CCMenuItemSprite::create(normalSprite, selectedSprite);
assignCallback(item, std::forward<std::remove_reference_t<decltype(callback)>>(callback)); assignCallback(item, std::move(callback));
return item; return item;
} }
@ -1261,57 +1224,57 @@ namespace geode::cocos {
cocos2d::CCNode* normalSprite, cocos2d::CCNode* normalSprite,
cocos2d::CCNode* selectedSprite, cocos2d::CCNode* selectedSprite,
cocos2d::CCNode* disabledSprite, cocos2d::CCNode* disabledSprite,
utils::MiniFunction<void(cocos2d::CCMenuItemSprite*)>&& callback std::function<void(cocos2d::CCMenuItemSprite*)> callback
) { ) {
auto item = cocos2d::CCMenuItemSprite::create(normalSprite, selectedSprite, disabledSprite); auto item = cocos2d::CCMenuItemSprite::create(normalSprite, selectedSprite, disabledSprite);
assignCallback(item, std::forward<std::remove_reference_t<decltype(callback)>>(callback)); assignCallback(item, std::move(callback));
return item; return item;
} }
static CCMenuItemSpriteExtra* createSpriteExtra( static CCMenuItemSpriteExtra* createSpriteExtra(
cocos2d::CCNode* normalSprite, cocos2d::CCNode* normalSprite,
utils::MiniFunction<void(CCMenuItemSpriteExtra*)>&& callback std::function<void(CCMenuItemSpriteExtra*)> callback
) { ) {
auto item = CCMenuItemSpriteExtra::create(normalSprite, nullptr, nullptr); auto item = CCMenuItemSpriteExtra::create(normalSprite, nullptr, nullptr);
assignCallback(item, std::forward<std::remove_reference_t<decltype(callback)>>(callback)); assignCallback(item, std::move(callback));
return item; return item;
} }
static CCMenuItemSpriteExtra* createSpriteExtraWithFilename( static CCMenuItemSpriteExtra* createSpriteExtraWithFilename(
std::string_view normalSpriteName, std::string_view normalSpriteName,
float scale, float scale,
utils::MiniFunction<void(CCMenuItemSpriteExtra*)>&& callback std::function<void(CCMenuItemSpriteExtra*)> callback
) { ) {
auto sprite = cocos2d::CCSprite::create(normalSpriteName.data()); auto sprite = cocos2d::CCSprite::create(normalSpriteName.data());
sprite->setScale(scale); sprite->setScale(scale);
return createSpriteExtra(sprite, std::forward<std::remove_reference_t<decltype(callback)>>(callback)); return createSpriteExtra(sprite, std::move(callback));
} }
static CCMenuItemSpriteExtra* createSpriteExtraWithFrameName( static CCMenuItemSpriteExtra* createSpriteExtraWithFrameName(
std::string_view normalSpriteName, std::string_view normalSpriteName,
float scale, float scale,
utils::MiniFunction<void(CCMenuItemSpriteExtra*)>&& callback std::function<void(CCMenuItemSpriteExtra*)> callback
) { ) {
auto sprite = cocos2d::CCSprite::createWithSpriteFrameName(normalSpriteName.data()); auto sprite = cocos2d::CCSprite::createWithSpriteFrameName(normalSpriteName.data());
sprite->setScale(scale); sprite->setScale(scale);
return createSpriteExtra(sprite, std::forward<std::remove_reference_t<decltype(callback)>>(callback)); return createSpriteExtra(sprite, std::move(callback));
} }
static CCMenuItemToggler* createToggler( static CCMenuItemToggler* createToggler(
cocos2d::CCNode* onSprite, cocos2d::CCNode* onSprite,
cocos2d::CCNode* offSprite, cocos2d::CCNode* offSprite,
utils::MiniFunction<void(CCMenuItemToggler*)>&& callback std::function<void(CCMenuItemToggler*)> callback
) { ) {
auto item = CCMenuItemToggler::create(offSprite, onSprite, nullptr, nullptr); auto item = CCMenuItemToggler::create(offSprite, onSprite, nullptr, nullptr);
assignCallback(item, std::forward<std::remove_reference_t<decltype(callback)>>(callback)); assignCallback(item, std::move(callback));
return item; return item;
} }
static CCMenuItemToggler* createTogglerWithStandardSprites( static CCMenuItemToggler* createTogglerWithStandardSprites(
float scale, float scale,
utils::MiniFunction<void(CCMenuItemToggler*)>&& callback std::function<void(CCMenuItemToggler*)> callback
) { ) {
auto offSprite = cocos2d::CCSprite::createWithSpriteFrameName("GJ_checkOff_001.png"); auto offSprite = cocos2d::CCSprite::createWithSpriteFrameName("GJ_checkOff_001.png");
auto onSprite = cocos2d::CCSprite::createWithSpriteFrameName("GJ_checkOn_001.png"); auto onSprite = cocos2d::CCSprite::createWithSpriteFrameName("GJ_checkOn_001.png");
@ -1319,14 +1282,14 @@ namespace geode::cocos {
offSprite->setScale(scale); offSprite->setScale(scale);
onSprite->setScale(scale); onSprite->setScale(scale);
return createToggler(onSprite, offSprite, std::forward<std::remove_reference_t<decltype(callback)>>(callback)); return createToggler(onSprite, offSprite, std::move(callback));
} }
static CCMenuItemToggler* createTogglerWithFilename( static CCMenuItemToggler* createTogglerWithFilename(
std::string_view onSpriteName, std::string_view onSpriteName,
std::string_view offSpriteName, std::string_view offSpriteName,
float scale, float scale,
utils::MiniFunction<void(CCMenuItemToggler*)>&& callback std::function<void(CCMenuItemToggler*)> callback
) { ) {
auto offSprite = cocos2d::CCSprite::create(offSpriteName.data()); auto offSprite = cocos2d::CCSprite::create(offSpriteName.data());
auto onSprite = cocos2d::CCSprite::create(onSpriteName.data()); auto onSprite = cocos2d::CCSprite::create(onSpriteName.data());
@ -1334,14 +1297,14 @@ namespace geode::cocos {
offSprite->setScale(scale); offSprite->setScale(scale);
onSprite->setScale(scale); onSprite->setScale(scale);
return createToggler(onSprite, offSprite, std::forward<std::remove_reference_t<decltype(callback)>>(callback)); return createToggler(onSprite, offSprite, std::move(callback));
} }
static CCMenuItemToggler* createTogglerWithFrameName( static CCMenuItemToggler* createTogglerWithFrameName(
std::string_view onSpriteName, std::string_view onSpriteName,
std::string_view offSpriteName, std::string_view offSpriteName,
float scale, float scale,
utils::MiniFunction<void(CCMenuItemToggler*)>&& callback std::function<void(CCMenuItemToggler*)> callback
) { ) {
auto offSprite = cocos2d::CCSprite::createWithSpriteFrameName(offSpriteName.data()); auto offSprite = cocos2d::CCSprite::createWithSpriteFrameName(offSpriteName.data());
auto onSprite = cocos2d::CCSprite::createWithSpriteFrameName(onSpriteName.data()); auto onSprite = cocos2d::CCSprite::createWithSpriteFrameName(onSpriteName.data());
@ -1349,15 +1312,15 @@ namespace geode::cocos {
offSprite->setScale(scale); offSprite->setScale(scale);
onSprite->setScale(scale); onSprite->setScale(scale);
return createToggler(onSprite, offSprite, std::forward<std::remove_reference_t<decltype(callback)>>(callback)); return createToggler(onSprite, offSprite, std::move(callback));
} }
template <class Node> template <class Node>
static void assignCallback( static void assignCallback(
cocos2d::CCMenuItem* item, cocos2d::CCMenuItem* item,
utils::MiniFunction<void(Node*)>&& callback std::function<void(Node*)> callback
) { ) {
auto lambda = LambdaCallback<Node>::create(std::forward<std::remove_reference_t<decltype(callback)>>(callback)); auto lambda = LambdaCallback<Node>::create(std::move(callback));
item->setTarget(lambda, menu_selector(LambdaCallback<Node>::execute)); item->setTarget(lambda, menu_selector(LambdaCallback<Node>::execute));
item->setUserObject("lambda-callback", lambda); item->setUserObject("lambda-callback", lambda);
} }
@ -1368,15 +1331,13 @@ namespace geode::cocos {
class CallFuncExtImpl : public cocos2d::CCActionInstant { class CallFuncExtImpl : public cocos2d::CCActionInstant {
public: public:
static CallFuncExtImpl* create(const F& func) { static CallFuncExtImpl* create(const F& func) {
auto ret = new CallFuncExtImpl; auto ret = new CallFuncExtImpl(func);
ret->m_func = func;
ret->autorelease(); ret->autorelease();
return ret; return ret;
} }
static CallFuncExtImpl* create(F&& func) { static CallFuncExtImpl* create(F&& func) {
auto ret = new CallFuncExtImpl; auto ret = new CallFuncExtImpl(std::move(func));
ret->m_func = std::move(func);
ret->autorelease(); ret->autorelease();
return ret; return ret;
} }
@ -1384,8 +1345,17 @@ namespace geode::cocos {
private: private:
F m_func; F m_func;
// F may not be default-constructible
CallFuncExtImpl(F&& func) : m_func(std::move(func)) {}
CallFuncExtImpl(F const& func) : m_func(func) {}
void update(float) override { void update(float) override {
if (m_func) this->m_func(); // Make sure any `std::function`s are valid
if constexpr (requires { static_cast<bool>(m_func); }) {
if (m_func) m_func();
} else {
m_func();
}
} }
}; };

View file

@ -1,6 +1,6 @@
#pragma once #pragma once
#include "Result.hpp" #include <Geode/Result.hpp>
#include "general.hpp" #include "general.hpp"
#include "../loader/Event.hpp" #include "../loader/Event.hpp"
#include "Task.hpp" #include "Task.hpp"
@ -13,14 +13,15 @@
template <> template <>
struct matjson::Serialize<std::filesystem::path> { struct matjson::Serialize<std::filesystem::path> {
static matjson::Value to_json(std::filesystem::path const& path) { static geode::Result<std::filesystem::path, std::string> fromJson(Value const& value)
return path.string(); {
GEODE_UNWRAP_INTO(auto str, value.asString());
return geode::Ok(std::filesystem::path(str).make_preferred());
} }
static std::filesystem::path from_json(matjson::Value const& value) {
return std::filesystem::path(value.as_string()).make_preferred(); static Value toJson(std::filesystem::path const& value)
} {
static bool is_json(matjson::Value const& value) { return Value(value.string());
return value.is_string();
} }
}; };
@ -32,10 +33,7 @@ namespace geode::utils::file {
template <class T> template <class T>
Result<T> readFromJson(std::filesystem::path const& file) { Result<T> readFromJson(std::filesystem::path const& file) {
GEODE_UNWRAP_INTO(auto json, readJson(file)); GEODE_UNWRAP_INTO(auto json, readJson(file));
if (!json.is<T>()) { return json.as<T>();
return Err("JSON is not of type {}", typeid(T).name());
}
return Ok(json.as<T>());
} }
GEODE_DLL Result<> writeString(std::filesystem::path const& path, std::string const& data); GEODE_DLL Result<> writeString(std::filesystem::path const& path, std::string const& data);
@ -162,7 +160,7 @@ namespace geode::utils::file {
* @param callback Callback to call with the progress of the unzip operation * @param callback Callback to call with the progress of the unzip operation
*/ */
void setProgressCallback( void setProgressCallback(
utils::MiniFunction<void(uint32_t, uint32_t)> callback std::function<void(uint32_t, uint32_t)> callback
); );
/** /**
@ -213,7 +211,7 @@ namespace geode::utils::file {
); );
static Result<> intoDir( static Result<> intoDir(
utils::MiniFunction<void(uint32_t, uint32_t)> progressCallback, std::function<void(uint32_t, uint32_t)> progressCallback,
Path const& from, Path const& from,
Path const& to, Path const& to,
bool deleteZipAfter = false bool deleteZipAfter = false
@ -281,7 +279,7 @@ namespace geode::utils::file {
public: public:
using Callback = void(FileWatchEvent*); using Callback = void(FileWatchEvent*);
ListenerResult handle(utils::MiniFunction<Callback> callback, FileWatchEvent* event); ListenerResult handle(std::function<Callback> callback, FileWatchEvent* event);
FileWatchFilter(std::filesystem::path const& path); FileWatchFilter(std::filesystem::path const& path);
}; };

View file

@ -1,11 +1,10 @@
#pragma once #pragma once
#include "Result.hpp" #include <Geode/Result.hpp>
#include "../DefaultInclude.hpp" #include "../DefaultInclude.hpp"
#include <chrono> #include <chrono>
#include <iomanip> #include <iomanip>
#include <sstream>
#include <string> #include <string>
#include <vector> #include <vector>
#include <filesystem> #include <filesystem>
@ -13,19 +12,11 @@
#include <charconv> #include <charconv>
#include <clocale> #include <clocale>
#include <type_traits> #include <type_traits>
#include <fmt/format.h>
namespace geode { namespace geode {
using ByteVector = std::vector<uint8_t>; using ByteVector = std::vector<uint8_t>;
// todo in v4: remove this
template <typename T>
[[deprecated("Use geode::toBytes instead")]]
ByteVector toByteArray(T const& a) {
ByteVector out;
out.resize(sizeof(T));
std::memcpy(out.data(), &a, sizeof(T));
return out;
}
template <typename T> template <typename T>
ByteVector toBytes(T const& a) { ByteVector toBytes(T const& a) {
ByteVector out; ByteVector out;
@ -72,9 +63,7 @@ namespace geode {
template <typename T> template <typename T>
std::string intToHex(T i) { std::string intToHex(T i) {
std::stringstream stream; return fmt::format("{:#x}", i);
stream << std::showbase << std::setbase(16) << (uint64_t)i;
return stream.str();
} }
/** /**
@ -83,15 +72,16 @@ namespace geode {
* @param num Number to convert to string * @param num Number to convert to string
* @param precision Precision of the converted number * @param precision Precision of the converted number
* @returns Number as string * @returns Number as string
* @note Precision has no effect on integers
*/ */
template <class Num> template <class Num>
std::string numToString(Num num, size_t precision = 0) { std::string numToString(Num num, size_t precision = 0) {
std::stringstream ss; if constexpr (std::is_floating_point_v<Num>) {
if (precision) { if (precision) {
ss << std::fixed << std::setprecision(precision); return fmt::format("{:.{}f}", num, precision);
}
} }
ss << num; return fmt::to_string(num);
return ss.str();
} }
/** /**
@ -125,7 +115,7 @@ namespace geode {
* @returns String as number, or Err if the string couldn't be converted * @returns String as number, or Err if the string couldn't be converted
*/ */
template <class Num> template <class Num>
Result<Num> numFromString(std::string_view const str, int base = 10) { Result<Num> numFromString(std::string_view str, int base = 10) {
if constexpr (std::is_floating_point_v<Num> if constexpr (std::is_floating_point_v<Num>
#if defined(__cpp_lib_to_chars) #if defined(__cpp_lib_to_chars)
&& false && false
@ -168,12 +158,18 @@ namespace geode {
*/ */
GEODE_DLL float getDisplayFactor(); GEODE_DLL float getDisplayFactor();
} }
template <class... Args>
requires (sizeof...(Args) > 0)
constexpr auto Err(fmt::format_string<Args...> fmt, Args&&... args) {
return Err(fmt::format(fmt, std::forward<Args>(args)...));
}
} }
template<> template<>
struct matjson::Serialize<geode::ByteVector> { struct matjson::Serialize<geode::ByteVector> {
static matjson::Value to_json(geode::ByteVector const& bytes) { static Value toJson(geode::ByteVector const& bytes) {
return matjson::Array(bytes.begin(), bytes.end()); return std::vector<matjson::Value>(bytes.begin(), bytes.end());
} }
}; };

View file

@ -1,6 +1,6 @@
#pragma once #pragma once
#include "Result.hpp" #include <Geode/Result.hpp>
#include <Geode/DefaultInclude.hpp> #include <Geode/DefaultInclude.hpp>
#include <functional> #include <functional>
@ -20,7 +20,7 @@ namespace geode::utils::map {
* false if not. * false if not.
*/ */
template <typename T, typename R, typename H> template <typename T, typename R, typename H>
bool contains(std::unordered_map<T, R, H> const& map, utils::MiniFunction<bool(R)> containFunc) { bool contains(std::unordered_map<T, R, H> const& map, std::function<bool(R)> containFunc) {
for (auto const& [_, r] : map) { for (auto const& [_, r] : map) {
if (containFunc(r)) return true; if (containFunc(r)) return true;
} }
@ -39,7 +39,7 @@ namespace geode::utils::map {
* a pointer. * a pointer.
*/ */
template <class T, class R, class H> template <class T, class R, class H>
R select(std::unordered_map<T, R, H> const& map, utils::MiniFunction<bool(R)> selectFunc) { R select(std::unordered_map<T, R, H> const& map, std::function<bool(R)> selectFunc) {
for (auto const& [_, r] : map) { for (auto const& [_, r] : map) {
if (selectFunc(r)) return r; if (selectFunc(r)) return r;
} }
@ -59,7 +59,7 @@ namespace geode::utils::map {
*/ */
template <class T, class R, class H> template <class T, class R, class H>
std::vector<R> selectAll( std::vector<R> selectAll(
std::unordered_map<T, R, H> const& map, utils::MiniFunction<bool(R)> selectFunc std::unordered_map<T, R, H> const& map, std::function<bool(R)> selectFunc
) { ) {
std::vector<R> res; std::vector<R> res;
for (auto const& [_, r] : map) { for (auto const& [_, r] : map) {
@ -111,7 +111,7 @@ namespace geode::utils::map {
template <class T1, class V1, class H1, class T2, class V2, class H2> template <class T1, class V1, class H1, class T2, class V2, class H2>
std::unordered_map<T2, V2, H2> remap( std::unordered_map<T2, V2, H2> remap(
std::unordered_map<T1, V1, H1> const& map, std::unordered_map<T1, V1, H1> const& map,
utils::MiniFunction<std::pair<T2, V2>(std::pair<T1, V1>)> remapFunc std::function<std::pair<T2, V2>(std::pair<T1, V1>)> remapFunc
) { ) {
std::unordered_map<T2, V2, H2> res; std::unordered_map<T2, V2, H2> res;
for (auto const& [t, v] : map) { for (auto const& [t, v] : map) {

View file

@ -1,7 +1,6 @@
#pragma once #pragma once
#include <Geode/DefaultInclude.hpp> #include <Geode/DefaultInclude.hpp>
#include "MiniFunction.hpp"
#include <string_view> #include <string_view>
namespace geode::utils::permission { namespace geode::utils::permission {
@ -21,5 +20,5 @@ namespace geode::utils::permission {
* @param permission The permission * @param permission The permission
* @param callback The callback, passed value is 'true' if permission was granted and 'false' otherwise. * @param callback The callback, passed value is 'true' if permission was granted and 'false' otherwise.
*/ */
void GEODE_DLL requestPermission(Permission permission, utils::MiniFunction<void(bool)> callback); void GEODE_DLL requestPermission(Permission permission, std::function<void(bool)> callback);
} }

View file

@ -4,6 +4,7 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include <compare> #include <compare>
#include "../DefaultInclude.hpp"
namespace geode::utils::string { namespace geode::utils::string {
/** /**

View file

@ -71,6 +71,7 @@ namespace geode::utils {
std::string m_msg; std::string m_msg;
Timer<Clock> m_timer; Timer<Clock> m_timer;
// @geode-ignore(geode-alternative)
LogPerformance(std::string const& msg = "", std::ostream& out = std::cout) : LogPerformance(std::string const& msg = "", std::ostream& out = std::cout) :
m_msg(msg), m_output(out) { m_msg(msg), m_output(out) {
m_timer = Timer<Clock>(); m_timer = Timer<Clock>();

View file

@ -2,7 +2,7 @@
#include <Geode/loader/Loader.hpp> // another great circular dependency fix #include <Geode/loader/Loader.hpp> // another great circular dependency fix
#include <matjson.hpp> #include <matjson.hpp>
#include "Result.hpp" #include <Geode/Result.hpp>
#include "Task.hpp" #include "Task.hpp"
#include <chrono> #include <chrono>
#include <optional> #include <optional>
@ -83,6 +83,15 @@ namespace geode::utils::web {
std::vector<std::string> headers() const; std::vector<std::string> headers() const;
std::optional<std::string> header(std::string_view name) const; std::optional<std::string> header(std::string_view name) const;
/**
* Retrieves a list of all headers from the response with a given name - there can be
* multiple headers with the same name, such as Set-Cookie, with each cookie in a separate
* header
* @param name name of the header
* @return std::optional<std::vector<std::string>>
*/
std::optional<std::vector<std::string>> getAllHeadersNamed(std::string_view name) const;
}; };
class GEODE_DLL WebProgress final { class GEODE_DLL WebProgress final {
@ -203,6 +212,15 @@ namespace geode::utils::web {
*/ */
WebRequest& followRedirects(bool enabled); WebRequest& followRedirects(bool enabled);
/**
* Enables or disables ignoring the content length header.
* The default is false.
*
* @param enabled
* @return WebRequest&
*/
WebRequest& ignoreContentLength(bool enabled);
/** /**
* Sets the Certificate Authority (CA) bundle content. * Sets the Certificate Authority (CA) bundle content.
* Defaults to not sending a CA bundle. * Defaults to not sending a CA bundle.
@ -277,9 +295,9 @@ namespace geode::utils::web {
/** /**
* Gets the request headers * Gets the request headers
* *
* @return std::unordered_map<std::string, std::string> * @return std::unordered_map<std::string, std::vector<std::string>>
*/ */
std::unordered_map<std::string, std::string> getHeaders() const; std::unordered_map<std::string, std::vector<std::string>> getHeaders() const;
/** /**
* Gets the parameters inside the URL * Gets the parameters inside the URL

Binary file not shown.

View file

@ -6,6 +6,7 @@
struct XINPUT_STATE; struct XINPUT_STATE;
struct XINPUT_CAPABILITIES; struct XINPUT_CAPABILITIES;
struct XINPUT_VIBRATION;
constexpr static auto MAX_PATH_CHARS = 32768u; constexpr static auto MAX_PATH_CHARS = 32768u;
@ -41,6 +42,17 @@ extern "C" DWORD XInputGetState(DWORD dwUserIndex, XINPUT_STATE *pState) {
return ERROR_DEVICE_NOT_CONNECTED; return ERROR_DEVICE_NOT_CONNECTED;
} }
#pragma comment(linker, "/export:XInputSetState,@3")
extern "C" DWORD XInputSetState(DWORD dwUserIndex, XINPUT_VIBRATION* pVibration) {
static auto fp = getFP("XInputSetState");
if (fp) {
using FPType = decltype(&XInputSetState);
return reinterpret_cast<FPType>(fp)(dwUserIndex, pVibration);
}
return ERROR_DEVICE_NOT_CONNECTED;
}
#pragma comment(linker, "/export:XInputGetCapabilities,@4") #pragma comment(linker, "/export:XInputGetCapabilities,@4")
extern "C" DWORD XInputGetCapabilities(DWORD dwUserIndex, DWORD dwFlags, XINPUT_CAPABILITIES *pCapabilities) { extern "C" DWORD XInputGetCapabilities(DWORD dwUserIndex, DWORD dwFlags, XINPUT_CAPABILITIES *pCapabilities) {
static auto fp = getFP("XInputGetCapabilities"); static auto fp = getFP("XInputGetCapabilities");

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

View file

@ -85,6 +85,12 @@
"name": "Enable Geode-Themed Colors", "name": "Enable Geode-Themed Colors",
"description": "When enabled, the Geode menu has a <ca>Geode-themed color scheme</c>. <cy>This does not affect any other menus!</c>" "description": "When enabled, the Geode menu has a <ca>Geode-themed color scheme</c>. <cy>This does not affect any other menus!</c>"
}, },
"infinite-local-mods-list": {
"type": "bool",
"default": false,
"name": "Expand Installed Mods List",
"description": "Make the installed mods list a single infinite scrollable list instead of having pages"
},
"copy-mods": { "copy-mods": {
"type": "custom:copy-mods", "type": "custom:copy-mods",
"name": "" "name": ""

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 16 KiB

View file

@ -13,7 +13,7 @@ namespace geode::stl {
void free(); void free();
char* getStorage(); char* getStorage();
void setStorage(const std::string_view); void setStorage(std::string_view);
size_t getSize(); size_t getSize();
void setSize(size_t); void setSize(size_t);

View file

@ -100,11 +100,11 @@ namespace gd {
bool string::operator==(string const& other) const { bool string::operator==(string const& other) const {
return std::string_view(*this) == std::string_view(other); return std::string_view(*this) == std::string_view(other);
} }
bool string::operator==(std::string_view const other) const { bool string::operator==(std::string_view other) const {
return std::string_view(*this) == other; return std::string_view(*this) == other;
} }
std::strong_ordering string::operator<=>(std::string_view const other) const { std::strong_ordering string::operator<=>(std::string_view other) const {
return static_cast<std::strong_ordering>(std::string_view(*this).compare(other) <=> 0); return static_cast<std::strong_ordering>(std::string_view(*this).compare(other) <=> 0);
} }

View file

@ -1026,14 +1026,6 @@ std::optional<AxisAlignment> AxisLayoutOptions::getCrossAxisAlignment() const {
return m_impl->m_crossAxisAlignment; return m_impl->m_crossAxisAlignment;
} }
AxisLayoutOptions* AxisLayoutOptions::setMaxScale(float scale) {
m_impl->m_scaleLimits.second = scale;
return this;
}
AxisLayoutOptions* AxisLayoutOptions::setMinScale(float scale) {
m_impl->m_scaleLimits.first = scale;
return this;
}
AxisLayoutOptions* AxisLayoutOptions::setScaleLimits(std::optional<float> min, std::optional<float> max) { AxisLayoutOptions* AxisLayoutOptions::setScaleLimits(std::optional<float> min, std::optional<float> max) {
m_impl->m_scaleLimits = { min, max }; m_impl->m_scaleLimits = { min, max };
return this; return this;

View file

@ -8,11 +8,15 @@ $execute {
// this is needed because the transitions in cocos uses dynamic cast to check // this is needed because the transitions in cocos uses dynamic cast to check
// layers, which fail on user layers due to typeinfo not matching // layers, which fail on user layers due to typeinfo not matching
#if defined(GEODE_IS_MAC) && GEODE_COMP_GD_VERSION != 22074
#error "Unsupported version for macOS dynamic cast fix, please update the addresses"
#endif
#if defined(GEODE_IS_INTEL_MAC) #if defined(GEODE_IS_INTEL_MAC)
void* dynamicCastAddr = reinterpret_cast<void*>(base::get() + 0x7dd5e7); void* dynamicCastAddr = reinterpret_cast<void*>(base::get() + 0x7ba1d8);
(void) Mod::get()->hook(dynamicCastAddr, &cast::typeinfoCastInternal, "__dynamic_cast"); (void) Mod::get()->hook(dynamicCastAddr, &cast::typeinfoCastInternal, "__dynamic_cast");
#elif defined(GEODE_IS_ARM_MAC) #elif defined(GEODE_IS_ARM_MAC)
void* dynamicCastAddr = reinterpret_cast<void*>(base::get() + 0x6dfb10); void* dynamicCastAddr = reinterpret_cast<void*>(base::get() + 0x6c8bcc);
(void)Mod::get()->hook(dynamicCastAddr, &cast::typeinfoCastInternal, "__dynamic_cast"); (void)Mod::get()->hook(dynamicCastAddr, &cast::typeinfoCastInternal, "__dynamic_cast");
#elif defined(GEODE_IS_ANDROID) #elif defined(GEODE_IS_ANDROID)
void* handle = dlopen("libcocos2dcpp.so", RTLD_LAZY | RTLD_NOLOAD); void* handle = dlopen("libcocos2dcpp.so", RTLD_LAZY | RTLD_NOLOAD);

View file

@ -62,10 +62,6 @@ public:
return meta; return meta;
} }
FieldContainer* getFieldContainer() {
return nullptr;
}
FieldContainer* getFieldContainer(char const* forClass) { FieldContainer* getFieldContainer(char const* forClass) {
if (!m_classFieldContainers.count(forClass)) { if (!m_classFieldContainers.count(forClass)) {
m_classFieldContainers[forClass] = new FieldContainer(); m_classFieldContainers[forClass] = new FieldContainer();
@ -104,16 +100,11 @@ size_t modifier::getFieldIndexForClass(char const* name) {
return s_nextIndex[name]++; return s_nextIndex[name]++;
} }
// not const because might modify contents
FieldContainer* CCNode::getFieldContainer() {
return GeodeNodeMetadata::set(this)->getFieldContainer();
}
FieldContainer* CCNode::getFieldContainer(char const* forClass) { FieldContainer* CCNode::getFieldContainer(char const* forClass) {
return GeodeNodeMetadata::set(this)->getFieldContainer(forClass); return GeodeNodeMetadata::set(this)->getFieldContainer(forClass);
} }
std::string CCNode::getID() { const std::string& CCNode::getID() {
return GeodeNodeMetadata::set(this)->m_id; return GeodeNodeMetadata::set(this)->m_id;
} }
@ -121,7 +112,11 @@ void CCNode::setID(std::string const& id) {
GeodeNodeMetadata::set(this)->m_id = id; GeodeNodeMetadata::set(this)->m_id = id;
} }
CCNode* CCNode::getChildByID(std::string const& id) { void CCNode::setID(std::string&& id) {
GeodeNodeMetadata::set(this)->m_id = std::move(id);
}
CCNode* CCNode::getChildByID(std::string_view id) {
for (auto child : CCArrayExt<CCNode*>(this->getChildren())) { for (auto child : CCArrayExt<CCNode*>(this->getChildren())) {
if (child->getID() == id) { if (child->getID() == id) {
return child; return child;
@ -130,7 +125,7 @@ CCNode* CCNode::getChildByID(std::string const& id) {
return nullptr; return nullptr;
} }
CCNode* CCNode::getChildByIDRecursive(std::string const& id) { CCNode* CCNode::getChildByIDRecursive(std::string_view id) {
if (auto child = this->getChildByID(id)) { if (auto child = this->getChildByID(id)) {
return child; return child;
} }
@ -189,7 +184,7 @@ private:
std::unique_ptr<NodeQuery> m_next = nullptr; std::unique_ptr<NodeQuery> m_next = nullptr;
public: public:
static Result<std::unique_ptr<NodeQuery>> parse(std::string const& query) { static Result<std::unique_ptr<NodeQuery>> parse(std::string_view query) {
if (query.empty()) { if (query.empty()) {
return Err("Query may not be empty"); return Err("Query may not be empty");
} }
@ -287,7 +282,7 @@ public:
} }
}; };
CCNode* CCNode::querySelector(std::string const& queryStr) { CCNode* CCNode::querySelector(std::string_view queryStr) {
auto res = NodeQuery::parse(queryStr); auto res = NodeQuery::parse(queryStr);
if (!res) { if (!res) {
log::error("Invalid CCNode::querySelector query '{}': {}", queryStr, res.unwrapErr()); log::error("Invalid CCNode::querySelector query '{}': {}", queryStr, res.unwrapErr());
@ -298,7 +293,7 @@ CCNode* CCNode::querySelector(std::string const& queryStr) {
return query->match(this); return query->match(this);
} }
void CCNode::removeChildByID(std::string const& id) { void CCNode::removeChildByID(std::string_view id) {
if (auto child = this->getChildByID(id)) { if (auto child = this->getChildByID(id)) {
this->removeChild(child); this->removeChild(child);
} }
@ -344,7 +339,7 @@ void CCNode::updateLayout(bool updateChildOrder) {
UserObjectSetEvent::UserObjectSetEvent(CCNode* node, std::string const& id, CCObject* value) UserObjectSetEvent::UserObjectSetEvent(CCNode* node, std::string const& id, CCObject* value)
: node(node), id(id), value(value) {} : node(node), id(id), value(value) {}
ListenerResult AttributeSetFilter::handle(MiniFunction<Callback> fn, UserObjectSetEvent* event) { ListenerResult AttributeSetFilter::handle(std::function<Callback> fn, UserObjectSetEvent* event) {
if (event->id == m_targetID) { if (event->id == m_targetID) {
fn(event); fn(event);
} }
@ -455,13 +450,4 @@ void CCNode::updateAnchoredPosition(Anchor anchor, CCPoint const& offset, CCPoin
} }
} }
#ifdef GEODE_EXPORTING
void CCNode::setAttribute(std::string const& attr, matjson::Value const& value) {}
std::optional<matjson::Value> CCNode::getAttributeInternal(std::string const& attr) {
return std::nullopt;
}
#endif
#pragma warning(pop) #pragma warning(pop)

View file

@ -75,6 +75,7 @@ struct CustomMenuLayer : Modify<CustomMenuLayer, MenuLayer> {
this->fixSocialMenu(); this->fixSocialMenu();
//this code doesnt run have fun figuring out why idc enough
if (auto node = this->getChildByID("settings-gamepad-icon")) { if (auto node = this->getChildByID("settings-gamepad-icon")) {
node->setPositionX( node->setPositionX(
bottomMenu->getChildByID("settings-button")->getPositionX() + winSize.width / 2 bottomMenu->getChildByID("settings-button")->getPositionX() + winSize.width / 2
@ -83,13 +84,12 @@ struct CustomMenuLayer : Modify<CustomMenuLayer, MenuLayer> {
} }
// show if some mods failed to load // show if some mods failed to load
if (Loader::get()->getProblems().size()) { if (Loader::get()->getLoadProblems().size()) {
static bool shownProblemPopup = false; static bool shownProblemPopup = false;
if (!shownProblemPopup) { if (!shownProblemPopup) {
shownProblemPopup = true; shownProblemPopup = true;
Notification::create("There were errors - see Geode page!", NotificationIcon::Error)->show(); Notification::create("There were errors - see Geode page!", NotificationIcon::Error)->show();
} }
if (m_fields->m_geodeButton) { if (m_fields->m_geodeButton) {
m_fields->m_exclamation = CCSprite::createWithSpriteFrameName("exMark_001.png"); m_fields->m_exclamation = CCSprite::createWithSpriteFrameName("exMark_001.png");
m_fields->m_exclamation->setPosition(m_fields->m_geodeButton->getContentSize() - ccp(10, 10)); m_fields->m_exclamation->setPosition(m_fields->m_geodeButton->getContentSize() - ccp(10, 10));

View file

@ -41,9 +41,9 @@ $execute {
// hook them to call our own handler // hook them to call our own handler
if (LoaderImpl::get()->isForwardCompatMode()) return; if (LoaderImpl::get()->isForwardCompatMode()) return;
#if GEODE_COMP_GD_VERSION == 22060 #if GEODE_COMP_GD_VERSION == 22074
const uintptr_t offset1 = 0x75d00; // member function in CCEGLView const uintptr_t offset1 = 0x75D90; // member function in CCEGLView
const uintptr_t offset2 = 0x75d60; // static function const uintptr_t offset2 = 0x75DF0; // static function
(void) Mod::get()->hook( (void) Mod::get()->hook(
reinterpret_cast<void*>(geode::base::getCocos() + offset1), reinterpret_cast<void*>(geode::base::getCocos() + offset1),

View file

@ -37,15 +37,15 @@ static void patchCall(uintptr_t addr, uintptr_t newCall) {
$execute { $execute {
if (LoaderImpl::get()->isForwardCompatMode()) return; if (LoaderImpl::get()->isForwardCompatMode()) return;
#if GEODE_COMP_GD_VERSION == 22040
// BitmapDC::~BitmapDC // BitmapDC::~BitmapDC
#if 0 // TODO: mat GEODE_COMP_GD_VERSION == 22040
patchCall(0xC9A56, (uintptr_t)&RemoveFontResourceWHook); patchCall(0xC9A56, (uintptr_t)&RemoveFontResourceWHook);
// BitmapDC::setFont // BitmapDC::setFont
patchCall(0xCB5BC, (uintptr_t)&RemoveFontResourceWHook); patchCall(0xCB5BC, (uintptr_t)&RemoveFontResourceWHook);
patchCall(0xCB642, (uintptr_t)&AddFontResourceWHook); patchCall(0xCB642, (uintptr_t)&AddFontResourceWHook);
#else #else
// #pragma message("Unsupported GD version!") #pragma message("Unsupported GD version!")
#endif #endif
}; };

View file

@ -2,10 +2,15 @@
using namespace geode::prelude; using namespace geode::prelude;
#ifdef GEODE_IS_WINDOWS
#include <Geode/modify/AppDelegate.hpp>
#else
#include <Geode/modify/AchievementNotifier.hpp>
#endif
namespace geode {
#ifdef GEODE_IS_WINDOWS #ifdef GEODE_IS_WINDOWS
#include <Geode/modify/AppDelegate.hpp>
struct SceneSwitch : Modify<SceneSwitch, AppDelegate> { struct SceneSwitch : Modify<SceneSwitch, AppDelegate> {
GEODE_FORWARD_COMPAT_DISABLE_HOOKS("persist disabled") GEODE_FORWARD_COMPAT_DISABLE_HOOKS("persist disabled")
void willSwitchToScene(CCScene* scene) { void willSwitchToScene(CCScene* scene) {
@ -15,8 +20,6 @@ struct SceneSwitch : Modify<SceneSwitch, AppDelegate> {
}; };
#else #else
#include <Geode/modify/AchievementNotifier.hpp>
struct SceneSwitch : Modify<SceneSwitch, AchievementNotifier> { struct SceneSwitch : Modify<SceneSwitch, AchievementNotifier> {
GEODE_FORWARD_COMPAT_DISABLE_HOOKS("persist disabled") GEODE_FORWARD_COMPAT_DISABLE_HOOKS("persist disabled")
void willSwitchToScene(CCScene* scene) { void willSwitchToScene(CCScene* scene) {
@ -26,3 +29,5 @@ struct SceneSwitch : Modify<SceneSwitch, AchievementNotifier> {
}; };
#endif #endif
}

View file

@ -8,7 +8,7 @@ using namespace geode::prelude;
namespace { namespace {
void saveModData() { void saveModData() {
log::info("Saving mod data..."); log::info("Saving mod data...");
log::pushNest(); log::NestScope nest;
auto begin = std::chrono::high_resolution_clock::now(); auto begin = std::chrono::high_resolution_clock::now();
@ -17,8 +17,6 @@ namespace {
auto end = std::chrono::high_resolution_clock::now(); auto end = std::chrono::high_resolution_clock::now();
auto time = std::chrono::duration_cast<std::chrono::milliseconds>(end - begin).count(); auto time = std::chrono::duration_cast<std::chrono::milliseconds>(end - begin).count();
log::info("Took {}s", static_cast<float>(time) / 1000.f); log::info("Took {}s", static_cast<float>(time) / 1000.f);
log::popNest();
} }
} }

View file

@ -47,6 +47,11 @@ $register_ids(MenuLayer) {
setIDSafe<CCLabelBMFont>(this, labelOffset++, "player-username"); setIDSafe<CCLabelBMFont>(this, labelOffset++, "player-username");
if(auto node = this->getChildByID("settings-gamepad-icon")) {
// hide it until someone figures out how to bind the positioning to the actual button
node->setVisible(false);
}
// main menu // main menu
if (auto menu = this->getChildByType<CCMenu>(0)) { if (auto menu = this->getChildByType<CCMenu>(0)) {
menu->setID("main-menu"); menu->setID("main-menu");

View file

@ -2,15 +2,14 @@
#include <Geode/DefaultInclude.hpp> #include <Geode/DefaultInclude.hpp>
//#include <Geode/utils/general.hpp> //#include <Geode/utils/general.hpp>
#include <Geode/utils/MiniFunction.hpp>
#include <filesystem> #include <filesystem>
#include <functional> #include <functional>
#include <string> #include <string>
class FileWatcher { class FileWatcher {
public: public:
using FileWatchCallback = geode::utils::MiniFunction<void(std::filesystem::path)>; using FileWatchCallback = std::function<void(std::filesystem::path)>;
using ErrorCallback = geode::utils::MiniFunction<void(std::string)>; using ErrorCallback = std::function<void(std::string)>;
protected: protected:
std::filesystem::path m_file; std::filesystem::path m_file;

View file

@ -20,7 +20,7 @@ protected:
std::string content; std::string content;
std::string optionA; std::string optionA;
std::string optionB; std::string optionB;
MiniFunction<void(bool)> after; std::function<void(bool)> after;
}; };
EventListener<server::ModDownloadFilter> m_download; EventListener<server::ModDownloadFilter> m_download;
@ -79,7 +79,7 @@ protected:
public: public:
void start() { void start() {
for (auto problem : Loader::get()->getProblems()) { for (auto problem : Loader::get()->getAllProblems()) {
switch (problem.type) { switch (problem.type) {
// Errors where the correct solution is to just delete the invalid .geode package // Errors where the correct solution is to just delete the invalid .geode package
case LoadProblem::Type::InvalidFile: case LoadProblem::Type::InvalidFile:

View file

@ -24,7 +24,8 @@ void crashlog::printGeodeInfo(std::stringstream& stream) {
<< "Loader Commit: " << about::getLoaderCommitHash() << "\n" << "Loader Commit: " << about::getLoaderCommitHash() << "\n"
<< "Bindings Commit: " << about::getBindingsCommitHash() << "\n" << "Bindings Commit: " << about::getBindingsCommitHash() << "\n"
<< "Installed mods: " << Loader::get()->getAllMods().size() << "\n" << "Installed mods: " << Loader::get()->getAllMods().size() << "\n"
<< "Problems: " << Loader::get()->getProblems().size() << "\n"; << "Outdated mods: " << Loader::get()->getOutdated().size() << "\n"
<< "Problems: " << Loader::get()->getLoadProblems().size() << "\n";
} }
void crashlog::printMods(std::stringstream& stream) { void crashlog::printMods(std::stringstream& stream) {
@ -44,7 +45,8 @@ void crashlog::printMods(std::stringstream& stream) {
stream << fmt::format("{} | [{}] {}\n", stream << fmt::format("{} | [{}] {}\n",
mod->isCurrentlyLoading() ? "o"sv : mod->isCurrentlyLoading() ? "o"sv :
mod->isEnabled() ? "x"sv : mod->isEnabled() ? "x"sv :
mod->hasProblems() ? "!"sv : // thank you for this bug report mod->hasLoadProblems() ? "!"sv : // thank you for this bug report
mod->targetsOutdatedVersion() ? "*"sv : // thank you very much for this bug report
mod->shouldLoad() ? "~"sv : mod->shouldLoad() ? "~"sv :
" "sv, " "sv,
mod->getVersion().toVString(), mod->getID() mod->getVersion().toVString(), mod->getID()

View file

@ -7,7 +7,6 @@
#include <Geode/loader/Loader.hpp> #include <Geode/loader/Loader.hpp>
#include <Geode/loader/Log.hpp> #include <Geode/loader/Log.hpp>
#include <Geode/loader/Mod.hpp> #include <Geode/loader/Mod.hpp>
#include <Geode/loader/SettingEvent.hpp>
#include <Geode/utils/JsonValidation.hpp> #include <Geode/utils/JsonValidation.hpp>
#include <loader/LogImpl.hpp> #include <loader/LogImpl.hpp>
@ -30,8 +29,7 @@ $on_mod(Loaded) {
std::vector<matjson::Value> res; std::vector<matjson::Value> res;
auto args = *event->messageData; auto args = *event->messageData;
JsonChecker checker(args); auto root = checkJson(args, "[ipc/list-mods]");
auto root = checker.root("[ipc/list-mods]").obj();
auto includeRunTimeInfo = root.has("include-runtime-info").get<bool>(); auto includeRunTimeInfo = root.has("include-runtime-info").get<bool>();
auto dontIncludeLoader = root.has("dont-include-loader").get<bool>(); auto dontIncludeLoader = root.has("dont-include-loader").get<bool>();
@ -69,13 +67,13 @@ void tryShowForwardCompat() {
return; return;
// TODO: change text later // TODO: change text later
console::messageBox( // console::messageBox(
"Forward Compatibility Warning", // "Forward Compatibility Warning",
"Geode is running in a newer version of GD than Geode targets.\n" // "Geode is running in a newer version of GD than Geode targets.\n"
"UI is going to be disabled, platform console is forced on and crashes can be more common.\n" // "UI is going to be disabled, platform console is forced on and crashes can be more common.\n"
"However, if your game crashes, it is probably caused by an outdated mod and not Geode itself.", // "However, if your game crashes, it is probably caused by an outdated mod and not Geode itself.",
Severity::Warning // Severity::Warning
); // );
Mod::get()->setSavedValue<std::string>( Mod::get()->setSavedValue<std::string>(
"last-forward-compat-warn-popup-ver", "last-forward-compat-warn-popup-ver",
@ -86,7 +84,7 @@ void tryShowForwardCompat() {
#ifdef GEODE_IS_WINDOWS #ifdef GEODE_IS_WINDOWS
bool safeModeCheck() { bool safeModeCheck() {
// yes this is quite funny // yes this is quite funny
if (GetAsyncKeyState(VK_SHIFT) == 0) { if (!(GetAsyncKeyState(VK_SHIFT) & (1 << 15))) {
return false; return false;
} }
@ -140,17 +138,18 @@ int geodeEntry(void* platformData) {
// set up internal mod, settings and data // set up internal mod, settings and data
log::info("Setting up internal mod"); log::info("Setting up internal mod");
log::pushNest(); {
auto internalSetupRes = LoaderImpl::get()->setupInternalMod(); log::NestScope nest;
log::popNest(); auto internalSetupRes = LoaderImpl::get()->setupInternalMod();
if (!internalSetupRes) { if (!internalSetupRes) {
console::messageBox( console::messageBox(
"Unable to Load Geode!", "Unable to Load Geode!",
"There was a fatal error setting up " "There was a fatal error setting up "
"the internal mod and Geode can not be loaded: " + internalSetupRes.unwrapErr() "the internal mod and Geode can not be loaded: " + internalSetupRes.unwrapErr()
); );
LoaderImpl::get()->forceReset(); LoaderImpl::get()->forceReset();
return 1; return 1;
}
} }
tryShowForwardCompat(); tryShowForwardCompat();
@ -163,26 +162,28 @@ int geodeEntry(void* platformData) {
// set up loader, load mods, etc. // set up loader, load mods, etc.
log::info("Setting up loader"); log::info("Setting up loader");
log::pushNest(); {
auto setupRes = LoaderImpl::get()->setup(); log::NestScope nest;
log::popNest(); auto setupRes = LoaderImpl::get()->setup();
if (!setupRes) { if (!setupRes) {
console::messageBox( console::messageBox(
"Unable to Load Geode!", "Unable to Load Geode!",
"There was an unknown fatal error setting up " "There was an unknown fatal error setting up "
"the loader and Geode can not be loaded. " "the loader and Geode can not be loaded. "
"(" + setupRes.unwrapErr() + ")" "(" + setupRes.unwrapErr() + ")"
); );
LoaderImpl::get()->forceReset(); LoaderImpl::get()->forceReset();
return 1; return 1;
}
} }
crashlog::setupPlatformHandlerPost(); crashlog::setupPlatformHandlerPost();
log::debug("Setting up IPC"); log::debug("Setting up IPC");
log::pushNest(); {
ipc::setup(); log::NestScope nest;
log::popNest(); ipc::setup();
}
// download and install new loader update in the background // download and install new loader update in the background

View file

@ -98,7 +98,7 @@ std::string_view Hook::Impl::getDisplayName() const {
} }
matjson::Value Hook::Impl::getRuntimeInfo() const { matjson::Value Hook::Impl::getRuntimeInfo() const {
auto json = matjson::Object(); matjson::Value json;
json["address"] = std::to_string(reinterpret_cast<uintptr_t>(m_address)); json["address"] = std::to_string(reinterpret_cast<uintptr_t>(m_address));
json["detour"] = std::to_string(reinterpret_cast<uintptr_t>(m_detour)); json["detour"] = std::to_string(reinterpret_cast<uintptr_t>(m_detour));
json["name"] = m_displayName; json["name"] = m_displayName;

View file

@ -19,7 +19,7 @@ ipc::IPCEvent::IPCEvent(
ipc::IPCEvent::~IPCEvent() {} ipc::IPCEvent::~IPCEvent() {}
ListenerResult ipc::IPCFilter::handle(utils::MiniFunction<Callback> fn, IPCEvent* event) { ListenerResult ipc::IPCFilter::handle(std::function<Callback> fn, IPCEvent* event) {
if (event->targetModID == m_modID && event->messageID == m_messageID) { if (event->targetModID == m_modID && event->messageID == m_messageID) {
event->replyData = fn(event); event->replyData = fn(event);
return ListenerResult::Stop; return ListenerResult::Stop;
@ -33,19 +33,18 @@ ipc::IPCFilter::IPCFilter(std::string const& modID, std::string const& messageID
matjson::Value ipc::processRaw(void* rawHandle, std::string const& buffer) { matjson::Value ipc::processRaw(void* rawHandle, std::string const& buffer) {
matjson::Value reply; matjson::Value reply;
std::string error; auto res = matjson::Value::parse(buffer);
auto res = matjson::parse(buffer, error); if (!res) {
if (error.size() > 0) { log::warn("Received IPC message that isn't valid JSON: {}", res.unwrapErr());
log::warn("Received IPC message that isn't valid JSON: {}", error);
return reply; return reply;
} }
matjson::Value json = res.value(); matjson::Value json = res.unwrap();
if (!json.contains("mod") || !json["mod"].is_string()) { if (!json.contains("mod") || !json["mod"].isString()) {
log::warn("Received IPC message without 'mod' field"); log::warn("Received IPC message without 'mod' field");
return reply; return reply;
} }
if (!json.contains("message") || !json["message"].is_string()) { if (!json.contains("message") || !json["message"].isString()) {
log::warn("Received IPC message without 'message' field"); log::warn("Received IPC message without 'message' field");
return reply; return reply;
} }
@ -55,6 +54,6 @@ matjson::Value ipc::processRaw(void* rawHandle, std::string const& buffer) {
} }
// log::debug("Posting IPC event"); // log::debug("Posting IPC event");
// ! warning: if the event system is ever made asynchronous this will break! // ! warning: if the event system is ever made asynchronous this will break!
IPCEvent(rawHandle, json["mod"].as_string(), json["message"].as_string(), data, reply).post(); IPCEvent(rawHandle, json["mod"].asString().unwrap(), json["message"].asString().unwrap(), data, reply).post();
return reply; return reply;
} }

View file

@ -68,13 +68,19 @@ std::vector<Mod*> Loader::getAllMods() {
std::vector<LoadProblem> Loader::getAllProblems() const { std::vector<LoadProblem> Loader::getAllProblems() const {
return m_impl->getProblems(); return m_impl->getProblems();
} }
std::vector<LoadProblem> Loader::getProblems() const { std::vector<LoadProblem> Loader::getLoadProblems() const {
std::vector<LoadProblem> result; std::vector<LoadProblem> result;
for (auto problem : this->getAllProblems()) { for (auto problem : this->getAllProblems()) {
if ( if (problem.isProblem()) {
problem.type != LoadProblem::Type::Recommendation && result.push_back(problem);
problem.type != LoadProblem::Type::Suggestion }
) { }
return result;
}
std::vector<LoadProblem> Loader::getOutdated() const {
std::vector<LoadProblem> result;
for (auto problem : this->getAllProblems()) {
if (problem.isOutdated()) {
result.push_back(problem); result.push_back(problem);
} }
} }
@ -83,10 +89,7 @@ std::vector<LoadProblem> Loader::getProblems() const {
std::vector<LoadProblem> Loader::getRecommendations() const { std::vector<LoadProblem> Loader::getRecommendations() const {
std::vector<LoadProblem> result; std::vector<LoadProblem> result;
for (auto problem : this->getAllProblems()) { for (auto problem : this->getAllProblems()) {
if ( if (problem.isSuggestion()) {
problem.type == LoadProblem::Type::Recommendation ||
problem.type == LoadProblem::Type::Suggestion
) {
result.push_back(problem); result.push_back(problem);
} }
} }
@ -109,14 +112,14 @@ std::vector<std::string> Loader::getLaunchArgumentNames() const {
return m_impl->getLaunchArgumentNames(); return m_impl->getLaunchArgumentNames();
} }
bool Loader::hasLaunchArgument(std::string_view const name) const { bool Loader::hasLaunchArgument(std::string_view name) const {
return m_impl->hasLaunchArgument(name); return m_impl->hasLaunchArgument(name);
} }
std::optional<std::string> Loader::getLaunchArgument(std::string_view const name) const { std::optional<std::string> Loader::getLaunchArgument(std::string_view name) const {
return m_impl->getLaunchArgument(name); return m_impl->getLaunchArgument(name);
} }
bool Loader::getLaunchFlag(std::string_view const name) const { bool Loader::getLaunchFlag(std::string_view name) const {
return m_impl->getLaunchFlag(name); return m_impl->getLaunchFlag(name);
} }

View file

@ -29,6 +29,9 @@
#include <string_view> #include <string_view>
#include <vector> #include <vector>
#include <server/DownloadManager.hpp>
#include <Geode/ui/Popup.hpp>
using namespace geode::prelude; using namespace geode::prelude;
Loader::Impl* LoaderImpl::get() { Loader::Impl* LoaderImpl::get() {
@ -42,6 +45,11 @@ Loader::Impl::~Impl() = default;
// Initialization // Initialization
bool Loader::Impl::isForwardCompatMode() { bool Loader::Impl::isForwardCompatMode() {
#ifdef GEODE_IS_ANDROID
// forward compat mode doesn't really make sense on android
return false;
#endif
if (!m_forwardCompatMode.has_value()) { if (!m_forwardCompatMode.has_value()) {
m_forwardCompatMode = !this->getGameVersion().empty() && m_forwardCompatMode = !this->getGameVersion().empty() &&
this->getGameVersion() != GEODE_STR(GEODE_GD_VERSION); this->getGameVersion() != GEODE_STR(GEODE_GD_VERSION);
@ -73,35 +81,32 @@ Result<> Loader::Impl::setup() {
if (this->supportsLaunchArguments()) { if (this->supportsLaunchArguments()) {
log::debug("Loading launch arguments"); log::debug("Loading launch arguments");
log::pushNest(); log::NestScope nest;
this->initLaunchArguments(); this->initLaunchArguments();
log::popNest();
} }
// on some platforms, using the crash handler overrides more convenient native handlers // on some platforms, using the crash handler overrides more convenient native handlers
if (!this->getLaunchFlag("disable-crash-handler")) { if (!this->getLaunchFlag("disable-crash-handler")) {
log::debug("Setting up crash handler"); log::debug("Setting up crash handler");
log::pushNest(); log::NestScope nest;
if (!crashlog::setupPlatformHandler()) { if (!crashlog::setupPlatformHandler()) {
log::debug("Failed to set up crash handler"); log::debug("Failed to set up crash handler");
} }
log::popNest();
} else { } else {
log::debug("Crash handler setup skipped"); log::debug("Crash handler setup skipped");
} }
log::debug("Loading hooks"); log::debug("Loading hooks");
log::pushNest(); if (log::NestScope nest; !this->loadHooks()) {
if (!this->loadHooks()) {
return Err("There were errors loading some hooks, see console for details"); return Err("There were errors loading some hooks, see console for details");
} }
log::popNest();
log::debug("Setting up directories"); log::debug("Setting up directories");
log::pushNest(); {
this->createDirectories(); log::NestScope nest;
this->addSearchPaths(); this->createDirectories();
log::popNest(); this->addSearchPaths();
}
// Trigger on_mod(Loaded) for the internal mod // Trigger on_mod(Loaded) for the internal mod
// this function is already on the gd thread, so this should be fine // this function is already on the gd thread, so this should be fine
@ -121,7 +126,7 @@ void Loader::Impl::addSearchPaths() {
void Loader::Impl::updateResources(bool forceReload) { void Loader::Impl::updateResources(bool forceReload) {
log::debug("Adding resources"); log::debug("Adding resources");
log::pushNest(); log::NestScope nest;
for (auto const& [_, mod] : m_mods) { for (auto const& [_, mod] : m_mods) {
if (!forceReload && ModImpl::getImpl(mod)->m_resourcesLoaded) if (!forceReload && ModImpl::getImpl(mod)->m_resourcesLoaded)
continue; continue;
@ -132,7 +137,6 @@ void Loader::Impl::updateResources(bool forceReload) {
// we have to call it in both places since setup is only called once ever, but updateResources is called // we have to call it in both places since setup is only called once ever, but updateResources is called
// on every texture reload // on every texture reload
CCFileUtils::get()->updatePaths(); CCFileUtils::get()->updatePaths();
log::popNest();
} }
std::vector<Mod*> Loader::Impl::getAllMods() { std::vector<Mod*> Loader::Impl::getAllMods() {
@ -170,24 +174,22 @@ bool Loader::Impl::isModVersionSupported(VersionInfo const& target) {
void Loader::Impl::saveData() { void Loader::Impl::saveData() {
for (auto& [id, mod] : m_mods) { for (auto& [id, mod] : m_mods) {
log::debug("{}", mod->getID()); log::debug("{}", mod->getID());
log::pushNest(); log::NestScope nest;
auto r = mod->saveData(); auto r = mod->saveData();
if (!r) { if (!r) {
log::warn("Unable to save data for mod \"{}\": {}", mod->getID(), r.unwrapErr()); log::warn("Unable to save data for mod \"{}\": {}", mod->getID(), r.unwrapErr());
} }
log::popNest();
} }
} }
void Loader::Impl::loadData() { void Loader::Impl::loadData() {
for (auto& [_, mod] : m_mods) { for (auto& [_, mod] : m_mods) {
log::debug("{}", mod->getID()); log::debug("{}", mod->getID());
log::pushNest(); log::NestScope nest;
auto r = mod->loadData(); auto r = mod->loadData();
if (!r) { if (!r) {
log::warn("Unable to load data for mod \"{}\": {}", mod->getID(), r.unwrapErr()); log::warn("Unable to load data for mod \"{}\": {}", mod->getID(), r.unwrapErr());
} }
log::popNest();
} }
} }
@ -230,7 +232,7 @@ void Loader::Impl::updateModResources(Mod* mod) {
return; return;
log::debug("{}", mod->getID()); log::debug("{}", mod->getID());
log::pushNest(); log::NestScope nest;
for (auto const& sheet : mod->getMetadata().getSpritesheets()) { for (auto const& sheet : mod->getMetadata().getSpritesheets()) {
log::debug("Adding sheet {}", sheet); log::debug("Adding sheet {}", sheet);
@ -250,8 +252,6 @@ void Loader::Impl::updateModResources(Mod* mod) {
CCSpriteFrameCache::get()->addSpriteFramesWithFile(plist.c_str()); CCSpriteFrameCache::get()->addSpriteFramesWithFile(plist.c_str());
} }
} }
log::popNest();
} }
void Loader::Impl::addProblem(LoadProblem const& problem) { void Loader::Impl::addProblem(LoadProblem const& problem) {
@ -267,14 +267,14 @@ void Loader::Impl::addProblem(LoadProblem const& problem) {
void Loader::Impl::queueMods(std::vector<ModMetadata>& modQueue) { void Loader::Impl::queueMods(std::vector<ModMetadata>& modQueue) {
for (auto const& dir : m_modSearchDirectories) { for (auto const& dir : m_modSearchDirectories) {
log::debug("Searching {}", dir); log::debug("Searching {}", dir);
log::pushNest(); log::NestScope nest;
for (auto const& entry : std::filesystem::directory_iterator(dir)) { for (auto const& entry : std::filesystem::directory_iterator(dir)) {
if (!std::filesystem::is_regular_file(entry) || if (!std::filesystem::is_regular_file(entry) ||
entry.path().extension() != GEODE_MOD_EXTENSION) entry.path().extension() != GEODE_MOD_EXTENSION)
continue; continue;
log::debug("Found {}", entry.path().filename()); log::debug("Found {}", entry.path().filename());
log::pushNest(); log::NestScope nest;
auto res = ModMetadata::createFromGeodeFile(entry.path()); auto res = ModMetadata::createFromGeodeFile(entry.path());
if (!res) { if (!res) {
@ -284,7 +284,6 @@ void Loader::Impl::queueMods(std::vector<ModMetadata>& modQueue) {
res.unwrapErr() res.unwrapErr()
}); });
log::error("Failed to queue: {}", res.unwrapErr()); log::error("Failed to queue: {}", res.unwrapErr());
log::popNest();
continue; continue;
} }
auto modMetadata = res.unwrap(); auto modMetadata = res.unwrap();
@ -302,14 +301,11 @@ void Loader::Impl::queueMods(std::vector<ModMetadata>& modQueue) {
"A mod with the same ID is already present." "A mod with the same ID is already present."
}); });
log::error("Failed to queue: a mod with the same ID is already queued"); log::error("Failed to queue: a mod with the same ID is already queued");
log::popNest();
continue; continue;
} }
modQueue.push_back(modMetadata); modQueue.push_back(modMetadata);
log::popNest();
} }
log::popNest();
} }
} }
@ -327,7 +323,7 @@ void Loader::Impl::populateModList(std::vector<ModMetadata>& modQueue) {
for (auto const& metadata : modQueue) { for (auto const& metadata : modQueue) {
log::debug("{} {}", metadata.getID(), metadata.getVersion()); log::debug("{} {}", metadata.getID(), metadata.getVersion());
log::pushNest(); log::NestScope nest;
auto mod = new Mod(metadata); auto mod = new Mod(metadata);
@ -339,20 +335,17 @@ void Loader::Impl::populateModList(std::vector<ModMetadata>& modQueue) {
res.unwrapErr() res.unwrapErr()
}); });
log::error("Failed to set up: {}", res.unwrapErr()); log::error("Failed to set up: {}", res.unwrapErr());
log::popNest();
continue; continue;
} }
m_mods.insert({metadata.getID(), mod}); m_mods.insert({metadata.getID(), mod});
log::popNest();
} }
} }
void Loader::Impl::buildModGraph() { void Loader::Impl::buildModGraph() {
for (auto const& [id, mod] : m_mods) { for (auto const& [id, mod] : m_mods) {
log::debug("{}", mod->getID()); log::debug("{}", mod->getID());
log::pushNest(); log::NestScope nest;
for (auto& dependency : mod->m_impl->m_metadata.m_impl->m_dependencies) { for (auto& dependency : mod->m_impl->m_metadata.m_impl->m_dependencies) {
log::debug("{}", dependency.id); log::debug("{}", dependency.id);
if (!m_mods.contains(dependency.id)) { if (!m_mods.contains(dependency.id)) {
@ -379,11 +372,38 @@ void Loader::Impl::buildModGraph() {
incompatibility.mod = incompatibility.mod =
m_mods.contains(incompatibility.id) ? m_mods[incompatibility.id] : nullptr; m_mods.contains(incompatibility.id) ? m_mods[incompatibility.id] : nullptr;
} }
log::popNest();
} }
} }
void Loader::Impl::loadModGraph(Mod* node, bool early) { void Loader::Impl::loadModGraph(Mod* node, bool early) {
// Check version first, as it's not worth trying to load a mod with an
// invalid target version
// Also this makes it so that when GD updates, outdated mods get shown as
// "Outdated" in the UI instead of "Missing Dependencies"
auto res = node->getMetadata().checkGameVersion();
if (!res) {
this->addProblem({
LoadProblem::Type::UnsupportedVersion,
node,
res.unwrapErr()
});
log::error("{}", res.unwrapErr());
return;
}
auto geodeVerRes = node->getMetadata().checkGeodeVersion();
if (!geodeVerRes) {
this->addProblem({
node->getMetadata().getGeodeVersion() > this->getVersion() ?
LoadProblem::Type::NeedsNewerGeodeVersion :
LoadProblem::Type::UnsupportedGeodeVersion,
node,
geodeVerRes.unwrapErr()
});
log::error("{}", geodeVerRes.unwrapErr());
return;
}
if (node->hasUnresolvedDependencies()) { if (node->hasUnresolvedDependencies()) {
log::debug("{} {} has unresolved dependencies", node->getID(), node->getVersion()); log::debug("{} {} has unresolved dependencies", node->getID(), node->getVersion());
return; return;
@ -393,12 +413,10 @@ void Loader::Impl::loadModGraph(Mod* node, bool early) {
return; return;
} }
log::debug("{} {}", node->getID(), node->getVersion()); log::NestScope nest;
log::pushNest();
if (node->isEnabled()) { if (node->isEnabled()) {
log::warn("Mod {} already loaded, this should never happen", node->getID()); log::warn("Mod {} already loaded, this should never happen", node->getID());
log::popNest();
return; return;
} }
@ -409,6 +427,7 @@ void Loader::Impl::loadModGraph(Mod* node, bool early) {
auto unzipFunction = [this, node]() { auto unzipFunction = [this, node]() {
log::debug("Unzip"); log::debug("Unzip");
log::NestScope nest;
auto res = node->m_impl->unzipGeodeFile(node->getMetadata()); auto res = node->m_impl->unzipGeodeFile(node->getMetadata());
return res; return res;
}; };
@ -416,6 +435,7 @@ void Loader::Impl::loadModGraph(Mod* node, bool early) {
auto loadFunction = [this, node, early]() { auto loadFunction = [this, node, early]() {
if (node->shouldLoad()) { if (node->shouldLoad()) {
log::debug("Load"); log::debug("Load");
log::NestScope nest;
auto res = node->m_impl->loadBinary(); auto res = node->m_impl->loadBinary();
if (!res) { if (!res) {
this->addProblem({ this->addProblem({
@ -441,36 +461,6 @@ void Loader::Impl::loadModGraph(Mod* node, bool early) {
}); });
log::error("{}", reason.value()); log::error("{}", reason.value());
m_refreshingModCount -= 1; m_refreshingModCount -= 1;
log::popNest();
return;
}
auto res = node->getMetadata().checkGameVersion();
if (!res) {
this->addProblem({
LoadProblem::Type::UnsupportedVersion,
node,
res.unwrapErr()
});
log::error("{}", res.unwrapErr());
m_refreshingModCount -= 1;
log::popNest();
return;
}
if (!this->isModVersionSupported(node->getMetadata().getGeodeVersion())) {
this->addProblem({
node->getMetadata().getGeodeVersion() > this->getVersion() ? LoadProblem::Type::NeedsNewerGeodeVersion : LoadProblem::Type::UnsupportedGeodeVersion,
node,
fmt::format(
"Geode version {}\nis required to run this mod\n(installed: {})",
node->getMetadata().getGeodeVersion().toVString(),
this->getVersion().toVString()
)
});
log::error("Unsupported Geode version: {}", node->getMetadata().getGeodeVersion());
m_refreshingModCount -= 1;
log::popNest();
return; return;
} }
} }
@ -485,7 +475,6 @@ void Loader::Impl::loadModGraph(Mod* node, bool early) {
}); });
log::error("Failed to unzip: {}", res.unwrapErr()); log::error("Failed to unzip: {}", res.unwrapErr());
m_refreshingModCount -= 1; m_refreshingModCount -= 1;
log::popNest();
return; return;
} }
loadFunction(); loadFunction();
@ -496,7 +485,7 @@ void Loader::Impl::loadModGraph(Mod* node, bool early) {
thread::setName("Mod Unzip"); thread::setName("Mod Unzip");
log::loadNest(nest); log::loadNest(nest);
auto res = unzipFunction(); auto res = unzipFunction();
this->queueInMainThread([=, this]() { this->queueInMainThread([=, this, res = std::move(res)]() {
auto prevNest = log::saveNest(); auto prevNest = log::saveNest();
log::loadNest(nest); log::loadNest(nest);
if (!res) { if (!res) {
@ -515,7 +504,6 @@ void Loader::Impl::loadModGraph(Mod* node, bool early) {
}); });
}).detach(); }).detach();
} }
log::popNest();
} }
void Loader::Impl::findProblems() { void Loader::Impl::findProblems() {
@ -524,8 +512,12 @@ void Loader::Impl::findProblems() {
log::debug("{} is not enabled", id); log::debug("{} is not enabled", id);
continue; continue;
} }
if (mod->targetsOutdatedVersion()) {
log::debug("{} is outdated", id);
continue;
}
log::debug("{}", id); log::debug("{}", id);
log::pushNest(); log::NestScope nest;
for (auto const& dep : mod->getMetadata().getDependencies()) { for (auto const& dep : mod->getMetadata().getDependencies()) {
if (dep.mod && dep.mod->isEnabled() && dep.version.compare(dep.mod->getVersion())) if (dep.mod && dep.mod->isEnabled() && dep.version.compare(dep.mod->getVersion()))
@ -652,18 +644,15 @@ void Loader::Impl::findProblems() {
}); });
log::error("{} failed to load for an unknown reason", id); log::error("{} failed to load for an unknown reason", id);
} }
log::popNest();
} }
} }
void Loader::Impl::refreshModGraph() { void Loader::Impl::refreshModGraph() {
log::info("Refreshing mod graph"); log::info("Refreshing mod graph");
log::pushNest(); log::NestScope nest;
if (m_isSetup) { if (m_isSetup) {
log::error("Cannot refresh mod graph after startup"); log::error("Cannot refresh mod graph after startup");
log::popNest();
return; return;
} }
@ -673,48 +662,51 @@ void Loader::Impl::refreshModGraph() {
m_loadingState = LoadingState::Queue; m_loadingState = LoadingState::Queue;
log::debug("Queueing mods"); log::debug("Queueing mods");
log::pushNest();
std::vector<ModMetadata> modQueue; std::vector<ModMetadata> modQueue;
this->queueMods(modQueue); {
log::popNest(); log::NestScope nest;
this->queueMods(modQueue);
}
m_loadingState = LoadingState::List; m_loadingState = LoadingState::List;
log::debug("Populating mod list"); log::debug("Populating mod list");
log::pushNest(); {
this->populateModList(modQueue); log::NestScope nest;
modQueue.clear(); this->populateModList(modQueue);
log::popNest(); modQueue.clear();
}
m_loadingState = LoadingState::Graph; m_loadingState = LoadingState::Graph;
log::debug("Building mod graph"); log::debug("Building mod graph");
log::pushNest(); {
this->buildModGraph(); log::NestScope nest;
log::popNest(); this->buildModGraph();
}
log::debug("Ordering mod stack"); log::debug("Ordering mod stack");
log::pushNest(); {
this->orderModStack(); log::NestScope nest;
log::popNest(); this->orderModStack();
}
m_loadingState = LoadingState::EarlyMods; m_loadingState = LoadingState::EarlyMods;
log::debug("Loading early mods"); log::debug("Loading early mods");
log::pushNest(); {
while (!m_modsToLoad.empty() && m_modsToLoad.front()->needsEarlyLoad()) { log::NestScope nest;
auto mod = m_modsToLoad.front(); while (!m_modsToLoad.empty() && m_modsToLoad.front()->needsEarlyLoad()) {
m_modsToLoad.pop_front(); auto mod = m_modsToLoad.front();
this->loadModGraph(mod, true); m_modsToLoad.pop_front();
this->loadModGraph(mod, true);
}
} }
log::popNest();
auto end = std::chrono::high_resolution_clock::now(); auto end = std::chrono::high_resolution_clock::now();
auto time = std::chrono::duration_cast<std::chrono::milliseconds>(end - begin).count(); auto time = std::chrono::duration_cast<std::chrono::milliseconds>(end - begin).count();
log::info("Took {}s. Continuing next frame...", static_cast<float>(time) / 1000.f); log::info("Took {}s. Continuing next frame...", static_cast<float>(time) / 1000.f);
log::popNest();
m_loadingState = LoadingState::Mods; m_loadingState = LoadingState::Mods;
queueInMainThread([&]() { queueInMainThread([this]() {
this->continueRefreshModGraph(); this->continueRefreshModGraph();
}); });
} }
@ -768,7 +760,7 @@ void Loader::Impl::orderModStack() {
void Loader::Impl::continueRefreshModGraph() { void Loader::Impl::continueRefreshModGraph() {
if (m_refreshingModCount != 0) { if (m_refreshingModCount != 0) {
queueInMainThread([&]() { queueInMainThread([this]() {
this->continueRefreshModGraph(); this->continueRefreshModGraph();
}); });
return; return;
@ -781,28 +773,27 @@ void Loader::Impl::continueRefreshModGraph() {
} }
log::info("Continuing mod graph refresh..."); log::info("Continuing mod graph refresh...");
log::pushNest(); log::NestScope nest;
m_timerBegin = std::chrono::high_resolution_clock::now(); m_timerBegin = std::chrono::high_resolution_clock::now();
switch (m_loadingState) { switch (m_loadingState) {
case LoadingState::Mods: case LoadingState::Mods:
if (!m_modsToLoad.empty()) { if (!m_modsToLoad.empty()) {
log::debug("Loading mods");
log::pushNest();
auto mod = m_modsToLoad.front(); auto mod = m_modsToLoad.front();
m_modsToLoad.pop_front(); m_modsToLoad.pop_front();
log::debug("Loading mod {} {}", mod->getID(), mod->getVersion());
this->loadModGraph(mod, false); this->loadModGraph(mod, false);
log::popNest();
break; break;
} }
m_loadingState = LoadingState::Problems; m_loadingState = LoadingState::Problems;
[[fallthrough]]; [[fallthrough]];
case LoadingState::Problems: case LoadingState::Problems:
log::debug("Finding problems"); log::debug("Finding problems");
log::pushNest(); {
this->findProblems(); log::NestScope nest;
log::popNest(); this->findProblems();
}
m_loadingState = LoadingState::Done; m_loadingState = LoadingState::Done;
{ {
auto end = std::chrono::high_resolution_clock::now(); auto end = std::chrono::high_resolution_clock::now();
@ -818,12 +809,10 @@ void Loader::Impl::continueRefreshModGraph() {
} }
if (m_loadingState != LoadingState::Done) { if (m_loadingState != LoadingState::Done) {
queueInMainThread([&]() { queueInMainThread([this]() {
this->continueRefreshModGraph(); this->continueRefreshModGraph();
}); });
} }
log::popNest();
} }
std::vector<LoadProblem> Loader::Impl::getProblems() const { std::vector<LoadProblem> Loader::Impl::getProblems() const {
@ -927,11 +916,11 @@ std::vector<std::string> Loader::Impl::getLaunchArgumentNames() const {
return map::keys(m_launchArgs); return map::keys(m_launchArgs);
} }
bool Loader::Impl::hasLaunchArgument(std::string_view const name) const { bool Loader::Impl::hasLaunchArgument(std::string_view name) const {
return m_launchArgs.find(std::string(name)) != m_launchArgs.end(); return m_launchArgs.find(std::string(name)) != m_launchArgs.end();
} }
std::optional<std::string> Loader::Impl::getLaunchArgument(std::string_view const name) const { std::optional<std::string> Loader::Impl::getLaunchArgument(std::string_view name) const {
auto value = m_launchArgs.find(std::string(name)); auto value = m_launchArgs.find(std::string(name));
if (value == m_launchArgs.end()) { if (value == m_launchArgs.end()) {
return std::nullopt; return std::nullopt;
@ -939,7 +928,7 @@ std::optional<std::string> Loader::Impl::getLaunchArgument(std::string_view cons
return std::optional(value->second); return std::optional(value->second);
} }
bool Loader::Impl::getLaunchFlag(std::string_view const name) const { bool Loader::Impl::getLaunchFlag(std::string_view name) const {
auto arg = this->getLaunchArgument(name); auto arg = this->getLaunchArgument(name);
return arg.has_value() && arg.value() == "true"; return arg.has_value() && arg.value() == "true";
} }
@ -967,3 +956,170 @@ bool Loader::Impl::isSafeMode() const {
void Loader::Impl::forceSafeMode() { void Loader::Impl::forceSafeMode() {
m_forceSafeMode = true; m_forceSafeMode = true;
} }
void Loader::Impl::installModManuallyFromFile(std::filesystem::path const& path, std::function<void()> after) {
auto res = ModMetadata::createFromGeodeFile(path);
if (!res) {
FLAlertLayer::create(
"Invalid File",
fmt::format(
"The path <cy>'{}'</c> is not a valid Geode mod: {}",
path.string(),
res.unwrapErr()
),
"OK"
)->show();
return;
}
auto meta = res.unwrap();
auto check = meta.checkTargetVersions();
if (!check) {
FLAlertLayer::create(
"Invalid Mod Version",
fmt::format(
"The mod <cy>{}</c> can not be installed: {}",
meta.getID(),
check.unwrapErr()
),
"OK"
)->show();
}
auto doInstallModFromFile = [this, path, meta, after]() {
std::error_code ec;
static size_t MAX_ATTEMPTS = 10;
// Figure out a free path to install to
auto installTo = dirs::getModsDir() / fmt::format("{}.geode", meta.getID());
size_t counter = 0;
while (std::filesystem::exists(installTo, ec) && counter < MAX_ATTEMPTS) {
installTo = dirs::getModsDir() / fmt::format("{}-{}.geode", meta.getID(), counter);
counter += 1;
}
// This is incredibly unlikely but theoretically possible
if (counter >= MAX_ATTEMPTS) {
FLAlertLayer::create(
"Unable to Install",
fmt::format(
"Unable to install mod <co>{}</c>: Can't find a free filename!",
meta.getID()
),
"OK"
)->show();
return;
}
// Actually copy the file over to the install directory
std::filesystem::copy_file(path, installTo, ec);
if (ec) {
FLAlertLayer::create(
"Unable to Install",
fmt::format(
"Unable to install mod <co>{}</c>: {} (Error code <cr>{}</c>)",
meta.getID(), ec.message(), ec.value()
),
"OK"
)->show();
return;
}
// Mark an updated mod as updated or add to the mods list
if (m_mods.contains(meta.getID())) {
m_mods.at(meta.getID())->m_impl->m_requestedAction = ModRequestedAction::Update;
}
// Otherwise add a new Mod
// This should be safe as all of the scary stuff in setup() is only relevant
// for mods that are actually running
else {
auto mod = new Mod(meta);
auto res = mod->m_impl->setup();
if (!res) {
log::error("Unable to set up manually installed mod: {}", res.unwrapErr());
}
(void)mod->enable();
m_mods.insert({ meta.getID(), mod });
}
if (after) after();
// No need for the user to go and manually clean up the file
createQuickPopup(
"Mod Installed",
fmt::format(
"Mod <co>{}</c> has been successfully installed from file! "
"<cy>Do you want to delete the original file?</c>",
meta.getName()
),
"OK", "Delete File",
[path](auto, bool btn2) {
if (btn2) {
std::error_code ec;
std::filesystem::remove(path, ec);
if (ec) {
FLAlertLayer::create(
"Unable to Delete",
fmt::format(
"Unable to delete <cy>{}</c>: {} (Error code <cr>{}</c>)",
path, ec.message(), ec.value()
),
"OK"
)->show();
}
// No need to show a confirmation popup if successful since that's
// to be assumed via pressing the button on the previous popup
}
}
);
};
if (auto existing = Loader::get()->getInstalledMod(meta.getID())) {
createQuickPopup(
"Already Installed",
fmt::format(
"The mod <cy>{}</c> <cj>{}</c> has already been installed "
"as version <cl>{}</c>. Do you want to <co>replace the "
"installed version with the file</c>?",
meta.getID(), meta.getVersion(),
existing->getVersion()
),
"Cancel", "Replace",
[doInstallModFromFile, path, existing, meta](auto, bool btn2) mutable {
std::error_code ec;
std::filesystem::remove(existing->getPackagePath(), ec);
if (ec) {
FLAlertLayer::create(
"Unable to Uninstall",
fmt::format(
"Unable to uninstall <cy>{}</c>: {} (Error code <cr>{}</c>)",
existing->getID(), ec.message(), ec.value()
),
"OK"
)->show();
return;
}
doInstallModFromFile();
}
);
return;
}
doInstallModFromFile();
}
bool Loader::Impl::isRestartRequired() const {
for (auto mod : Loader::get()->getAllMods()) {
if (mod->getRequestedAction() != ModRequestedAction::None) {
return true;
}
if (ModSettingsManager::from(mod)->restartRequired()) {
return true;
}
}
if (server::ModDownloadManager::get()->wantsRestart()) {
return true;
}
return false;
}

View file

@ -7,10 +7,9 @@
#include <Geode/loader/Loader.hpp> #include <Geode/loader/Loader.hpp>
#include <Geode/loader/Log.hpp> #include <Geode/loader/Log.hpp>
#include <Geode/loader/Mod.hpp> #include <Geode/loader/Mod.hpp>
#include <Geode/utils/Result.hpp> #include <Geode/Result.hpp>
#include <Geode/utils/map.hpp> #include <Geode/utils/map.hpp>
#include <Geode/utils/ranges.hpp> #include <Geode/utils/ranges.hpp>
#include <Geode/utils/MiniFunction.hpp>
#include "ModImpl.hpp" #include "ModImpl.hpp"
#include <crashlog.hpp> #include <crashlog.hpp>
#include <mutex> #include <mutex>
@ -41,7 +40,7 @@ namespace geode {
LoadingState m_loadingState = LoadingState::None; LoadingState m_loadingState = LoadingState::None;
std::vector<utils::MiniFunction<void(void)>> m_mainThreadQueue; std::vector<std::function<void(void)>> m_mainThreadQueue;
mutable std::mutex m_mainThreadMutex; mutable std::mutex m_mainThreadMutex;
std::vector<std::pair<Hook*, Mod*>> m_uninitializedHooks; std::vector<std::pair<Hook*, Mod*>> m_uninitializedHooks;
bool m_readyToHook = false; bool m_readyToHook = false;
@ -116,9 +115,9 @@ namespace geode {
std::string getLaunchCommand() const; std::string getLaunchCommand() const;
void initLaunchArguments(); void initLaunchArguments();
std::vector<std::string> getLaunchArgumentNames() const; std::vector<std::string> getLaunchArgumentNames() const;
bool hasLaunchArgument(std::string_view const name) const; bool hasLaunchArgument(std::string_view name) const;
std::optional<std::string> getLaunchArgument(std::string_view const name) const; std::optional<std::string> getLaunchArgument(std::string_view name) const;
bool getLaunchFlag(std::string_view const name) const; bool getLaunchFlag(std::string_view name) const;
void updateResources(bool forceReload); void updateResources(bool forceReload);
@ -139,6 +138,12 @@ namespace geode {
bool isSafeMode() const; bool isSafeMode() const;
// enables safe mode, even if the launch arg wasnt provided // enables safe mode, even if the launch arg wasnt provided
void forceSafeMode(); void forceSafeMode();
// This will potentially start a whole sequence of popups that guide the
// user through installing the specific .geode file
void installModManuallyFromFile(std::filesystem::path const& path, std::function<void()> after);
bool isRestartRequired() const;
}; };
class LoaderImpl : public Loader::Impl { class LoaderImpl : public Loader::Impl {

View file

@ -20,10 +20,6 @@ std::string Mod::getName() const {
return m_impl->getName(); return m_impl->getName();
} }
std::string Mod::getDeveloper() const {
return m_impl->getDevelopers().empty() ? "" : m_impl->getDevelopers().front();
}
std::vector<std::string> Mod::getDevelopers() const { std::vector<std::string> Mod::getDevelopers() const {
return m_impl->getDevelopers(); return m_impl->getDevelopers();
} }
@ -101,9 +97,6 @@ std::vector<Mod*> Mod::getDependants() const {
} }
#endif #endif
std::optional<VersionInfo> Mod::hasAvailableUpdate() const {
return std::nullopt;
}
Mod::CheckUpdatesTask Mod::checkUpdates() const { Mod::CheckUpdatesTask Mod::checkUpdates() const {
return server::checkUpdates(this).map( return server::checkUpdates(this).map(
[](auto* result) -> Mod::CheckUpdatesTask::Value { [](auto* result) -> Mod::CheckUpdatesTask::Value {
@ -155,28 +148,14 @@ std::vector<std::string> Mod::getSettingKeys() const {
return m_impl->getSettingKeys(); return m_impl->getSettingKeys();
} }
bool Mod::hasSetting(std::string_view const key) const { bool Mod::hasSetting(std::string_view key) const {
return m_impl->hasSetting(key); return m_impl->hasSetting(key);
} }
std::optional<Setting> Mod::getSettingDefinition(std::string_view const key) const { std::shared_ptr<Setting> Mod::getSetting(std::string_view key) const {
return m_impl->m_settings->getLegacyDefinition(std::string(key));
}
SettingValue* Mod::getSetting(std::string_view const key) const {
return m_impl->m_settings->getLegacy(std::string(key)).get();
}
std::shared_ptr<SettingV3> Mod::getSettingV3(std::string_view const key) const {
return m_impl->m_settings->get(std::string(key)); return m_impl->m_settings->get(std::string(key));
} }
void Mod::registerCustomSetting(std::string_view const key, std::unique_ptr<SettingValue> value) {
auto reg = m_impl->m_settings->registerLegacyCustomSetting(key, std::move(value));
if (!reg) {
log::error("Unable to register custom setting: {}", reg.unwrapErr());
}
}
Result<> Mod::registerCustomSettingType(std::string_view type, SettingGenerator generator) { Result<> Mod::registerCustomSettingType(std::string_view type, SettingGenerator generator) {
return m_impl->m_settings->registerCustomSettingType(type, generator); return m_impl->m_settings->registerCustomSettingType(type, generator);
} }
@ -185,15 +164,15 @@ std::vector<std::string> Mod::getLaunchArgumentNames() const {
return m_impl->getLaunchArgumentNames(); return m_impl->getLaunchArgumentNames();
} }
bool Mod::hasLaunchArgument(std::string_view const name) const { bool Mod::hasLaunchArgument(std::string_view name) const {
return m_impl->hasLaunchArgument(name); return m_impl->hasLaunchArgument(name);
} }
std::optional<std::string> Mod::getLaunchArgument(std::string_view const name) const { std::optional<std::string> Mod::getLaunchArgument(std::string_view name) const {
return m_impl->getLaunchArgument(name); return m_impl->getLaunchArgument(name);
} }
bool Mod::getLaunchFlag(std::string_view const name) const { bool Mod::getLaunchFlag(std::string_view name) const {
return m_impl->getLaunchFlag(name); return m_impl->getLaunchFlag(name);
} }
@ -241,7 +220,7 @@ ModRequestedAction Mod::getRequestedAction() const {
return m_impl->getRequestedAction(); return m_impl->getRequestedAction();
} }
bool Mod::depends(std::string_view const id) const { bool Mod::depends(std::string_view id) const {
return m_impl->depends(id); return m_impl->depends(id);
} }
@ -269,12 +248,20 @@ void Mod::setLoggingEnabled(bool enabled) {
m_impl->setLoggingEnabled(enabled); m_impl->setLoggingEnabled(enabled);
} }
bool Mod::hasSavedValue(std::string_view const key) { bool Mod::hasSavedValue(std::string_view key) {
return this->getSaveContainer().contains(key); return this->getSaveContainer().contains(key);
} }
bool Mod::hasProblems() const { bool Mod::hasLoadProblems() const {
return m_impl->hasProblems(); return m_impl->hasLoadProblems();
}
std::optional<LoadProblem> Mod::targetsOutdatedVersion() const {
for (auto problem : this->getAllProblems()) {
if (problem.isOutdated()) {
return problem;
}
}
return std::nullopt;
} }
std::vector<LoadProblem> Mod::getAllProblems() const { std::vector<LoadProblem> Mod::getAllProblems() const {
return m_impl->getProblems(); return m_impl->getProblems();
@ -282,10 +269,7 @@ std::vector<LoadProblem> Mod::getAllProblems() const {
std::vector<LoadProblem> Mod::getProblems() const { std::vector<LoadProblem> Mod::getProblems() const {
std::vector<LoadProblem> result; std::vector<LoadProblem> result;
for (auto problem : this->getAllProblems()) { for (auto problem : this->getAllProblems()) {
if ( if (problem.isProblem()) {
problem.type != LoadProblem::Type::Recommendation &&
problem.type != LoadProblem::Type::Suggestion
) {
result.push_back(problem); result.push_back(problem);
} }
} }
@ -294,10 +278,7 @@ std::vector<LoadProblem> Mod::getProblems() const {
std::vector<LoadProblem> Mod::getRecommendations() const { std::vector<LoadProblem> Mod::getRecommendations() const {
std::vector<LoadProblem> result; std::vector<LoadProblem> result;
for (auto problem : this->getAllProblems()) { for (auto problem : this->getAllProblems()) {
if ( if (problem.isSuggestion()) {
problem.type == LoadProblem::Type::Recommendation ||
problem.type == LoadProblem::Type::Suggestion
) {
result.push_back(problem); result.push_back(problem);
} }
} }

Some files were not shown because too many files have changed in this diff Show more