Compare commits

...

76 commits

Author SHA1 Message Date
Justin
46f70cf4a5
Merge 4a40835f71 into 47f0b555f3 2024-11-12 19:26:58 -05:00
mat
47f0b555f3
Merge pull request #1144 from geode-sdk/v4
Geode 4.0.0
2024-11-12 21:13:07 -03:00
mat
708e6dc7f2
disable macos build for now
Some checks failed
Build Binaries / Build Windows (push) Has been cancelled
Build Binaries / Build macOS (push) Has been cancelled
Build Binaries / Build Android (64-bit) (push) Has been cancelled
Build Binaries / Build Android (32-bit) (push) Has been cancelled
Build Binaries / Publish (push) Has been cancelled
2024-11-12 20:17:07 -03:00
Fleeym
f96ea5e727 fix: write it a bit better
Some checks are pending
Build Binaries / Build Windows (push) Waiting to run
Build Binaries / Build macOS (push) Waiting to run
Build Binaries / Build Android (64-bit) (push) Waiting to run
Build Binaries / Build Android (32-bit) (push) Waiting to run
Build Binaries / Publish (push) Blocked by required conditions
2024-11-13 00:24:01 +02:00
Fleeym
0b2fc66a89 fix: don't show outdated if wantsRestart 2024-11-13 00:20:49 +02:00
Fleeym
df2528c8a5 fix: fix ModItem only displaying outdated GD 2024-11-13 00:11:01 +02:00
Fleeym
03a4387c80 bump version to v4.0.0-alpha.1 2024-11-12 23:47:56 +02:00
HJfod
5c85b3c48d Merge branch 'v4' of https://github.com/geode-sdk/geode into v4 2024-11-12 23:05:10 +02:00
HJfod
123b3abff3 somes settings UI fixes 2024-11-12 23:05:03 +02:00
Cvolton
6d13f7837d
fix outdated geode error swap 2024-11-12 21:12:05 +01:00
Cvolton
9b95301472
also show the loading circle on the former progress bar condition 2024-11-12 20:37:47 +01:00
Cvolton
02845d967e
disable the modlist slider 2024-11-12 20:35:01 +01:00
Cvolton
2d66279243
limit geode mod list error to the frame 2024-11-12 20:17:01 +01:00
Cvolton
b662aac29d
update messageboxfix to 2.2074 2024-11-12 19:56:15 +01:00
matcool
c197e2913c update libraries 2024-11-12 14:39:08 -03:00
matcool
ab196b9adf allow sending progress from Task coroutines by using co_yield 2024-11-12 12:23:50 -03:00
Oleksandr Nemesh
0ee9aebdee
add 2.2074 timestamp (#1143)
Some checks are pending
Build Binaries / Build Windows (push) Waiting to run
Build Binaries / Build macOS (push) Waiting to run
Build Binaries / Build Android (64-bit) (push) Waiting to run
Build Binaries / Build Android (32-bit) (push) Waiting to run
Build Binaries / Publish (push) Blocked by required conditions
2024-11-12 12:53:15 +01:00
matcool
00cc510d5b fix typename 2024-11-12 02:41:35 -03:00
matcool
e61b2c0595 co_await support for geode Task
see comment at the bottom of the header for more information
2024-11-12 02:32:05 -03:00
HJfod
acad3d2a8d Merge branch 'v4' of https://github.com/geode-sdk/geode into v4
Some checks are pending
Build Binaries / Build Windows (push) Waiting to run
Build Binaries / Build macOS (push) Waiting to run
Build Binaries / Build Android (64-bit) (push) Waiting to run
Build Binaries / Build Android (32-bit) (push) Waiting to run
Build Binaries / Publish (push) Blocked by required conditions
2024-11-12 01:03:37 +02:00
HJfod
09fa872781 show outdated mods in the UI + make outdated Geode ver count for that too 2024-11-12 01:03:19 +02:00
Chloe
d415c949d0
add android gameversion to mappings 2024-11-11 15:53:26 -07:00
Chloe
c9e97af18a
disable forward compat mode on android 2024-11-11 15:53:02 -07:00
Chloe
9d6b2954e5
ok there 2024-11-11 15:21:34 -07:00
Chloe
bcb856a302
update android binaries 2024-11-11 15:14:32 -07:00
Cvolton
f5f336532f
fix the stupid misaligned controller button 2024-11-11 21:36:39 +01:00
matcool
324883140a add Task::chain 2024-11-11 16:43:38 -03:00
altalk23
b1ab3eb373 thank you mat 2024-11-11 22:43:04 +03:00
altalk23
6db3084062 priorities production ready (not tested) 2024-11-11 22:40:30 +03:00
altalk23
673317d3cb hook priority changes with pre/post and Priority class for static priority values 2024-11-11 22:38:03 +03:00
altalk23
9c1f48ee64 update tuliphook take 2 2024-11-11 21:37:25 +03:00
altalk23
2940de38bc update tuliphook 2024-11-11 21:33:34 +03:00
altalk23
0c469b98fe update json and result 2024-11-11 21:25:06 +03:00
matcool
4ba8751c68 update json to 3.0.1
Some checks are pending
Build Binaries / Build Windows (push) Waiting to run
Build Binaries / Build macOS (push) Waiting to run
Build Binaries / Build Android (64-bit) (push) Waiting to run
Build Binaries / Build Android (32-bit) (push) Waiting to run
Build Binaries / Publish (push) Blocked by required conditions
2024-11-10 19:28:19 -03:00
HJfod
a3beed16f5 silly alk revert 2024-11-11 00:25:16 +02:00
Cvolton
038788bf57
fix safe mode dialog randomly appearing 2024-11-10 20:17:44 +01:00
dankmeme01
76db1268bf change some const string refs to string views and change getID to return a ref 2024-11-10 17:47:09 +01:00
dankmeme01
084fea220e code cleanup: remove unnecessary const in function parameters
Some checks are pending
Build Binaries / Build Windows (push) Waiting to run
Build Binaries / Build macOS (push) Waiting to run
Build Binaries / Build Android (64-bit) (push) Waiting to run
Build Binaries / Build Android (32-bit) (push) Waiting to run
Build Binaries / Publish (push) Blocked by required conditions
2024-11-10 16:05:20 +01:00
matcool
deab3d2517 remove GEODE_STATIC_PTR and GEODE_STATIC_VAR 2024-11-10 10:38:59 -03:00
matcool
7b171c56c9 avoid collision with result's geode_concat macro 2024-11-10 10:14:46 -03:00
HJfod
12e8bbb6a2 split problems into load problems and outdated GD version 2024-11-10 12:55:56 +02:00
HJfod
c9afa75367 move mod list arrows a bit to not overlap 2024-11-10 12:20:55 +02:00
HJfod
cad670fb31 hide search toggle 2024-11-10 12:20:44 +02:00
HJfod
efb1fbf729 add option for single-page local mods list 2024-11-10 12:17:14 +02:00
HJfod
a3b306a4e0 turns out that path was case-insensitive now... 2024-11-10 11:59:14 +02:00
HJfod
9d4e6ba0e4 header fixes (i think this might make compiling without PCH possible?) 2024-11-10 11:51:49 +02:00
matcool
be9ba27ba4 only set gd version to 2.207 on desktop
Some checks are pending
Build Binaries / Build Windows (push) Waiting to run
Build Binaries / Build macOS (push) Waiting to run
Build Binaries / Build Android (64-bit) (push) Waiting to run
Build Binaries / Build Android (32-bit) (push) Waiting to run
Build Binaries / Publish (push) Blocked by required conditions
2024-11-10 01:28:57 -03:00
matcool
2b0970d341 bump version 2024-11-10 01:26:25 -03:00
matcool
4f837a0f8b Merge remote-tracking branch 'origin/2207' into v4 2024-11-10 00:43:28 -03:00
matcool
5b10a91d8c do a few v4 todos 2024-11-10 00:43:06 -03:00
matcool
31d7c9d099 fix mod descriptions (nasty bug)
Some checks are pending
Build Binaries / Build Windows (push) Waiting to run
Build Binaries / Build macOS (push) Waiting to run
Build Binaries / Build Android (64-bit) (push) Waiting to run
Build Binaries / Build Android (32-bit) (push) Waiting to run
Build Binaries / Publish (push) Blocked by required conditions
this is a very bad issue with the result library :( alk pls fix
2024-11-09 18:34:12 -03:00
Chloe
70be7c6061
how much will this break 2024-11-09 14:33:03 -07:00
Chloe
5eda75311d
fix macos build 2024-11-09 14:27:24 -07:00
matcool
13b7cfc488 actually now 2024-11-09 16:04:38 -03:00
Justin
39dc184b88
2.207 patch updates (#1140)
Some checks failed
Build Binaries / Build Windows (push) Has been cancelled
Build Binaries / Build macOS (push) Has been cancelled
Build Binaries / Build Android (64-bit) (push) Has been cancelled
Build Binaries / Build Android (32-bit) (push) Has been cancelled
Build Binaries / Publish (push) Has been cancelled
* Update gdTimestampMap.hpp

* Update CMakeLists.txt

* Add files via upload
2024-11-09 19:55:51 +01:00
matcool
e8ef9b79c8 fix build 2024-11-09 15:51:20 -03:00
matcool
9ed55c4e7b matjson 3/3, compiles but mod about.md dont load for some reason 2024-11-09 15:16:24 -03:00
Cvolton
af23cf81d3 fix cceglview 2024-11-09 16:55:39 +01:00
matcool
d1d34aab8f update cpm to 0.40.2 2024-11-09 12:31:54 -03:00
matcool
30f872cc87 Revert "add 2.207 support"
This reverts commit 8aa2c2283a.
2024-11-09 12:29:19 -03:00
altalk23
108721dd3f matjson 2/?
Some checks are pending
Build Binaries / Build Windows (push) Waiting to run
Build Binaries / Build macOS (push) Waiting to run
Build Binaries / Build Android (64-bit) (push) Waiting to run
Build Binaries / Build Android (32-bit) (push) Waiting to run
Build Binaries / Publish (push) Blocked by required conditions
2024-11-09 09:14:00 +03:00
altalk23
f2ec5fa3f8 matjson status 1/? 2024-11-09 09:05:21 +03:00
dankmeme01
4c4770bfd3 add 2.207 support 2024-11-09 02:13:17 +01:00
dankmeme01
8aa2c2283a add 2.207 support 2024-11-09 01:42:39 +01:00
Justin
4a40835f71
Change approach 2024-11-08 12:23:18 -05:00
Justin
74d0924bcb
Part 2: Geode SDK 2024-11-08 10:20:30 -05:00
altalk23
088eddbb7b c++ as well
Some checks failed
Build Binaries / Build Windows (push) Has been cancelled
Build Binaries / Build macOS (push) Has been cancelled
Build Binaries / Build Android (64-bit) (push) Has been cancelled
Build Binaries / Build Android (32-bit) (push) Has been cancelled
Build Binaries / Publish (push) Has been cancelled
2024-11-07 20:42:33 +03:00
altalk23
693fadd9bc i love clang sometimes 2024-11-07 20:41:05 +03:00
altalk23
37e5e9f00b update tuliphook 2024-11-07 20:29:37 +03:00
The Bearodactyl
12b70db212 add hashtag symbol to CommonFilter::Any (#1131) 2024-11-07 20:29:01 +03:00
Justin
842b342dc5 yeah... 2024-11-07 20:29:01 +03:00
altalk23
bdb0c99e17 update to new result 2024-11-04 23:24:20 +03:00
altalk23
8320cf6057 Alias V3 settings to default 2024-11-04 21:27:14 +03:00
altalk23
bed622243b remove minifunction 2024-11-04 20:42:09 +03:00
altalk23
985b3aedb5 removing deprecated things part 2 (compiles) 2024-11-04 20:28:38 +03:00
altalk23
50ab4ebed7 removing deprecated things, part 1 (does not compile) 2024-11-04 20:14:23 +03:00
154 changed files with 2124 additions and 11007 deletions

View file

@ -137,6 +137,7 @@ jobs:
build-mac: build-mac:
name: Build macOS name: Build macOS
runs-on: macos-latest runs-on: macos-latest
if: false
env: env:
SCCACHE_CACHE_MULTIARCH: 1 SCCACHE_CACHE_MULTIARCH: 1

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.0.3")
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-alpha.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

@ -164,7 +164,27 @@ function(setup_geode_mod proname)
set(HAS_HEADERS Off) set(HAS_HEADERS Off)
endif() endif()
if (HAS_HEADERS AND WIN32) if (GEODE_BUNDLE_PDB AND WIN32 AND (CMAKE_BUILD_TYPE STREQUAL "Debug" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo"))
if (HAS_HEADERS)
add_custom_target(${proname}_PACKAGE ALL
DEPENDS ${proname} ${CMAKE_CURRENT_SOURCE_DIR}/mod.json
COMMAND ${GEODE_CLI} package new ${CMAKE_CURRENT_SOURCE_DIR}
--binary $<TARGET_FILE:${proname}> $<TARGET_LINKER_FILE:${proname}> $<TARGET_PDB_FILE:${proname}>
--output ${CMAKE_CURRENT_BINARY_DIR}/${MOD_ID}.geode
${INSTALL_ARG} ${PDB_ARG}
VERBATIM USES_TERMINAL
)
else()
add_custom_target(${proname}_PACKAGE ALL
DEPENDS ${proname} ${CMAKE_CURRENT_SOURCE_DIR}/mod.json
COMMAND ${GEODE_CLI} package new ${CMAKE_CURRENT_SOURCE_DIR}
--binary $<TARGET_FILE:${proname}> $<TARGET_PDB_FILE:${proname}>
--output ${CMAKE_CURRENT_BINARY_DIR}/${MOD_ID}.geode
${INSTALL_ARG} ${PDB_ARG}
VERBATIM USES_TERMINAL
)
endif()
elseif (HAS_HEADERS AND WIN32)
# this adds the .lib file on windows, which is needed for linking with the headers # this adds the .lib file on windows, which is needed for linking with the headers
add_custom_target(${proname}_PACKAGE ALL add_custom_target(${proname}_PACKAGE ALL
DEPENDS ${proname} ${CMAKE_CURRENT_SOURCE_DIR}/mod.json DEPENDS ${proname} ${CMAKE_CURRENT_SOURCE_DIR}/mod.json

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 public:
[[deprecated("Will be removed, it's an ABI break")]]
GEODE_DLL std::optional<matjson::Value> getAttributeInternal(std::string const& attribute);
#endif
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

@ -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;

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;
@ -64,10 +64,7 @@ namespace geode {
friend class DispatchEvent; friend class DispatchEvent;
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;

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"
@ -23,9 +22,7 @@
#include <unordered_map> #include <unordered_map>
#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;
@ -54,22 +51,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 +89,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 +116,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 +155,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 +179,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 +189,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 +197,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 +224,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 +243,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 +276,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 +417,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 +446,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
*/ */
@ -237,8 +219,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 +290,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;
@ -57,12 +57,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" namespace geode {
#include "../utils/file.hpp" using Setting = SettingV3;
#include <matjson.hpp> using SettingGenerator = SettingGeneratorV3;
#include <optional>
#include <unordered_set> template <class T, class V = T>
#include <cocos2d.h> using SettingBaseValue = SettingBaseValueV3<T, V>;
#pragma warning(push) using TitleSetting = TitleSettingV3;
#pragma warning(disable : 4275) using BoolSetting = BoolSettingV3;
using IntSetting = IntSettingV3;
namespace geode { using FloatSetting = FloatSettingV3;
class SettingNode; using StringSetting = StringSettingV3;
class SettingValue; using FileSetting = FileSettingV3;
using Color3BSetting = Color3BSettingV3;
struct JsonMaybeObject; using Color4BSetting = Color4BSettingV3;
struct JsonMaybeValue;
using SettingNode = SettingNodeV3;
/** template <class S>
* A Setting for a boolean value. Represented in-game as a simple toggle using SettingValueNode = SettingValueNodeV3<S>;
*/
struct GEODE_DLL BoolSetting final { using SettingChangedEvent = SettingChangedEventV3;
using ValueType = bool; using SettingChangedFilter = SettingChangedFilterV3;
using SettingNodeSizeChangeEvent = SettingNodeSizeChangeEventV3;
std::optional<std::string> name; using SettingNodeValueChangeEvent = SettingNodeValueChangeEventV3;
std::optional<std::string> description;
bool defaultValue; template <class T, class Lambda>
EventListener<SettingChangedFilter>* listenForSettingChanges(std::string_view settingKey, Lambda&& callback, Mod* mod = getMod()) {
static Result<BoolSetting> parse(JsonMaybeObject& obj); return listenForSettingChangesV3<T>(settingKey, std::forward<Lambda>(callback), mod);
}; }
/** template <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(settingKey, std::forward<Lambda>(callback), mod);
*/ }
struct GEODE_DLL IntSetting final {
using ValueType = int64_t; inline EventListener<SettingChangedFilter>* listenForAllSettingChanges(
std::function<void(std::shared_ptr<SettingV3>)> const& callback,
std::optional<std::string> name; Mod* mod = getMod()
std::optional<std::string> description; ) {
ValueType defaultValue; return listenForAllSettingChangesV3(callback, mod);
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>
bool GeodeSettingValue<T>::save(matjson::Value& json) const {
json = m_value;
return true;
}
}
#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,6 +142,9 @@ 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;
/// @brief Get a hook by name
/// @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 const& name) { Result<Hook*> getHook(std::string const& name) {
if (m_hooks.find(name) == m_hooks.end()) { if (m_hooks.find(name) == m_hooks.end()) {
return Err("Hook not in this modify"); return Err("Hook not in this modify");
@ -121,15 +152,128 @@ namespace geode::modifier {
return Ok(m_hooks[name].get()); return Ok(m_hooks[name].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 const& 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 const& 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 const& 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 ids of the mods to set the priority after
/// @returns Ok if the hook was found and the priority was set, Err if the hook was not found
template<class... C> requires (std::is_convertible_v<C, std::string> && ...)
Result<> setHookPriorityAfter(std::string const& name, C&&... after) {
GEODE_UNWRAP_INTO(auto hook, this->getHook(name));
([&](){
auto mod = Loader::get()->getInstalledMod(after);
if (!mod) return;
auto hooks = mod->getHooks();
auto func = [=](){
for (auto modHook : hooks) {
if (modHook->getAddress() != hook->getAddress()) continue;
auto priority = hook->getPriority();
if (priority < modHook->getPriority()) {
hook->setPriority(modHook->getPriority() + 1);
}
}
};
if (Loader::get()->isModLoaded(mod)) {
func();
}
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 ids of the mods to set the priority before
/// @returns Ok if the hook was found and the priority was set, Err if the hook was not found
template<class... C> requires (std::is_convertible_v<C, std::string> && ...)
Result<> setHookPriorityBefore(std::string const& name, C&&... before) {
GEODE_UNWRAP_INTO(auto hook, this->getHook(name));
([&](){
auto mod = Loader::get()->getInstalledMod(before);
if (!mod) return;
auto hooks = mod->getHooks();
auto func = [=](){
for (auto modHook : hooks) {
if (modHook->getAddress() != hook->getAddress()) continue;
auto priority = hook->getPriority();
if (priority > modHook->getPriority()) {
hook->setPriority(modHook->getPriority() - 1);
}
}
};
if (Loader::get()->isModLoaded(mod)) {
func();
}
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 ids of the mods to set the priority after
/// @returns Ok if the hook was found and the priority was set, Err if the hook was not found
template<class... C> requires (std::is_convertible_v<C, std::string> && ...)
Result<> setHookPriorityAfterPre(std::string const& name, C&&... after) {
return this->setHookPriorityAfter(name, Priority::NormalPre, std::forward<C>(after)...);
}
/// @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 ids of the mods to set the priority before
/// @returns Ok if the hook was found and the priority was set, Err if the hook was not found
template<class... C> requires (std::is_convertible_v<C, std::string> && ...)
Result<> setHookPriorityBeforePre(std::string const& name, C&&... before) {
return this->setHookPriorityBefore(name, Priority::NormalPre, std::forward<C>(before)...);
}
/// @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 ids of the mods to set the priority after
/// @returns Ok if the hook was found and the priority was set, Err if the hook was not found
template<class... C> requires (std::is_convertible_v<C, std::string> && ...)
Result<> setHookPriorityAfterPost(std::string const& name, C&&... after) {
return this->setHookPriorityBefore(name, Priority::NormalPost, std::forward<C>(after)...);
}
/// @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 ids of the mods to set the priority before
/// @returns Ok if the hook was found and the priority was set, Err if the hook was not found
template<class... C> requires (std::is_convertible_v<C, std::string> && ...)
Result<> setHookPriorityBeforePost(std::string const& name, C&&... before) {
return this->setHookPriorityAfter(name, Priority::NormalPost, std::forward<C>(before)...);
}
// unordered_map<handles> idea // unordered_map<handles> idea
ModifyBase() { ModifyBase() {
struct EboCheck : ModifyDerived::Base { struct EboCheck : ModifyDerived::Base {
@ -158,7 +302,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

@ -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

@ -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)
*/ */

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,448 +1,430 @@
#pragma once #pragma once
#include "../include/ccMacros.h" #include <cocos2d.h>
#include "../cocoa/CCAffineTransform.h" #include <Geode/platform/platform.hpp>
#include "../cocoa/CCArray.h" #include <optional>
#include <Geode/platform/platform.hpp> #include <memory>
#include <optional>
#include <memory> namespace geode {
NS_CC_BEGIN #pragma warning(push)
#pragma warning(disable: 4275)
class CCNode;
/**
#pragma warning(push) * Layouts automatically handle the positioning of nodes. Use CCNode::setLayout
#pragma warning(disable: 4275) * to apply a layout to a node, and then use CCNode::updateLayout to apply
* the layout's positioning. Geode comes with a few default layouts like
/** * RowLayout, ColumnLayout, and GridLayout, but if you need a different kind
* Layouts automatically handle the positioning of nodes. Use CCNode::setLayout * of layout you can inherit from the Layout class.
* to apply a layout to a node, and then use CCNode::updateLayout to apply */
* the layout's positioning. Geode comes with a few default layouts like class GEODE_DLL Layout : public cocos2d::CCObject {
* RowLayout, ColumnLayout, and GridLayout, but if you need a different kind protected:
* of layout you can inherit from the Layout class. cocos2d::CCArray* getNodesToPosition(cocos2d::CCNode* forNode) const;
*/
class GEODE_DLL Layout : public CCObject { bool m_ignoreInvisibleChildren = false;
protected:
CCArray* getNodesToPosition(CCNode* forNode) const; public:
/**
bool m_ignoreInvisibleChildren = false; * Automatically apply the layout's positioning on a set of nodes
* @param on Node to apply the layout on. Position's the node's children
public: * according to the layout. The content size of the node should be
/** * respected as a boundary the layout shouldn't overflow. The node may be
* Automatically apply the layout's positioning on a set of nodes * rescaled to better fit its contents
* @param on Node to apply the layout on. Position's the node's children */
* according to the layout. The content size of the node should be virtual void apply(cocos2d::CCNode* on) = 0;
* respected as a boundary the layout shouldn't overflow. The node may be
* rescaled to better fit its contents /**
*/ * Get how much space this layout would like to take up for a given target
virtual void apply(CCNode* on) = 0; */
virtual cocos2d::CCSize getSizeHint(cocos2d::CCNode* on) const = 0;
/**
* Get how much space this layout would like to take up for a given target void ignoreInvisibleChildren(bool ignore);
*/ bool isIgnoreInvisibleChildren() const;
virtual CCSize getSizeHint(CCNode* on) const = 0;
virtual ~Layout() = default;
void ignoreInvisibleChildren(bool ignore); };
bool isIgnoreInvisibleChildren() const;
class GEODE_DLL LayoutOptions : public cocos2d::CCObject {
virtual ~Layout() = default; public:
}; virtual ~LayoutOptions() = default;
};
class GEODE_DLL LayoutOptions : public CCObject {
public: /**
virtual ~LayoutOptions() = default; * The direction of an AxisLayout
}; */
enum class Axis {
/** Row,
* The direction of an AxisLayout Column,
*/ };
enum class Axis {
Row, /**
Column, * Specifies the alignment of something in an AxisLayout
}; */
enum class AxisAlignment {
/** // Align items to the start
* Specifies the alignment of something in an AxisLayout // |ooo......|
*/ Start,
enum class AxisAlignment { // All items are centered
// Align items to the start // |...ooo...|
// |ooo......| Center,
Start, // Align items to the end
// All items are centered // |......ooo|
// |...ooo...| End,
Center, // Each item gets the same portion from the layout (disregards gap)
// Align items to the end // |.o..o..o.|
// |......ooo| Even,
End, // Space between each item is the same (disregards gap)
// Each item gets the same portion from the layout (disregards gap) // |o...o...o|
// |.o..o..o.| Between,
Even, };
// Space between each item is the same (disregards gap)
// |o...o...o| constexpr float AXISLAYOUT_DEFAULT_MIN_SCALE = 0.65f;
Between, constexpr int AXISLAYOUT_DEFAULT_PRIORITY = 0;
};
/**
constexpr float AXISLAYOUT_DEFAULT_MIN_SCALE = 0.65f; * Options for controlling the behaviour of individual nodes in an AxisLayout
constexpr int AXISLAYOUT_DEFAULT_PRIORITY = 0; * @example
* auto node = CCNode::create();
/** * // this node will have 10 units of spacing between it and the next one
* Options for controlling the behaviour of individual nodes in an AxisLayout * node->setLayoutOptions(
* @example * AxisLayoutOptions::create()
* auto node = CCNode::create(); * ->setNextGap(10.f)
* // this node will have 10 units of spacing between it and the next one * );
* node->setLayoutOptions( * someNodeWithALayout->addChild(node);
* AxisLayoutOptions::create() */
* ->setNextGap(10.f) class GEODE_DLL AxisLayoutOptions : public LayoutOptions {
* ); protected:
* someNodeWithALayout->addChild(node); class Impl;
*/
class GEODE_DLL AxisLayoutOptions : public LayoutOptions { std::unique_ptr<Impl> m_impl;
protected:
class Impl; AxisLayoutOptions();
std::unique_ptr<Impl> m_impl; public:
static AxisLayoutOptions* create();
AxisLayoutOptions();
virtual ~AxisLayoutOptions();
public:
static AxisLayoutOptions* create(); std::optional<bool> getAutoScale() const;
// @note Use hasExplicitMaxScale to know if the default scale has been overwritten
virtual ~AxisLayoutOptions(); float getMaxScale() const;
// @note Use hasExplicitMinScale to know if the default scale has been overwritten
std::optional<bool> getAutoScale() const; float getMinScale() const;
// @note Use hasExplicitMaxScale to know if the default scale has been overwritten bool hasExplicitMaxScale() const;
float getMaxScale() const; bool hasExplicitMinScale() const;
// @note Use hasExplicitMinScale to know if the default scale has been overwritten float getRelativeScale() const;
float getMinScale() const; std::optional<float> getLength() const;
bool hasExplicitMaxScale() const; std::optional<float> getPrevGap() const;
bool hasExplicitMinScale() const; std::optional<float> getNextGap() const;
float getRelativeScale() const; bool getBreakLine() const;
std::optional<float> getLength() const; bool getSameLine() const;
std::optional<float> getPrevGap() const; int getScalePriority() const;
std::optional<float> getNextGap() const; std::optional<AxisAlignment> getCrossAxisAlignment() const;
bool getBreakLine() const;
bool getSameLine() const; /**
int getScalePriority() const; * Set the limits to what the node can be scaled to. Passing `std::nullopt`
std::optional<AxisAlignment> getCrossAxisAlignment() const; * uses the parent layout's default min / max scales
*/
/** AxisLayoutOptions* setScaleLimits(std::optional<float> min, std::optional<float> max);
* Set the maximum scale this node can be if it's contained in an
* auto-scaled layout. Default is 1 /**
*/ * Set the relative scale of this node compared to other nodes if it's
[[deprecated("Use AxisLayoutOptions::setScaleLimits")]] * contained in an auto-scaled layout. Default is 1
AxisLayoutOptions* setMaxScale(float scale); */
AxisLayoutOptions* setRelativeScale(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 * Set auto-scaling for this node, overriding the layout's auto-scale
*/ * setting. If nullopt, the layout's auto-scale options will be used
[[deprecated("Use AxisLayoutOptions::setScaleLimits")]] */
AxisLayoutOptions* setMinScale(float scale); AxisLayoutOptions* setAutoScale(std::optional<bool> enabled);
/** /**
* Set the limits to what the node can be scaled to. Passing `std::nullopt` * Set an absolute length for this node. If nullopt, the length will be
* uses the parent layout's default min / max scales * dynamically calculated based on content size
*/ */
AxisLayoutOptions* setScaleLimits(std::optional<float> min, std::optional<float> max); AxisLayoutOptions* setLength(std::optional<float> length);
/** /**
* Set the relative scale of this node compared to other nodes if it's * Override the default gap in the layout between this node and the
* contained in an auto-scaled layout. Default is 1 * previous one. If nullopt, the default gap of the layout will be used
*/ */
AxisLayoutOptions* setRelativeScale(float scale); AxisLayoutOptions* setPrevGap(std::optional<float> gap);
/** /**
* Set auto-scaling for this node, overriding the layout's auto-scale * Override the default gap in the layout between this node and the next
* setting. If nullopt, the layout's auto-scale options will be used * one. If nullopt, the default gap of the layout will be used
*/ */
AxisLayoutOptions* setAutoScale(std::optional<bool> enabled); AxisLayoutOptions* setNextGap(std::optional<float> gap);
/** /**
* Set an absolute length for this node. If nullopt, the length will be * If enabled, the node will always cause a growable axis layout to break
* dynamically calculated based on content size * into a new line even if the current line could've fit the next node
*/ */
AxisLayoutOptions* setLength(std::optional<float> length); AxisLayoutOptions* setBreakLine(bool enable);
/** /**
* Override the default gap in the layout between this node and the * If enabled, the node will be forced to be on the same line as the
* previous one. If nullopt, the default gap of the layout will be used * previous node even if doing this would overflow
*/ */
AxisLayoutOptions* setPrevGap(std::optional<float> gap); AxisLayoutOptions* setSameLine(bool enable);
/** /**
* Override the default gap in the layout between this node and the next * Set the scale priority of this node. Nodes with higher priority will be
* one. If nullopt, the default gap of the layout will be used * scaled down first before nodes with lower priority when an auto-scaled
*/ * layout attempts to fit its contents. Default is
AxisLayoutOptions* setNextGap(std::optional<float> gap); * AXISLAYOUT_DEFAULT_PRIORITY
* @note For optimal performance, the priorities should all be close to
/** * each other with no gaps
* If enabled, the node will always cause a growable axis layout to break */
* into a new line even if the current line could've fit the next node AxisLayoutOptions* setScalePriority(int priority);
*/
AxisLayoutOptions* setBreakLine(bool enable); /**
* Override the cross axis alignment for this node in the layout
/** */
* If enabled, the node will be forced to be on the same line as the AxisLayoutOptions* setCrossAxisAlignment(std::optional<AxisAlignment> alignment);
* previous node even if doing this would overflow };
*/
AxisLayoutOptions* setSameLine(bool enable); /**
* A multi-purpose dynamic layout for arranging nodes along an axis. Can be
/** * used to arrange nodes in a single line, a grid, or a flex layout. The
* Set the scale priority of this node. Nodes with higher priority will be * RowLayout and ColumnLayout classes function as simple thin wrappers over
* scaled down first before nodes with lower priority when an auto-scaled * AxisLayout. The positioning of individual nodes in the layout can be
* layout attempts to fit its contents. Default is * further controlled using AxisLayoutOptions
* AXISLAYOUT_DEFAULT_PRIORITY * @warning Calculating layouts can get increasingly expensive for large
* @note For optimal performance, the priorities should all be close to * amounts of child nodes being fit into a small space - while this should
* each other with no gaps * never prove a real performance concern as most layouts only have a few
*/ * hundred children at the very most, be aware that you probably shouldn't
AxisLayoutOptions* setScalePriority(int priority); * call CCNode::updateLayout every frame for a menu with thousands of children
* @example
/** * auto menu = CCMenu::create();
* Override the cross axis alignment for this node in the layout * // The menu's children will be arranged horizontally, unless they overflow
*/ * // the content size width in which case a new line will be inserted and
AxisLayoutOptions* setCrossAxisAlignment(std::optional<AxisAlignment> alignment); * // aligned to the left. The menu automatically will automatically grow in
}; * // height to fit all the rows
* menu->setLayout(
/** * RowLayout::create()
* A multi-purpose dynamic layout for arranging nodes along an axis. Can be * ->setGap(10.f)
* used to arrange nodes in a single line, a grid, or a flex layout. The * ->setGrowCrossAxis(true)
* RowLayout and ColumnLayout classes function as simple thin wrappers over * ->setAxisAlignment(AxisAlignment::Start)
* AxisLayout. The positioning of individual nodes in the layout can be * );
* further controlled using AxisLayoutOptions * menu->setContentSize({ 200.f, 0.f });
* @warning Calculating layouts can get increasingly expensive for large * menu->addChild(...);
* amounts of child nodes being fit into a small space - while this should * menu->updateLayout();
* never prove a real performance concern as most layouts only have a few */
* hundred children at the very most, be aware that you probably shouldn't class GEODE_DLL AxisLayout : public Layout {
* call CCNode::updateLayout every frame for a menu with thousands of children protected:
* @example class Impl;
* auto menu = CCMenu::create();
* // The menu's children will be arranged horizontally, unless they overflow std::unique_ptr<Impl> m_impl;
* // the content size width in which case a new line will be inserted and
* // aligned to the left. The menu automatically will automatically grow in AxisLayout(Axis);
* // height to fit all the rows
* menu->setLayout( public:
* RowLayout::create() /**
* ->setGap(10.f) * Create a new AxisLayout. Note that this class is not automatically
* ->setGrowCrossAxis(true) * managed by default, so you must assign it to a CCNode or manually
* ->setAxisAlignment(AxisAlignment::Start) * manage the memory yourself. See the chainable setters on AxisLayout for
* ); * what options you can customize for the layout
* menu->setContentSize({ 200.f, 0.f }); * @param axis The direction of the layout
* menu->addChild(...); * @note For convenience, you can use the RowLayout and ColumnLayout
* menu->updateLayout(); * classes, which are just thin wrappers over AxisLayout
*/ * @returns Created AxisLayout
class GEODE_DLL AxisLayout : public Layout { */
protected: static AxisLayout* create(Axis axis = Axis::Row);
class Impl;
virtual ~AxisLayout();
std::unique_ptr<Impl> m_impl;
void apply(cocos2d::CCNode* on) override;
AxisLayout(Axis); cocos2d::CCSize getSizeHint(cocos2d::CCNode* on) const override;
public: Axis getAxis() const;
/** AxisAlignment getAxisAlignment() const;
* Create a new AxisLayout. Note that this class is not automatically AxisAlignment getCrossAxisAlignment() const;
* managed by default, so you must assign it to a CCNode or manually AxisAlignment getCrossAxisLineAlignment() const;
* manage the memory yourself. See the chainable setters on AxisLayout for float getGap() const;
* what options you can customize for the layout bool getAxisReverse() const;
* @param axis The direction of the layout bool getCrossAxisReverse() const;
* @note For convenience, you can use the RowLayout and ColumnLayout bool getAutoScale() const;
* classes, which are just thin wrappers over AxisLayout bool getGrowCrossAxis() const;
* @returns Created AxisLayout bool getCrossAxisOverflow() const;
*/ std::optional<float> getAutoGrowAxis() const;
static AxisLayout* create(Axis axis = Axis::Row); float getDefaultMinScale() const;
float getDefaultMaxScale() const;
virtual ~AxisLayout();
AxisLayout* setAxis(Axis axis);
void apply(CCNode* on) override; /**
CCSize getSizeHint(CCNode* on) const override; * Sets where to align the target node's children on the main axis (X-axis
* for Row, Y-axis for Column)
Axis getAxis() const; */
AxisAlignment getAxisAlignment() const; AxisLayout* setAxisAlignment(AxisAlignment align);
AxisAlignment getCrossAxisAlignment() const; /**
AxisAlignment getCrossAxisLineAlignment() const; * Sets where to align the target node's children on the cross-axis (Y-axis
float getGap() const; * for Row, X-axis for Column)
bool getAxisReverse() const; */
bool getCrossAxisReverse() const; AxisLayout* setCrossAxisAlignment(AxisAlignment align);
bool getAutoScale() const; /**
bool getGrowCrossAxis() const; * Sets where to align the target node's children on the cross-axis for
bool getCrossAxisOverflow() const; * each row (Y-axis for Row, X-axis for Column)
std::optional<float> getAutoGrowAxis() const; */
float getDefaultMinScale() const; AxisLayout* setCrossAxisLineAlignment(AxisAlignment align);
float getDefaultMaxScale() const; /**
* The spacing between the children of the node this layout applies to.
AxisLayout* setAxis(Axis axis); * Measured as the space between their edges, not centres. Does not apply
/** * on the main / cross axis if their alignment is AxisAlignment::Even
* Sets where to align the target node's children on the main axis (X-axis */
* for Row, Y-axis for Column) AxisLayout* setGap(float gap);
*/ /**
AxisLayout* setAxisAlignment(AxisAlignment align); * Whether to reverse the direction of the children in this layout or not
/** */
* Sets where to align the target node's children on the cross-axis (Y-axis AxisLayout* setAxisReverse(bool reverse);
* for Row, X-axis for Column) /**
*/ * Whether to reverse the direction of the rows on the cross-axis or not
AxisLayout* setCrossAxisAlignment(AxisAlignment align); */
/** AxisLayout* setCrossAxisReverse(bool reverse);
* Sets where to align the target node's children on the cross-axis for /**
* each row (Y-axis for Row, X-axis for Column) * If enabled, then the layout may scale the target's children if they are
*/ * about to overflow. Assumes that all the childrens' intended scale is 1
AxisLayout* setCrossAxisLineAlignment(AxisAlignment align); */
/** AxisLayout* setAutoScale(bool enable);
* The spacing between the children of the node this layout applies to. /**
* Measured as the space between their edges, not centres. Does not apply * If true, if the main axis overflows extra nodes will be placed on new
* on the main / cross axis if their alignment is AxisAlignment::Even * rows/columns on the cross-axis
*/ */
AxisLayout* setGap(float gap); AxisLayout* setGrowCrossAxis(bool expand);
/** /**
* Whether to reverse the direction of the children in this layout or not * If true, the cross-axis content size of the target node will be
*/ * automatically adjusted to fit the children
AxisLayout* setAxisReverse(bool reverse); */
/** AxisLayout* setCrossAxisOverflow(bool allow);
* Whether to reverse the direction of the rows on the cross-axis or not /**
*/ * If not `std::nullopt`, then the axis will be automatically extended to
AxisLayout* setCrossAxisReverse(bool reverse); * fit all items in a single row whose minimum length is the specified.
/** * Useful for scrollable list layer contents
* If enabled, then the layout may scale the target's children if they are */
* about to overflow. Assumes that all the childrens' intended scale is 1 AxisLayout* setAutoGrowAxis(std::optional<float> allowAndMinLength);
*/ /**
AxisLayout* setAutoScale(bool enable); * Set the default minimum/maximum scales for nodes in the layout
/** */
* If true, if the main axis overflows extra nodes will be placed on new AxisLayout* setDefaultScaleLimits(float min, float max);
* rows/columns on the cross-axis };
*/
AxisLayout* setGrowCrossAxis(bool expand); /**
/** * Simple layout for arranging nodes in a row (horizontal line)
* If true, the cross-axis content size of the target node will be */
* automatically adjusted to fit the children class GEODE_DLL RowLayout : public AxisLayout {
*/ protected:
AxisLayout* setCrossAxisOverflow(bool allow); RowLayout();
/**
* If not `std::nullopt`, then the axis will be automatically extended to public:
* fit all items in a single row whose minimum length is the specified. /**
* Useful for scrollable list layer contents * Create a new RowLayout. See the chainable setters on RowLayout for
*/ * what options you can customize for the layout
AxisLayout* setAutoGrowAxis(std::optional<float> allowAndMinLength); * @returns Created RowLayout
/** */
* Set the default minimum/maximum scales for nodes in the layout static RowLayout* create();
*/ };
AxisLayout* setDefaultScaleLimits(float min, float max);
}; /**
* Simple layout for arranging nodes in a column (vertical line)
/** */
* Simple layout for arranging nodes in a row (horizontal line) class GEODE_DLL ColumnLayout : public AxisLayout {
*/ protected:
class GEODE_DLL RowLayout : public AxisLayout { ColumnLayout();
protected:
RowLayout(); public:
/**
public: * Create a new ColumnLayout. See the chainable setters on RowLayout for
/** * what options you can customize for the layout
* Create a new RowLayout. See the chainable setters on RowLayout for * @returns Created ColumnLayout
* what options you can customize for the layout */
* @returns Created RowLayout static ColumnLayout* create();
*/ };
static RowLayout* create();
}; /**
* The relative position of a node to its parent in an AnchorLayout
/** */
* Simple layout for arranging nodes in a column (vertical line) enum class Anchor {
*/ Center,
class GEODE_DLL ColumnLayout : public AxisLayout { TopLeft,
protected: Top,
ColumnLayout(); TopRight,
Right,
public: BottomRight,
/** Bottom,
* Create a new ColumnLayout. See the chainable setters on RowLayout for BottomLeft,
* what options you can customize for the layout Left,
* @returns Created ColumnLayout };
*/
static ColumnLayout* create(); /**
}; * Options for customizing a node's position in an AnchorLayout
*/
/** class GEODE_DLL AnchorLayoutOptions : public LayoutOptions {
* The relative position of a node to its parent in an AnchorLayout protected:
*/ Anchor m_anchor = Anchor::Center;
enum class Anchor { cocos2d::CCPoint m_offset = cocos2d::CCPointZero;
Center,
TopLeft, public:
Top, static AnchorLayoutOptions* create();
TopRight,
Right, Anchor getAnchor() const;
BottomRight, cocos2d::CCPoint getOffset() const;
Bottom,
BottomLeft, AnchorLayoutOptions* setAnchor(Anchor anchor);
Left, AnchorLayoutOptions* setOffset(cocos2d::CCPoint const& offset);
}; };
/** /**
* Options for customizing a node's position in an AnchorLayout * A layout for positioning nodes at specific positions relative to their
*/ * parent's content size. See `Anchor` for available anchoring options. Useful
class GEODE_DLL AnchorLayoutOptions : public LayoutOptions { * for example for popups, where a popup using `AnchorLayout` can be
protected: * automatically resized without needing to manually shuffle nodes around
Anchor m_anchor = Anchor::Center; */
CCPoint m_offset = CCPointZero; class GEODE_DLL AnchorLayout : public Layout {
public:
public: static AnchorLayout* create();
static AnchorLayoutOptions* create();
void apply(cocos2d::CCNode* on) override;
Anchor getAnchor() const; cocos2d::CCSize getSizeHint(cocos2d::CCNode* on) const override;
CCPoint getOffset() const;
/**
AnchorLayoutOptions* setAnchor(Anchor anchor); * Get a position according to anchoring rules, with the same algorithm as
AnchorLayoutOptions* setOffset(CCPoint const& offset); * `AnchorLayout` uses to position its nodes
}; * @param in The node whose content size to use as a reference
* @param anchor The anchor position
/** * @param offset Offset from the anchor
* A layout for positioning nodes at specific positions relative to their * @returns A position in `in` for the anchored and offsetted location
* parent's content size. See `Anchor` for available anchoring options. Useful */
* for example for popups, where a popup using `AnchorLayout` can be static cocos2d::CCPoint getAnchoredPosition(cocos2d::CCNode* in, Anchor anchor, cocos2d::CCPoint const& offset);
* automatically resized without needing to manually shuffle nodes around };
*/
class GEODE_DLL AnchorLayout : public Layout { /**
public: * A layout for automatically copying the content size of a node to other nodes.
static AnchorLayout* create(); * Basically main use case is for FLAlertLayers (setting the size of the
* background and `m_buttonMenu` based on `m_mainLayer`)
void apply(CCNode* on) override; */
CCSize getSizeHint(CCNode* on) const override; class GEODE_DLL CopySizeLayout : public AnchorLayout {
protected:
/** cocos2d::CCArray* m_targets;
* Get a position according to anchoring rules, with the same algorithm as
* `AnchorLayout` uses to position its nodes public:
* @param in The node whose content size to use as a reference static CopySizeLayout* create();
* @param anchor The anchor position virtual ~CopySizeLayout();
* @param offset Offset from the anchor
* @returns A position in `in` for the anchored and offsetted location /**
*/ * Add a target to be automatically resized. Any targets' layouts will
static CCPoint getAnchoredPosition(CCNode* in, Anchor anchor, CCPoint const& offset); * also be updated when this layout is updated
}; */
CopySizeLayout* add(cocos2d::CCNode* target);
/** /**
* A layout for automatically copying the content size of a node to other nodes. * Remove a target from being automatically resized
* Basically main use case is for FLAlertLayers (setting the size of the */
* background and `m_buttonMenu` based on `m_mainLayer`) CopySizeLayout* remove(cocos2d::CCNode* target);
*/
class GEODE_DLL CopySizeLayout : public cocos2d::AnchorLayout { void apply(cocos2d::CCNode* in) override;
protected: cocos2d::CCSize getSizeHint(cocos2d::CCNode* in) const override;
cocos2d::CCArray* m_targets; };
public: #pragma warning(pop)
static CopySizeLayout* create();
virtual ~CopySizeLayout(); }
/**
* Add a target to be automatically resized. Any targets' layouts will
* also be updated when this layout is updated
*/
CopySizeLayout* add(cocos2d::CCNode* target);
/**
* Remove a target from being automatically resized
*/
CopySizeLayout* remove(cocos2d::CCNode* target);
void apply(cocos2d::CCNode* in) override;
cocos2d::CCSize getSizeHint(cocos2d::CCNode* in) const override;
};
#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
@ -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](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.toString());
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 '#'
@ -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);
} }

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,6 +1,6 @@
#pragma once #pragma once
#include "Result.hpp" #include <Geode/Result.hpp>
#include "../DefaultInclude.hpp" #include "../DefaultInclude.hpp"
#include <chrono> #include <chrono>
@ -13,19 +13,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;
@ -125,7 +117,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 +160,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

@ -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>

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"
},
"developer-title": { "developer-title": {
"type": "title", "type": "title",
"name": "Developer Settings" "name": "Developer Settings"

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

@ -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

@ -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

@ -46,6 +46,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)) {

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>();
@ -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;
} }

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

@ -42,6 +42,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);
@ -384,6 +389,37 @@ void Loader::Impl::buildModGraph() {
} }
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());
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());
log::popNest();
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;
@ -444,35 +480,6 @@ void Loader::Impl::loadModGraph(Mod* node, bool early) {
log::popNest(); log::popNest();
return; 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;
}
} }
if (early) { if (early) {
@ -496,20 +503,20 @@ 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();
auto prevNest = log::saveNest();
log::loadNest(nest);
if (!res) {
this->addProblem({
LoadProblem::Type::UnzipFailed,
node,
res.unwrapErr()
});
log::error("Failed to unzip: {}", res.unwrapErr());
m_refreshingModCount -= 1;
log::loadNest(prevNest);
return;
}
this->queueInMainThread([=, this]() { this->queueInMainThread([=, this]() {
auto prevNest = log::saveNest();
log::loadNest(nest);
if (!res) {
this->addProblem({
LoadProblem::Type::UnzipFailed,
node,
res.unwrapErr()
});
log::error("Failed to unzip: {}", res.unwrapErr());
m_refreshingModCount -= 1;
log::loadNest(prevNest);
return;
}
loadFunction(); loadFunction();
log::loadNest(prevNest); log::loadNest(prevNest);
}); });
@ -524,6 +531,10 @@ 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::pushNest();
@ -927,11 +938,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 +950,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";
} }

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);

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);
} }
} }

View file

@ -12,7 +12,7 @@ Mod* ModStateEvent::getMod() const {
return m_mod; return m_mod;
} }
ListenerResult ModStateFilter::handle(utils::MiniFunction<Callback> fn, ModStateEvent* event) { ListenerResult ModStateFilter::handle(std::function<Callback> fn, ModStateEvent* event) {
// log::debug("Event mod filter: {}, {}, {}, {}", m_mod, static_cast<int>(m_type), event->getMod(), static_cast<int>(event->getType())); // log::debug("Event mod filter: {}, {}, {}, {}", m_mod, static_cast<int>(m_type), event->getMod(), static_cast<int>(event->getType()));
if ((!m_mod || event->getMod() == m_mod) && event->getType() == m_type) { if ((!m_mod || event->getMod() == m_mod) && event->getType() == m_type) {
fn(event); fn(event);

View file

@ -51,7 +51,9 @@ Result<> Mod::Impl::setup() {
(void) utils::file::createDirectoryAll(m_saveDirPath); (void) utils::file::createDirectoryAll(m_saveDirPath);
// always create temp dir for all mods, even if disabled, so resources can be loaded // always create temp dir for all mods, even if disabled, so resources can be loaded
GEODE_UNWRAP(this->createTempDir().expect("Unable to create temp dir: {error}")); GEODE_UNWRAP(this->createTempDir().mapErr([](auto const& err) {
return fmt::format("Unable to create temp dir: {}", err);
}));
m_settings = std::make_unique<ModSettingsManager>(m_metadata); m_settings = std::make_unique<ModSettingsManager>(m_metadata);
auto loadRes = this->loadData(); auto loadRes = this->loadData();
@ -188,15 +190,12 @@ Result<> Mod::Impl::loadData() {
auto savedPath = m_saveDirPath / "saved.json"; auto savedPath = m_saveDirPath / "saved.json";
if (std::filesystem::exists(savedPath)) { if (std::filesystem::exists(savedPath)) {
GEODE_UNWRAP_INTO(auto data, utils::file::readString(savedPath)); GEODE_UNWRAP_INTO(auto data, utils::file::readString(savedPath));
std::string error; m_saved = GEODE_UNWRAP(matjson::parse(data).mapErr([](auto&& err) {
auto res = matjson::parse(data, error); return fmt::format("Unable to parse saved values: {}", err);
if (error.size() > 0) { }));
return Err("Unable to parse saved values: " + error); if (!m_saved.isObject()) {
}
m_saved = res.value();
if (!m_saved.is_object()) {
log::warn("saved.json was somehow not an object, forcing it to one"); log::warn("saved.json was somehow not an object, forcing it to one");
m_saved = matjson::Object(); m_saved = matjson::Value::object();
} }
} }
@ -229,19 +228,19 @@ Result<> Mod::Impl::saveData() {
} }
bool Mod::Impl::hasSettings() const { bool Mod::Impl::hasSettings() const {
return m_metadata.getSettingsV3().size(); return m_metadata.getSettings().size();
} }
std::vector<std::string> Mod::Impl::getSettingKeys() const { std::vector<std::string> Mod::Impl::getSettingKeys() const {
std::vector<std::string> keys; std::vector<std::string> keys;
for (auto& [key, _] : m_metadata.getSettingsV3()) { for (auto& [key, _] : m_metadata.getSettings()) {
keys.push_back(key); keys.push_back(key);
} }
return keys; return keys;
} }
bool Mod::Impl::hasSetting(std::string_view const key) const { bool Mod::Impl::hasSetting(std::string_view key) const {
for (auto& setting : m_metadata.getSettingsV3()) { for (auto& setting : m_metadata.getSettings()) {
if (setting.first == key) { if (setting.first == key) {
return true; return true;
} }
@ -249,7 +248,7 @@ bool Mod::Impl::hasSetting(std::string_view const key) const {
return false; return false;
} }
std::string Mod::Impl::getLaunchArgumentName(std::string_view const name) const { std::string Mod::Impl::getLaunchArgumentName(std::string_view name) const {
return this->getID() + "." + std::string(name); return this->getID() + "." + std::string(name);
} }
@ -264,15 +263,15 @@ std::vector<std::string> Mod::Impl::getLaunchArgumentNames() const {
return names; return names;
} }
bool Mod::Impl::hasLaunchArgument(std::string_view const name) const { bool Mod::Impl::hasLaunchArgument(std::string_view name) const {
return Loader::get()->hasLaunchArgument(this->getLaunchArgumentName(name)); return Loader::get()->hasLaunchArgument(this->getLaunchArgumentName(name));
} }
std::optional<std::string> Mod::Impl::getLaunchArgument(std::string_view const name) const { std::optional<std::string> Mod::Impl::getLaunchArgument(std::string_view name) const {
return Loader::get()->getLaunchArgument(this->getLaunchArgumentName(name)); return Loader::get()->getLaunchArgument(this->getLaunchArgumentName(name));
} }
bool Mod::Impl::getLaunchFlag(std::string_view const name) const { bool Mod::Impl::getLaunchFlag(std::string_view name) const {
return Loader::get()->getLaunchFlag(this->getLaunchArgumentName(name)); return Loader::get()->getLaunchFlag(this->getLaunchArgumentName(name));
} }
@ -318,7 +317,6 @@ Result<> Mod::Impl::loadBinary() {
ModStateEvent(m_self, ModEventType::Loaded).post(); ModStateEvent(m_self, ModEventType::Loaded).post();
ModStateEvent(m_self, ModEventType::Enabled).post();
ModStateEvent(m_self, ModEventType::DataLoaded).post(); ModStateEvent(m_self, ModEventType::DataLoaded).post();
m_isCurrentlyLoading = false; m_isCurrentlyLoading = false;
@ -385,7 +383,7 @@ Result<> Mod::Impl::uninstall(bool deleteSaveData) {
ModRequestedAction::Uninstall; ModRequestedAction::Uninstall;
// Make loader forget the mod should be disabled // Make loader forget the mod should be disabled
Mod::get()->getSaveContainer().try_erase("should-load-" + m_metadata.getID()); Mod::get()->getSaveContainer().erase("should-load-" + m_metadata.getID());
std::error_code ec; std::error_code ec;
std::filesystem::remove(m_metadata.getPath(), ec); std::filesystem::remove(m_metadata.getPath(), ec);
@ -440,7 +438,7 @@ bool Mod::Impl::hasUnresolvedIncompatibilities() const {
return false; return false;
} }
bool Mod::Impl::depends(std::string_view const id) const { bool Mod::Impl::depends(std::string_view id) const {
return utils::ranges::contains(m_metadata.getDependencies(), [id](ModMetadata::Dependency const& t) { return utils::ranges::contains(m_metadata.getDependencies(), [id](ModMetadata::Dependency const& t) {
return t.id == id; return t.id == id;
}); });
@ -672,17 +670,15 @@ std::string_view Mod::Impl::expandSpriteName(std::string_view name) {
ModJson Mod::Impl::getRuntimeInfo() const { ModJson Mod::Impl::getRuntimeInfo() const {
auto json = m_metadata.toJSON(); auto json = m_metadata.toJSON();
auto obj = matjson::Object(); auto obj = matjson::Value::object();
obj["hooks"] = matjson::Array(); obj["hooks"] = matjson::Value::array();
for (auto hook : m_hooks) { for (auto hook : m_hooks) {
obj["hooks"].as_array().push_back(ModJson(hook->getRuntimeInfo())); obj["hooks"].push(ModJson(hook->getRuntimeInfo()));
} }
obj["patches"] = matjson::Array(); obj["patches"] = matjson::Value::array();
for (auto patch : m_patches) { for (auto patch : m_patches) {
obj["patches"].as_array().push_back(ModJson(patch->getRuntimeInfo())); obj["patches"].push(ModJson(patch->getRuntimeInfo()));
} }
// TODO: so which one is it
// obj["enabled"] = m_enabled;
obj["loaded"] = m_enabled; obj["loaded"] = m_enabled;
obj["temp-dir"] = this->getTempDir(); obj["temp-dir"] = this->getTempDir();
obj["save-dir"] = this->getSaveDir(); obj["save-dir"] = this->getSaveDir();
@ -708,11 +704,11 @@ bool Mod::Impl::isCurrentlyLoading() const {
return m_isCurrentlyLoading; return m_isCurrentlyLoading;
} }
bool Mod::Impl::hasProblems() const { bool Mod::Impl::hasLoadProblems() const {
for (auto const& item : m_problems) { for (auto const& problem : m_problems) {
if (item.type <= LoadProblem::Type::Recommendation) if (problem.isProblem()) {
continue; return true;
return true; }
} }
return false; return false;
} }
@ -722,12 +718,9 @@ std::vector<LoadProblem> Mod::Impl::getProblems() const {
} }
static Result<ModMetadata> getModImplInfo() { static Result<ModMetadata> getModImplInfo() {
std::string error; auto json = GEODE_UNWRAP(matjson::parse(about::getLoaderModJson()).mapErr([](auto&& err) {
auto res = matjson::parse(about::getLoaderModJson(), error); return fmt::format("Unable to parse mod.json: {}", err);
if (error.size() > 0) { }));
return Err("Unable to parse mod.json: " + error);
}
matjson::Value json = res.value();
GEODE_UNWRAP_INTO(auto info, ModMetadata::create(json)); GEODE_UNWRAP_INTO(auto info, ModMetadata::create(json));
return Ok(info); return Ok(info);

View file

@ -47,7 +47,7 @@ namespace geode {
/** /**
* Saved values * Saved values
*/ */
matjson::Value m_saved = matjson::Object(); matjson::Value m_saved = matjson::Value();
/** /**
* Setting values. This is behind unique_ptr for interior mutability * Setting values. This is behind unique_ptr for interior mutability
*/ */
@ -112,13 +112,13 @@ namespace geode {
bool hasSettings() const; bool hasSettings() const;
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;
std::string getLaunchArgumentName(std::string_view const name) const; std::string getLaunchArgumentName(std::string_view name) const;
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;
Result<Hook*> claimHook(std::shared_ptr<Hook> hook); Result<Hook*> claimHook(std::shared_ptr<Hook> hook);
Result<> disownHook(Hook* hook); Result<> disownHook(Hook* hook);
@ -136,7 +136,7 @@ namespace geode {
// 1.3.0 additions // 1.3.0 additions
ModRequestedAction getRequestedAction() const; ModRequestedAction getRequestedAction() const;
bool depends(std::string_view const id) const; bool depends(std::string_view id) const;
Result<> updateDependencies(); Result<> updateDependencies();
bool hasUnresolvedDependencies() const; bool hasUnresolvedDependencies() const;
bool hasUnresolvedIncompatibilities() const; bool hasUnresolvedIncompatibilities() const;
@ -151,7 +151,7 @@ namespace geode {
std::vector<LoadProblem> getProblems() const; std::vector<LoadProblem> getProblems() const;
bool hasProblems() const; bool hasLoadProblems() const;
bool shouldLoad() const; bool shouldLoad() const;
bool isCurrentlyLoading() const; bool isCurrentlyLoading() const;
}; };

View file

@ -113,14 +113,14 @@ Result<ModMetadata> ModMetadata::Impl::createFromSchemaV010(ModJson const& rawJs
auto checkerRoot = fmt::format( auto checkerRoot = fmt::format(
"[{}/v0.0.0/mod.json]", "[{}/v0.0.0/mod.json]",
rawJson.contains("id") ? rawJson["id"].as_string() : "unknown.mod" rawJson.contains("id") ? GEODE_UNWRAP(rawJson["id"].asString()) : "unknown.mod"
); );
// JsonChecker did it this way too // JsonChecker did it this way too
try { try {
checkerRoot = fmt::format( checkerRoot = fmt::format(
"[{}/{}/mod.json]", "[{}/{}/mod.json]",
rawJson.contains("id") ? rawJson["id"].as_string() : "unknown.mod", rawJson.contains("id") ? GEODE_UNWRAP(rawJson["id"].asString()) : "unknown.mod",
rawJson.contains("version") ? rawJson["version"].as<VersionInfo>().toVString() : "v0.0.0" rawJson.contains("version") ? GEODE_UNWRAP(rawJson["version"].as<VersionInfo>()).toVString() : "v0.0.0"
); );
} }
catch (...) { } catch (...) { }
@ -304,11 +304,14 @@ Result<ModMetadata> ModMetadata::Impl::createFromSchemaV010(ModJson const& rawJs
Result<ModMetadata> ModMetadata::Impl::create(ModJson const& json) { Result<ModMetadata> ModMetadata::Impl::create(ModJson const& json) {
// Check mod.json target version // Check mod.json target version
auto schema = about::getLoaderVersion(); auto schema = about::getLoaderVersion();
if (json.contains("geode") && json["geode"].is_string()) { if (json.contains("geode") && json["geode"].isString()) {
GEODE_UNWRAP_INTO( GEODE_UNWRAP_INTO(
schema, schema,
VersionInfo::parse(json["geode"].as_string()) VersionInfo::parse(GEODE_UNWRAP(json["geode"].asString())).mapErr(
.expect("[mod.json] has invalid target loader version: {error}") [](auto const& err) {
return fmt::format("[mod.json] has invalid target loader version: {}", err);
}
)
); );
} }
else { else {
@ -352,13 +355,9 @@ Result<ModMetadata> ModMetadata::Impl::create(ModJson const& json) {
Result<ModMetadata> ModMetadata::Impl::createFromFile(std::filesystem::path const& path) { Result<ModMetadata> ModMetadata::Impl::createFromFile(std::filesystem::path const& path) {
GEODE_UNWRAP_INTO(auto read, utils::file::readString(path)); GEODE_UNWRAP_INTO(auto read, utils::file::readString(path));
std::string error; GEODE_UNWRAP_INTO(auto info, ModMetadata::create(GEODE_UNWRAP(matjson::parse(read).mapErr([&](auto const& err) {
auto res = matjson::parse(read, error); return fmt::format("Unable to parse mod.json: {}", err);
if (error.size() > 0) { }))));
return Err(std::string("Unable to parse mod.json: ") + error);
}
GEODE_UNWRAP_INTO(auto info, ModMetadata::create(res.value()));
auto impl = info.m_impl.get(); auto impl = info.m_impl.get();
@ -382,25 +381,24 @@ Result<ModMetadata> ModMetadata::Impl::createFromGeodeZip(file::Unzip& unzip) {
// Read mod.json & parse if possible // Read mod.json & parse if possible
GEODE_UNWRAP_INTO( GEODE_UNWRAP_INTO(
auto jsonData, unzip.extract("mod.json").expect("Unable to read mod.json: {error}") auto jsonData, unzip.extract("mod.json").mapErr([](auto const& err) {
return fmt::format("Unable to extract mod.json: {}", err);
})
); );
std::string error; ModJson json = GEODE_UNWRAP(matjson::parse(std::string(jsonData.begin(), jsonData.end())).mapErr([](auto const& err) {
auto res = matjson::parse(std::string(jsonData.begin(), jsonData.end()), error); return fmt::format("Unable to parse mod.json: {}", err);
if (error.size() > 0) { }));
return Err(std::string("Unable to parse mod.json: ") + error);
}
ModJson json = res.value();
auto res2 = ModMetadata::create(json); auto info = GEODE_UNWRAP(ModMetadata::create(json).mapErr([&](auto const& err) {
if (!res2) { return fmt::format("\"{}\" - {}", unzip.getPath().string(), err);
return Err("\"" + unzip.getPath().string() + "\" - " + res2.unwrapErr()); }));
}
auto info = res2.unwrap();
auto impl = info.m_impl.get(); auto impl = info.m_impl.get();
impl->m_path = unzip.getPath(); impl->m_path = unzip.getPath();
GEODE_UNWRAP(info.addSpecialFiles(unzip).expect("Unable to add extra files: {error}")); GEODE_UNWRAP(info.addSpecialFiles(unzip).mapErr([](auto const& err) {
return fmt::format("Unable to add extra files: {}", err);
}));
return Ok(info); return Ok(info);
} }
@ -409,7 +407,11 @@ Result<> ModMetadata::Impl::addSpecialFiles(file::Unzip& unzip) {
// unzip known MD files // unzip known MD files
for (auto& [file, target] : this->getSpecialFiles()) { for (auto& [file, target] : this->getSpecialFiles()) {
if (unzip.hasEntry(file)) { if (unzip.hasEntry(file)) {
GEODE_UNWRAP_INTO(auto data, unzip.extract(file).expect("Unable to extract \"{}\"", file)); // reference to local binding 'file' declared in enclosing function
std::string_view fileStr(file);
GEODE_UNWRAP_INTO(auto data, unzip.extract(fileStr).mapErr([&](auto const& err) {
return fmt::format("Unable to extract \"{}\": {}", fileStr, err);
}));
*target = sanitizeDetailsData(std::string(data.begin(), data.end())); *target = sanitizeDetailsData(std::string(data.begin(), data.end()));
} }
} }
@ -477,12 +479,6 @@ std::string ModMetadata::getName() const {
return m_impl->m_name; return m_impl->m_name;
} }
std::string ModMetadata::getDeveloper() const {
// m_developers should be guaranteed to never be empty, but this is
// just in case it is anyway somehow
return m_impl->m_developers.empty() ? "" : m_impl->m_developers.front();
}
std::string ModMetadata::formatDeveloperDisplayString(std::vector<std::string> const& developers) { std::string ModMetadata::formatDeveloperDisplayString(std::vector<std::string> const& developers) {
switch (developers.size()) { switch (developers.size()) {
case 0: return "Unknown"; break; case 0: return "Unknown"; break;
@ -510,9 +506,6 @@ std::optional<std::string> ModMetadata::getChangelog() const {
std::optional<std::string> ModMetadata::getSupportInfo() const { std::optional<std::string> ModMetadata::getSupportInfo() const {
return m_impl->m_supportInfo; return m_impl->m_supportInfo;
} }
std::optional<std::string> ModMetadata::getRepository() const {
return m_impl->m_links.getSourceURL();
}
ModMetadataLinks ModMetadata::getLinks() const { ModMetadataLinks ModMetadata::getLinks() const {
return m_impl->m_links; return m_impl->m_links;
} }
@ -528,19 +521,7 @@ std::vector<ModMetadata::Incompatibility> ModMetadata::getIncompatibilities() co
std::vector<std::string> ModMetadata::getSpritesheets() const { std::vector<std::string> ModMetadata::getSpritesheets() const {
return m_impl->m_spritesheets; return m_impl->m_spritesheets;
} }
std::vector<std::pair<std::string, Setting>> ModMetadata::getSettings() const { std::vector<std::pair<std::string, matjson::Value>> ModMetadata::getSettings() const {
std::vector<std::pair<std::string, Setting>> res;
for (auto [key, sett] : m_impl->m_settings) {
auto checker = JsonChecker(sett);
auto value = checker.root("");
auto legacy = Setting::parse(key, m_impl->m_id, value);
if (!checker.isError() && legacy.isOk()) {
res.push_back(std::make_pair(key, *legacy));
}
}
return res;
}
std::vector<std::pair<std::string, matjson::Value>> ModMetadata::getSettingsV3() const {
return m_impl->m_settings; return m_impl->m_settings;
} }
std::unordered_set<std::string> ModMetadata::getTags() const { std::unordered_set<std::string> ModMetadata::getTags() const {
@ -641,10 +622,6 @@ void ModMetadata::setIncompatibilities(std::vector<Incompatibility> const& value
void ModMetadata::setSpritesheets(std::vector<std::string> const& value) { void ModMetadata::setSpritesheets(std::vector<std::string> const& value) {
m_impl->m_spritesheets = value; m_impl->m_spritesheets = value;
} }
void ModMetadata::setSettings(std::vector<std::pair<std::string, Setting>> const& value) {
// intentionally no-op because no one is supposed to be using this
// without subscribing to "internals are not stable" mentality
}
void ModMetadata::setSettings(std::vector<std::pair<std::string, matjson::Value>> const& value) { void ModMetadata::setSettings(std::vector<std::pair<std::string, matjson::Value>> const& value) {
m_impl->m_settings = value; m_impl->m_settings = value;
} }

View file

@ -4,7 +4,7 @@
#include <Geode/loader/Mod.hpp> #include <Geode/loader/Mod.hpp>
#include <Geode/utils/JsonValidation.hpp> #include <Geode/utils/JsonValidation.hpp>
#include <Geode/utils/VersionInfo.hpp> #include <Geode/utils/VersionInfo.hpp>
#include <Geode/loader/SettingV3.hpp> #include <Geode/loader/Setting.hpp>
using namespace geode::prelude; using namespace geode::prelude;
@ -74,52 +74,44 @@ namespace geode {
template <> template <>
struct matjson::Serialize<geode::ModMetadata::Dependency::Importance> { struct matjson::Serialize<geode::ModMetadata::Dependency::Importance> {
static matjson::Value GEODE_DLL to_json(geode::ModMetadata::Dependency::Importance const& importance) { static geode::Result<geode::ModMetadata::Dependency::Importance, std::string> fromJson(Value const& value)
switch (importance) { {
case geode::ModMetadata::Dependency::Importance::Required: return {"required"}; auto str = GEODE_UNWRAP(value.asString());
case geode::ModMetadata::Dependency::Importance::Recommended: return {"recommended"}; if (str == "required") return geode::Ok(geode::ModMetadata::Dependency::Importance::Required);
case geode::ModMetadata::Dependency::Importance::Suggested: return {"suggested"}; if (str == "recommended") return geode::Ok(geode::ModMetadata::Dependency::Importance::Recommended);
default: return {"unknown"}; if (str == "suggested") return geode::Ok(geode::ModMetadata::Dependency::Importance::Suggested);
} return geode::Err("Invalid importance");
}
static geode::ModMetadata::Dependency::Importance GEODE_DLL from_json(matjson::Value const& importance) {
auto impStr = importance.as_string();
if (impStr == "required")
return geode::ModMetadata::Dependency::Importance::Required;
if (impStr == "recommended")
return geode::ModMetadata::Dependency::Importance::Recommended;
if (impStr == "suggested")
return geode::ModMetadata::Dependency::Importance::Suggested;
throw matjson::JsonException(R"(Expected importance to be "required", "recommended" or "suggested")");
} }
static bool is_json(matjson::Value const& value) { static Value toJson(geode::ModMetadata::Dependency::Importance const& value)
return value.is_string(); {
switch (value) {
case geode::ModMetadata::Dependency::Importance::Required: return "required";
case geode::ModMetadata::Dependency::Importance::Recommended: return "recommended";
case geode::ModMetadata::Dependency::Importance::Suggested: return "suggested";
}
return "unknown";
} }
}; };
template <> template <>
struct matjson::Serialize<geode::ModMetadata::Incompatibility::Importance> { struct matjson::Serialize<geode::ModMetadata::Incompatibility::Importance> {
static matjson::Value GEODE_DLL to_json(geode::ModMetadata::Incompatibility::Importance const& importance) { static geode::Result<geode::ModMetadata::Incompatibility::Importance, std::string> fromJson(Value const& value)
switch (importance) { {
case geode::ModMetadata::Incompatibility::Importance::Breaking: return {"breaking"}; auto str = GEODE_UNWRAP(value.asString());
case geode::ModMetadata::Incompatibility::Importance::Conflicting: return {"conflicting"}; if (str == "breaking") return geode::Ok(geode::ModMetadata::Incompatibility::Importance::Breaking);
case geode::ModMetadata::Incompatibility::Importance::Superseded: return {"superseded"}; if (str == "conflicting") return geode::Ok(geode::ModMetadata::Incompatibility::Importance::Conflicting);
default: return {"unknown"}; if (str == "superseded") return geode::Ok(geode::ModMetadata::Incompatibility::Importance::Superseded);
} return geode::Err("Invalid importance");
}
static geode::ModMetadata::Incompatibility::Importance GEODE_DLL from_json(matjson::Value const& importance) {
auto impStr = importance.as_string();
if (impStr == "breaking")
return geode::ModMetadata::Incompatibility::Importance::Breaking;
if (impStr == "conflicting")
return geode::ModMetadata::Incompatibility::Importance::Conflicting;
if (impStr == "superseded")
return geode::ModMetadata::Incompatibility::Importance::Superseded;
throw matjson::JsonException(R"(Expected importance to be "breaking", "conflicting", or "superseded")");
} }
static bool is_json(matjson::Value const& value) { static Value toJson(geode::ModMetadata::Incompatibility::Importance const& value)
return value.is_string(); {
switch (value) {
case geode::ModMetadata::Incompatibility::Importance::Breaking: return "breaking";
case geode::ModMetadata::Incompatibility::Importance::Conflicting: return "conflicting";
case geode::ModMetadata::Incompatibility::Importance::Superseded: return "superseded";
}
return "unknown";
} }
}; };

View file

@ -8,24 +8,36 @@ using namespace geode::prelude;
// #1 no need to duplicate the built-in settings between all mods // #1 no need to duplicate the built-in settings between all mods
// #2 easier lookup of custom settings if a mod uses another mod's custom setting type // #2 easier lookup of custom settings if a mod uses another mod's custom setting type
namespace {
auto changeToGenerator(auto&& function) {
return [function](
std::string const& key,
std::string const& modID,
matjson::Value const& json
) -> Result<std::shared_ptr<SettingV3>> {
return function(key, modID, json).map([](auto&& ptr) {
return std::shared_ptr<SettingV3>(ptr);
});
};
}
}
class SharedSettingTypesPool final { class SharedSettingTypesPool final {
private: private:
std::unordered_map<std::string, SettingGenerator> m_types; std::unordered_map<std::string, SettingGenerator> m_types;
SharedSettingTypesPool() : m_types({ SharedSettingTypesPool() : m_types({
// todo in v4: remove this { "title", changeToGenerator(TitleSettingV3::parse) },
{ "custom", &LegacyCustomSettingV3::parse }, { "bool", changeToGenerator(BoolSettingV3::parse) },
{ "title", &TitleSettingV3::parse }, { "int", changeToGenerator(IntSettingV3::parse) },
{ "bool", &BoolSettingV3::parse }, { "float", changeToGenerator(FloatSettingV3::parse) },
{ "int", &IntSettingV3::parse }, { "string", changeToGenerator(StringSettingV3::parse) },
{ "float", &FloatSettingV3::parse }, { "file", changeToGenerator(FileSettingV3::parse) },
{ "string", &StringSettingV3::parse }, { "folder", changeToGenerator(FileSettingV3::parse) },
{ "file", &FileSettingV3::parse }, { "path", changeToGenerator(FileSettingV3::parse) },
{ "folder", &FileSettingV3::parse }, { "rgb", changeToGenerator(Color3BSettingV3::parse) },
{ "path", &FileSettingV3::parse }, { "color", changeToGenerator(Color3BSettingV3::parse) },
{ "rgb", &Color3BSettingV3::parse }, { "rgba", changeToGenerator(Color4BSettingV3::parse) },
{ "color", &Color3BSettingV3::parse },
{ "rgba", &Color4BSettingV3::parse },
}) {} }) {}
public: public:
@ -80,9 +92,7 @@ public:
struct SettingInfo final { struct SettingInfo final {
std::string type; std::string type;
matjson::Value json; matjson::Value json;
std::shared_ptr<SettingV3> v3 = nullptr; std::shared_ptr<Setting> v3 = nullptr;
// todo: remove in v4
std::shared_ptr<SettingValue> legacy = nullptr;
}; };
std::string modID; std::string modID;
std::unordered_map<std::string, SettingInfo> settings; std::unordered_map<std::string, SettingInfo> settings;
@ -114,16 +124,11 @@ public:
// Store the value in an intermediary so if `save` fails the existing // Store the value in an intermediary so if `save` fails the existing
// value loaded from disk isn't overwritten // value loaded from disk isn't overwritten
matjson::Value value; matjson::Value value;
try { if (sett.v3->save(value)) {
if (sett.v3->save(value)) { this->savedata[key] = value;
this->savedata[key] = value;
}
else {
log::error("Unable to save setting '{}' for mod {}", key, this->modID);
}
} }
catch(matjson::JsonException const& e) { else {
log::error("Unable to save setting '{}' for mod {} (JSON exception): {}", key, this->modID, e.what()); log::error("Unable to save setting '{}' for mod {}", key, this->modID);
} }
} }
} }
@ -139,7 +144,7 @@ public:
continue; continue;
} }
if (auto v3 = (*gen)(key, modID, setting.json)) { if (auto v3 = (*gen)(key, modID, setting.json)) {
setting.v3 = *v3; setting.v3 = v3.unwrap();
this->loadSettingValueFromSave(key); this->loadSettingValueFromSave(key);
} }
else { else {
@ -161,7 +166,7 @@ ModSettingsManager::ModSettingsManager(ModMetadata const& metadata)
: m_impl(std::make_unique<Impl>()) : m_impl(std::make_unique<Impl>())
{ {
m_impl->modID = metadata.getID(); m_impl->modID = metadata.getID();
for (auto const& [key, json] : metadata.getSettingsV3()) { for (auto const& [key, json] : metadata.getSettings()) {
auto setting = Impl::SettingInfo(); auto setting = Impl::SettingInfo();
setting.json = json; setting.json = json;
auto root = checkJson(json, "setting"); auto root = checkJson(json, "setting");
@ -197,33 +202,13 @@ Result<> ModSettingsManager::registerCustomSettingType(std::string_view type, Se
m_impl->createSettings(); m_impl->createSettings();
return Ok(); return Ok();
} }
Result<> ModSettingsManager::registerLegacyCustomSetting(std::string_view key, std::unique_ptr<SettingValue>&& ptr) {
auto id = std::string(key);
if (!m_impl->settings.count(id)) {
return Err("No such setting '{}' in mod {}", id, m_impl->modID);
}
auto& sett = m_impl->settings.at(id);
if (auto custom = typeinfo_pointer_cast<LegacyCustomSettingV3>(sett.v3)) {
if (!custom->getValue()) {
custom->setValue(std::move(ptr));
m_impl->loadSettingValueFromSave(id);
}
else {
return Err("Setting '{}' in mod {} has already been registed", id, m_impl->modID);
}
}
else {
return Err("Setting '{}' in mod {} is not a legacy custom setting", id, m_impl->modID);
}
return Ok();
}
Result<> ModSettingsManager::load(matjson::Value const& json) { Result<> ModSettingsManager::load(matjson::Value const& json) {
if (json.is_object()) { if (json.isObject()) {
// Save this so when custom settings are registered they can load their // Save this so when custom settings are registered they can load their
// values properly // values properly
m_impl->savedata = json.as_object(); m_impl->savedata = json;
for (auto const& [key, _] : json.as_object()) { for (auto const& [key, _] : json) {
m_impl->loadSettingValueFromSave(key); m_impl->loadSettingValueFromSave(key);
} }
} }
@ -240,37 +225,10 @@ matjson::Value& ModSettingsManager::getSaveData() {
return m_impl->savedata; return m_impl->savedata;
} }
std::shared_ptr<SettingV3> ModSettingsManager::get(std::string_view key) { std::shared_ptr<Setting> ModSettingsManager::get(std::string_view key) {
auto id = std::string(key); auto id = std::string(key);
return m_impl->settings.count(id) ? m_impl->settings.at(id).v3 : nullptr; return m_impl->settings.count(id) ? m_impl->settings.at(id).v3 : nullptr;
} }
std::shared_ptr<SettingValue> ModSettingsManager::getLegacy(std::string_view key) {
auto id = std::string(key);
if (!m_impl->settings.count(id)) {
return nullptr;
}
auto& info = m_impl->settings.at(id);
// If this setting has alreay been given a legacy interface, give that
if (info.legacy) {
return info.legacy;
}
// Uninitialized settings are null
if (!info.v3) {
return nullptr;
}
// Generate new legacy interface
if (auto legacy = info.v3->convertToLegacyValue()) {
info.legacy.swap(*legacy);
return info.legacy;
}
return nullptr;
}
std::optional<Setting> ModSettingsManager::getLegacyDefinition(std::string_view key) {
if (auto s = this->get(key)) {
return s->convertToLegacy();
}
return std::nullopt;
}
bool ModSettingsManager::restartRequired() const { bool ModSettingsManager::restartRequired() const {
return m_impl->restartRequired; return m_impl->restartRequired;

View file

@ -92,8 +92,8 @@ Result<> Patch::Impl::updateBytes(const ByteVector& bytes) {
if (m_enabled) { if (m_enabled) {
auto res = this->disable(); auto res = this->disable();
if (!res) return Err("Failed to update patch: {}", res.unwrapErr()); if (!res) return Err("Failed to update patch: {}", res.unwrapErr());
res = this->enable(); auto res2 = this->enable();
if (!res) return Err("Failed to update patch: {}", res.unwrapErr()); if (!res2) return Err("Failed to update patch: {}", res2.unwrapErr());
} }
return Ok(); return Ok();
@ -104,7 +104,7 @@ uintptr_t Patch::Impl::getAddress() const {
} }
matjson::Value Patch::Impl::getRuntimeInfo() const { matjson::Value Patch::Impl::getRuntimeInfo() const {
auto json = matjson::Object(); auto json = matjson::Value::object();
json["address"] = std::to_string(reinterpret_cast<uintptr_t>(m_address)); json["address"] = std::to_string(reinterpret_cast<uintptr_t>(m_address));
json["original"] = m_original; json["original"] = m_original;
json["patch"] = m_patch; json["patch"] = m_patch;

View file

@ -1,498 +0,0 @@
#include <ui/mods/settings/GeodeSettingNode.hpp>
#include <Geode/loader/Mod.hpp>
#include <Geode/loader/Setting.hpp>
#include <Geode/loader/SettingV3.hpp>
#include <Geode/loader/SettingEvent.hpp>
#include <Geode/loader/SettingNode.hpp>
#include <Geode/utils/general.hpp>
#include <Geode/utils/JsonValidation.hpp>
#include <regex>
using namespace geode::prelude;
template<class T>
static void parseCommon(T& sett, JsonMaybeObject& obj) {
obj.has("name").into(sett.name);
obj.has("description").into(sett.description);
if (auto defValue = obj.needs("default")) {
// Platform-specific default value
if (defValue.is<matjson::Object>()) {
auto def = defValue.obj();
if (auto plat = def.has(PlatformID::toShortString(GEODE_PLATFORM_TARGET, true))) {
plat.into(sett.defaultValue);
}
else {
defValue.into(sett.defaultValue);
}
}
else {
defValue.into(sett.defaultValue);
}
}
}
Result<BoolSetting> BoolSetting::parse(JsonMaybeObject& obj) {
BoolSetting sett;
parseCommon(sett, obj);
return Ok(sett);
}
Result<IntSetting> IntSetting::parse(JsonMaybeObject& obj) {
IntSetting sett;
parseCommon(sett, obj);
obj.has("min").into(sett.min);
obj.has("max").into(sett.max);
if (auto controls = obj.has("control").obj()) {
controls.has("arrows").into(sett.controls.arrows);
controls.has("big-arrows").into(sett.controls.bigArrows);
controls.has("arrow-step").into(sett.controls.arrowStep);
controls.has("big-arrow-step").into(sett.controls.bigArrowStep);
controls.has("slider").into(sett.controls.slider);
controls.has("slider-step").into(sett.controls.sliderStep);
controls.has("input").into(sett.controls.input);
}
return Ok(sett);
}
Result<FloatSetting> FloatSetting::parse(JsonMaybeObject& obj) {
FloatSetting sett;
parseCommon(sett, obj);
obj.has("min").into(sett.min);
obj.has("max").into(sett.max);
if (auto controls = obj.has("control").obj()) {
controls.has("arrows").into(sett.controls.arrows);
controls.has("big-arrows").into(sett.controls.bigArrows);
controls.has("arrow-step").into(sett.controls.arrowStep);
controls.has("big-arrow-step").into(sett.controls.bigArrowStep);
controls.has("slider").into(sett.controls.slider);
controls.has("slider-step").into(sett.controls.sliderStep);
controls.has("input").into(sett.controls.input);
}
return Ok(sett);
}
Result<StringSetting> StringSetting::parse(JsonMaybeObject& obj) {
StringSetting sett;
parseCommon(sett, obj);
obj.has("match").into(sett.controls->match);
obj.has("filter").into(sett.controls->filter);
obj.has("one-of").into(sett.controls->options);
return Ok(sett);
}
Result<FileSetting> FileSetting::parse(JsonMaybeObject& obj) {
FileSetting sett;
parseCommon(sett, obj);
if (auto controls = obj.has("control").obj()) {
for (auto& item : controls.has("filters").iterate()) {
if (auto iobj = item.obj()) {
Filter filter;
iobj.has("description").into(filter.description);
std::vector<matjson::Value> files;
iobj.has("files").into(files);
for (auto& i : files) {
filter.files.insert(i.as<std::string>());
}
sett.controls.filters.push_back(filter);
}
}
}
return Ok(sett);
}
Result<ColorSetting> ColorSetting::parse(JsonMaybeObject& obj) {
ColorSetting sett;
parseCommon(sett, obj);
return Ok(sett);
}
Result<ColorAlphaSetting> ColorAlphaSetting::parse(JsonMaybeObject& obj) {
ColorAlphaSetting sett;
parseCommon(sett, obj);
return Ok(sett);
}
Result<Setting> Setting::parse(
std::string const& key,
std::string const& mod,
JsonMaybeValue& value
) {
auto sett = Setting();
sett.m_key = key;
sett.m_modID = mod;
if (auto obj = value.obj()) {
std::string type;
obj.needs("type").into(type);
if (type.size()) {
switch (hash(type.c_str())) {
case hash("bool"): {
GEODE_UNWRAP_INTO(sett.m_kind, BoolSetting::parse(obj));
} break;
case hash("int"): {
GEODE_UNWRAP_INTO(sett.m_kind, IntSetting::parse(obj));
} break;
case hash("float"): {
GEODE_UNWRAP_INTO(sett.m_kind, FloatSetting::parse(obj));
} break;
case hash("string"): {
GEODE_UNWRAP_INTO(sett.m_kind, StringSetting::parse(obj));
} break;
case hash("rgb"): case hash("color"): {
GEODE_UNWRAP_INTO(sett.m_kind, ColorSetting::parse(obj));
} break;
case hash("rgba"): {
GEODE_UNWRAP_INTO(sett.m_kind, ColorAlphaSetting::parse(obj));
} break;
case hash("path"): case hash("file"): {
GEODE_UNWRAP_INTO(sett.m_kind, FileSetting::parse(obj));
} break;
case hash("custom"): {
sett.m_kind = CustomSetting {
.json = std::make_shared<ModJson>(obj.json())
};
// skip checking unknown keys
return Ok(sett);
} break;
default: return Err("Unknown setting type \"" + type + "\"");
}
}
// this is handled before the setting is parsed
obj.addKnownKey("platforms");
obj.checkUnknownKeys();
}
// if the type wasn't an object or a string, the JsonChecker that gave the
// JsonMaybeValue will fail eventually so we can continue on
return Ok(sett);
}
Setting::Setting(
std::string const& key,
std::string const& mod,
SettingKind const& kind
) : m_key(key), m_modID(mod), m_kind(kind) {}
std::unique_ptr<SettingValue> Setting::createDefaultValue() const {
return std::visit(makeVisitor {
[&](BoolSetting const& sett) -> std::unique_ptr<SettingValue> {
return std::make_unique<BoolSettingValue>(m_key, m_modID, sett);
},
[&](IntSetting const& sett) -> std::unique_ptr<SettingValue> {
return std::make_unique<IntSettingValue>(m_key, m_modID, sett);
},
[&](FloatSetting const& sett) -> std::unique_ptr<SettingValue> {
return std::make_unique<FloatSettingValue>(m_key, m_modID, sett);
},
[&](StringSetting const& sett) -> std::unique_ptr<SettingValue> {
return std::make_unique<StringSettingValue>(m_key, m_modID, sett);
},
[&](FileSetting const& sett) -> std::unique_ptr<SettingValue> {
return std::make_unique<FileSettingValue>(m_key, m_modID, sett);
},
[&](ColorSetting const& sett) -> std::unique_ptr<SettingValue> {
return std::make_unique<ColorSettingValue>(m_key, m_modID, sett);
},
[&](ColorAlphaSetting const& sett) -> std::unique_ptr<SettingValue> {
return std::make_unique<ColorAlphaSettingValue>(m_key, m_modID, sett);
},
[&](auto const& sett) -> std::unique_ptr<SettingValue> {
return nullptr;
},
}, m_kind);
}
bool Setting::isCustom() const {
return std::holds_alternative<CustomSetting>(m_kind);
}
std::string Setting::getDisplayName() const {
return std::visit(makeVisitor {
[&](CustomSetting const& sett) {
return std::string();
},
[&](auto const& sett) {
return sett.name.value_or(m_key);
},
}, m_kind);
}
std::optional<std::string> Setting::getDescription() const {
return std::visit(makeVisitor {
[&](CustomSetting const& sett) -> std::optional<std::string> {
return std::nullopt;
},
[&](auto const& sett) {
return sett.description;
},
}, m_kind);
}
std::string Setting::getModID() const {
return m_modID;
}
// StringSetting
StringSetting::StringSetting() : name(), description(), defaultValue(), controls(new Data()) {}
StringSetting::StringSetting(StringSetting const& other) : name(other.name), description(other.description),
defaultValue(other.defaultValue), controls(new Data(*other.controls)) {}
StringSetting::StringSetting(StringSetting&& other) noexcept : name(std::move(other.name)), description(std::move(other.description)),
defaultValue(std::move(other.defaultValue)), controls(std::move(other.controls)) {}
StringSetting& StringSetting::operator=(StringSetting const& other) {
name = other.name;
description = other.description;
defaultValue = other.defaultValue;
*controls = *other.controls;
return *this;
}
StringSetting& StringSetting::operator=(StringSetting&& other) noexcept {
name = std::move(other.name);
description = std::move(other.description);
defaultValue = std::move(other.defaultValue);
controls = std::move(other.controls);
return *this;
}
StringSetting::~StringSetting() = default;
// SettingValue
SettingValue::SettingValue(std::string const& key, std::string const& mod)
: m_key(key), m_modID(mod) {}
std::string SettingValue::getKey() const {
return m_key;
}
std::string SettingValue::getModID() const {
return m_modID;
}
void SettingValue::valueChanged() {
if (auto mod = Loader::get()->getLoadedMod(m_modID)) {
if (auto sett = mod->getSettingV3(m_key)) {
sett->markChanged();
}
}
}
// GeodeSettingValue & SettingValueSetter specializations
#define IMPL_NODE_AND_SETTERS(type_) \
template<> \
SettingNode* GeodeSettingValue< \
type_##Setting \
>::createNode(float width) { \
return type_##SettingNode::create(this, width); \
} \
template<> \
typename GeodeSettingValue<type_##Setting>::ValueType \
GeodeSettingValue<type_##Setting>::getValue() const { \
using S = typename SettingTypeForValueType<ValueType>::SettingType; \
if (auto mod = Loader::get()->getInstalledMod(m_modID)) { \
if (auto setting = typeinfo_pointer_cast<S>(mod->getSettingV3(m_key))) {\
return setting->getValue(); \
} \
} \
return m_value; \
} \
template<> \
void GeodeSettingValue< \
type_##Setting \
>::setValue(ValueType const& value) { \
using S = typename SettingTypeForValueType<ValueType>::SettingType; \
if (auto mod = Loader::get()->getInstalledMod(m_modID)) { \
if (auto setting = typeinfo_pointer_cast<S>(mod->getSettingV3(m_key))) {\
return setting->setValue(value); \
} \
} \
} \
template<> \
Result<> GeodeSettingValue< \
type_##Setting \
>::validate(ValueType const& value) const { \
using S = typename SettingTypeForValueType<ValueType>::SettingType; \
if (auto mod = Loader::get()->getInstalledMod(m_modID)) { \
if (auto setting = typeinfo_pointer_cast<S>(mod->getSettingV3(m_key))) {\
return setting->isValid(value); \
} \
} \
return Ok(); \
} \
template<> \
typename type_##Setting::ValueType SettingValueSetter< \
typename type_##Setting::ValueType \
>::get(SettingValue* setting) { \
using S = typename SettingTypeForValueType<typename type_##Setting::ValueType>::SettingType; \
if (auto mod = Loader::get()->getInstalledMod(setting->getModID())) { \
if (auto sett = typeinfo_pointer_cast<S>(mod->getSettingV3(setting->getKey()))) { \
return sett->getValue(); \
} \
} \
return typename type_##Setting::ValueType(); \
} \
template<> \
void SettingValueSetter< \
typename type_##Setting::ValueType \
>::set( \
SettingValue* setting, \
typename type_##Setting::ValueType const& value \
) { \
using S = typename SettingTypeForValueType<typename type_##Setting::ValueType>::SettingType; \
if (auto mod = Loader::get()->getInstalledMod(setting->getModID())) { \
if (auto sett = typeinfo_pointer_cast<S>(mod->getSettingV3(setting->getKey()))) { \
return sett->setValue(value); \
} \
} \
}
#define IMPL_TO_VALID(type_) \
template<> \
typename GeodeSettingValue<type_##Setting>::Valid \
GeodeSettingValue<type_##Setting>::toValid( \
typename type_##Setting::ValueType const& value \
) const
// instantiate values
namespace geode {
template class GeodeSettingValue<BoolSetting>;
template class GeodeSettingValue<IntSetting>;
template class GeodeSettingValue<FloatSetting>;
template class GeodeSettingValue<StringSetting>;
template class GeodeSettingValue<FileSetting>;
template class GeodeSettingValue<ColorSetting>;
template class GeodeSettingValue<ColorAlphaSetting>;
}
IMPL_TO_VALID(Bool) {
return { value, std::nullopt };
}
IMPL_TO_VALID(Int) {
if (m_definition.min && value < m_definition.min) {
return { m_definition.min.value(), fmt::format(
"Value must be more than or equal to {}",
m_definition.min.value()
) };
}
if (m_definition.max && value > m_definition.max) {
return { m_definition.max.value(), fmt::format(
"Value must be less than or equal to {}",
m_definition.max.value()
) };
}
return { value, std::nullopt };
}
IMPL_TO_VALID(Float) {
if (m_definition.min && value < m_definition.min) {
return { m_definition.min.value(), fmt::format(
"Value must be more than or equal to {}",
m_definition.min.value()
) };
}
if (m_definition.max && value > m_definition.max) {
return { m_definition.max.value(), fmt::format(
"Value must be less than or equal to {}",
m_definition.max.value()
) };
}
return { value, std::nullopt };
}
IMPL_TO_VALID(String) {
if (m_definition.controls->match) {
if (!std::regex_match(value, std::regex{m_definition.controls->match.value()})) {
return {
m_definition.defaultValue,
fmt::format(
"Value must match regex {}",
m_definition.controls->match.value()
)
};
}
}
else if (m_definition.controls->options) {
if (std::find(
m_definition.controls->options.value().begin(),
m_definition.controls->options.value().end(),
value
) == m_definition.controls->options.value().end()) {
return {
m_definition.defaultValue,
fmt::format(
"Value must be one of {}",
fmt::join(m_definition.controls->options.value(), ", ")
)
};
}
}
return { value, std::nullopt };
}
IMPL_TO_VALID(File) {
return { value, std::nullopt };
}
IMPL_TO_VALID(Color) {
return { value, std::nullopt };
}
IMPL_TO_VALID(ColorAlpha) {
return { value, std::nullopt };
}
IMPL_NODE_AND_SETTERS(Bool);
IMPL_NODE_AND_SETTERS(Int);
IMPL_NODE_AND_SETTERS(Float);
IMPL_NODE_AND_SETTERS(String);
IMPL_NODE_AND_SETTERS(File);
IMPL_NODE_AND_SETTERS(Color);
IMPL_NODE_AND_SETTERS(ColorAlpha);
// instantiate value setters
namespace geode {
template struct SettingValueSetter<typename BoolSetting::ValueType>;
template struct SettingValueSetter<typename IntSetting::ValueType>;
template struct SettingValueSetter<typename FloatSetting::ValueType>;
template struct SettingValueSetter<typename StringSetting::ValueType>;
template struct SettingValueSetter<typename FileSetting::ValueType>;
template struct SettingValueSetter<typename ColorSetting::ValueType>;
template struct SettingValueSetter<typename ColorAlphaSetting::ValueType>;
}
// SettingChangedEvent
SettingChangedEvent::SettingChangedEvent(Mod* mod, SettingValue* value)
: mod(mod), value(value) {}
// SettingChangedFilter
ListenerResult SettingChangedFilter::handle(
utils::MiniFunction<Callback> fn, SettingChangedEvent* event
) {
if (m_modID == event->mod->getID() &&
(!m_targetKey || m_targetKey.value() == event->value->getKey())
) {
fn(event->value);
}
return ListenerResult::Propagate;
}
SettingChangedFilter::SettingChangedFilter(
std::string const& modID,
std::optional<std::string> const& settingKey
) : m_modID(modID), m_targetKey(settingKey) {}

View file

@ -1,25 +0,0 @@
#include <Geode/loader/SettingNode.hpp>
#include <Geode/utils/cocos.hpp>
using namespace geode::prelude;
void SettingNode::dispatchChanged() {
if (m_delegate) {
m_delegate->settingValueChanged(this);
}
}
void SettingNode::dispatchCommitted() {
if (m_delegate) {
m_delegate->settingValueCommitted(this);
}
}
bool SettingNode::init(SettingValue* value) {
m_value = value;
return true;
}
void SettingNode::setDelegate(SettingNodeDelegate* delegate) {
m_delegate = delegate;
}

View file

@ -0,0 +1,16 @@
#pragma once
#include "SettingNodeV3.hpp"
using namespace geode::prelude;
using TitleSettingNode = TitleSettingNodeV3;
using BoolSettingNode = BoolSettingNodeV3;
template <class S>
using NumberSettingNode = NumberSettingNodeV3<S>;
using IntSettingNode = IntSettingNodeV3;
using FloatSettingNode = FloatSettingNodeV3;
using StringSettingNode = StringSettingNodeV3;
using FileSettingNode = FileSettingNodeV3;
using Color3BSettingNode = Color3BSettingNodeV3;
using Color4BSettingNode = Color4BSettingNodeV3;
using UnresolvedCustomSettingNode = UnresolvedCustomSettingNodeV3;

View file

@ -1,5 +1,4 @@
#include "SettingNodeV3.hpp" #include "SettingNodeV3.hpp"
#include <Geode/loader/SettingNode.hpp>
#include <Geode/utils/ColorProvider.hpp> #include <Geode/utils/ColorProvider.hpp>
#include <Geode/utils/ranges.hpp> #include <Geode/utils/ranges.hpp>
#include <Geode/loader/Dirs.hpp> #include <Geode/loader/Dirs.hpp>
@ -137,7 +136,7 @@ void SettingNodeV3::updateState(CCNode* invoker) {
m_impl->bg->setOpacity(75); m_impl->bg->setOpacity(75);
} }
m_impl->nameMenu->setContentWidth(this->getContentWidth() - m_impl->buttonMenu->getContentWidth() - 20); m_impl->nameMenu->setContentWidth(this->getContentWidth() - m_impl->buttonMenu->getContentWidth() - 25);
m_impl->nameMenu->updateLayout(); m_impl->nameMenu->updateLayout();
} }
@ -463,6 +462,7 @@ void FileSettingNodeV3::updateState(CCNode* invoker) {
// which is clever and good UX but also a hack so I also need to hack to support that // which is clever and good UX but also a hack so I also need to hack to support that
const auto isTextualDefaultValue = [this, setting = this->getSetting()]() { const auto isTextualDefaultValue = [this, setting = this->getSetting()]() {
if (this->hasNonDefaultValue()) return false; if (this->hasNonDefaultValue()) return false;
if (setting->getDefaultValue().string().size() > 20) return false;
std::error_code ec; std::error_code ec;
return setting->isFolder() ? return setting->isFolder() ?
!std::filesystem::is_directory(setting->getDefaultValue(), ec) : !std::filesystem::is_directory(setting->getDefaultValue(), ec) :
@ -683,51 +683,3 @@ UnresolvedCustomSettingNodeV3* UnresolvedCustomSettingNodeV3::create(std::string
CC_SAFE_DELETE(ret); CC_SAFE_DELETE(ret);
return nullptr; return nullptr;
} }
// LegacyCustomSettingToV3Node
bool LegacyCustomSettingToV3Node::init(std::shared_ptr<LegacyCustomSettingV3> original, float width) {
if (!SettingNodeV3::init(original, width))
return false;
this->getNameMenu()->setVisible(false);
this->getButtonMenu()->setVisible(false);
m_original = original->getValue()->createNode(width);
m_original->setDelegate(this);
this->setContentSize({ width, m_original->getContentHeight() });
this->addChildAtPosition(m_original, Anchor::BottomLeft, ccp(0, 0), ccp(0, 0));
return true;
}
void LegacyCustomSettingToV3Node::settingValueChanged(SettingNode*) {
SettingNodeValueChangeEventV3(this, false).post();
}
void LegacyCustomSettingToV3Node::settingValueCommitted(SettingNode*) {
SettingNodeValueChangeEventV3(this, true).post();
}
void LegacyCustomSettingToV3Node::onCommit() {
m_original->commit();
}
bool LegacyCustomSettingToV3Node::hasUncommittedChanges() const {
return m_original->hasUncommittedChanges();
}
bool LegacyCustomSettingToV3Node::hasNonDefaultValue() const {
return m_original->hasNonDefaultValue();
}
void LegacyCustomSettingToV3Node::onResetToDefault() {
m_original->resetToDefault();
}
LegacyCustomSettingToV3Node* LegacyCustomSettingToV3Node::create(std::shared_ptr<LegacyCustomSettingV3> original, float width) {
auto ret = new LegacyCustomSettingToV3Node();
if (ret && ret->init(original, width)) {
ret->autorelease();
return ret;
}
CC_SAFE_DELETE(ret);
return nullptr;
}

View file

@ -1,7 +1,6 @@
#pragma once #pragma once
#include <Geode/loader/SettingV3.hpp> #include <Geode/loader/Setting.hpp>
#include <Geode/loader/SettingNode.hpp>
#include <Geode/binding/CCMenuItemToggler.hpp> #include <Geode/binding/CCMenuItemToggler.hpp>
#include <Geode/binding/ColorChannelSprite.hpp> #include <Geode/binding/ColorChannelSprite.hpp>
#include <Geode/binding/Slider.hpp> #include <Geode/binding/Slider.hpp>
@ -314,25 +313,3 @@ public:
bool hasNonDefaultValue() const override; bool hasNonDefaultValue() const override;
void onResetToDefault() override; void onResetToDefault() override;
}; };
// If these classes do get exposed in headers,
// LegacyCustomSettingToV3Node SHOULD NOT BE EXPOSED!!!!!! DO NOT DO THAT!!!!
class LegacyCustomSettingToV3Node : public SettingNodeV3, public SettingNodeDelegate {
protected:
SettingNode* m_original;
bool init(std::shared_ptr<LegacyCustomSettingV3> original, float width);
void onCommit() override;
void settingValueChanged(SettingNode*) override;
void settingValueCommitted(SettingNode*) override;
public:
static LegacyCustomSettingToV3Node* create(std::shared_ptr<LegacyCustomSettingV3> original, float width);
bool hasUncommittedChanges() const override;
bool hasNonDefaultValue() const override;
void onResetToDefault() override;
};

View file

@ -1,5 +1,5 @@
#include <Geode/loader/SettingV3.hpp> #include <Geode/loader/Mod.hpp>
#include <Geode/loader/SettingEvent.hpp> #include <Geode/loader/Setting.hpp>
#include <Geode/loader/ModSettingsManager.hpp> #include <Geode/loader/ModSettingsManager.hpp>
#include <Geode/utils/ranges.hpp> #include <Geode/utils/ranges.hpp>
#include <Geode/utils/string.hpp> #include <Geode/utils/string.hpp>
@ -7,6 +7,7 @@
#include <Geode/utils/JsonValidation.hpp> #include <Geode/utils/JsonValidation.hpp>
#include <regex> #include <regex>
#include "SettingNodeV3.hpp" #include "SettingNodeV3.hpp"
#include <matjson/std.hpp>
using namespace geode::prelude; using namespace geode::prelude;
@ -46,7 +47,7 @@ namespace enable_if_parsing {
if (!mod->hasSetting(settingID)) { if (!mod->hasSetting(settingID)) {
return Err("Mod '{}' does not have setting '{}'", mod->getName(), settingID); return Err("Mod '{}' does not have setting '{}'", mod->getName(), settingID);
} }
if (!typeinfo_pointer_cast<BoolSettingV3>(mod->getSettingV3(settingID))) { if (!typeinfo_pointer_cast<BoolSettingV3>(mod->getSetting(settingID))) {
return Err("Setting '{}' in mod '{}' is not a boolean setting", settingID, mod->getName()); return Err("Setting '{}' in mod '{}' is not a boolean setting", settingID, mod->getName());
} }
} }
@ -60,7 +61,7 @@ namespace enable_if_parsing {
// This is an if-check just in case, even though check() should already // This is an if-check just in case, even though check() should already
// make sure that getSettingV3 is guaranteed to return true // make sure that getSettingV3 is guaranteed to return true
auto name = settingID; auto name = settingID;
if (auto sett = mod->getSettingV3(settingID)) { if (auto sett = mod->getSetting(settingID)) {
name = sett->getDisplayName(); name = sett->getDisplayName();
} }
if (modID == defaultModID) { if (modID == defaultModID) {
@ -153,18 +154,21 @@ namespace enable_if_parsing {
return Ok(); return Ok();
} }
Result<> eval(std::string const& defaultModID) const override { Result<> eval(std::string const& defaultModID) const override {
Result<> err = Ok(); std::optional<std::string> err;
for (auto& comp : components) { for (auto& comp : components) {
auto res = comp->eval(defaultModID); auto res = comp->eval(defaultModID);
if (res) { if (res) {
return Ok(); return Ok();
} }
// Only show first condition that isn't met // Only show first condition that isn't met
if (err.isOk()) { if (!err.has_value()) {
err = Err(res.unwrapErr()); err = res.unwrapErr();
} }
} }
return err; if (err.has_value()) {
return Err(*err);
}
return Ok();
} }
}; };
@ -236,7 +240,10 @@ namespace enable_if_parsing {
auto original = m_index; auto original = m_index;
auto ret = this->nextWord(); auto ret = this->nextWord();
m_index = original; m_index = original;
return ret ? *ret : std::nullopt; if (!ret) {
return std::nullopt;
}
return ret.unwrap();
} }
Result<std::unique_ptr<Component>> nextComponent() { Result<std::unique_ptr<Component>> nextComponent() {
GEODE_UNWRAP_INTO(auto maybeWord, this->nextWord()); GEODE_UNWRAP_INTO(auto maybeWord, this->nextWord());
@ -423,7 +430,7 @@ public:
std::optional<std::string> settingKey; std::optional<std::string> settingKey;
}; };
ListenerResult SettingChangedFilterV3::handle(utils::MiniFunction<Callback> fn, SettingChangedEventV3* event) { ListenerResult SettingChangedFilterV3::handle(std::function<Callback> fn, SettingChangedEventV3* event) {
if ( if (
event->getSetting()->getModID() == m_impl->modID && event->getSetting()->getModID() == m_impl->modID &&
!m_impl->settingKey || event->getSetting()->getKey() == m_impl->settingKey !m_impl->settingKey || event->getSetting()->getKey() == m_impl->settingKey
@ -447,7 +454,7 @@ SettingChangedFilterV3::SettingChangedFilterV3(Mod* mod, std::optional<std::stri
SettingChangedFilterV3::SettingChangedFilterV3(SettingChangedFilterV3 const&) = default; SettingChangedFilterV3::SettingChangedFilterV3(SettingChangedFilterV3 const&) = default;
EventListener<SettingChangedFilterV3>* geode::listenForAllSettingChanges( EventListener<SettingChangedFilterV3>* geode::listenForAllSettingChangesV3(
std::function<void(std::shared_ptr<SettingV3>)> const& callback, std::function<void(std::shared_ptr<SettingV3>)> const& callback,
Mod* mod Mod* mod
) { ) {
@ -573,20 +580,7 @@ void SettingV3::markChanged() {
manager->markRestartRequired(); manager->markRestartRequired();
} }
SettingChangedEventV3(shared_from_this()).post(); SettingChangedEventV3(shared_from_this()).post();
if (manager) {
// Use ModSettingsManager rather than convertToLegacyValue since it
// caches the result and we want to have that for performance
SettingChangedEvent(this->getMod(), manager->getLegacy(this->getKey()).get()).post();
}
} }
std::optional<Setting> SettingV3::convertToLegacy() const {
return std::nullopt;
}
std::optional<std::shared_ptr<SettingValue>> SettingV3::convertToLegacyValue() const {
return std::nullopt;
}
class TitleSettingV3::Impl final { class TitleSettingV3::Impl final {
public: public:
}; };
@ -618,63 +612,6 @@ bool TitleSettingV3::isDefaultValue() const {
} }
void TitleSettingV3::reset() {} void TitleSettingV3::reset() {}
class LegacyCustomSettingV3::Impl final {
public:
matjson::Value json;
std::shared_ptr<SettingValue> legacyValue = nullptr;
};
LegacyCustomSettingV3::LegacyCustomSettingV3(PrivateMarker) : m_impl(std::make_shared<Impl>()) {}
Result<std::shared_ptr<LegacyCustomSettingV3>> LegacyCustomSettingV3::parse(std::string const& key, std::string const& modID, matjson::Value const& json) {
auto ret = std::make_shared<LegacyCustomSettingV3>(PrivateMarker());
ret->init(key, modID);
ret->m_impl->json = json;
return Ok(ret);
}
std::shared_ptr<SettingValue> LegacyCustomSettingV3::getValue() const {
return m_impl->legacyValue;
}
void LegacyCustomSettingV3::setValue(std::shared_ptr<SettingValue> value) {
m_impl->legacyValue = value;
}
bool LegacyCustomSettingV3::load(matjson::Value const& json) {
if (m_impl->legacyValue) {
return m_impl->legacyValue->load(json);
}
return true;
}
bool LegacyCustomSettingV3::save(matjson::Value& json) const {
if (m_impl->legacyValue) {
return m_impl->legacyValue->save(json);
}
return true;
}
SettingNodeV3* LegacyCustomSettingV3::createNode(float width) {
if (m_impl->legacyValue) {
return LegacyCustomSettingToV3Node::create(
std::static_pointer_cast<LegacyCustomSettingV3>(shared_from_this()), width
);
}
return UnresolvedCustomSettingNodeV3::create(this->getKey(), this->getMod(), width);
}
bool LegacyCustomSettingV3::isDefaultValue() const {
return true;
}
void LegacyCustomSettingV3::reset() {}
std::optional<Setting> LegacyCustomSettingV3::convertToLegacy() const {
return Setting(this->getKey(), this->getModID(), SettingKind(CustomSetting {
.json = std::make_shared<ModJson>(m_impl->json)
}));
}
std::optional<std::shared_ptr<SettingValue>> LegacyCustomSettingV3::convertToLegacyValue() const {
return m_impl->legacyValue ? std::optional(m_impl->legacyValue) : std::nullopt;
}
class BoolSettingV3::Impl final { class BoolSettingV3::Impl final {
public: public:
}; };
@ -699,17 +636,6 @@ SettingNodeV3* BoolSettingV3::createNode(float width) {
); );
} }
std::optional<Setting> BoolSettingV3::convertToLegacy() const {
return Setting(this->getKey(), this->getModID(), SettingKind(BoolSetting {
.name = this->getName(),
.description = this->getDescription(),
.defaultValue = this->getDefaultValue(),
}));
}
std::optional<std::shared_ptr<SettingValue>> BoolSettingV3::convertToLegacyValue() const {
return this->convertToLegacy()->createDefaultValue();
}
class IntSettingV3::Impl final { class IntSettingV3::Impl final {
public: public:
std::optional<int64_t> minValue; std::optional<int64_t> minValue;
@ -819,28 +745,6 @@ SettingNodeV3* IntSettingV3::createNode(float width) {
); );
} }
std::optional<Setting> IntSettingV3::convertToLegacy() const {
return Setting(this->getKey(), this->getModID(), SettingKind(IntSetting {
.name = this->getName(),
.description = this->getDescription(),
.defaultValue = this->getDefaultValue(),
.min = this->getMinValue(),
.max = this->getMaxValue(),
.controls = {
.arrows = this->isArrowsEnabled(),
.bigArrows = this->isBigArrowsEnabled(),
.arrowStep = this->getArrowStepSize(),
.bigArrowStep = this->getBigArrowStepSize(),
.slider = this->isSliderEnabled(),
.sliderStep = this->getSliderSnap(),
.input = this->isInputEnabled(),
},
}));
}
std::optional<std::shared_ptr<SettingValue>> IntSettingV3::convertToLegacyValue() const {
return this->convertToLegacy()->createDefaultValue();
}
class FloatSettingV3::Impl final { class FloatSettingV3::Impl final {
public: public:
std::optional<double> minValue; std::optional<double> minValue;
@ -948,27 +852,6 @@ SettingNodeV3* FloatSettingV3::createNode(float width) {
); );
} }
std::optional<Setting> FloatSettingV3::convertToLegacy() const {
return Setting(this->getKey(), this->getModID(), SettingKind(FloatSetting {
.name = this->getName(),
.description = this->getDescription(),
.defaultValue = this->getDefaultValue(),
.min = this->getMinValue(),
.max = this->getMaxValue(),
.controls = {
.arrows = this->isArrowsEnabled(),
.bigArrows = this->isBigArrowsEnabled(),
.arrowStep = static_cast<size_t>(this->getArrowStepSize()),
.bigArrowStep = static_cast<size_t>(this->getBigArrowStepSize()),
.slider = this->isSliderEnabled(),
.sliderStep = this->getSliderSnap(),
.input = this->isInputEnabled(),
},
}));
}
std::optional<std::shared_ptr<SettingValue>> FloatSettingV3::convertToLegacyValue() const {
return this->convertToLegacy()->createDefaultValue();
}
class StringSettingV3::Impl final { class StringSettingV3::Impl final {
public: public:
@ -1026,20 +909,6 @@ SettingNodeV3* StringSettingV3::createNode(float width) {
); );
} }
std::optional<Setting> StringSettingV3::convertToLegacy() const {
auto setting = StringSetting();
setting.name = this->getName();
setting.description = this->getDescription();
setting.defaultValue = this->getDefaultValue();
setting.controls->filter = this->getAllowedCharacters();
setting.controls->match = this->getRegexValidator();
setting.controls->options = this->getEnumOptions();
return Setting(this->getKey(), this->getModID(), SettingKind(setting));
}
std::optional<std::shared_ptr<SettingValue>> StringSettingV3::convertToLegacyValue() const {
return this->convertToLegacy()->createDefaultValue();
}
class FileSettingV3::Impl final { class FileSettingV3::Impl final {
public: public:
bool folder = false; bool folder = false;
@ -1153,18 +1022,6 @@ SettingNodeV3* FileSettingV3::createNode(float width) {
); );
} }
std::optional<Setting> FileSettingV3::convertToLegacy() const {
auto setting = FileSetting();
setting.name = this->getName();
setting.description = this->getDescription();
setting.defaultValue = this->getDefaultValue();
setting.controls.filters = this->getFilters().value_or(std::vector<utils::file::FilePickOptions::Filter>());
return Setting(this->getKey(), this->getModID(), SettingKind(setting));
}
std::optional<std::shared_ptr<SettingValue>> FileSettingV3::convertToLegacyValue() const {
return this->convertToLegacy()->createDefaultValue();
}
class Color3BSettingV3::Impl final { class Color3BSettingV3::Impl final {
public: public:
}; };
@ -1189,17 +1046,6 @@ SettingNodeV3* Color3BSettingV3::createNode(float width) {
); );
} }
std::optional<Setting> Color3BSettingV3::convertToLegacy() const {
auto setting = ColorSetting();
setting.name = this->getName();
setting.description = this->getDescription();
setting.defaultValue = this->getDefaultValue();
return Setting(this->getKey(), this->getModID(), SettingKind(setting));
}
std::optional<std::shared_ptr<SettingValue>> Color3BSettingV3::convertToLegacyValue() const {
return this->convertToLegacy()->createDefaultValue();
}
class Color4BSettingV3::Impl final { class Color4BSettingV3::Impl final {
public: public:
}; };
@ -1223,14 +1069,3 @@ SettingNodeV3* Color4BSettingV3::createNode(float width) {
std::static_pointer_cast<Color4BSettingV3>(shared_from_this()), width std::static_pointer_cast<Color4BSettingV3>(shared_from_this()), width
); );
} }
std::optional<Setting> Color4BSettingV3::convertToLegacy() const {
auto setting = ColorAlphaSetting();
setting.name = this->getName();
setting.description = this->getDescription();
setting.defaultValue = this->getDefaultValue();
return Setting(this->getKey(), this->getModID(), SettingKind(setting));
}
std::optional<std::shared_ptr<SettingValue>> Color4BSettingV3::convertToLegacyValue() const {
return this->convertToLegacy()->createDefaultValue();
}

View file

@ -1,7 +1,9 @@
#include <Geode/loader/Tulip.hpp> #include <Geode/loader/Tulip.hpp>
tulip::hook::Result<void*> geode::hook::createWrapper( using namespace geode::prelude;
Result<void*> geode::hook::createWrapper(
void* address, void* address,
tulip::hook::WrapperMetadata const& metadata tulip::hook::WrapperMetadata const& metadata
) noexcept { ) noexcept {

View file

@ -16,7 +16,7 @@ updater::ResourceDownloadEvent::ResourceDownloadEvent(
) : status(std::move(status)) {} ) : status(std::move(status)) {}
ListenerResult updater::ResourceDownloadFilter::handle( ListenerResult updater::ResourceDownloadFilter::handle(
const utils::MiniFunction<Callback>& fn, const std::function<Callback>& fn,
ResourceDownloadEvent* event ResourceDownloadEvent* event
) { ) {
fn(event); fn(event);
@ -30,7 +30,7 @@ updater::LoaderUpdateEvent::LoaderUpdateEvent(
) : status(std::move(status)) {} ) : status(std::move(status)) {}
ListenerResult updater::LoaderUpdateFilter::handle( ListenerResult updater::LoaderUpdateFilter::handle(
const utils::MiniFunction<Callback>& fn, const std::function<Callback>& fn,
LoaderUpdateEvent* event LoaderUpdateEvent* event
) { ) {
fn(event); fn(event);
@ -45,8 +45,8 @@ std::optional<matjson::Value> s_latestGithubRelease;
bool s_isNewUpdateDownloaded = false; bool s_isNewUpdateDownloaded = false;
void updater::fetchLatestGithubRelease( void updater::fetchLatestGithubRelease(
const utils::MiniFunction<void(matjson::Value const&)>& then, const std::function<void(matjson::Value const&)>& then,
utils::MiniFunction<void(std::string const&)> expect, bool force std::function<void(std::string const&)> expect, bool force
) { ) {
if (s_latestGithubRelease) { if (s_latestGithubRelease) {
return then(s_latestGithubRelease.value()); return then(s_latestGithubRelease.value());
@ -83,7 +83,7 @@ void updater::fetchLatestGithubRelease(
} }
else { else {
Mod::get()->setSavedValue("last-modified-auto-update-check", response->header("Last-Modified").value_or("")); Mod::get()->setSavedValue("last-modified-auto-update-check", response->header("Last-Modified").value_or(""));
s_latestGithubRelease = *json; s_latestGithubRelease = json.unwrap();
then(*s_latestGithubRelease); then(*s_latestGithubRelease);
} }
} }
@ -102,13 +102,10 @@ void updater::downloadLatestLoaderResources() {
log::debug("Downloading latest resources", Loader::get()->getVersion().toVString()); log::debug("Downloading latest resources", Loader::get()->getVersion().toVString());
fetchLatestGithubRelease( fetchLatestGithubRelease(
[](matjson::Value const& raw) { [](matjson::Value const& raw) {
auto json = raw; auto root = checkJson(raw, "[]");
JsonChecker checker(json);
auto root = checker.root("[]").obj();
// find release asset // find release asset
for (auto asset : root.needs("assets").iterate()) { for (auto& obj : root.needs("assets").items()) {
auto obj = asset.obj();
if (obj.needs("name").get<std::string>() == "resources.zip") { if (obj.needs("name").get<std::string>() == "resources.zip") {
updater::tryDownloadLoaderResources( updater::tryDownloadLoaderResources(
obj.needs("browser_download_url").get<std::string>(), obj.needs("browser_download_url").get<std::string>(),
@ -144,7 +141,7 @@ void updater::tryDownloadLoaderResources(std::string const& url, bool tryLatestO
// unzip resources zip // unzip resources zip
auto unzip = file::Unzip::create(response->data()); auto unzip = file::Unzip::create(response->data());
if (unzip) { if (unzip) {
auto ok = unzip->extractAllTo(resourcesDir); auto ok = unzip.unwrap().extractAllTo(resourcesDir);
if (ok) { if (ok) {
updater::updateSpecialFiles(); updater::updateSpecialFiles();
ResourceDownloadEvent(UpdateFinished()).post(); ResourceDownloadEvent(UpdateFinished()).post();
@ -206,13 +203,10 @@ void updater::downloadLoaderResources(bool useLatestRelease) {
RUNNING_REQUESTS.erase("@downloadLoaderResources"); RUNNING_REQUESTS.erase("@downloadLoaderResources");
if (response->ok()) { if (response->ok()) {
if (auto ok = response->json()) { if (auto ok = response->json()) {
auto json = ok.unwrap(); auto root = checkJson(ok.unwrap(), "[]");
JsonChecker checker(json);
auto root = checker.root("[]").obj();
// find release asset // find release asset
for (auto asset : root.needs("assets").iterate()) { for (auto& obj : root.needs("assets").items()) {
auto obj = asset.obj();
if (obj.needs("name").get<std::string>() == "resources.zip") { if (obj.needs("name").get<std::string>() == "resources.zip") {
updater::tryDownloadLoaderResources( updater::tryDownloadLoaderResources(
obj.needs("browser_download_url").get<std::string>(), obj.needs("browser_download_url").get<std::string>(),
@ -314,7 +308,7 @@ void updater::downloadLoaderUpdate(std::string const& url) {
// unzip resources zip // unzip resources zip
auto unzip = file::Unzip::create(response->data()); auto unzip = file::Unzip::create(response->data());
if (unzip) { if (unzip) {
auto ok = unzip->extractAllTo(targetDir); auto ok = unzip.unwrap().extractAllTo(targetDir);
if (ok) { if (ok) {
s_isNewUpdateDownloaded = true; s_isNewUpdateDownloaded = true;
LoaderUpdateEvent(UpdateFinished()).post(); LoaderUpdateEvent(UpdateFinished()).post();
@ -362,9 +356,7 @@ void updater::checkForLoaderUpdates() {
// Check for updates in the background // Check for updates in the background
fetchLatestGithubRelease( fetchLatestGithubRelease(
[](matjson::Value const& raw) { [](matjson::Value const& raw) {
auto json = raw; auto root = checkJson(raw, "[]");
JsonChecker checker(json);
auto root = checker.root("[]").obj();
VersionInfo ver { 0, 0, 0 }; VersionInfo ver { 0, 0, 0 };
root.needs("tag_name").into(ver); root.needs("tag_name").into(ver);
@ -388,8 +380,7 @@ void updater::checkForLoaderUpdates() {
} }
// find release asset // find release asset
for (auto asset : root.needs("assets").iterate()) { for (auto& obj : root.needs("assets").items()) {
auto obj = asset.obj();
if (string::endsWith( if (string::endsWith(
obj.needs("name").get<std::string>(), obj.needs("name").get<std::string>(),
fmt::format("{}.zip", PlatformID::toShortString(GEODE_PLATFORM_TARGET, true)) fmt::format("{}.zip", PlatformID::toShortString(GEODE_PLATFORM_TARGET, true))

View file

@ -2,7 +2,6 @@
#include <string> #include <string>
#include <matjson.hpp> #include <matjson.hpp>
#include <Geode/utils/MiniFunction.hpp>
#include <Geode/loader/Event.hpp> #include <Geode/loader/Event.hpp>
namespace geode::updater { namespace geode::updater {
@ -20,7 +19,7 @@ namespace geode::updater {
public: public:
using Callback = void(ResourceDownloadEvent*); using Callback = void(ResourceDownloadEvent*);
static ListenerResult handle(const utils::MiniFunction<Callback>& fn, ResourceDownloadEvent* event); static ListenerResult handle(const std::function<Callback>& fn, ResourceDownloadEvent* event);
ResourceDownloadFilter(); ResourceDownloadFilter();
}; };
@ -33,7 +32,7 @@ namespace geode::updater {
public: public:
using Callback = void(LoaderUpdateEvent*); using Callback = void(LoaderUpdateEvent*);
static ListenerResult handle(const utils::MiniFunction<Callback>& fn, LoaderUpdateEvent* event); static ListenerResult handle(const std::function<Callback>& fn, LoaderUpdateEvent* event);
LoaderUpdateFilter(); LoaderUpdateFilter();
}; };
@ -43,8 +42,8 @@ namespace geode::updater {
void downloadLatestLoaderResources(); void downloadLatestLoaderResources();
void downloadLoaderUpdate(std::string const& url); void downloadLoaderUpdate(std::string const& url);
void fetchLatestGithubRelease( void fetchLatestGithubRelease(
const utils::MiniFunction<void(matjson::Value const&)>& then, const std::function<void(matjson::Value const&)>& then,
utils::MiniFunction<void(std::string const&)> expect, std::function<void(std::string const&)> expect,
bool force = false bool force = false
); );

View file

@ -21,6 +21,7 @@ std::string Loader::Impl::getGameVersion() {
case 37: m_gdVersion = "2.200"; break; case 37: m_gdVersion = "2.200"; break;
case 38: m_gdVersion = "2.205"; break; case 38: m_gdVersion = "2.205"; break;
case 39: m_gdVersion = "2.206"; break; case 39: m_gdVersion = "2.206"; break;
case 40: m_gdVersion = "2.2074"; break;
default: m_gdVersion = std::to_string(version_code); default: m_gdVersion = std::to_string(version_code);
} }
} }

View file

@ -261,12 +261,12 @@ int writeAndGetPid() {
auto res = file::readString(pidFile); auto res = file::readString(pidFile);
if (!res) { if (!res) {
log::warn("Failed to read last-pid file: {}", res.error()); log::warn("Failed to read last-pid file: {}", res.unwrapErr());
} }
else { else {
auto res = numFromString<int>(res.unwrap()); auto res = numFromString<int>(res.unwrap());
if (!res) { if (!res) {
log::warn("Failed to parse last-pid file: {}", res.error()); log::warn("Failed to parse last-pid file: {}", res.unwrapErr());
} }
else lastPid = res.unwrap(); else lastPid = res.unwrap();
} }
@ -281,7 +281,7 @@ int writeAndGetPid() {
auto res = file::writeString(pidFile, std::to_string(getpid())); auto res = file::writeString(pidFile, std::to_string(getpid()));
if (!res) { if (!res) {
log::warn("Failed to write last-pid file: {}", res.error()); log::warn("Failed to write last-pid file: {}", res.unwrapErr());
} }
return lastPid; return lastPid;

View file

@ -101,7 +101,7 @@ namespace geode::stl {
} }
// TODO: add a copyFrom(string const&) to take advantage // TODO: add a copyFrom(string const&) to take advantage
// of gnustl refcounted strings // of gnustl refcounted strings
void StringImpl::setStorage(const std::string_view str) { void StringImpl::setStorage(std::string_view str) {
this->free(); this->free();
if (str.size() == 0) { if (str.size() == 0) {

View file

@ -6,14 +6,13 @@ using namespace geode::prelude;
#include <Geode/utils/web.hpp> #include <Geode/utils/web.hpp>
#include <filesystem> #include <filesystem>
#include <Geode/utils/general.hpp> #include <Geode/utils/general.hpp>
#include <Geode/utils/MiniFunction.hpp>
#include <Geode/utils/permission.hpp> #include <Geode/utils/permission.hpp>
#include <Geode/utils/Task.hpp> #include <Geode/utils/Task.hpp>
#include <Geode/loader/Loader.hpp> #include <Geode/loader/Loader.hpp>
#include <Geode/binding/AppDelegate.hpp> #include <Geode/binding/AppDelegate.hpp>
#include <Geode/loader/Log.hpp> #include <Geode/loader/Log.hpp>
#include <Geode/binding/MenuLayer.hpp> #include <Geode/binding/MenuLayer.hpp>
#include <Geode/utils/Result.hpp> #include <Geode/Result.hpp>
#include <Geode/DefaultInclude.hpp> #include <Geode/DefaultInclude.hpp>
#include <optional> #include <optional>
#include <mutex> #include <mutex>
@ -139,9 +138,9 @@ bool utils::file::openFolder(std::filesystem::path const& path) {
} }
std::mutex s_callbackMutex; std::mutex s_callbackMutex;
static utils::MiniFunction<void(Result<std::filesystem::path>)> s_fileCallback {}; static std::function<void(Result<std::filesystem::path>)> s_fileCallback {};
static utils::MiniFunction<void(Result<std::vector<std::filesystem::path>>)> s_filesCallback {}; static std::function<void(Result<std::vector<std::filesystem::path>>)> s_filesCallback {};
static utils::MiniFunction<bool()> s_taskCancelled {}; static std::function<bool()> s_taskCancelled {};
extern "C" extern "C"
JNIEXPORT void JNICALL Java_com_geode_launcher_utils_GeodeUtils_selectFileCallback( JNIEXPORT void JNICALL Java_com_geode_launcher_utils_GeodeUtils_selectFileCallback(
@ -356,7 +355,7 @@ bool geode::utils::permission::getPermissionStatus(Permission permission) {
return false; return false;
} }
static MiniFunction<void(bool)> s_permissionCallback; static std::function<void(bool)> s_permissionCallback;
extern "C" extern "C"
JNIEXPORT void JNICALL Java_com_geode_launcher_utils_GeodeUtils_permissionCallback( JNIEXPORT void JNICALL Java_com_geode_launcher_utils_GeodeUtils_permissionCallback(
@ -371,7 +370,7 @@ JNIEXPORT void JNICALL Java_com_geode_launcher_utils_GeodeUtils_permissionCallba
} }
} }
void geode::utils::permission::requestPermission(Permission permission, utils::MiniFunction<void(bool)> callback) { void geode::utils::permission::requestPermission(Permission permission, std::function<void(bool)> callback) {
s_permissionCallback = callback; s_permissionCallback = callback;
JniMethodInfo info; JniMethodInfo info;
if (JniHelper::getStaticMethodInfo(info, "com/geode/launcher/utils/GeodeUtils", "requestPermission", "(Ljava/lang/String;)V")) { if (JniHelper::getStaticMethodInfo(info, "com/geode/launcher/utils/GeodeUtils", "requestPermission", "(Ljava/lang/String;)V")) {

View file

@ -39,6 +39,6 @@ bool geode::utils::permission::getPermissionStatus(Permission permission) {
return true; // unimplemented return true; // unimplemented
} }
void geode::utils::permission::requestPermission(Permission permission, utils::MiniFunction<void(bool)> callback) { void geode::utils::permission::requestPermission(Permission permission, std::function<void(bool)> callback) {
callback(true); // unimplemented callback(true); // unimplemented
} }

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