Compare commits
133 commits
05fe8d22b5
...
e32f38929e
Author | SHA1 | Date | |
---|---|---|---|
|
e32f38929e | ||
|
7e760b192b | ||
|
00fa3b6236 | ||
|
1f3b2145e1 | ||
|
418b971da4 | ||
|
00ab705154 | ||
|
1ff24f09c6 | ||
|
6e86b38990 | ||
|
7bcf50da57 | ||
|
38f3385c90 | ||
|
4d5e465ade | ||
|
6e11d0a6b0 | ||
|
c94a533d1c | ||
|
b9fb2f6778 | ||
|
3fa91241aa | ||
|
302eea1f47 | ||
|
06eb32310c | ||
|
a7f15bcc01 | ||
|
5919c7e09a | ||
|
17bf7722c9 | ||
|
22210956bb | ||
|
d6f0c597f1 | ||
|
f6c23220d8 | ||
|
9fe3d133e9 | ||
|
3081164600 | ||
|
bebc7b4074 | ||
|
5645399a9f | ||
|
1a201e1d65 | ||
|
5592ef68c3 | ||
|
da92090108 | ||
|
64d9a289a3 | ||
|
fb504cbf83 | ||
|
9834cb2a22 | ||
|
ecec11fa93 | ||
|
0e8d4c60bc | ||
|
81cd8d1bca | ||
|
48cad613c7 | ||
|
f9fed578aa | ||
|
13b30e591a | ||
|
893b03e313 | ||
|
7c4e06d20c | ||
|
e881dc5ef2 | ||
|
280b6efa94 | ||
|
01807fedc9 | ||
|
22a11b96e2 | ||
|
6679a690a2 | ||
|
ebd4c920f5 | ||
|
71f56ef49e | ||
|
1af10eea7f | ||
|
37fb2a3ddf | ||
|
290e0e0a34 | ||
|
a4f8295784 | ||
|
9bebb9819c | ||
|
6765bbbc75 | ||
|
6dbe289a7a | ||
|
47f0b555f3 | ||
|
708e6dc7f2 | ||
|
f96ea5e727 | ||
|
0b2fc66a89 | ||
|
df2528c8a5 | ||
|
03a4387c80 | ||
|
24294c7361 | ||
|
74a70112a4 | ||
|
5c85b3c48d | ||
|
123b3abff3 | ||
|
6d13f7837d | ||
|
9b95301472 | ||
|
02845d967e | ||
|
2d66279243 | ||
|
b662aac29d | ||
|
c197e2913c | ||
|
ab196b9adf | ||
|
0ee9aebdee | ||
|
00cc510d5b | ||
|
e61b2c0595 | ||
|
acad3d2a8d | ||
|
09fa872781 | ||
|
d415c949d0 | ||
|
c9e97af18a | ||
|
9d6b2954e5 | ||
|
bcb856a302 | ||
|
f5f336532f | ||
|
324883140a | ||
|
b1ab3eb373 | ||
|
6db3084062 | ||
|
673317d3cb | ||
|
9c1f48ee64 | ||
|
2940de38bc | ||
|
0c469b98fe | ||
|
4ba8751c68 | ||
|
a3beed16f5 | ||
|
038788bf57 | ||
|
76db1268bf | ||
|
084fea220e | ||
|
deab3d2517 | ||
|
7b171c56c9 | ||
|
12e8bbb6a2 | ||
|
c9afa75367 | ||
|
cad670fb31 | ||
|
efb1fbf729 | ||
|
a3b306a4e0 | ||
|
9d4e6ba0e4 | ||
|
be9ba27ba4 | ||
|
2b0970d341 | ||
|
4f837a0f8b | ||
|
5b10a91d8c | ||
|
31d7c9d099 | ||
|
70be7c6061 | ||
|
5eda75311d | ||
|
13b7cfc488 | ||
|
39dc184b88 | ||
|
e8ef9b79c8 | ||
|
9ed55c4e7b | ||
|
af23cf81d3 | ||
|
d1d34aab8f | ||
|
30f872cc87 | ||
|
108721dd3f | ||
|
f2ec5fa3f8 | ||
|
4c4770bfd3 | ||
|
8aa2c2283a | ||
|
088eddbb7b | ||
|
693fadd9bc | ||
|
37e5e9f00b | ||
|
12b70db212 | ||
|
842b342dc5 | ||
|
544689c945 | ||
|
44a70d391b | ||
|
bdb0c99e17 | ||
|
8320cf6057 | ||
|
bed622243b | ||
|
985b3aedb5 | ||
|
50ab4ebed7 | ||
|
7566292157 |
51
CHANGELOG.md
|
@ -1,5 +1,56 @@
|
|||
# Geode Changelog
|
||||
|
||||
## v4.0.0-beta.1
|
||||
* Button to manually install mods from files (e881dc5)
|
||||
* Add `ModRequestedAction::Update` (e881dc5)
|
||||
* Add `ModMetadata::checkGeodeVersion` and `ModMetadata::checkTargetVersions` (e881dc5)
|
||||
* Add `geode::createModLogo` for creating a logo from a `.geode` package (e881dc5)
|
||||
* Tags now use names provided by the server (893b03e)
|
||||
* Add web support for multiple request headers with same name (#1150)
|
||||
* Fix `Task::chain` using the wrong type in the impl (22a11b9)
|
||||
* Fix installing mods not checking the current version (#1148)
|
||||
* Fix searching for mods ignoring geode and gd version (#1153)
|
||||
* Fix crash when checking tags (01807fe)
|
||||
* Fix 'Outdated' label being visible while updating (6679a69)
|
||||
* Fix log nesting issue (0e8d4c6)
|
||||
* Remove forward compat message box as it confuses users (5592ef6)
|
||||
* Fix crash on opening mod changelogs (9834cb2)
|
||||
* Make `ColorPickPopup` pimpl (1a201e1)
|
||||
* Fix lag issue in `ColorPickPopup` (3081164)
|
||||
* Change return type of `ModSettingsManager::save` (da92090)
|
||||
* Fix every misspelling of successfully (#1151)
|
||||
* Allow building geode itself in debug mode (5645399)
|
||||
|
||||
## v4.0.0-alpha.1
|
||||
* Support for the 2.2074 update
|
||||
* Developers, see [this page for a migration guide](https://docs.geode-sdk.org/tutorials/migrate-v4)
|
||||
* Major API breaks:
|
||||
* Remove everything previously marked deprecated
|
||||
* `utils::MiniFunction` removed
|
||||
* Rewritten `geode::Result` class
|
||||
* Rewritten matjson library
|
||||
* Settings V2 completely removed, use V3 now
|
||||
* `JsonChecker` removed
|
||||
* Add new system for ordered hook priority, [see docs](https://docs.geode-sdk.org/tutorials/hookpriority) (673317d, 6db3084)
|
||||
* C++20 coroutine support for `geode::Task`, [see docs](https://docs.geode-sdk.org/tutorials/tasks#coroutines) (e61b2c0, ab196b9)
|
||||
* Add `Task::chain`, [see docs](https://docs.geode-sdk.org/tutorials/tasks#chaining-tasks) (3248831)
|
||||
* Single page local mods list (efb1fbf)
|
||||
* Split mod problems into load and outdated (12e8bbb, 09fa872, df2528c)
|
||||
* This means mods made for outdated gd or geode versions no longer count as actual errors, resulting in less clutter in the ui
|
||||
* Fix safe mode popup on windows showing up when not supposed to (038788b)
|
||||
* WebRequest::ignoreContentLength (#1126)
|
||||
* Lots of smaller fixes to the geode ui (c9afa75, f5f3365, 2d66279, 02845d9, 9b95301, 6d13f78, 123b3ab, 0b2fc66, f96ea5e, cad670f)
|
||||
* Fix CCArrayExt::pop_back() return type (#1130)
|
||||
* Add missing spanish translations to installer (#1145)
|
||||
* Add hashtag symbol to CommonFilter::Any (#1131)
|
||||
* Disable forward compat on android (c9e97af)
|
||||
|
||||
## v3.9.2
|
||||
* Fix searching for mods returning unavailable mods (#1149)
|
||||
|
||||
## v3.9.1
|
||||
* Fix mod downloads not checking version (f575187)
|
||||
|
||||
## v3.9.0
|
||||
* Many changes to the settings ui (#1108)
|
||||
* Fuzzy search is now more reasonable
|
||||
|
|
|
@ -189,8 +189,8 @@ include(cmake/Platform.cmake)
|
|||
include(cmake/GeodeFile.cmake)
|
||||
|
||||
if (NOT DEFINED GEODE_GD_VERSION)
|
||||
set(GEODE_GD_VERSION 2.206)
|
||||
set(GEODE_COMP_GD_VERSION 22060)
|
||||
set(GEODE_GD_VERSION 2.2074)
|
||||
set(GEODE_COMP_GD_VERSION 22074)
|
||||
endif()
|
||||
|
||||
target_compile_definitions(
|
||||
|
@ -236,8 +236,9 @@ if (ANDROID)
|
|||
endif()
|
||||
|
||||
set(MAT_JSON_AS_INTERFACE ON)
|
||||
CPMAddPackage("gh:geode-sdk/json#cda9807")
|
||||
CPMAddPackage("gh:fmtlib/fmt#10.2.1")
|
||||
CPMAddPackage("gh:geode-sdk/result@1.2.1")
|
||||
CPMAddPackage("gh:geode-sdk/json@3.1.0")
|
||||
CPMAddPackage("gh:fmtlib/fmt#11.0.2")
|
||||
|
||||
target_compile_definitions(${PROJECT_NAME} INTERFACE MAT_JSON_DYNAMIC=1)
|
||||
|
||||
|
@ -266,7 +267,7 @@ if (DEFINED GEODE_TULIPHOOK_REPO_PATH)
|
|||
message(STATUS "Using ${GEODE_TULIPHOOK_REPO_PATH} for TulipHook")
|
||||
add_subdirectory(${GEODE_TULIPHOOK_REPO_PATH} ${GEODE_TULIPHOOK_REPO_PATH}/build)
|
||||
else()
|
||||
CPMAddPackage("gh:geode-sdk/TulipHook#d1d9559")
|
||||
CPMAddPackage("gh:geode-sdk/TulipHook@2.4.1")
|
||||
endif()
|
||||
set(CMAKE_WARN_DEPRECATED ON CACHE BOOL "" FORCE)
|
||||
|
||||
|
@ -296,7 +297,7 @@ target_include_directories(GeodeBindings PUBLIC
|
|||
${GEODE_LOADER_PATH}/include/Geode/fmod
|
||||
)
|
||||
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)
|
||||
|
||||
if (NOT EXISTS ${GEODE_BIN_PATH})
|
||||
|
|
2
VERSION
|
@ -1 +1 @@
|
|||
3.9.0
|
||||
4.0.0-beta.1
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
#
|
||||
# SPDX-FileCopyrightText: Copyright (c) 2019-2023 Lars Melchior and contributors
|
||||
|
||||
set(CPM_DOWNLOAD_VERSION 0.39.0)
|
||||
set(CPM_HASH_SUM "66639bcac9dd2907b2918de466783554c1334446b9874e90d38e3778d404c2ef")
|
||||
set(CPM_DOWNLOAD_VERSION 0.40.2)
|
||||
set(CPM_HASH_SUM "c8cdc32c03816538ce22781ed72964dc864b2a34a310d3b7104812a5ca2d835d")
|
||||
|
||||
if(CPM_SOURCE_CACHE)
|
||||
set(CPM_DOWNLOAD_LOCATION "${CPM_SOURCE_CACHE}/cpm/CPM_${CPM_DOWNLOAD_VERSION}.cmake")
|
||||
|
|
|
@ -72,19 +72,27 @@ elseif (GEODE_TARGET_PLATFORM STREQUAL "Win64")
|
|||
target_link_libraries(${PROJECT_NAME} INTERFACE
|
||||
${GEODE_LOADER_PATH}/include/link/win64/libcocos2d.lib
|
||||
${GEODE_LOADER_PATH}/include/link/win64/libExtensions.lib
|
||||
${GEODE_LOADER_PATH}/include/link/win64/ssl.lib
|
||||
${GEODE_LOADER_PATH}/include/link/win64/crypto.lib
|
||||
${GEODE_LOADER_PATH}/include/link/win64/nghttp2.lib
|
||||
${GEODE_LOADER_PATH}/include/link/win64/ngtcp2.lib
|
||||
${GEODE_LOADER_PATH}/include/link/win64/nghttp3.lib
|
||||
${GEODE_LOADER_PATH}/include/link/win64/ngtcp2_crypto_boringssl.lib
|
||||
${GEODE_LOADER_PATH}/include/link/win64/libcurl.lib
|
||||
${GEODE_LOADER_PATH}/include/link/win64/glew32.lib
|
||||
${GEODE_LOADER_PATH}/include/link/win64/gdstring.lib
|
||||
${GEODE_LOADER_PATH}/include/link/win64/fmod.lib
|
||||
opengl32
|
||||
)
|
||||
|
||||
if (PROJECT_IS_TOP_LEVEL AND CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
target_link_libraries(${PROJECT_NAME} INTERFACE
|
||||
${GEODE_LOADER_PATH}/include/link/win64/gd-libcurl.lib
|
||||
)
|
||||
else()
|
||||
target_link_libraries(${PROJECT_NAME} INTERFACE
|
||||
${GEODE_LOADER_PATH}/include/link/win64/ssl.lib
|
||||
${GEODE_LOADER_PATH}/include/link/win64/crypto.lib
|
||||
${GEODE_LOADER_PATH}/include/link/win64/nghttp2.lib
|
||||
${GEODE_LOADER_PATH}/include/link/win64/ngtcp2.lib
|
||||
${GEODE_LOADER_PATH}/include/link/win64/nghttp3.lib
|
||||
${GEODE_LOADER_PATH}/include/link/win64/ngtcp2_crypto_boringssl.lib
|
||||
${GEODE_LOADER_PATH}/include/link/win64/libcurl.lib
|
||||
)
|
||||
endif()
|
||||
|
||||
# Windows links against .lib and not .dll
|
||||
set(GEODE_OUTPUT_NAME "Geode")
|
||||
set(GEODE_PLATFORM_BINARY "Geode.lib")
|
||||
|
|
16
installer/windows/Language Files/PortugueseExtra.nsh
Normal file
|
@ -0,0 +1,16 @@
|
|||
!insertmacro LANGFILE_EXT Portuguese
|
||||
|
||||
!pragma warning disable 6030
|
||||
${LangFileString} MUI_TEXT_WELCOME_INFO_TEXT "O instalador irá guiá-lo através da instalação de $(^NameDA).$\r$\n$\r$\nAntes de iniciar a instalação, certifique-se de que o Geometry Dash não está aberto.$\r$\n$\r$\n$_CLICK"
|
||||
${LangFileString} MUI_UNTEXT_WELCOME_INFO_TEXT "O instalador irá guiá-lo através da desinstalação de $(^NameDA).$\r$\n$\r$\nAntes de iniciar a desinstalação, certifique-se de que o Geometry Dash não está aberto.$\r$\n$\r$\n$_CLICK"
|
||||
!pragma warning default 6030
|
||||
|
||||
; installer
|
||||
|
||||
${LangFileString} GEODE_TEXT_GD_MISSING "$\r$\n$\r$\nEsse caminho não tem o Geometry Dash instalado!"
|
||||
${LangFileString} GEODE_TEXT_GD_OLD "$\r$\n$\r$\nA sua versão do Geometry Dash é demasiado antiga para esta versão do Geode!"
|
||||
${LangFileString} GEODE_TEXT_MOD_LOADER_ALREADY_INSTALLED "Esse caminho já tem outros mods instalados!$\r$\nEles serão substituídos pelo Geode. (the dll trademark)"
|
||||
|
||||
; uninstaller
|
||||
|
||||
${LangFileString} GEODE_UNTEXT_GEODE_MISSING "Esse caminho não tem o Geode instalado!"
|
|
@ -1,15 +1,15 @@
|
|||
!insertmacro LANGFILE_EXT SpanishInternational
|
||||
|
||||
!pragma warning disable 6030
|
||||
${LangFileString} MUI_TEXT_WELCOME_INFO_TEXT "Este asistente le guiará durante la instalación de $(^NameDA) en su computadora.$\r$\n$\r$\nAntes de iniciar la instalación, asegúrese de que Geometry Dash no se está ejecutando$\r$\n$\r$\n$_CLICK"
|
||||
${LangFileString} MUI_UNTEXT_WELCOME_INFO_TEXT "Este asistente le guiará durante la desinstalación de $(^NameDA).$\r$\n$\r$\nAntes de iniciar la desinstalación, asegúrese de que Geometry Dash no se está ejecutando.$\r$\n$\r$\n$_CLICK"
|
||||
${LangFileString} MUI_TEXT_WELCOME_INFO_TEXT "Este asistente le guiará durante la instalación de Geode en su computadora.$\r$\n$\r$\nAntes de iniciar la instalación, asegúrese de que Geometry Dash no se está ejecutando$\r$\n$\r$\n$_CLICK"
|
||||
${LangFileString} MUI_UNTEXT_WELCOME_INFO_TEXT "Este asistente le guiará durante la desinstalación de Geode.$\r$\n$\r$\nAntes de iniciar la desinstalación, asegúrese de que Geometry Dash no se está ejecutando.$\r$\n$\r$\n$_CLICK"
|
||||
!pragma warning default 6030
|
||||
|
||||
; installer
|
||||
|
||||
${LangFileString} GEODE_TEXT_GD_MISSING "$\r$\n$\r$\n¡Geometry Dash no está instalado en esta ruta!"
|
||||
${LangFileString} GEODE_TEXT_GD_OLD "$\r$\n$\r$\nYour version of Geometry Dash is too old for this version of Geode!"
|
||||
${LangFileString} GEODE_TEXT_MOD_LOADER_ALREADY_INSTALLED "This path has other mods installed!$\r$\nThey will be overwritten by Geode. (the dll trademark)"
|
||||
${LangFileString} GEODE_TEXT_GD_OLD "$\r$\n$\r$\n¡Su versión de Geometry Dash es demasiado antigua para esta versión de Geode!"
|
||||
${LangFileString} GEODE_TEXT_MOD_LOADER_ALREADY_INSTALLED "¡Esta ruta ya tiene otros mods instalados!$\r$\nVan a ser sobreescritos por Geode. (the dll trademark)"
|
||||
|
||||
; uninstaller
|
||||
|
||||
|
|
|
@ -58,6 +58,7 @@
|
|||
!insertmacro GEODE_LANGUAGE "Polish"
|
||||
!insertmacro GEODE_LANGUAGE "Russian"
|
||||
!insertmacro GEODE_LANGUAGE "PortugueseBR"
|
||||
!insertmacro GEODE_LANGUAGE "Portuguese"
|
||||
!insertmacro GEODE_LANGUAGE "Ukrainian"
|
||||
!insertmacro GEODE_LANGUAGE "Czech"
|
||||
!insertmacro GEODE_LANGUAGE "Turkish"
|
||||
|
|
|
@ -5,21 +5,10 @@
|
|||
#include <Geode/platform/platform.hpp>
|
||||
#include <variant>
|
||||
|
||||
#define GEODE_STATIC_PTR(type, name) \
|
||||
static type* s_##name; \
|
||||
inline type* name() { \
|
||||
if (!s_##name) s_##name = new type(); \
|
||||
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)
|
||||
#if !defined(GEODE_CONCAT)
|
||||
#define GEODE_WRAPPER_CONCAT(x, y) x##y
|
||||
#define GEODE_CONCAT(x, y) GEODE_WRAPPER_CONCAT(x, y)
|
||||
#endif
|
||||
|
||||
#define GEODE_WRAPPER_STR(...) #__VA_ARGS__
|
||||
#define GEODE_STR(...) GEODE_WRAPPER_STR(__VA_ARGS__)
|
||||
|
|
|
@ -6,7 +6,8 @@
|
|||
#include "ui/EnterLayerEvent.hpp"
|
||||
#include "ui/BasedButtonSprite.hpp"
|
||||
#include "ui/IconButtonSprite.hpp"
|
||||
#include "ui/InputNode.hpp"
|
||||
#include "ui/Layout.hpp"
|
||||
#include "ui/SpacerNode.hpp"
|
||||
#include "ui/General.hpp"
|
||||
#include "ui/ListView.hpp"
|
||||
#include "ui/MDPopup.hpp"
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include "DefaultInclude.hpp"
|
||||
#include "utils/Result.hpp"
|
||||
#include "utils/VersionInfo.hpp"
|
||||
#include "utils/ranges.hpp"
|
||||
#include "utils/casts.hpp"
|
||||
|
@ -12,5 +11,4 @@
|
|||
#include "utils/permission.hpp"
|
||||
#include "utils/general.hpp"
|
||||
#include "utils/timer.hpp"
|
||||
#include "utils/MiniFunction.hpp"
|
||||
#include "utils/ObjcHook.hpp"
|
||||
|
|
|
@ -93,7 +93,7 @@ namespace gd {
|
|||
bool empty() 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 {
|
||||
return *this == std::string_view(other);
|
||||
}
|
||||
|
@ -101,7 +101,7 @@ namespace gd {
|
|||
return *this == std::string_view(other);
|
||||
}
|
||||
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 {
|
||||
return *this <=> std::string_view(other);
|
||||
}
|
||||
|
|
63
loader/include/Geode/cocos/base_nodes/CCNode.h
vendored
|
@ -37,7 +37,6 @@
|
|||
#include "../kazmath/include/kazmath/kazmath.h"
|
||||
#include "../script_support/CCScriptSupport.h"
|
||||
#include "../include/CCProtocols.h"
|
||||
#include "Layout.hpp"
|
||||
#include "../../loader/Event.hpp"
|
||||
#include <Geode/utils/casts.hpp>
|
||||
|
||||
|
@ -45,6 +44,12 @@
|
|||
#include <matjson.hpp>
|
||||
#endif
|
||||
|
||||
namespace geode {
|
||||
class Layout;
|
||||
class LayoutOptions;
|
||||
enum class Anchor;
|
||||
}
|
||||
|
||||
NS_CC_BEGIN
|
||||
|
||||
class CCCamera;
|
||||
|
@ -871,31 +876,19 @@ public:
|
|||
private:
|
||||
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 void addEventListenerInternal(
|
||||
std::string const& id,
|
||||
geode::EventListenerProtocol* protocol
|
||||
);
|
||||
|
||||
#ifdef GEODE_EXPORTING
|
||||
[[deprecated("Will be removed, it's an ABI break")]]
|
||||
GEODE_DLL std::optional<matjson::Value> getAttributeInternal(std::string const& attribute);
|
||||
#endif
|
||||
|
||||
public:
|
||||
#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
|
||||
|
||||
public:
|
||||
/**
|
||||
* Get the string ID of this node
|
||||
* @returns The ID, or an empty string if the node has no ID.
|
||||
* @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
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @param id ID of the child
|
||||
* @returns The child, or nullptr if none was found
|
||||
* @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
|
||||
|
@ -920,7 +923,7 @@ public:
|
|||
* @returns The child, or nullptr if none was found
|
||||
* @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
|
||||
|
@ -936,14 +939,14 @@ public:
|
|||
* ->getChildByID("mod.id/epic-button")`
|
||||
* @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.
|
||||
* @param id The ID of the node
|
||||
* @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
|
||||
|
@ -990,13 +993,13 @@ public:
|
|||
* CCLayers / CCMenus, this will change where the children are located
|
||||
* @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
|
||||
* @returns The current layout, or nullptr if no layout is set
|
||||
* @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
|
||||
* set, nothing happens
|
||||
|
@ -1011,13 +1014,13 @@ public:
|
|||
* @param apply Whether to update the layout of the parent node
|
||||
* @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
|
||||
* @returns The current layout options, or nullptr if no options are set
|
||||
* @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
|
||||
* 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
|
||||
* @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
|
||||
* in its parent where the anchor specifies, and then the offset is used to
|
||||
|
@ -1044,7 +1047,7 @@ public:
|
|||
*/
|
||||
GEODE_DLL void addChildAtPosition(
|
||||
CCNode* child,
|
||||
Anchor anchor,
|
||||
geode::Anchor anchor,
|
||||
CCPoint const& offset,
|
||||
CCPoint const& nodeAnchor,
|
||||
bool useAnchorLayout = true
|
||||
|
@ -1057,7 +1060,7 @@ public:
|
|||
* @param offset Where to place the child relative to the anchor
|
||||
* @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
|
||||
* have a parent; if the child already has AnchorLayoutOptions set, those
|
||||
|
@ -1068,7 +1071,7 @@ public:
|
|||
* @note Geode addition
|
||||
*/
|
||||
GEODE_DLL void updateAnchoredPosition(
|
||||
Anchor anchor,
|
||||
geode::Anchor anchor,
|
||||
CCPoint const& offset,
|
||||
CCPoint const& nodeAnchor
|
||||
);
|
||||
|
@ -1102,7 +1105,7 @@ public:
|
|||
template <class Filter, class... Args>
|
||||
geode::EventListenerProtocol* addEventListener(
|
||||
std::string const& id,
|
||||
geode::utils::MiniFunction<typename Filter::Callback> callback,
|
||||
std::function<typename Filter::Callback> callback,
|
||||
Args&&... args
|
||||
) {
|
||||
auto listener = new geode::EventListener<Filter>(
|
||||
|
@ -1113,7 +1116,7 @@ public:
|
|||
}
|
||||
template <class Filter, class... Args>
|
||||
geode::EventListenerProtocol* addEventListener(
|
||||
geode::utils::MiniFunction<typename Filter::Callback> callback,
|
||||
std::function<typename Filter::Callback> callback,
|
||||
Args&&... args
|
||||
) {
|
||||
return this->addEventListener<Filter, Args...>(
|
||||
|
@ -1923,7 +1926,7 @@ namespace geode {
|
|||
std::string m_targetID;
|
||||
|
||||
public:
|
||||
ListenerResult handle(utils::MiniFunction<Callback> fn, UserObjectSetEvent* event);
|
||||
ListenerResult handle(std::function<Callback> fn, UserObjectSetEvent* event);
|
||||
|
||||
AttributeSetFilter(std::string const& id);
|
||||
};
|
||||
|
|
|
@ -28,6 +28,8 @@
|
|||
#include "../../include/cocos2d.h"
|
||||
#include "../ExtensionMacros.h"
|
||||
|
||||
enum class GJHttpType;
|
||||
|
||||
NS_CC_EXT_BEGIN
|
||||
|
||||
class CC_DLL CCHttpClient;
|
||||
|
@ -216,6 +218,50 @@ public:
|
|||
return _headers;
|
||||
}
|
||||
|
||||
inline int getType() {
|
||||
return _type;
|
||||
}
|
||||
|
||||
inline void setType(int type) {
|
||||
_type = type;
|
||||
}
|
||||
|
||||
// @note Geode addition
|
||||
inline void setType(GJHttpType type) {
|
||||
_type = static_cast<int>(type);
|
||||
}
|
||||
|
||||
inline bool getShouldCancel() {
|
||||
return _shouldCancel;
|
||||
}
|
||||
|
||||
inline void setShouldCancel(bool shouldCancel) {
|
||||
_shouldCancel = shouldCancel;
|
||||
}
|
||||
|
||||
inline int getDownloadProgress() {
|
||||
return _downloadProgress;
|
||||
}
|
||||
|
||||
inline void setDownloadProgress(int downloadProgress) {
|
||||
_downloadProgress = downloadProgress;
|
||||
}
|
||||
|
||||
inline int getReadTimeout() {
|
||||
return _readTimeout;
|
||||
}
|
||||
|
||||
inline void setReadTimeout(int readTimeout) {
|
||||
_readTimeout = readTimeout;
|
||||
}
|
||||
|
||||
inline int getConnectTimeout() {
|
||||
return _connectTimeout;
|
||||
}
|
||||
|
||||
inline void setConnectTimeout(int connectTimeout) {
|
||||
_connectTimeout = connectTimeout;
|
||||
}
|
||||
|
||||
protected:
|
||||
// properties
|
||||
|
@ -229,13 +275,15 @@ protected:
|
|||
gd::vector<gd::string> _headers; /// custom http headers
|
||||
|
||||
// @note RobTop Addition
|
||||
int _requestTypeGJ;
|
||||
int _type;
|
||||
// @note RobTop Addition
|
||||
bool _shouldCancel;
|
||||
// @note RobTop Addition
|
||||
int _downloadProgress;
|
||||
// @note RobTop Addition
|
||||
int _readTimeout;
|
||||
// @note RobTop Addition
|
||||
int _connectTimeout;
|
||||
};
|
||||
|
||||
NS_CC_EXT_END
|
||||
|
|
1
loader/include/Geode/cocos/include/cocos2d.h
vendored
|
@ -59,7 +59,6 @@ THE SOFTWARE.
|
|||
// base_nodes
|
||||
#include "../base_nodes/CCNode.h"
|
||||
#include "../base_nodes/CCAtlasNode.h"
|
||||
#include "../base_nodes/SpacerNode.hpp"
|
||||
|
||||
// cocoa
|
||||
#include "../cocoa/CCAffineTransform.h"
|
||||
|
|
|
@ -142,7 +142,7 @@ public:
|
|||
/**
|
||||
* @note RobTop addition
|
||||
*/
|
||||
void toggleFullScreen(bool fullscreen, bool borderless);
|
||||
void toggleFullScreen(bool fullscreen, bool borderless, bool fix);
|
||||
|
||||
/**
|
||||
* @note RobTop addition
|
||||
|
@ -185,6 +185,8 @@ public:
|
|||
|
||||
protected:
|
||||
static CCEGLView* s_pEglView;
|
||||
// @note unknown members here
|
||||
uint8_t m_unkPad[8];
|
||||
bool m_bCaptured;
|
||||
// Robtop Removal
|
||||
// HWND m_hWnd;
|
||||
|
@ -203,6 +205,8 @@ protected:
|
|||
int m_nRetinaFactor;
|
||||
// @note RobTop Addition
|
||||
bool m_bCursorHidden;
|
||||
// @note may be before m_bCursorHidden
|
||||
int m_unkSize4;
|
||||
// Robtop Removal
|
||||
// LPCWSTR m_menu;
|
||||
// Robtop Removal
|
||||
|
@ -225,6 +229,8 @@ public:
|
|||
// @note RobTop Addition
|
||||
bool m_bIsBorderless;
|
||||
// @note RobTop Addition
|
||||
bool m_bIsFix;
|
||||
// @note RobTop Addition
|
||||
bool m_bShouldHideCursor;
|
||||
// @note RobTop Addition
|
||||
bool m_bCursorLocked;
|
||||
|
|
|
@ -208,9 +208,9 @@ namespace cocos2d
|
|||
|
||||
/**
|
||||
* Custom function added for geode; returns if the
|
||||
* zip file was succesfully decoded.
|
||||
* zip file was successfully decoded.
|
||||
*
|
||||
* @return true if the zip was succesfully loaded,
|
||||
* @return true if the zip was successfully loaded,
|
||||
* false otherwise.
|
||||
*
|
||||
* @since geode v1.0.0
|
||||
|
|
5994
loader/include/Geode/external/result/result.hpp
vendored
|
@ -53,7 +53,7 @@ namespace geode {
|
|||
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) {
|
||||
return std::apply(fn, event->getArgs());
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include "../utils/casts.hpp"
|
||||
#include "../utils/MiniFunction.hpp"
|
||||
|
||||
#include <Geode/DefaultInclude.hpp>
|
||||
#include <type_traits>
|
||||
|
@ -52,6 +51,7 @@ namespace geode {
|
|||
|
||||
private:
|
||||
static DefaultEventListenerPool* create();
|
||||
DefaultEventListenerPool();
|
||||
|
||||
public:
|
||||
bool add(EventListenerProtocol* listener) override;
|
||||
|
@ -64,10 +64,7 @@ namespace geode {
|
|||
friend class DispatchEvent;
|
||||
|
||||
template <class... Args>
|
||||
friend class DispatchFilter;
|
||||
|
||||
// todo: make this private in Geode 4.0.0
|
||||
DefaultEventListenerPool();
|
||||
friend class DispatchFilter;
|
||||
};
|
||||
|
||||
class GEODE_DLL EventListenerProtocol {
|
||||
|
@ -103,7 +100,7 @@ namespace geode {
|
|||
using Callback = ListenerResult(T*);
|
||||
using Event = T;
|
||||
|
||||
ListenerResult handle(utils::MiniFunction<Callback> fn, T* e) {
|
||||
ListenerResult handle(std::function<Callback> fn, T* e) {
|
||||
return fn(e);
|
||||
}
|
||||
|
||||
|
@ -156,7 +153,7 @@ namespace geode {
|
|||
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_filter.setListener(this);
|
||||
|
@ -193,10 +190,7 @@ namespace geode {
|
|||
this->enable();
|
||||
}
|
||||
|
||||
void bind(utils::MiniFunction<Callback> const& fn) {
|
||||
m_callback = fn;
|
||||
}
|
||||
void bind(utils::MiniFunction<Callback>&& fn) {
|
||||
void bind(std::function<Callback> fn) {
|
||||
m_callback = fn;
|
||||
}
|
||||
|
||||
|
@ -218,12 +212,12 @@ namespace geode {
|
|||
return m_filter;
|
||||
}
|
||||
|
||||
utils::MiniFunction<Callback>& getCallback() {
|
||||
std::function<Callback>& getCallback() {
|
||||
return m_callback;
|
||||
}
|
||||
|
||||
protected:
|
||||
utils::MiniFunction<Callback> m_callback = nullptr;
|
||||
std::function<Callback> m_callback = nullptr;
|
||||
T m_filter;
|
||||
};
|
||||
|
||||
|
|
|
@ -57,7 +57,7 @@ namespace geode::ipc {
|
|||
std::string m_messageID;
|
||||
|
||||
public:
|
||||
ListenerResult handle(utils::MiniFunction<Callback> fn, IPCEvent* event);
|
||||
ListenerResult handle(std::function<Callback> fn, IPCEvent* event);
|
||||
IPCFilter(
|
||||
std::string const& modID,
|
||||
std::string const& messageID
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <filesystem>
|
||||
#include "../utils/Result.hpp"
|
||||
#include "../utils/MiniFunction.hpp"
|
||||
#include <Geode/Result.hpp>
|
||||
#include "Log.hpp"
|
||||
#include "ModEvent.hpp"
|
||||
#include "ModMetadata.hpp"
|
||||
|
@ -15,7 +14,7 @@
|
|||
#include <string_view>
|
||||
|
||||
namespace geode {
|
||||
using ScheduledFunction = utils::MiniFunction<void()>;
|
||||
using ScheduledFunction = std::function<void()>;
|
||||
|
||||
struct InvalidGeodeFile {
|
||||
std::filesystem::path path;
|
||||
|
@ -47,6 +46,21 @@ namespace geode {
|
|||
Type type;
|
||||
std::variant<std::filesystem::path, ModMetadata, Mod*> cause;
|
||||
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;
|
||||
|
@ -92,7 +106,8 @@ namespace geode {
|
|||
Mod* getLoadedMod(std::string const& id) const;
|
||||
std::vector<Mod*> getAllMods();
|
||||
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;
|
||||
|
||||
/**
|
||||
|
@ -103,41 +118,36 @@ namespace geode {
|
|||
* Returns whether the specified launch argument was passed in via the command line.
|
||||
* @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
|
||||
* with the format `--geode:arg-name=value`.
|
||||
* @param name The argument name
|
||||
* @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
|
||||
* value is exactly `true`.
|
||||
* @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.
|
||||
* @param name The argument name
|
||||
*/
|
||||
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);
|
||||
if (!str.has_value()) {
|
||||
return std::nullopt;
|
||||
return Err(fmt::format("Launch argument '{}' not found", name));
|
||||
}
|
||||
std::string parseError;
|
||||
auto jsonOpt = matjson::parse(str.value(), parseError);
|
||||
if (!jsonOpt.has_value()) {
|
||||
log::debug("Parsing launch argument '{}' failed: {}", name, parseError);
|
||||
return std::nullopt;
|
||||
auto jsonOpt = matjson::Value::parse(str.value());
|
||||
if (jsonOpt.isErr()) {
|
||||
return Err(fmt::format("Parsing launch argument '{}' failed: {}", name, jsonOpt.unwrapErr()));
|
||||
}
|
||||
auto value = jsonOpt.value();
|
||||
if (!value.is<T>()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return value.as<T>();
|
||||
auto value = jsonOpt.unwrap();
|
||||
return value.template as<T>();
|
||||
}
|
||||
|
||||
void queueInMainThread(ScheduledFunction&& func);
|
||||
|
|
|
@ -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 {
|
||||
|
||||
class Mod;
|
||||
|
@ -120,6 +114,37 @@ namespace geode {
|
|||
popNest(getMod());
|
||||
}
|
||||
|
||||
struct NestScope {
|
||||
private:
|
||||
bool m_active = true;
|
||||
public:
|
||||
NestScope() {
|
||||
pushNest();
|
||||
}
|
||||
|
||||
NestScope(NestScope const&) {
|
||||
pushNest();
|
||||
}
|
||||
|
||||
NestScope(NestScope&& other) {
|
||||
other.m_active = false;
|
||||
}
|
||||
|
||||
NestScope& operator=(NestScope const&) {
|
||||
pushNest();
|
||||
return *this;
|
||||
}
|
||||
|
||||
NestScope& operator=(NestScope&& other) {
|
||||
other.m_active = false;
|
||||
return *this;
|
||||
}
|
||||
|
||||
~NestScope() {
|
||||
if (m_active) popNest();
|
||||
}
|
||||
};
|
||||
|
||||
class Nest final {
|
||||
private:
|
||||
class Impl;
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
#include "../DefaultInclude.hpp"
|
||||
#include "../cocos/support/zip_support/ZipUtils.h"
|
||||
#include "../utils/Result.hpp"
|
||||
#include <Geode/Result.hpp>
|
||||
#include "../utils/VersionInfo.hpp"
|
||||
#include "../utils/general.hpp"
|
||||
|
||||
|
@ -10,7 +10,6 @@
|
|||
#include "Hook.hpp"
|
||||
#include "ModMetadata.hpp"
|
||||
#include "Setting.hpp"
|
||||
#include "SettingV3.hpp"
|
||||
#include "Types.hpp"
|
||||
#include "Loader.hpp"
|
||||
|
||||
|
@ -23,9 +22,7 @@
|
|||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace geode {
|
||||
class SettingV3;
|
||||
|
||||
namespace geode {
|
||||
template <class T>
|
||||
struct HandleToSaved : public T {
|
||||
Mod* m_mod;
|
||||
|
@ -44,7 +41,8 @@ namespace geode {
|
|||
Enable,
|
||||
Disable,
|
||||
Uninstall,
|
||||
UninstallWithSaveData
|
||||
UninstallWithSaveData,
|
||||
Update
|
||||
};
|
||||
|
||||
static constexpr bool modRequestedActionIsToggle(ModRequestedAction action) {
|
||||
|
@ -54,22 +52,6 @@ namespace geode {
|
|||
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();
|
||||
|
||||
class ModImpl;
|
||||
|
@ -108,8 +90,6 @@ namespace geode {
|
|||
|
||||
std::string getID() const;
|
||||
std::string getName() const;
|
||||
[[deprecated("Use Mod::getDevelopers instead")]]
|
||||
std::string getDeveloper() const;
|
||||
std::vector<std::string> getDevelopers() const;
|
||||
std::optional<std::string> getDescription() const;
|
||||
std::optional<std::string> getDetails() const;
|
||||
|
@ -137,17 +117,6 @@ namespace geode {
|
|||
std::vector<Mod*> getDependants() const;
|
||||
#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>>;
|
||||
/**
|
||||
* Check if this Mod has updates available on the mods index. If
|
||||
|
@ -187,52 +156,15 @@ namespace geode {
|
|||
* declared in `mod.json`)
|
||||
*/
|
||||
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,
|
||||
* or if it's a custom setting that has not yet been registered using
|
||||
* `Mod::registerCustomSettingType`
|
||||
* @param key The key of the setting as defined in `mod.json`
|
||||
*/
|
||||
std::shared_ptr<SettingV3> getSettingV3(std::string_view const 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));
|
||||
}
|
||||
std::shared_ptr<Setting> getSetting(std::string_view key) const;
|
||||
|
||||
/**
|
||||
* Register a custom setting type. See
|
||||
|
@ -248,7 +180,7 @@ namespace geode {
|
|||
* Returns a prefixed launch argument name. See `Mod::getLaunchArgument`
|
||||
* 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.
|
||||
*/
|
||||
|
@ -258,7 +190,7 @@ namespace geode {
|
|||
* for details about mod-specific launch arguments.
|
||||
* @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`
|
||||
* with the argument name prefixed by the mod ID. For example, a launch argument named
|
||||
|
@ -266,20 +198,20 @@ namespace geode {
|
|||
* @param name The argument name
|
||||
* @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`
|
||||
* for details about mod-specific launch arguments.
|
||||
* @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`
|
||||
* for details about mod-specific launch arguments.
|
||||
* @param name The argument name
|
||||
*/
|
||||
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));
|
||||
}
|
||||
|
||||
|
@ -293,18 +225,18 @@ namespace geode {
|
|||
* setting type has a `getValue` function which returns the value
|
||||
*/
|
||||
template <class T>
|
||||
T getSettingValue(std::string_view const key) const {
|
||||
T getSettingValue(std::string_view key) const {
|
||||
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 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;
|
||||
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();
|
||||
sett->setValue(value);
|
||||
return old;
|
||||
|
@ -312,30 +244,28 @@ namespace geode {
|
|||
return T();
|
||||
}
|
||||
|
||||
bool hasSavedValue(std::string_view const key);
|
||||
bool hasSavedValue(std::string_view key);
|
||||
|
||||
template <class T>
|
||||
T getSavedValue(std::string_view const key) {
|
||||
static_assert(geode::typeImplementsIsJSON<T>(), "T must implement is_json in matjson::Serialize<T>, otherwise this always returns default value.");
|
||||
T getSavedValue(std::string_view key) {
|
||||
auto& saved = this->getSaveContainer();
|
||||
if (saved.contains(key)) {
|
||||
if (auto value = saved.try_get<T>(key)) {
|
||||
return *value;
|
||||
}
|
||||
if (auto res = saved.get(key).andThen([](auto&& v) {
|
||||
return v.template as<T>();
|
||||
}); res.isOk()) {
|
||||
return res.unwrap();
|
||||
}
|
||||
return T();
|
||||
}
|
||||
|
||||
template <class T>
|
||||
T getSavedValue(std::string_view const key, T const& defaultValue) {
|
||||
static_assert(geode::typeImplementsIsJSON<T>(), "T must implement is_json in matjson::Serialize<T>, otherwise this always returns default value.");
|
||||
T getSavedValue(std::string_view key, T const& defaultValue) {
|
||||
auto& saved = this->getSaveContainer();
|
||||
if (saved.contains(key)) {
|
||||
if (auto value = saved.try_get<T>(key)) {
|
||||
return *value;
|
||||
}
|
||||
if (auto res = saved.get(key).andThen([](auto&& v) {
|
||||
return v.template as<T>();
|
||||
}); res.isOk()) {
|
||||
return res.unwrap();
|
||||
}
|
||||
saved[key] = defaultValue;
|
||||
saved[key] = matjson::Value(defaultValue);
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
|
@ -347,7 +277,7 @@ namespace geode {
|
|||
* @returns The old value
|
||||
*/
|
||||
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 old = this->getSavedValue<T>(key);
|
||||
saved[key] = value;
|
||||
|
@ -488,7 +418,7 @@ namespace geode {
|
|||
* Check whether or not this 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
|
||||
|
@ -517,7 +447,17 @@ namespace geode {
|
|||
bool isLoggingEnabled() const;
|
||||
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> getProblems() const;
|
||||
std::vector<LoadProblem> getRecommendations() const;
|
||||
|
|
|
@ -10,9 +10,6 @@ namespace geode {
|
|||
|
||||
enum class ModEventType {
|
||||
Loaded,
|
||||
Unloaded,
|
||||
Enabled,
|
||||
Disabled,
|
||||
DataLoaded,
|
||||
DataSaved,
|
||||
};
|
||||
|
@ -43,7 +40,7 @@ namespace geode {
|
|||
Mod* m_mod;
|
||||
|
||||
public:
|
||||
ListenerResult handle(utils::MiniFunction<Callback> fn, ModStateEvent* event);
|
||||
ListenerResult handle(std::function<Callback> fn, ModStateEvent* event);
|
||||
|
||||
/**
|
||||
* Create a mod state listener
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include "../utils/Result.hpp"
|
||||
#include <Geode/Result.hpp>
|
||||
#include "../utils/VersionInfo.hpp"
|
||||
#include "Setting.hpp"
|
||||
#include "Types.hpp"
|
||||
|
||||
#include <matjson.hpp>
|
||||
|
@ -123,13 +122,6 @@ namespace geode {
|
|||
* character set.
|
||||
*/
|
||||
[[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
|
||||
*/
|
||||
|
@ -155,11 +147,6 @@ namespace geode {
|
|||
* (see MDTextArea for more info)
|
||||
*/
|
||||
[[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
|
||||
*/
|
||||
|
@ -184,12 +171,7 @@ namespace geode {
|
|||
* Mod settings
|
||||
* @note Not a map because insertion order must be preserved
|
||||
*/
|
||||
[[nodiscard, deprecated("Use getSettingsV3")]] std::vector<std::pair<std::string, Setting>> 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;
|
||||
[[nodiscard]] std::vector<std::pair<std::string, matjson::Value>> getSettings() const;
|
||||
/**
|
||||
* Get the tags for this mod
|
||||
*/
|
||||
|
@ -216,9 +198,19 @@ namespace geode {
|
|||
|
||||
/**
|
||||
* Checks if mod can be installed on the current GD version.
|
||||
* Returns Ok() if it can, Err otherwise.
|
||||
* Returns Ok() if it can, Err explaining why not otherwise.
|
||||
*/
|
||||
Result<> checkGameVersion() const;
|
||||
/**
|
||||
* Checks if mod can be installed on the current Geode version.
|
||||
* Returns Ok() if it can, Err explaining why not otherwise.
|
||||
*/
|
||||
Result<> checkGeodeVersion() const;
|
||||
/**
|
||||
* Checks if mod can be installed on the current GD & Geode version.
|
||||
* Returns Ok() if it can, Err explaining why not otherwise.
|
||||
*/
|
||||
Result<> checkTargetVersions() const;
|
||||
|
||||
#if defined(GEODE_EXPOSE_SECRET_INTERNALS_IN_HEADERS_DO_NOT_DEFINE_PLEASE)
|
||||
void setPath(std::filesystem::path const& value);
|
||||
|
@ -237,8 +229,6 @@ namespace geode {
|
|||
void setDependencies(std::vector<Dependency> const& value);
|
||||
void setIncompatibilities(std::vector<Incompatibility> 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 setTags(std::unordered_set<std::string> const& value);
|
||||
void setNeedsEarlyLoad(bool const& value);
|
||||
|
@ -310,7 +300,8 @@ namespace geode {
|
|||
|
||||
template <>
|
||||
struct matjson::Serialize<geode::ModMetadata> {
|
||||
static matjson::Value to_json(geode::ModMetadata const& info) {
|
||||
return info.toJSON();
|
||||
static Value toJson(geode::ModMetadata const& value)
|
||||
{
|
||||
return Value(value.toJSON());
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <Geode/DefaultInclude.hpp>
|
||||
#include "SettingV3.hpp"
|
||||
#include "Setting.hpp"
|
||||
|
||||
namespace geode {
|
||||
class Mod;
|
||||
|
@ -40,12 +40,8 @@ namespace geode {
|
|||
* The format of the savedata will be an object with the keys being
|
||||
* setting IDs and then the values the values of the saved settings
|
||||
* @note If saving a setting fails, it will log a warning to the console
|
||||
* @warning This will overwrite the whole `json` parameter - be sure to
|
||||
* pass the full settings savedata to `load()` so you can be sure that
|
||||
* unregistered custom settings' saved values don't disappear!
|
||||
* @todo in v4: make this return the value instead lol
|
||||
*/
|
||||
void save(matjson::Value& json);
|
||||
matjson::Value save();
|
||||
|
||||
/**
|
||||
* Get the savedata for settings, aka the JSON object that contains all
|
||||
|
@ -57,12 +53,8 @@ namespace geode {
|
|||
matjson::Value& getSaveData();
|
||||
|
||||
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<SettingValue> getLegacy(std::string_view key);
|
||||
std::optional<Setting> getLegacyDefinition(std::string_view key);
|
||||
std::shared_ptr<Setting> get(std::string_view key);
|
||||
|
||||
/**
|
||||
* Returns true if any setting with the `"restart-required"` attribute
|
||||
|
|
|
@ -1,324 +1,46 @@
|
|||
#pragma once
|
||||
|
||||
#include "Types.hpp"
|
||||
#include "../DefaultInclude.hpp"
|
||||
#include "../utils/Result.hpp"
|
||||
#include "../utils/file.hpp"
|
||||
#include <matjson.hpp>
|
||||
#include <optional>
|
||||
#include <unordered_set>
|
||||
#include <cocos2d.h>
|
||||
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4275)
|
||||
|
||||
namespace geode {
|
||||
class SettingNode;
|
||||
class SettingValue;
|
||||
|
||||
struct JsonMaybeObject;
|
||||
struct JsonMaybeValue;
|
||||
|
||||
/**
|
||||
* A Setting for a boolean value. Represented in-game as a simple toggle
|
||||
*/
|
||||
struct GEODE_DLL BoolSetting final {
|
||||
using ValueType = bool;
|
||||
|
||||
std::optional<std::string> name;
|
||||
std::optional<std::string> description;
|
||||
bool defaultValue;
|
||||
|
||||
static Result<BoolSetting> parse(JsonMaybeObject& obj);
|
||||
};
|
||||
|
||||
/**
|
||||
* A Setting for an integer value. The value can be limited using the min
|
||||
* and max options
|
||||
*/
|
||||
struct GEODE_DLL IntSetting final {
|
||||
using ValueType = int64_t;
|
||||
|
||||
std::optional<std::string> name;
|
||||
std::optional<std::string> description;
|
||||
ValueType defaultValue;
|
||||
std::optional<ValueType> min;
|
||||
std::optional<ValueType> max;
|
||||
struct {
|
||||
bool arrows = true;
|
||||
bool bigArrows = false;
|
||||
size_t arrowStep = 1;
|
||||
size_t bigArrowStep = 5;
|
||||
bool slider = true;
|
||||
std::optional<ValueType> sliderStep = std::nullopt;
|
||||
bool input = true;
|
||||
} controls;
|
||||
|
||||
static Result<IntSetting> parse(JsonMaybeObject& obj);
|
||||
};
|
||||
|
||||
/**
|
||||
* A Setting for a float value. The value can be limited using the min
|
||||
* and max options
|
||||
*/
|
||||
struct GEODE_DLL FloatSetting final {
|
||||
using ValueType = double;
|
||||
|
||||
std::optional<std::string> name;
|
||||
std::optional<std::string> description;
|
||||
ValueType defaultValue;
|
||||
std::optional<ValueType> min;
|
||||
std::optional<ValueType> max;
|
||||
struct {
|
||||
bool arrows = true;
|
||||
bool bigArrows = false;
|
||||
size_t arrowStep = 1;
|
||||
size_t bigArrowStep = 5;
|
||||
bool slider = true;
|
||||
std::optional<ValueType> sliderStep = std::nullopt;
|
||||
bool input = true;
|
||||
} controls;
|
||||
|
||||
static Result<FloatSetting> parse(JsonMaybeObject& obj);
|
||||
};
|
||||
|
||||
/**
|
||||
* A Setting for a string value
|
||||
*/
|
||||
struct GEODE_DLL StringSetting final {
|
||||
using ValueType = std::string;
|
||||
|
||||
std::optional<std::string> name;
|
||||
std::optional<std::string> description;
|
||||
ValueType defaultValue;
|
||||
struct Data {
|
||||
/**
|
||||
* A regex the string must successfully match against
|
||||
*/
|
||||
std::optional<std::string> match;
|
||||
|
||||
/**
|
||||
* The CCTextInputNode's allowed character filter
|
||||
*/
|
||||
std::optional<std::string> filter;
|
||||
|
||||
/**
|
||||
* A list of options the user can choose from
|
||||
*/
|
||||
std::optional<std::vector<std::string>> options;
|
||||
};
|
||||
std::unique_ptr<Data> controls;
|
||||
|
||||
std::array<uint8_t, sizeof(Data::match) + sizeof(Data::filter) - sizeof(controls)> m_padding;
|
||||
|
||||
StringSetting();
|
||||
StringSetting(StringSetting const& other);
|
||||
StringSetting(StringSetting&& other) noexcept;
|
||||
StringSetting& operator=(StringSetting const& other);
|
||||
StringSetting& operator=(StringSetting&& other) noexcept;
|
||||
~StringSetting();
|
||||
|
||||
|
||||
static Result<StringSetting> parse(JsonMaybeObject& obj);
|
||||
};
|
||||
|
||||
/**
|
||||
* A Setting for a file input. Lets the user select a file from their
|
||||
* local device
|
||||
*/
|
||||
struct GEODE_DLL FileSetting final {
|
||||
using ValueType = std::filesystem::path;
|
||||
using Filter = utils::file::FilePickOptions::Filter;
|
||||
|
||||
std::optional<std::string> name;
|
||||
std::optional<std::string> description;
|
||||
ValueType defaultValue;
|
||||
struct {
|
||||
std::vector<Filter> filters;
|
||||
} controls;
|
||||
|
||||
static Result<FileSetting> parse(JsonMaybeObject& obj);
|
||||
};
|
||||
|
||||
/**
|
||||
* A Setting for an RGB color. See ColorAlphaSetting for a setting that
|
||||
* also allows customizing alpha
|
||||
*/
|
||||
struct GEODE_DLL ColorSetting final {
|
||||
using ValueType = cocos2d::ccColor3B;
|
||||
|
||||
std::optional<std::string> name;
|
||||
std::optional<std::string> description;
|
||||
ValueType defaultValue;
|
||||
|
||||
static Result<ColorSetting> parse(JsonMaybeObject& obj);
|
||||
};
|
||||
|
||||
/**
|
||||
* A Setting for an RGBA color. See ColorSetting for a setting that doesn't
|
||||
* have alpha
|
||||
*/
|
||||
struct GEODE_DLL ColorAlphaSetting final {
|
||||
using ValueType = cocos2d::ccColor4B;
|
||||
|
||||
std::optional<std::string> name;
|
||||
std::optional<std::string> description;
|
||||
ValueType defaultValue;
|
||||
|
||||
static Result<ColorAlphaSetting> parse(JsonMaybeObject& obj);
|
||||
};
|
||||
|
||||
/**
|
||||
* A custom setting, defined by the mod. See
|
||||
* [the tutorial page](https://docs.geode-sdk.org/mods/settings) for more
|
||||
* information about how to create custom settings
|
||||
*/
|
||||
struct GEODE_DLL CustomSetting final {
|
||||
std::shared_ptr<ModJson> json;
|
||||
};
|
||||
|
||||
using SettingKind = std::variant<
|
||||
BoolSetting,
|
||||
IntSetting,
|
||||
FloatSetting,
|
||||
StringSetting,
|
||||
FileSetting,
|
||||
ColorSetting,
|
||||
ColorAlphaSetting,
|
||||
CustomSetting
|
||||
>;
|
||||
|
||||
/**
|
||||
* Represents a saved value for a mod that can be customized by the user
|
||||
* through an in-game UI. This class is for modeling the setting's
|
||||
* definition - what values are accepted, its name etc.
|
||||
* See [the tutorial page](https://docs.geode-sdk.org/mods/settings)
|
||||
* for more information about how settings work
|
||||
* @see SettingValue
|
||||
* @see SettingNode
|
||||
*/
|
||||
struct GEODE_DLL Setting final {
|
||||
private:
|
||||
std::string m_key;
|
||||
std::string m_modID;
|
||||
SettingKind m_kind;
|
||||
|
||||
Setting() = default;
|
||||
|
||||
public:
|
||||
static Result<Setting> parse(
|
||||
std::string const& key,
|
||||
std::string const& mod,
|
||||
JsonMaybeValue& obj
|
||||
);
|
||||
Setting(
|
||||
std::string const& key,
|
||||
std::string const& mod,
|
||||
SettingKind const& kind
|
||||
);
|
||||
|
||||
template<class T>
|
||||
std::optional<T> get() {
|
||||
if (std::holds_alternative<T>(m_kind)) {
|
||||
return std::get<T>(m_kind);
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::unique_ptr<SettingValue> createDefaultValue() const;
|
||||
bool isCustom() const;
|
||||
std::string getDisplayName() const;
|
||||
std::optional<std::string> getDescription() const;
|
||||
std::string getModID() const;
|
||||
};
|
||||
|
||||
/**
|
||||
* Stores the actual, current value of a Setting. See
|
||||
* [the tutorial page](https://docs.geode-sdk.org/mods/settings) for more
|
||||
* information, and how to create custom settings
|
||||
*/
|
||||
class GEODE_DLL SettingValue {
|
||||
protected:
|
||||
std::string m_key;
|
||||
std::string m_modID;
|
||||
|
||||
SettingValue(std::string const& key, std::string const& mod);
|
||||
|
||||
void valueChanged();
|
||||
|
||||
public:
|
||||
virtual ~SettingValue() = default;
|
||||
virtual bool load(matjson::Value const& json) = 0;
|
||||
virtual bool save(matjson::Value& json) const = 0;
|
||||
virtual SettingNode* createNode(float width) = 0;
|
||||
|
||||
std::string getKey() const;
|
||||
std::string getModID() const;
|
||||
};
|
||||
|
||||
template<class T>
|
||||
class GeodeSettingValue final : public SettingValue {
|
||||
public:
|
||||
using ValueType = typename T::ValueType;
|
||||
|
||||
protected:
|
||||
ValueType m_value;
|
||||
T m_definition;
|
||||
|
||||
using Valid = std::pair<ValueType, std::optional<std::string>>;
|
||||
|
||||
GEODE_DLL Valid toValid(ValueType const& value) const;
|
||||
|
||||
public:
|
||||
GeodeSettingValue(std::string const& key, std::string const& modID, T const& definition)
|
||||
: SettingValue(key, modID),
|
||||
m_definition(definition),
|
||||
m_value(definition.defaultValue) {}
|
||||
|
||||
bool load(matjson::Value const& json) override;
|
||||
bool save(matjson::Value& json) const;
|
||||
|
||||
GEODE_DLL SettingNode* createNode(float width) override;
|
||||
T castDefinition() const {
|
||||
return m_definition;
|
||||
}
|
||||
Setting getDefinition() const {
|
||||
return Setting(m_key, m_modID, m_definition);
|
||||
}
|
||||
|
||||
GEODE_DLL ValueType getValue() const;
|
||||
GEODE_DLL void setValue(ValueType const& value);
|
||||
GEODE_DLL Result<> validate(ValueType const& value) const;
|
||||
};
|
||||
|
||||
using BoolSettingValue = GeodeSettingValue<BoolSetting>;
|
||||
using IntSettingValue = GeodeSettingValue<IntSetting>;
|
||||
using FloatSettingValue = GeodeSettingValue<FloatSetting>;
|
||||
using StringSettingValue = GeodeSettingValue<StringSetting>;
|
||||
using FileSettingValue = GeodeSettingValue<FileSetting>;
|
||||
using ColorSettingValue = GeodeSettingValue<ColorSetting>;
|
||||
using ColorAlphaSettingValue = GeodeSettingValue<ColorAlphaSetting>;
|
||||
|
||||
// todo: remove in v3
|
||||
|
||||
template<class T>
|
||||
struct [[deprecated("Use SettingTypeForValueType from SettingV3 instead")]] GEODE_DLL SettingValueSetter {
|
||||
static T get(SettingValue* setting);
|
||||
static void set(SettingValue* setting, T const& value);
|
||||
};
|
||||
|
||||
template<class T>
|
||||
bool GeodeSettingValue<T>::load(matjson::Value const& json) {
|
||||
if (!json.is<ValueType>()) return false;
|
||||
m_value = json.as<ValueType>();
|
||||
return true;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
bool GeodeSettingValue<T>::save(matjson::Value& json) const {
|
||||
json = m_value;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma warning(pop)
|
||||
#pragma once
|
||||
|
||||
#include "SettingV3.hpp"
|
||||
|
||||
namespace geode {
|
||||
using Setting = SettingV3;
|
||||
using SettingGenerator = SettingGeneratorV3;
|
||||
|
||||
template <class T, class V = T>
|
||||
using SettingBaseValue = SettingBaseValueV3<T, V>;
|
||||
|
||||
using TitleSetting = TitleSettingV3;
|
||||
using BoolSetting = BoolSettingV3;
|
||||
using IntSetting = IntSettingV3;
|
||||
using FloatSetting = FloatSettingV3;
|
||||
using StringSetting = StringSettingV3;
|
||||
using FileSetting = FileSettingV3;
|
||||
using Color3BSetting = Color3BSettingV3;
|
||||
using Color4BSetting = Color4BSettingV3;
|
||||
|
||||
using SettingNode = SettingNodeV3;
|
||||
template <class S>
|
||||
using SettingValueNode = SettingValueNodeV3<S>;
|
||||
|
||||
using SettingChangedEvent = SettingChangedEventV3;
|
||||
using SettingChangedFilter = SettingChangedFilterV3;
|
||||
using SettingNodeSizeChangeEvent = SettingNodeSizeChangeEventV3;
|
||||
using SettingNodeValueChangeEvent = SettingNodeValueChangeEventV3;
|
||||
|
||||
template <class T, class Lambda>
|
||||
EventListener<SettingChangedFilter>* listenForSettingChanges(std::string_view settingKey, Lambda&& callback, Mod* mod = getMod()) {
|
||||
return listenForSettingChangesV3<T>(settingKey, std::forward<Lambda>(callback), mod);
|
||||
}
|
||||
|
||||
template <class Lambda>
|
||||
EventListener<SettingChangedFilter>* listenForSettingChanges(std::string_view settingKey, Lambda&& callback, Mod* mod = getMod()) {
|
||||
return listenForSettingChangesV3(settingKey, std::forward<Lambda>(callback), mod);
|
||||
}
|
||||
|
||||
inline EventListener<SettingChangedFilter>* listenForAllSettingChanges(
|
||||
std::function<void(std::shared_ptr<SettingV3>)> const& callback,
|
||||
Mod* mod = getMod()
|
||||
) {
|
||||
return listenForAllSettingChangesV3(callback, mod);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
}
|
|
@ -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;
|
||||
};
|
||||
}
|
|
@ -3,22 +3,17 @@
|
|||
#include "../DefaultInclude.hpp"
|
||||
#include <optional>
|
||||
#include <cocos2d.h>
|
||||
// todo: remove this header in 4.0.0
|
||||
#include "Setting.hpp"
|
||||
#include "../utils/cocos.hpp"
|
||||
#include "../utils/file.hpp"
|
||||
// this unfortunately has to be included because of C++ templates
|
||||
#include "../utils/JsonValidation.hpp"
|
||||
#include "../utils/function.hpp"
|
||||
|
||||
// todo in v4: this can be removed as well as the friend decl in LegacyCustomSettingV3
|
||||
class LegacyCustomSettingToV3Node;
|
||||
class ModSettingsPopup;
|
||||
|
||||
namespace geode {
|
||||
class ModSettingsManager;
|
||||
class SettingNodeV3;
|
||||
// todo in v4: remove this
|
||||
class SettingValue;
|
||||
|
||||
class GEODE_DLL SettingV3 : public std::enable_shared_from_this<SettingV3> {
|
||||
private:
|
||||
|
@ -125,8 +120,6 @@ namespace geode {
|
|||
*/
|
||||
void markChanged();
|
||||
|
||||
friend class ::geode::SettingValue;
|
||||
|
||||
public:
|
||||
SettingV3();
|
||||
virtual ~SettingV3();
|
||||
|
@ -183,20 +176,9 @@ namespace geode {
|
|||
* Reset this setting's value back to its original value
|
||||
*/
|
||||
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& modID,
|
||||
matjson::Value const& json
|
||||
|
@ -324,14 +306,15 @@ namespace geode {
|
|||
}
|
||||
|
||||
bool load(matjson::Value const& json) override {
|
||||
if (json.is<T>()) {
|
||||
m_impl->value = json.as<T>();
|
||||
return true;
|
||||
auto res = json.as<T>();
|
||||
if (res.isErr()) {
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
m_impl->value = res.unwrap();
|
||||
return true;
|
||||
}
|
||||
bool save(matjson::Value& json) const override {
|
||||
json = m_impl->value;
|
||||
json = matjson::Value(m_impl->value);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
@ -357,37 +340,6 @@ namespace geode {
|
|||
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> {
|
||||
private:
|
||||
class Impl;
|
||||
|
@ -404,9 +356,6 @@ namespace geode {
|
|||
Result<> isValid(bool value) const 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> {
|
||||
|
@ -436,9 +385,6 @@ namespace geode {
|
|||
bool isInputEnabled() const;
|
||||
|
||||
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> {
|
||||
|
@ -468,9 +414,6 @@ namespace geode {
|
|||
bool isInputEnabled() const;
|
||||
|
||||
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> {
|
||||
|
@ -493,9 +436,6 @@ namespace geode {
|
|||
std::optional<std::vector<std::string>> getEnumOptions() const;
|
||||
|
||||
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&> {
|
||||
|
@ -519,9 +459,6 @@ namespace geode {
|
|||
std::optional<std::vector<utils::file::FilePickOptions::Filter>> getFilters() const;
|
||||
|
||||
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> {
|
||||
|
@ -540,9 +477,6 @@ namespace geode {
|
|||
Result<> isValid(cocos2d::ccColor3B value) const 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> {
|
||||
|
@ -561,9 +495,6 @@ namespace geode {
|
|||
Result<> isValid(cocos2d::ccColor4B value) const 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 {
|
||||
|
@ -724,7 +655,7 @@ namespace geode {
|
|||
public:
|
||||
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
|
||||
* @param modID Mod whose settings to listen to
|
||||
|
@ -800,7 +731,7 @@ namespace geode {
|
|||
};
|
||||
|
||||
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;
|
||||
return new EventListener(
|
||||
[callback = std::move(callback)](std::shared_ptr<SettingV3> setting) {
|
||||
|
@ -811,11 +742,11 @@ namespace geode {
|
|||
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)>>;
|
||||
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,
|
||||
Mod* mod = getMod()
|
||||
);
|
||||
|
|
|
@ -2,12 +2,13 @@
|
|||
|
||||
#include <Geode/platform/platform.hpp>
|
||||
#include <tulip/TulipHook.hpp>
|
||||
#include "../Prelude.hpp"
|
||||
|
||||
namespace geode::hook {
|
||||
/**
|
||||
* Create a calling convention wrapper for a function.
|
||||
*/
|
||||
GEODE_DLL tulip::hook::Result<void*> createWrapper(
|
||||
GEODE_DLL Result<void*> createWrapper(
|
||||
void* address,
|
||||
tulip::hook::WrapperMetadata const& metadata
|
||||
) noexcept;
|
||||
|
|
|
@ -89,14 +89,11 @@ namespace geode {
|
|||
constexpr std::string_view GEODE_MOD_EXTENSION = ".geode";
|
||||
|
||||
class Mod;
|
||||
class Setting;
|
||||
class Loader;
|
||||
class Hook;
|
||||
class VersionInfo;
|
||||
|
||||
class Unknown;
|
||||
using unknownmemfn_t = void (Unknown::*)();
|
||||
using unknownfn_t = void (*)();
|
||||
|
||||
namespace modifier {
|
||||
template <class, class>
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
#pragma once
|
||||
|
||||
#include "../utils/MiniFunction.hpp"
|
||||
#include "Traits.hpp"
|
||||
|
||||
#include <Geode/loader/Loader.hpp>
|
||||
|
@ -20,7 +19,7 @@ namespace geode::modifier {
|
|||
class FieldContainer {
|
||||
private:
|
||||
std::vector<void*> m_containedFields;
|
||||
std::vector<utils::MiniFunction<void(void*)>> m_destructorFunctions;
|
||||
std::vector<std::function<void(void*)>> m_destructorFunctions;
|
||||
|
||||
public:
|
||||
~FieldContainer() {
|
||||
|
@ -40,9 +39,9 @@ namespace geode::modifier {
|
|||
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_destructorFunctions.at(index) = destructor;
|
||||
m_destructorFunctions.at(index) = std::move(destructor);
|
||||
return m_containedFields.at(index);
|
||||
}
|
||||
|
||||
|
|
|
@ -104,8 +104,36 @@
|
|||
} \
|
||||
} 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>
|
||||
class ModifyDerive;
|
||||
|
||||
|
@ -114,22 +142,179 @@ namespace geode::modifier {
|
|||
public:
|
||||
std::map<std::string, std::shared_ptr<Hook>> m_hooks;
|
||||
|
||||
Result<Hook*> getHook(std::string const& name) {
|
||||
if (m_hooks.find(name) == m_hooks.end()) {
|
||||
/// @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_view name) {
|
||||
auto key = std::string(name);
|
||||
if (m_hooks.find(key) == m_hooks.end()) {
|
||||
return Err("Hook not in this modify");
|
||||
}
|
||||
return Ok(m_hooks[name].get());
|
||||
return Ok(m_hooks[key].get());
|
||||
}
|
||||
|
||||
Result<> setHookPriority(std::string const& name, int32_t priority) {
|
||||
auto res = this->getHook(name);
|
||||
if (!res) {
|
||||
return Err(res.unwrapErr());
|
||||
}
|
||||
res.unwrap()->setPriority(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<> setHookPriority(std::string_view name, int32_t priority = Priority::Normal) {
|
||||
GEODE_UNWRAP_INTO(auto hook, this->getHook(name));
|
||||
hook->setPriority(priority);
|
||||
return Ok();
|
||||
}
|
||||
|
||||
/// @brief Set the priority of a hook
|
||||
/// @param name The name of the hook to set the priority of
|
||||
/// @param priority The priority to set the hook to
|
||||
/// @returns Ok if the hook was found and the priority was set, Err if the hook was not found
|
||||
Result<> setHookPriorityPre(std::string_view name, int32_t priority = Priority::Normal) {
|
||||
return this->setHookPriority(name, priority);
|
||||
}
|
||||
|
||||
/// @brief Set the priority of a hook
|
||||
/// @param name The name of the hook to set the priority of
|
||||
/// @param priority The priority to set the hook to
|
||||
/// @returns Ok if the hook was found and the priority was set, Err if the hook was not found
|
||||
Result<> setHookPriorityPost(std::string_view name, int32_t priority = Priority::Normal) {
|
||||
return this->setHookPriority(name, -priority);
|
||||
}
|
||||
|
||||
/// @brief Set the priority of a hook to be after another hook in different mods
|
||||
/// @param name The name of the hook to set the priority of
|
||||
/// @param after The mod to set the priority after
|
||||
/// @returns Ok if the hook was found and the priority was set, Err if the hook was not found
|
||||
Result<> setHookPriorityAfter(std::string_view name, Mod* mod) {
|
||||
GEODE_UNWRAP_INTO(auto hook, this->getHook(name));
|
||||
auto func = [=](ModStateEvent* event){
|
||||
auto hooks = mod->getHooks();
|
||||
for (auto modHook : hooks) {
|
||||
if (modHook->getAddress() != hook->getAddress()) continue;
|
||||
auto priority = modHook->getPriority();
|
||||
if (hook->getPriority() <= priority) {
|
||||
hook->setPriority(priority + 1);
|
||||
}
|
||||
}
|
||||
return ListenerResult::Propagate;
|
||||
};
|
||||
if (mod->isEnabled()) {
|
||||
func(nullptr);
|
||||
}
|
||||
else {
|
||||
new EventListener(func, ModStateFilter(mod, ModEventType::Loaded));
|
||||
}
|
||||
return Ok();
|
||||
}
|
||||
|
||||
/// @brief Set the priority of a hook to be after another hook in different mods
|
||||
/// @param name The name of the hook to set the priority of
|
||||
/// @param after The mod id of the mod to set the priority after
|
||||
/// @returns Ok if the hook was found and the priority was set, Err if the hook was not found
|
||||
Result<> setHookPriorityAfter(std::string_view name, std::string_view after) {
|
||||
auto mod = Loader::get()->getInstalledMod(std::string(after));
|
||||
if (!mod) return Err("Mod not found");
|
||||
return this->setHookPriorityAfter(name, mod);
|
||||
}
|
||||
|
||||
/// @brief Set the priority of a hook to be before another hook in different mods
|
||||
/// @param name The name of the hook to set the priority of
|
||||
/// @param before The mod to set the priority before
|
||||
/// @returns Ok if the hook was found and the priority was set, Err if the hook was not found
|
||||
Result<> setHookPriorityBefore(std::string_view name, Mod* mod) {
|
||||
GEODE_UNWRAP_INTO(auto hook, this->getHook(name));
|
||||
auto func = [=](ModStateEvent* event){
|
||||
auto hooks = mod->getHooks();
|
||||
for (auto modHook : hooks) {
|
||||
if (modHook->getAddress() != hook->getAddress()) continue;
|
||||
auto priority = modHook->getPriority();
|
||||
if (hook->getPriority() >= priority) {
|
||||
hook->setPriority(priority - 1);
|
||||
}
|
||||
}
|
||||
return ListenerResult::Propagate;
|
||||
};
|
||||
if (mod->isEnabled()) {
|
||||
func(nullptr);
|
||||
}
|
||||
else {
|
||||
new EventListener(func, ModStateFilter(mod, ModEventType::Loaded));
|
||||
}
|
||||
return Ok();
|
||||
}
|
||||
|
||||
/// @brief Set the priority of a hook to be before another hook in different mods
|
||||
/// @param name The name of the hook to set the priority of
|
||||
/// @param before The mod id of the mod to set the priority before
|
||||
/// @returns Ok if the hook was found and the priority was set, Err if the hook was not found
|
||||
Result<> setHookPriorityBefore(std::string_view name, std::string_view before) {
|
||||
auto mod = Loader::get()->getInstalledMod(std::string(before));
|
||||
if (!mod) return Err("Mod not found");
|
||||
return this->setHookPriorityBefore(name, mod);
|
||||
}
|
||||
|
||||
/// @brief Set the priority of a hook to be after another hook in different mods
|
||||
/// @param name The name of the hook to set the priority of
|
||||
/// @param after The mod id of the mod to set the priority after
|
||||
/// @returns Ok if the hook was found and the priority was set, Err if the hook was not found
|
||||
Result<> setHookPriorityAfterPre(std::string_view name, std::string_view after) {
|
||||
return this->setHookPriorityAfter(name, after);
|
||||
}
|
||||
|
||||
/// @brief Set the priority of a hook to be after another hook in different mods
|
||||
/// @param name The name of the hook to set the priority of
|
||||
/// @param before The mod to set the priority after
|
||||
/// @returns Ok if the hook was found and the priority was set, Err if the hook was not found
|
||||
Result<> setHookPriorityAfterPre(std::string_view name, Mod* mod) {
|
||||
return this->setHookPriorityAfter(name, mod);
|
||||
}
|
||||
|
||||
/// @brief Set the priority of a hook to be before another hook in different mods
|
||||
/// @param name The name of the hook to set the priority of
|
||||
/// @param before The mod id of the mod to set the priority before
|
||||
/// @returns Ok if the hook was found and the priority was set, Err if the hook was not found
|
||||
Result<> setHookPriorityBeforePre(std::string_view name, std::string_view before) {
|
||||
return this->setHookPriorityBefore(name, before);
|
||||
}
|
||||
|
||||
/// @brief Set the priority of a hook to be before another hook in different mods
|
||||
/// @param name The name of the hook to set the priority of
|
||||
/// @param before The mod to set the priority before
|
||||
/// @returns Ok if the hook was found and the priority was set, Err if the hook was not found
|
||||
Result<> setHookPriorityBeforePre(std::string_view name, Mod* mod) {
|
||||
return this->setHookPriorityBefore(name, mod);
|
||||
}
|
||||
|
||||
/// @brief Set the priority of a hook to be after another hook in different mods
|
||||
/// @param name The name of the hook to set the priority of
|
||||
/// @param after The mod id of the mod to set the priority after
|
||||
/// @returns Ok if the hook was found and the priority was set, Err if the hook was not found
|
||||
Result<> setHookPriorityAfterPost(std::string_view name, std::string_view after) {
|
||||
return this->setHookPriorityBefore(name, after);
|
||||
}
|
||||
|
||||
/// @brief Set the priority of a hook to be after another hook in different mods
|
||||
/// @param name The name of the hook to set the priority of
|
||||
/// @param before The mod to set the priority after
|
||||
/// @returns Ok if the hook was found and the priority was set, Err if the hook was not found
|
||||
Result<> setHookPriorityAfterPost(std::string_view name, Mod* mod) {
|
||||
return this->setHookPriorityBefore(name, mod);
|
||||
}
|
||||
|
||||
/// @brief Set the priority of a hook to be before another hook in different mods
|
||||
/// @param name The name of the hook to set the priority of
|
||||
/// @param before The mod id of the mod to set the priority before
|
||||
/// @returns Ok if the hook was found and the priority was set, Err if the hook was not found
|
||||
Result<> setHookPriorityBeforePost(std::string_view name, std::string_view before) {
|
||||
return this->setHookPriorityAfter(name, before);
|
||||
}
|
||||
|
||||
/// @brief Set the priority of a hook to be before another hook in different mods
|
||||
/// @param name The name of the hook to set the priority of
|
||||
/// @param before The mod to set the priority before
|
||||
/// @returns Ok if the hook was found and the priority was set, Err if the hook was not found
|
||||
Result<> setHookPriorityBeforePost(std::string_view name, Mod* mod) {
|
||||
return this->setHookPriorityAfter(name, mod);
|
||||
}
|
||||
|
||||
// unordered_map<handles> idea
|
||||
ModifyBase() {
|
||||
struct EboCheck : ModifyDerived::Base {
|
||||
|
@ -158,7 +343,7 @@ namespace geode::modifier {
|
|||
for (auto& [uuid, hook] : m_hooks) {
|
||||
auto res = Mod::get()->claimHook(hook);
|
||||
if (!res) {
|
||||
log::error("Failed to claim hook {}: {}", hook->getDisplayName(), res.error());
|
||||
log::error("Failed to claim hook {}: {}", hook->getDisplayName(), res.unwrapErr());
|
||||
}
|
||||
else {
|
||||
added.push_back(uuid);
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
|
||||
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) && !defined(__CYGWIN__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) && !defined(__CYGWIN__)
|
||||
#define GEODE_WINDOWS(...) __VA_ARGS__
|
||||
#define GEODE_DESKTOP(...) __VA_ARGS__
|
||||
#define GEODE_MOBILE(...)
|
||||
#define GEODE_IS_WINDOWS
|
||||
#define GEODE_IS_DESKTOP
|
||||
#define GEODE_PLATFORM_NAME "Windows"
|
||||
|
@ -43,6 +45,8 @@
|
|||
#define GEODE_INTEL_MAC(...)
|
||||
#define GEODE_ARM_MAC(...)
|
||||
#define GEODE_IOS(...) __VA_ARGS__
|
||||
#define GEODE_DESKTOP(...)
|
||||
#define GEODE_MOBILE(...) __VA_ARGS__
|
||||
#define GEODE_IS_IOS
|
||||
#define GEODE_IS_MOBILE
|
||||
#define GEODE_PLATFORM_NAME "iOS"
|
||||
|
@ -53,6 +57,8 @@
|
|||
#else
|
||||
#define GEODE_IOS(...)
|
||||
#define GEODE_MACOS(...) __VA_ARGS__
|
||||
#define GEODE_DESKTOP(...) __VA_ARGS__
|
||||
#define GEODE_MOBILE(...)
|
||||
#define GEODE_IS_MACOS
|
||||
#define GEODE_IS_DESKTOP
|
||||
#define GEODE_PLATFORM_EXTENSION ".dylib"
|
||||
|
@ -84,6 +90,8 @@
|
|||
// Android
|
||||
#if defined(__ANDROID__)
|
||||
#define GEODE_ANDROID(...) __VA_ARGS__
|
||||
#define GEODE_MOBILE(...) __VA_ARGS__
|
||||
#define GEODE_DESKTOP(...)
|
||||
#define GEODE_IS_ANDROID
|
||||
#define GEODE_IS_MOBILE
|
||||
#define GEODE_CALL
|
||||
|
|
|
@ -116,14 +116,22 @@ namespace geode {
|
|||
public:
|
||||
// todo in v4: make these flags and add archless Mac and Android as well as Desktop and Mobile and remove Linux
|
||||
enum {
|
||||
Unknown = -1,
|
||||
Windows = 0,
|
||||
MacIntel = 1,
|
||||
MacArm = 2,
|
||||
iOS = 3,
|
||||
Android32 = 4,
|
||||
Android64 = 5,
|
||||
Linux = 6,
|
||||
Unknown = 0b000000,
|
||||
Windows = 0b000001,
|
||||
Android32 = 0b000010,
|
||||
Android64 = 0b000100,
|
||||
MacIntel = 0b001000,
|
||||
MacArm = 0b010000,
|
||||
iOS = 0b100000,
|
||||
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);
|
||||
|
@ -190,7 +198,6 @@ namespace geode {
|
|||
case iOS: return "iOS";
|
||||
case Android32: return "Android32";
|
||||
case Android64: return "Android64";
|
||||
case Linux: return "Linux";
|
||||
default: break;
|
||||
}
|
||||
return "Undefined";
|
||||
|
@ -206,7 +213,6 @@ namespace geode {
|
|||
case iOS: return "ios";
|
||||
case Android32: return ignoreArch ? "android" : "android32";
|
||||
case Android64: return ignoreArch ? "android" : "android64";
|
||||
case Linux: return "linux";
|
||||
default: break;
|
||||
}
|
||||
return "undefined";
|
||||
|
|
|
@ -13,7 +13,7 @@ namespace geode {
|
|||
virtual void updateColor(cocos2d::ccColor4B const& color) {}
|
||||
};
|
||||
|
||||
// todo in v4: make this pimpl and maybe use events over the delegate?
|
||||
// todo in v4: maybe use events over the delegate?
|
||||
// thing with events is that if you just filter via ColorPickPopup* it
|
||||
// won't work unless you automatically detach the filter when closing the
|
||||
// popup (otherwise opening another popup really quickly will just be
|
||||
|
@ -24,18 +24,8 @@ namespace geode {
|
|||
public cocos2d::extension::ColorPickerDelegate,
|
||||
public TextInputDelegate {
|
||||
protected:
|
||||
cocos2d::ccColor4B m_color;
|
||||
cocos2d::ccColor4B m_originalColor;
|
||||
cocos2d::extension::CCControlColourPicker* m_picker;
|
||||
Slider* m_opacitySlider = nullptr;
|
||||
TextInput* m_rInput;
|
||||
TextInput* m_gInput;
|
||||
TextInput* m_bInput;
|
||||
TextInput* m_hexInput;
|
||||
TextInput* m_opacityInput = nullptr;
|
||||
ColorPickPopupDelegate* m_delegate = nullptr;
|
||||
cocos2d::CCSprite* m_newColorSpr;
|
||||
CCMenuItemSpriteExtra* m_resetBtn;
|
||||
class Impl;
|
||||
std::unique_ptr<Impl> m_impl;
|
||||
|
||||
static constexpr auto TAG_OPACITY_INPUT = 0;
|
||||
static constexpr auto TAG_R_INPUT = 1;
|
||||
|
@ -43,10 +33,13 @@ namespace geode {
|
|||
static constexpr auto TAG_B_INPUT = 3;
|
||||
static constexpr auto TAG_HEX_INPUT = 4;
|
||||
|
||||
ColorPickPopup();
|
||||
~ColorPickPopup();
|
||||
bool setup(cocos2d::ccColor4B const& color, bool isRGBA) override;
|
||||
|
||||
void onOpacitySlider(cocos2d::CCObject* sender);
|
||||
void onReset(cocos2d::CCObject* sender);
|
||||
void onClose(cocos2d::CCObject* sender) override;
|
||||
|
||||
void textChanged(CCTextInputNode* input) override;
|
||||
void colorValueChanged(cocos2d::ccColor3B color) override;
|
||||
|
|
|
@ -30,7 +30,7 @@ namespace geode {
|
|||
std::optional<std::string> m_targetID;
|
||||
|
||||
public:
|
||||
ListenerResult handle(utils::MiniFunction<Callback> fn, AEnterLayerEvent* event);
|
||||
ListenerResult handle(std::function<Callback> fn, AEnterLayerEvent* event);
|
||||
|
||||
AEnterLayerFilter(
|
||||
std::optional<std::string> const& id
|
||||
|
@ -63,7 +63,7 @@ namespace geode {
|
|||
std::optional<std::string> m_targetID;
|
||||
|
||||
public:
|
||||
ListenerResult handle(utils::MiniFunction<Callback> fn, EnterLayerEvent<N>* event) {
|
||||
ListenerResult handle(std::function<Callback> fn, EnterLayerEvent<N>* event) {
|
||||
if (m_targetID == event->getID()) {
|
||||
fn(static_cast<T*>(event));
|
||||
}
|
||||
|
|
|
@ -133,11 +133,6 @@ namespace geode {
|
|||
*/
|
||||
GEODE_DLL void openSupportPopup(Mod* mod);
|
||||
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)
|
||||
*/
|
||||
|
@ -160,6 +155,10 @@ namespace geode {
|
|||
* Create a logo sprite for a mod
|
||||
*/
|
||||
GEODE_DLL cocos2d::CCNode* createModLogo(Mod* mod);
|
||||
/**
|
||||
* Create a logo sprite for a mod from a .geode file
|
||||
*/
|
||||
GEODE_DLL cocos2d::CCNode* createModLogo(std::filesystem::path const& geodePackage);
|
||||
/**
|
||||
* Create a logo sprite for a mod downloaded from the Geode servers. The
|
||||
* logo is initially a loading circle, with the actual sprite downloaded
|
||||
|
|
|
@ -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();
|
||||
};
|
||||
}
|
|
@ -1,448 +1,430 @@
|
|||
#pragma once
|
||||
|
||||
#include "../include/ccMacros.h"
|
||||
#include "../cocoa/CCAffineTransform.h"
|
||||
#include "../cocoa/CCArray.h"
|
||||
#include <Geode/platform/platform.hpp>
|
||||
#include <optional>
|
||||
#include <memory>
|
||||
|
||||
NS_CC_BEGIN
|
||||
|
||||
class CCNode;
|
||||
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable: 4275)
|
||||
|
||||
/**
|
||||
* Layouts automatically handle the positioning of nodes. Use CCNode::setLayout
|
||||
* 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
|
||||
* of layout you can inherit from the Layout class.
|
||||
*/
|
||||
class GEODE_DLL Layout : public CCObject {
|
||||
protected:
|
||||
CCArray* getNodesToPosition(CCNode* forNode) const;
|
||||
|
||||
bool m_ignoreInvisibleChildren = false;
|
||||
|
||||
public:
|
||||
/**
|
||||
* 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
|
||||
* 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
|
||||
* rescaled to better fit its contents
|
||||
*/
|
||||
virtual void apply(CCNode* on) = 0;
|
||||
|
||||
/**
|
||||
* Get how much space this layout would like to take up for a given target
|
||||
*/
|
||||
virtual CCSize getSizeHint(CCNode* on) const = 0;
|
||||
|
||||
void ignoreInvisibleChildren(bool ignore);
|
||||
bool isIgnoreInvisibleChildren() const;
|
||||
|
||||
virtual ~Layout() = default;
|
||||
};
|
||||
|
||||
class GEODE_DLL LayoutOptions : public CCObject {
|
||||
public:
|
||||
virtual ~LayoutOptions() = default;
|
||||
};
|
||||
|
||||
/**
|
||||
* The direction of an AxisLayout
|
||||
*/
|
||||
enum class Axis {
|
||||
Row,
|
||||
Column,
|
||||
};
|
||||
|
||||
/**
|
||||
* Specifies the alignment of something in an AxisLayout
|
||||
*/
|
||||
enum class AxisAlignment {
|
||||
// Align items to the start
|
||||
// |ooo......|
|
||||
Start,
|
||||
// All items are centered
|
||||
// |...ooo...|
|
||||
Center,
|
||||
// Align items to the end
|
||||
// |......ooo|
|
||||
End,
|
||||
// Each item gets the same portion from the layout (disregards gap)
|
||||
// |.o..o..o.|
|
||||
Even,
|
||||
// Space between each item is the same (disregards gap)
|
||||
// |o...o...o|
|
||||
Between,
|
||||
};
|
||||
|
||||
constexpr float AXISLAYOUT_DEFAULT_MIN_SCALE = 0.65f;
|
||||
constexpr int AXISLAYOUT_DEFAULT_PRIORITY = 0;
|
||||
|
||||
/**
|
||||
* Options for controlling the behaviour of individual nodes in an AxisLayout
|
||||
* @example
|
||||
* auto node = CCNode::create();
|
||||
* // this node will have 10 units of spacing between it and the next one
|
||||
* node->setLayoutOptions(
|
||||
* AxisLayoutOptions::create()
|
||||
* ->setNextGap(10.f)
|
||||
* );
|
||||
* someNodeWithALayout->addChild(node);
|
||||
*/
|
||||
class GEODE_DLL AxisLayoutOptions : public LayoutOptions {
|
||||
protected:
|
||||
class Impl;
|
||||
|
||||
std::unique_ptr<Impl> m_impl;
|
||||
|
||||
AxisLayoutOptions();
|
||||
|
||||
public:
|
||||
static AxisLayoutOptions* create();
|
||||
|
||||
virtual ~AxisLayoutOptions();
|
||||
|
||||
std::optional<bool> getAutoScale() const;
|
||||
// @note Use hasExplicitMaxScale to know if the default scale has been overwritten
|
||||
float getMaxScale() const;
|
||||
// @note Use hasExplicitMinScale to know if the default scale has been overwritten
|
||||
float getMinScale() const;
|
||||
bool hasExplicitMaxScale() const;
|
||||
bool hasExplicitMinScale() const;
|
||||
float getRelativeScale() const;
|
||||
std::optional<float> getLength() const;
|
||||
std::optional<float> getPrevGap() const;
|
||||
std::optional<float> getNextGap() const;
|
||||
bool getBreakLine() const;
|
||||
bool getSameLine() const;
|
||||
int getScalePriority() const;
|
||||
std::optional<AxisAlignment> getCrossAxisAlignment() const;
|
||||
|
||||
/**
|
||||
* Set the maximum scale this node can be if it's contained in an
|
||||
* auto-scaled layout. Default is 1
|
||||
*/
|
||||
[[deprecated("Use AxisLayoutOptions::setScaleLimits")]]
|
||||
AxisLayoutOptions* setMaxScale(float scale);
|
||||
|
||||
/**
|
||||
* Set the minimum scale this node can be if it's contained in an
|
||||
* auto-scaled layout. Default is AXISLAYOUT_DEFAULT_MIN_SCALE
|
||||
*/
|
||||
[[deprecated("Use AxisLayoutOptions::setScaleLimits")]]
|
||||
AxisLayoutOptions* setMinScale(float scale);
|
||||
|
||||
/**
|
||||
* Set the limits to what the node can be scaled to. Passing `std::nullopt`
|
||||
* uses the parent layout's default min / max scales
|
||||
*/
|
||||
AxisLayoutOptions* setScaleLimits(std::optional<float> min, std::optional<float> max);
|
||||
|
||||
/**
|
||||
* Set the relative scale of this node compared to other nodes if it's
|
||||
* contained in an auto-scaled layout. Default is 1
|
||||
*/
|
||||
AxisLayoutOptions* setRelativeScale(float 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
|
||||
*/
|
||||
AxisLayoutOptions* setAutoScale(std::optional<bool> enabled);
|
||||
|
||||
/**
|
||||
* Set an absolute length for this node. If nullopt, the length will be
|
||||
* dynamically calculated based on content size
|
||||
*/
|
||||
AxisLayoutOptions* setLength(std::optional<float> length);
|
||||
|
||||
/**
|
||||
* Override the default gap in the layout between this node and the
|
||||
* previous one. If nullopt, the default gap of the layout will be used
|
||||
*/
|
||||
AxisLayoutOptions* setPrevGap(std::optional<float> gap);
|
||||
|
||||
/**
|
||||
* Override the default gap in the layout between this node and the next
|
||||
* one. If nullopt, the default gap of the layout will be used
|
||||
*/
|
||||
AxisLayoutOptions* setNextGap(std::optional<float> gap);
|
||||
|
||||
/**
|
||||
* 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* setBreakLine(bool enable);
|
||||
|
||||
/**
|
||||
* If enabled, the node will be forced to be on the same line as the
|
||||
* previous node even if doing this would overflow
|
||||
*/
|
||||
AxisLayoutOptions* setSameLine(bool enable);
|
||||
|
||||
/**
|
||||
* Set the scale priority of this node. Nodes with higher priority will be
|
||||
* scaled down first before nodes with lower priority when an auto-scaled
|
||||
* layout attempts to fit its contents. Default is
|
||||
* AXISLAYOUT_DEFAULT_PRIORITY
|
||||
* @note For optimal performance, the priorities should all be close to
|
||||
* each other with no gaps
|
||||
*/
|
||||
AxisLayoutOptions* setScalePriority(int priority);
|
||||
|
||||
/**
|
||||
* Override the cross axis alignment for this node in the layout
|
||||
*/
|
||||
AxisLayoutOptions* setCrossAxisAlignment(std::optional<AxisAlignment> alignment);
|
||||
};
|
||||
|
||||
/**
|
||||
* 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
|
||||
* RowLayout and ColumnLayout classes function as simple thin wrappers over
|
||||
* AxisLayout. The positioning of individual nodes in the layout can be
|
||||
* further controlled using AxisLayoutOptions
|
||||
* @warning Calculating layouts can get increasingly expensive for large
|
||||
* amounts of child nodes being fit into a small space - while this should
|
||||
* 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
|
||||
* call CCNode::updateLayout every frame for a menu with thousands of children
|
||||
* @example
|
||||
* auto menu = CCMenu::create();
|
||||
* // 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
|
||||
* // aligned to the left. The menu automatically will automatically grow in
|
||||
* // height to fit all the rows
|
||||
* menu->setLayout(
|
||||
* RowLayout::create()
|
||||
* ->setGap(10.f)
|
||||
* ->setGrowCrossAxis(true)
|
||||
* ->setAxisAlignment(AxisAlignment::Start)
|
||||
* );
|
||||
* menu->setContentSize({ 200.f, 0.f });
|
||||
* menu->addChild(...);
|
||||
* menu->updateLayout();
|
||||
*/
|
||||
class GEODE_DLL AxisLayout : public Layout {
|
||||
protected:
|
||||
class Impl;
|
||||
|
||||
std::unique_ptr<Impl> m_impl;
|
||||
|
||||
AxisLayout(Axis);
|
||||
|
||||
public:
|
||||
/**
|
||||
* Create a new AxisLayout. Note that this class is not automatically
|
||||
* managed by default, so you must assign it to a CCNode or manually
|
||||
* manage the memory yourself. See the chainable setters on AxisLayout for
|
||||
* what options you can customize for the layout
|
||||
* @param axis The direction of the layout
|
||||
* @note For convenience, you can use the RowLayout and ColumnLayout
|
||||
* classes, which are just thin wrappers over AxisLayout
|
||||
* @returns Created AxisLayout
|
||||
*/
|
||||
static AxisLayout* create(Axis axis = Axis::Row);
|
||||
|
||||
virtual ~AxisLayout();
|
||||
|
||||
void apply(CCNode* on) override;
|
||||
CCSize getSizeHint(CCNode* on) const override;
|
||||
|
||||
Axis getAxis() const;
|
||||
AxisAlignment getAxisAlignment() const;
|
||||
AxisAlignment getCrossAxisAlignment() const;
|
||||
AxisAlignment getCrossAxisLineAlignment() const;
|
||||
float getGap() const;
|
||||
bool getAxisReverse() const;
|
||||
bool getCrossAxisReverse() const;
|
||||
bool getAutoScale() const;
|
||||
bool getGrowCrossAxis() const;
|
||||
bool getCrossAxisOverflow() const;
|
||||
std::optional<float> getAutoGrowAxis() const;
|
||||
float getDefaultMinScale() const;
|
||||
float getDefaultMaxScale() const;
|
||||
|
||||
AxisLayout* setAxis(Axis axis);
|
||||
/**
|
||||
* Sets where to align the target node's children on the main axis (X-axis
|
||||
* for Row, Y-axis for Column)
|
||||
*/
|
||||
AxisLayout* setAxisAlignment(AxisAlignment align);
|
||||
/**
|
||||
* Sets where to align the target node's children on the cross-axis (Y-axis
|
||||
* for Row, X-axis for Column)
|
||||
*/
|
||||
AxisLayout* setCrossAxisAlignment(AxisAlignment align);
|
||||
/**
|
||||
* Sets where to align the target node's children on the cross-axis for
|
||||
* each row (Y-axis for Row, X-axis for Column)
|
||||
*/
|
||||
AxisLayout* setCrossAxisLineAlignment(AxisAlignment align);
|
||||
/**
|
||||
* The spacing between the children of the node this layout applies to.
|
||||
* Measured as the space between their edges, not centres. Does not apply
|
||||
* on the main / cross axis if their alignment is AxisAlignment::Even
|
||||
*/
|
||||
AxisLayout* setGap(float gap);
|
||||
/**
|
||||
* Whether to reverse the direction of the children in this layout or not
|
||||
*/
|
||||
AxisLayout* setAxisReverse(bool reverse);
|
||||
/**
|
||||
* Whether to reverse the direction of the rows on the cross-axis or not
|
||||
*/
|
||||
AxisLayout* setCrossAxisReverse(bool reverse);
|
||||
/**
|
||||
* 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* setAutoScale(bool enable);
|
||||
/**
|
||||
* If true, if the main axis overflows extra nodes will be placed on new
|
||||
* rows/columns on the cross-axis
|
||||
*/
|
||||
AxisLayout* setGrowCrossAxis(bool expand);
|
||||
/**
|
||||
* If true, the cross-axis content size of the target node will be
|
||||
* automatically adjusted to fit the children
|
||||
*/
|
||||
AxisLayout* setCrossAxisOverflow(bool allow);
|
||||
/**
|
||||
* If not `std::nullopt`, then the axis will be automatically extended to
|
||||
* fit all items in a single row whose minimum length is the specified.
|
||||
* Useful for scrollable list layer contents
|
||||
*/
|
||||
AxisLayout* setAutoGrowAxis(std::optional<float> allowAndMinLength);
|
||||
/**
|
||||
* Set the default minimum/maximum scales for nodes in the layout
|
||||
*/
|
||||
AxisLayout* setDefaultScaleLimits(float min, float max);
|
||||
};
|
||||
|
||||
/**
|
||||
* Simple layout for arranging nodes in a row (horizontal line)
|
||||
*/
|
||||
class GEODE_DLL RowLayout : public AxisLayout {
|
||||
protected:
|
||||
RowLayout();
|
||||
|
||||
public:
|
||||
/**
|
||||
* Create a new RowLayout. See the chainable setters on RowLayout for
|
||||
* what options you can customize for the layout
|
||||
* @returns Created RowLayout
|
||||
*/
|
||||
static RowLayout* create();
|
||||
};
|
||||
|
||||
/**
|
||||
* Simple layout for arranging nodes in a column (vertical line)
|
||||
*/
|
||||
class GEODE_DLL ColumnLayout : public AxisLayout {
|
||||
protected:
|
||||
ColumnLayout();
|
||||
|
||||
public:
|
||||
/**
|
||||
* Create a new ColumnLayout. See the chainable setters on RowLayout for
|
||||
* what options you can customize for the layout
|
||||
* @returns Created ColumnLayout
|
||||
*/
|
||||
static ColumnLayout* create();
|
||||
};
|
||||
|
||||
/**
|
||||
* The relative position of a node to its parent in an AnchorLayout
|
||||
*/
|
||||
enum class Anchor {
|
||||
Center,
|
||||
TopLeft,
|
||||
Top,
|
||||
TopRight,
|
||||
Right,
|
||||
BottomRight,
|
||||
Bottom,
|
||||
BottomLeft,
|
||||
Left,
|
||||
};
|
||||
|
||||
/**
|
||||
* Options for customizing a node's position in an AnchorLayout
|
||||
*/
|
||||
class GEODE_DLL AnchorLayoutOptions : public LayoutOptions {
|
||||
protected:
|
||||
Anchor m_anchor = Anchor::Center;
|
||||
CCPoint m_offset = CCPointZero;
|
||||
|
||||
public:
|
||||
static AnchorLayoutOptions* create();
|
||||
|
||||
Anchor getAnchor() const;
|
||||
CCPoint getOffset() const;
|
||||
|
||||
AnchorLayoutOptions* setAnchor(Anchor anchor);
|
||||
AnchorLayoutOptions* setOffset(CCPoint const& offset);
|
||||
};
|
||||
|
||||
/**
|
||||
* A layout for positioning nodes at specific positions relative to their
|
||||
* parent's content size. See `Anchor` for available anchoring options. Useful
|
||||
* for example for popups, where a popup using `AnchorLayout` can be
|
||||
* automatically resized without needing to manually shuffle nodes around
|
||||
*/
|
||||
class GEODE_DLL AnchorLayout : public Layout {
|
||||
public:
|
||||
static AnchorLayout* create();
|
||||
|
||||
void apply(CCNode* on) override;
|
||||
CCSize getSizeHint(CCNode* on) const override;
|
||||
|
||||
/**
|
||||
* Get a position according to anchoring rules, with the same algorithm as
|
||||
* `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
|
||||
* @returns A position in `in` for the anchored and offsetted location
|
||||
*/
|
||||
static CCPoint getAnchoredPosition(CCNode* in, Anchor anchor, CCPoint const& offset);
|
||||
};
|
||||
|
||||
/**
|
||||
* A layout for automatically copying the content size of a node to other nodes.
|
||||
* Basically main use case is for FLAlertLayers (setting the size of the
|
||||
* background and `m_buttonMenu` based on `m_mainLayer`)
|
||||
*/
|
||||
class GEODE_DLL CopySizeLayout : public cocos2d::AnchorLayout {
|
||||
protected:
|
||||
cocos2d::CCArray* m_targets;
|
||||
|
||||
public:
|
||||
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
|
||||
#pragma once
|
||||
|
||||
#include <cocos2d.h>
|
||||
#include <Geode/platform/platform.hpp>
|
||||
#include <optional>
|
||||
#include <memory>
|
||||
|
||||
namespace geode {
|
||||
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable: 4275)
|
||||
|
||||
/**
|
||||
* Layouts automatically handle the positioning of nodes. Use CCNode::setLayout
|
||||
* 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
|
||||
* of layout you can inherit from the Layout class.
|
||||
*/
|
||||
class GEODE_DLL Layout : public cocos2d::CCObject {
|
||||
protected:
|
||||
cocos2d::CCArray* getNodesToPosition(cocos2d::CCNode* forNode) const;
|
||||
|
||||
bool m_ignoreInvisibleChildren = false;
|
||||
|
||||
public:
|
||||
/**
|
||||
* 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
|
||||
* 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
|
||||
* rescaled to better fit its contents
|
||||
*/
|
||||
virtual void apply(cocos2d::CCNode* on) = 0;
|
||||
|
||||
/**
|
||||
* Get how much space this layout would like to take up for a given target
|
||||
*/
|
||||
virtual cocos2d::CCSize getSizeHint(cocos2d::CCNode* on) const = 0;
|
||||
|
||||
void ignoreInvisibleChildren(bool ignore);
|
||||
bool isIgnoreInvisibleChildren() const;
|
||||
|
||||
virtual ~Layout() = default;
|
||||
};
|
||||
|
||||
class GEODE_DLL LayoutOptions : public cocos2d::CCObject {
|
||||
public:
|
||||
virtual ~LayoutOptions() = default;
|
||||
};
|
||||
|
||||
/**
|
||||
* The direction of an AxisLayout
|
||||
*/
|
||||
enum class Axis {
|
||||
Row,
|
||||
Column,
|
||||
};
|
||||
|
||||
/**
|
||||
* Specifies the alignment of something in an AxisLayout
|
||||
*/
|
||||
enum class AxisAlignment {
|
||||
// Align items to the start
|
||||
// |ooo......|
|
||||
Start,
|
||||
// All items are centered
|
||||
// |...ooo...|
|
||||
Center,
|
||||
// Align items to the end
|
||||
// |......ooo|
|
||||
End,
|
||||
// Each item gets the same portion from the layout (disregards gap)
|
||||
// |.o..o..o.|
|
||||
Even,
|
||||
// Space between each item is the same (disregards gap)
|
||||
// |o...o...o|
|
||||
Between,
|
||||
};
|
||||
|
||||
constexpr float AXISLAYOUT_DEFAULT_MIN_SCALE = 0.65f;
|
||||
constexpr int AXISLAYOUT_DEFAULT_PRIORITY = 0;
|
||||
|
||||
/**
|
||||
* Options for controlling the behaviour of individual nodes in an AxisLayout
|
||||
* @example
|
||||
* auto node = CCNode::create();
|
||||
* // this node will have 10 units of spacing between it and the next one
|
||||
* node->setLayoutOptions(
|
||||
* AxisLayoutOptions::create()
|
||||
* ->setNextGap(10.f)
|
||||
* );
|
||||
* someNodeWithALayout->addChild(node);
|
||||
*/
|
||||
class GEODE_DLL AxisLayoutOptions : public LayoutOptions {
|
||||
protected:
|
||||
class Impl;
|
||||
|
||||
std::unique_ptr<Impl> m_impl;
|
||||
|
||||
AxisLayoutOptions();
|
||||
|
||||
public:
|
||||
static AxisLayoutOptions* create();
|
||||
|
||||
virtual ~AxisLayoutOptions();
|
||||
|
||||
std::optional<bool> getAutoScale() const;
|
||||
// @note Use hasExplicitMaxScale to know if the default scale has been overwritten
|
||||
float getMaxScale() const;
|
||||
// @note Use hasExplicitMinScale to know if the default scale has been overwritten
|
||||
float getMinScale() const;
|
||||
bool hasExplicitMaxScale() const;
|
||||
bool hasExplicitMinScale() const;
|
||||
float getRelativeScale() const;
|
||||
std::optional<float> getLength() const;
|
||||
std::optional<float> getPrevGap() const;
|
||||
std::optional<float> getNextGap() const;
|
||||
bool getBreakLine() const;
|
||||
bool getSameLine() const;
|
||||
int getScalePriority() const;
|
||||
std::optional<AxisAlignment> getCrossAxisAlignment() const;
|
||||
|
||||
/**
|
||||
* Set the limits to what the node can be scaled to. Passing `std::nullopt`
|
||||
* uses the parent layout's default min / max scales
|
||||
*/
|
||||
AxisLayoutOptions* setScaleLimits(std::optional<float> min, std::optional<float> max);
|
||||
|
||||
/**
|
||||
* Set the relative scale of this node compared to other nodes if it's
|
||||
* contained in an auto-scaled layout. Default is 1
|
||||
*/
|
||||
AxisLayoutOptions* setRelativeScale(float 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
|
||||
*/
|
||||
AxisLayoutOptions* setAutoScale(std::optional<bool> enabled);
|
||||
|
||||
/**
|
||||
* Set an absolute length for this node. If nullopt, the length will be
|
||||
* dynamically calculated based on content size
|
||||
*/
|
||||
AxisLayoutOptions* setLength(std::optional<float> length);
|
||||
|
||||
/**
|
||||
* Override the default gap in the layout between this node and the
|
||||
* previous one. If nullopt, the default gap of the layout will be used
|
||||
*/
|
||||
AxisLayoutOptions* setPrevGap(std::optional<float> gap);
|
||||
|
||||
/**
|
||||
* Override the default gap in the layout between this node and the next
|
||||
* one. If nullopt, the default gap of the layout will be used
|
||||
*/
|
||||
AxisLayoutOptions* setNextGap(std::optional<float> gap);
|
||||
|
||||
/**
|
||||
* 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* setBreakLine(bool enable);
|
||||
|
||||
/**
|
||||
* If enabled, the node will be forced to be on the same line as the
|
||||
* previous node even if doing this would overflow
|
||||
*/
|
||||
AxisLayoutOptions* setSameLine(bool enable);
|
||||
|
||||
/**
|
||||
* Set the scale priority of this node. Nodes with higher priority will be
|
||||
* scaled down first before nodes with lower priority when an auto-scaled
|
||||
* layout attempts to fit its contents. Default is
|
||||
* AXISLAYOUT_DEFAULT_PRIORITY
|
||||
* @note For optimal performance, the priorities should all be close to
|
||||
* each other with no gaps
|
||||
*/
|
||||
AxisLayoutOptions* setScalePriority(int priority);
|
||||
|
||||
/**
|
||||
* Override the cross axis alignment for this node in the layout
|
||||
*/
|
||||
AxisLayoutOptions* setCrossAxisAlignment(std::optional<AxisAlignment> alignment);
|
||||
};
|
||||
|
||||
/**
|
||||
* 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
|
||||
* RowLayout and ColumnLayout classes function as simple thin wrappers over
|
||||
* AxisLayout. The positioning of individual nodes in the layout can be
|
||||
* further controlled using AxisLayoutOptions
|
||||
* @warning Calculating layouts can get increasingly expensive for large
|
||||
* amounts of child nodes being fit into a small space - while this should
|
||||
* 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
|
||||
* call CCNode::updateLayout every frame for a menu with thousands of children
|
||||
* @example
|
||||
* auto menu = CCMenu::create();
|
||||
* // 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
|
||||
* // aligned to the left. The menu automatically will automatically grow in
|
||||
* // height to fit all the rows
|
||||
* menu->setLayout(
|
||||
* RowLayout::create()
|
||||
* ->setGap(10.f)
|
||||
* ->setGrowCrossAxis(true)
|
||||
* ->setAxisAlignment(AxisAlignment::Start)
|
||||
* );
|
||||
* menu->setContentSize({ 200.f, 0.f });
|
||||
* menu->addChild(...);
|
||||
* menu->updateLayout();
|
||||
*/
|
||||
class GEODE_DLL AxisLayout : public Layout {
|
||||
protected:
|
||||
class Impl;
|
||||
|
||||
std::unique_ptr<Impl> m_impl;
|
||||
|
||||
AxisLayout(Axis);
|
||||
|
||||
public:
|
||||
/**
|
||||
* Create a new AxisLayout. Note that this class is not automatically
|
||||
* managed by default, so you must assign it to a CCNode or manually
|
||||
* manage the memory yourself. See the chainable setters on AxisLayout for
|
||||
* what options you can customize for the layout
|
||||
* @param axis The direction of the layout
|
||||
* @note For convenience, you can use the RowLayout and ColumnLayout
|
||||
* classes, which are just thin wrappers over AxisLayout
|
||||
* @returns Created AxisLayout
|
||||
*/
|
||||
static AxisLayout* create(Axis axis = Axis::Row);
|
||||
|
||||
virtual ~AxisLayout();
|
||||
|
||||
void apply(cocos2d::CCNode* on) override;
|
||||
cocos2d::CCSize getSizeHint(cocos2d::CCNode* on) const override;
|
||||
|
||||
Axis getAxis() const;
|
||||
AxisAlignment getAxisAlignment() const;
|
||||
AxisAlignment getCrossAxisAlignment() const;
|
||||
AxisAlignment getCrossAxisLineAlignment() const;
|
||||
float getGap() const;
|
||||
bool getAxisReverse() const;
|
||||
bool getCrossAxisReverse() const;
|
||||
bool getAutoScale() const;
|
||||
bool getGrowCrossAxis() const;
|
||||
bool getCrossAxisOverflow() const;
|
||||
std::optional<float> getAutoGrowAxis() const;
|
||||
float getDefaultMinScale() const;
|
||||
float getDefaultMaxScale() const;
|
||||
|
||||
AxisLayout* setAxis(Axis axis);
|
||||
/**
|
||||
* Sets where to align the target node's children on the main axis (X-axis
|
||||
* for Row, Y-axis for Column)
|
||||
*/
|
||||
AxisLayout* setAxisAlignment(AxisAlignment align);
|
||||
/**
|
||||
* Sets where to align the target node's children on the cross-axis (Y-axis
|
||||
* for Row, X-axis for Column)
|
||||
*/
|
||||
AxisLayout* setCrossAxisAlignment(AxisAlignment align);
|
||||
/**
|
||||
* Sets where to align the target node's children on the cross-axis for
|
||||
* each row (Y-axis for Row, X-axis for Column)
|
||||
*/
|
||||
AxisLayout* setCrossAxisLineAlignment(AxisAlignment align);
|
||||
/**
|
||||
* The spacing between the children of the node this layout applies to.
|
||||
* Measured as the space between their edges, not centres. Does not apply
|
||||
* on the main / cross axis if their alignment is AxisAlignment::Even
|
||||
*/
|
||||
AxisLayout* setGap(float gap);
|
||||
/**
|
||||
* Whether to reverse the direction of the children in this layout or not
|
||||
*/
|
||||
AxisLayout* setAxisReverse(bool reverse);
|
||||
/**
|
||||
* Whether to reverse the direction of the rows on the cross-axis or not
|
||||
*/
|
||||
AxisLayout* setCrossAxisReverse(bool reverse);
|
||||
/**
|
||||
* 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* setAutoScale(bool enable);
|
||||
/**
|
||||
* If true, if the main axis overflows extra nodes will be placed on new
|
||||
* rows/columns on the cross-axis
|
||||
*/
|
||||
AxisLayout* setGrowCrossAxis(bool expand);
|
||||
/**
|
||||
* If true, the cross-axis content size of the target node will be
|
||||
* automatically adjusted to fit the children
|
||||
*/
|
||||
AxisLayout* setCrossAxisOverflow(bool allow);
|
||||
/**
|
||||
* If not `std::nullopt`, then the axis will be automatically extended to
|
||||
* fit all items in a single row whose minimum length is the specified.
|
||||
* Useful for scrollable list layer contents
|
||||
*/
|
||||
AxisLayout* setAutoGrowAxis(std::optional<float> allowAndMinLength);
|
||||
/**
|
||||
* Set the default minimum/maximum scales for nodes in the layout
|
||||
*/
|
||||
AxisLayout* setDefaultScaleLimits(float min, float max);
|
||||
};
|
||||
|
||||
/**
|
||||
* Simple layout for arranging nodes in a row (horizontal line)
|
||||
*/
|
||||
class GEODE_DLL RowLayout : public AxisLayout {
|
||||
protected:
|
||||
RowLayout();
|
||||
|
||||
public:
|
||||
/**
|
||||
* Create a new RowLayout. See the chainable setters on RowLayout for
|
||||
* what options you can customize for the layout
|
||||
* @returns Created RowLayout
|
||||
*/
|
||||
static RowLayout* create();
|
||||
};
|
||||
|
||||
/**
|
||||
* Simple layout for arranging nodes in a column (vertical line)
|
||||
*/
|
||||
class GEODE_DLL ColumnLayout : public AxisLayout {
|
||||
protected:
|
||||
ColumnLayout();
|
||||
|
||||
public:
|
||||
/**
|
||||
* Create a new ColumnLayout. See the chainable setters on RowLayout for
|
||||
* what options you can customize for the layout
|
||||
* @returns Created ColumnLayout
|
||||
*/
|
||||
static ColumnLayout* create();
|
||||
};
|
||||
|
||||
/**
|
||||
* The relative position of a node to its parent in an AnchorLayout
|
||||
*/
|
||||
enum class Anchor {
|
||||
Center,
|
||||
TopLeft,
|
||||
Top,
|
||||
TopRight,
|
||||
Right,
|
||||
BottomRight,
|
||||
Bottom,
|
||||
BottomLeft,
|
||||
Left,
|
||||
};
|
||||
|
||||
/**
|
||||
* Options for customizing a node's position in an AnchorLayout
|
||||
*/
|
||||
class GEODE_DLL AnchorLayoutOptions : public LayoutOptions {
|
||||
protected:
|
||||
Anchor m_anchor = Anchor::Center;
|
||||
cocos2d::CCPoint m_offset = cocos2d::CCPointZero;
|
||||
|
||||
public:
|
||||
static AnchorLayoutOptions* create();
|
||||
|
||||
Anchor getAnchor() const;
|
||||
cocos2d::CCPoint getOffset() const;
|
||||
|
||||
AnchorLayoutOptions* setAnchor(Anchor anchor);
|
||||
AnchorLayoutOptions* setOffset(cocos2d::CCPoint const& offset);
|
||||
};
|
||||
|
||||
/**
|
||||
* A layout for positioning nodes at specific positions relative to their
|
||||
* parent's content size. See `Anchor` for available anchoring options. Useful
|
||||
* for example for popups, where a popup using `AnchorLayout` can be
|
||||
* automatically resized without needing to manually shuffle nodes around
|
||||
*/
|
||||
class GEODE_DLL AnchorLayout : public Layout {
|
||||
public:
|
||||
static AnchorLayout* create();
|
||||
|
||||
void apply(cocos2d::CCNode* on) override;
|
||||
cocos2d::CCSize getSizeHint(cocos2d::CCNode* on) const override;
|
||||
|
||||
/**
|
||||
* Get a position according to anchoring rules, with the same algorithm as
|
||||
* `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
|
||||
* @returns A position in `in` for the anchored and offsetted location
|
||||
*/
|
||||
static cocos2d::CCPoint getAnchoredPosition(cocos2d::CCNode* in, Anchor anchor, cocos2d::CCPoint const& offset);
|
||||
};
|
||||
|
||||
/**
|
||||
* A layout for automatically copying the content size of a node to other nodes.
|
||||
* Basically main use case is for FLAlertLayers (setting the size of the
|
||||
* background and `m_buttonMenu` based on `m_mainLayer`)
|
||||
*/
|
||||
class GEODE_DLL CopySizeLayout : public AnchorLayout {
|
||||
protected:
|
||||
cocos2d::CCArray* m_targets;
|
||||
|
||||
public:
|
||||
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)
|
||||
|
||||
}
|
|
@ -11,13 +11,13 @@ namespace geode {
|
|||
class GEODE_DLL MDPopup :
|
||||
public Popup<
|
||||
std::string const&, std::string const&, char const*, char const*,
|
||||
utils::MiniFunction<void(bool)>> {
|
||||
std::function<void(bool)>> {
|
||||
protected:
|
||||
utils::MiniFunction<void(bool)> m_onClick = nullptr;
|
||||
std::function<void(bool)> m_onClick = nullptr;
|
||||
|
||||
bool setup(
|
||||
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;
|
||||
|
||||
void onBtn(CCObject*);
|
||||
|
@ -27,7 +27,7 @@ namespace geode {
|
|||
public:
|
||||
static MDPopup* create(
|
||||
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
|
||||
);
|
||||
};
|
||||
}
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
|
||||
#include <Geode/binding/CCMenuItemSpriteExtra.hpp>
|
||||
#include <Geode/binding/FLAlertLayer.hpp>
|
||||
#include <Geode/utils/MiniFunction.hpp>
|
||||
#include <Geode/utils/cocos.hpp>
|
||||
#include <Geode/ui/Layout.hpp>
|
||||
|
||||
namespace geode {
|
||||
template <class... InitArgs>
|
||||
|
@ -51,7 +51,7 @@ namespace geode {
|
|||
}
|
||||
|
||||
public:
|
||||
ListenerResult handle(utils::MiniFunction<Callback> fn, CloseEvent* event) {
|
||||
ListenerResult handle(std::function<Callback> fn, CloseEvent* event) {
|
||||
if (event->getPopup() == m_impl->popup) {
|
||||
fn(event);
|
||||
}
|
||||
|
@ -104,7 +104,7 @@ namespace geode {
|
|||
m_mainLayer->setPosition(winSize / 2);
|
||||
m_mainLayer->setContentSize(m_size);
|
||||
m_mainLayer->setLayout(
|
||||
cocos2d::CopySizeLayout::create()
|
||||
geode::CopySizeLayout::create()
|
||||
->add(m_buttonMenu)
|
||||
->add(m_bgSprite)
|
||||
);
|
||||
|
@ -119,7 +119,7 @@ namespace geode {
|
|||
closeSpr, this, (cocos2d::SEL_MenuHandler)(&Popup::onClose)
|
||||
);
|
||||
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 {
|
||||
m_closeBtn->setPosition(-m_size.width / 2 + 3.f, m_size.height / 2 - 3.f);
|
||||
|
@ -137,14 +137,6 @@ namespace geode {
|
|||
}
|
||||
|
||||
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
|
||||
* `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->setZOrder(2);
|
||||
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 {
|
||||
auto winSize = cocos2d::CCDirector::get()->getWinSize();
|
||||
|
@ -221,21 +213,21 @@ namespace geode {
|
|||
|
||||
GEODE_DLL FLAlertLayer* createQuickPopup(
|
||||
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(
|
||||
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(
|
||||
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(
|
||||
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
|
||||
);
|
||||
}
|
||||
|
|
|
@ -8,6 +8,8 @@
|
|||
#include <Geode/utils/cocos.hpp>
|
||||
|
||||
namespace geode {
|
||||
struct SceneSwitch;
|
||||
|
||||
class GEODE_DLL SceneManager final {
|
||||
protected:
|
||||
std::vector<Ref<cocos2d::CCNode>> m_persistedNodes;
|
||||
|
@ -15,6 +17,10 @@ namespace geode {
|
|||
|
||||
virtual ~SceneManager();
|
||||
|
||||
void willSwitchToScene(cocos2d::CCScene* scene);
|
||||
|
||||
friend struct SceneSwitch;
|
||||
|
||||
public:
|
||||
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.
|
||||
*/
|
||||
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);
|
||||
};
|
||||
}
|
||||
|
|
|
@ -14,13 +14,13 @@ namespace geode {
|
|||
protected:
|
||||
std::vector<T> m_list;
|
||||
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;
|
||||
CCMenuItemSpriteExtra* m_prevBtn;
|
||||
CCMenuItemSpriteExtra* m_nextBtn;
|
||||
|
||||
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;
|
||||
|
||||
|
@ -94,7 +94,7 @@ namespace geode {
|
|||
|
||||
public:
|
||||
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();
|
||||
if (ret->init(width, list, onChange)) {
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
#pragma once
|
||||
|
||||
#include "CCNode.h"
|
||||
#include <cocos2d.h>
|
||||
|
||||
NS_CC_BEGIN
|
||||
namespace geode {
|
||||
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable: 4275)
|
||||
|
@ -22,7 +22,7 @@ NS_CC_BEGIN
|
|||
* @note If you want to specify a minimum width for a SpacerNode, add
|
||||
* AxisLayoutOptions for it and use setLength
|
||||
*/
|
||||
class GEODE_DLL SpacerNode : public CCNode {
|
||||
class GEODE_DLL SpacerNode : public cocos2d::CCNode {
|
||||
protected:
|
||||
size_t m_grow;
|
||||
|
||||
|
@ -61,9 +61,9 @@ public:
|
|||
*/
|
||||
class GEODE_DLL SpacerNodeChild : public SpacerNode {
|
||||
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:
|
||||
/**
|
||||
|
@ -73,11 +73,11 @@ public:
|
|||
* factors (akin to CSS flew grow)
|
||||
* @param grow The grow factor for this node. Default is 1
|
||||
*/
|
||||
static SpacerNodeChild* create(CCNode* child, size_t grow = 1);
|
||||
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)
|
||||
|
||||
NS_CC_END
|
||||
}
|
|
@ -24,34 +24,34 @@ namespace geode {
|
|||
*/
|
||||
class GEODE_DLL SimpleTextArea : public cocos2d::CCNode {
|
||||
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, const float scale, const float width);
|
||||
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, float scale, float width);
|
||||
|
||||
void setFont(const std::string& font);
|
||||
std::string getFont();
|
||||
void setColor(const cocos2d::ccColor4B& color);
|
||||
cocos2d::ccColor4B getColor();
|
||||
void setAlignment(const cocos2d::CCTextAlignment alignment);
|
||||
void setAlignment(cocos2d::CCTextAlignment alignment);
|
||||
cocos2d::CCTextAlignment getAlignment();
|
||||
void setWrappingMode(const WrappingMode mode);
|
||||
void setWrappingMode(WrappingMode mode);
|
||||
WrappingMode getWrappingMode();
|
||||
void setText(const std::string& text);
|
||||
std::string getText();
|
||||
void setMaxLines(const size_t maxLines);
|
||||
void setMaxLines(size_t maxLines);
|
||||
size_t getMaxLines();
|
||||
void setWidth(const float width);
|
||||
void setWidth(float width);
|
||||
float getWidth();
|
||||
void setScale(const float scale) override;
|
||||
void setScale(float scale) override;
|
||||
float getScale() override;
|
||||
void setLinePadding(const float padding);
|
||||
void setLinePadding(float padding);
|
||||
float getLinePadding();
|
||||
std::vector<cocos2d::CCLabelBMFont*> getLines();
|
||||
float getHeight();
|
||||
float getLineHeight();
|
||||
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_artificialWidth = false;
|
||||
|
@ -67,9 +67,9 @@ namespace geode {
|
|||
float m_lineHeight = 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);
|
||||
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 updateLinesWordWrap(bool spaceWrap);
|
||||
void updateLinesCutoffWrap();
|
||||
|
|
|
@ -122,7 +122,7 @@ namespace geode {
|
|||
* to distinguish between bold, italic and
|
||||
* regular text.
|
||||
*/
|
||||
using Font = utils::MiniFunction<Label(int)>;
|
||||
using Font = std::function<Label(int)>;
|
||||
|
||||
protected:
|
||||
cocos2d::CCPoint m_origin = cocos2d::CCPointZero;
|
||||
|
|
|
@ -21,7 +21,7 @@ namespace geode {
|
|||
std::string m_id;
|
||||
|
||||
public:
|
||||
ListenerResult handle(utils::MiniFunction<Callback> fn, ColorProvidedEvent* event);
|
||||
ListenerResult handle(std::function<Callback> fn, ColorProvidedEvent* event);
|
||||
|
||||
ColorProvidedFilter(std::string const& id);
|
||||
};
|
||||
|
|
|
@ -4,8 +4,7 @@
|
|||
#include "../loader/Log.hpp"
|
||||
#include <set>
|
||||
#include <variant>
|
||||
#include <Geode/utils/MiniFunction.hpp>
|
||||
#include <Geode/utils/Result.hpp>
|
||||
#include <Geode/Result.hpp>
|
||||
|
||||
namespace geode {
|
||||
struct JsonChecker;
|
||||
|
@ -73,231 +72,11 @@ namespace geode {
|
|||
}
|
||||
|
||||
template <class T>
|
||||
using JsonValueValidator = utils::MiniFunction<bool(T const&)>;
|
||||
using JsonValueValidator = std::function<bool(T const&)>;
|
||||
|
||||
struct JsonMaybeObject;
|
||||
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 {
|
||||
protected:
|
||||
class Impl;
|
||||
|
@ -325,21 +104,14 @@ namespace geode {
|
|||
return this->getJSONRef();
|
||||
}
|
||||
else {
|
||||
try {
|
||||
if (this->getJSONRef().is<T>()) {
|
||||
return this->getJSONRef().as<T>();
|
||||
}
|
||||
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);
|
||||
auto res = this->getJSONRef().as<T>();
|
||||
if (res) {
|
||||
return res.unwrap();
|
||||
}
|
||||
this->setError(
|
||||
"unexpected type {}",
|
||||
this->matJsonTypeToString(this->getJSONRef().type())
|
||||
);
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
@ -451,6 +223,13 @@ namespace geode {
|
|||
* @returns The key, which is a no-op value if it didn't exist
|
||||
*/
|
||||
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
|
||||
* value is an object. If the key doesn't exist, sets an error and
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
};
|
||||
}
|
|
@ -1,9 +1,11 @@
|
|||
#pragma once
|
||||
#include <Geode/modify/Modify.hpp>
|
||||
#include "cocos.hpp"
|
||||
|
||||
namespace geode::node_ids {
|
||||
using namespace cocos2d;
|
||||
|
||||
static constexpr int32_t GEODE_ID_PRIORITY = 0x100000;
|
||||
static constexpr int32_t GEODE_ID_PRIORITY = Priority::VeryEarlyPost;
|
||||
|
||||
template <class T = CCNode>
|
||||
requires std::is_base_of_v<CCNode, T>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include "../loader/Hook.hpp"
|
||||
#include "Result.hpp"
|
||||
#include <Geode/Result.hpp>
|
||||
|
||||
namespace geode {
|
||||
namespace hook {
|
||||
|
|
|
@ -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)
|
||||
}
|
|
@ -1,13 +1,21 @@
|
|||
#pragma once
|
||||
|
||||
#include "general.hpp"
|
||||
#include "MiniFunction.hpp"
|
||||
#include "../loader/Event.hpp"
|
||||
#include "../loader/Loader.hpp"
|
||||
#include <mutex>
|
||||
#include <string_view>
|
||||
#include <coroutine>
|
||||
|
||||
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
|
||||
* unknown point in the future. Tasks can report their progress, and will
|
||||
|
@ -87,7 +95,7 @@ namespace geode {
|
|||
enum class Status {
|
||||
/// The task is still running or waiting to start
|
||||
Pending,
|
||||
/// The task has succesfully finished
|
||||
/// The task has successfully finished
|
||||
Finished,
|
||||
/// The task has been cancelled
|
||||
Cancelled,
|
||||
|
@ -141,7 +149,7 @@ namespace geode {
|
|||
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -153,8 +161,14 @@ namespace geode {
|
|||
template <std::move_constructible T2, std::move_constructible P2>
|
||||
friend class Task;
|
||||
|
||||
template <class, class>
|
||||
friend struct geode_internal::TaskPromise;
|
||||
|
||||
template <class, class>
|
||||
friend struct geode_internal::TaskAwaiter;
|
||||
|
||||
public:
|
||||
Handle(PrivateMarker, std::string_view const name) : m_name(name) {}
|
||||
Handle(PrivateMarker, std::string_view name) : m_name(name) {}
|
||||
~Handle() {
|
||||
// If this Task was still pending when the Handle was destroyed,
|
||||
// it can no longer be listened to so just cancel and cleanup
|
||||
|
@ -249,11 +263,11 @@ namespace geode {
|
|||
|
||||
using Value = T;
|
||||
using Progress = P;
|
||||
using PostResult = utils::MiniFunction<void(Result)>;
|
||||
using PostProgress = utils::MiniFunction<void(P)>;
|
||||
using HasBeenCancelled = utils::MiniFunction<bool()>;
|
||||
using Run = utils::MiniFunction<Result(PostProgress, HasBeenCancelled)>;
|
||||
using RunWithCallback = utils::MiniFunction<void(PostResult, PostProgress, HasBeenCancelled)>;
|
||||
using PostResult = std::function<void(Result&&)>;
|
||||
using PostProgress = std::function<void(P)>;
|
||||
using HasBeenCancelled = std::function<bool()>;
|
||||
using Run = std::function<Result(PostProgress, HasBeenCancelled)>;
|
||||
using RunWithCallback = std::function<void(PostResult, PostProgress, HasBeenCancelled)>;
|
||||
|
||||
using Callback = void(Event*);
|
||||
|
||||
|
@ -308,6 +322,12 @@ namespace geode {
|
|||
template <std::move_constructible T2, std::move_constructible P2>
|
||||
friend class Task;
|
||||
|
||||
template <class, class>
|
||||
friend struct geode_internal::TaskPromise;
|
||||
|
||||
template <class, class>
|
||||
friend struct geode_internal::TaskAwaiter;
|
||||
|
||||
public:
|
||||
// Allow default-construction
|
||||
Task() : m_handle(nullptr) {}
|
||||
|
@ -394,7 +414,7 @@ namespace geode {
|
|||
* Create a new Task that is immediately cancelled
|
||||
* @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));
|
||||
Task::cancel(task.m_handle);
|
||||
return task;
|
||||
|
@ -405,7 +425,7 @@ namespace geode {
|
|||
* @param value The value the Task shall be finished with
|
||||
* @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));
|
||||
Task::finish(task.m_handle, std::move(value));
|
||||
return task;
|
||||
|
@ -417,7 +437,7 @@ namespace geode {
|
|||
* function MUST be synchronous - Task creates the thread for you!
|
||||
* @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));
|
||||
std::thread([handle = std::weak_ptr(task.m_handle), name = std::string(name), body = std::move(body)] {
|
||||
utils::thread::setName(fmt::format("Task '{}'", name));
|
||||
|
@ -450,7 +470,7 @@ namespace geode {
|
|||
* calls will always be ignored
|
||||
* @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));
|
||||
std::thread([handle = std::weak_ptr(task.m_handle), name = std::string(name), body = std::move(body)] {
|
||||
utils::thread::setName(fmt::format("Task '{}'", name));
|
||||
|
@ -485,7 +505,7 @@ namespace geode {
|
|||
* were cancelled!
|
||||
*/
|
||||
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>;
|
||||
|
||||
// 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
|
||||
*/
|
||||
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 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
|
||||
* the mapped task is appended to the end
|
||||
*/ 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);
|
||||
}
|
||||
|
||||
|
@ -670,7 +690,7 @@ namespace geode {
|
|||
* the mapped task is appended to the end
|
||||
*/ template <class ResultMapper>
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -752,7 +772,94 @@ namespace geode {
|
|||
this->listen(std::move(onResult), [](auto const&) {}, [] {});
|
||||
}
|
||||
|
||||
ListenerResult handle(utils::MiniFunction<Callback> fn, Event* e) {
|
||||
/**
|
||||
* Create a new Task that listens to this Task and maps the values using
|
||||
* the provided function. The new Task will only start when this Task finishes.
|
||||
* @param mapper Function that makes a new task given the finished value of this task.
|
||||
* The function signature should be `Task<NewType, NewProgress>(T*)`, and it will be executed
|
||||
* on the main thread.
|
||||
* @param name The name of the Task; used for debugging.
|
||||
* @return The new Task that will run when this Task finishes.
|
||||
* @note Progress from this task is not sent through, only progress from the new task is.
|
||||
*/
|
||||
template <std::invocable<T*> Mapper>
|
||||
auto chain(Mapper mapper, std::string_view name = "<Chained Task>") const -> decltype(mapper(std::declval<T*>())) {
|
||||
using NewTask = decltype(mapper(std::declval<T*>()));
|
||||
using NewType = typename NewTask::Value;
|
||||
using NewProgress = typename NewTask::Progress;
|
||||
|
||||
std::unique_lock<std::recursive_mutex> lock(m_handle->m_mutex);
|
||||
|
||||
if (m_handle->m_status == Status::Cancelled) {
|
||||
// if the current task has been cancelled already, make an immediate cancelled task
|
||||
return NewTask::cancelled();
|
||||
}
|
||||
else if (m_handle->m_status == Status::Finished) {
|
||||
// if the current task is already done, we can just call the mapper directly
|
||||
return mapper(&*m_handle->m_resultValue);
|
||||
}
|
||||
|
||||
// otherwise, make a wrapper task that waits for the current task to finish,
|
||||
// and then runs the mapper on the result. this new task will also wait for the task
|
||||
// created by the mapper to finish, and will just forward the values through.
|
||||
// do this because we cant really change the handle of the task we already returned
|
||||
|
||||
NewTask task = NewTask::Handle::create(fmt::format("{} <- {}", name, m_handle->m_name));
|
||||
|
||||
task.m_handle->m_extraData = std::make_unique<typename NewTask::Handle::ExtraData>(
|
||||
// make the first event listener that waits for the current task
|
||||
static_cast<void*>(new EventListener<Task>(
|
||||
[handle = std::weak_ptr(task.m_handle), mapper = std::move(mapper)](Event* event) mutable {
|
||||
if (auto v = event->getValue()) {
|
||||
auto newInnerTask = mapper(v);
|
||||
// this is scary.. but it doesn't seem to crash lol
|
||||
handle.lock()->m_extraData = std::make_unique<typename NewTask::Handle::ExtraData>(
|
||||
// make the second event listener that waits for the mapper's task
|
||||
// and just forwards everything through
|
||||
static_cast<void*>(new EventListener<NewTask>(
|
||||
[handle](typename NewTask::Event* event) mutable {
|
||||
if (auto v = event->getValue()) {
|
||||
NewTask::finish(handle.lock(), std::move(*v));
|
||||
}
|
||||
else if (auto p = event->getProgress()) {
|
||||
NewTask::progress(handle.lock(), std::move(*p));
|
||||
}
|
||||
else if (event->isCancelled()) {
|
||||
NewTask::cancel(handle.lock());
|
||||
}
|
||||
},
|
||||
std::move(newInnerTask)
|
||||
)),
|
||||
+[](void* ptr) {
|
||||
delete static_cast<EventListener<NewTask>*>(ptr);
|
||||
},
|
||||
+[](void* ptr) {
|
||||
static_cast<EventListener<NewTask>*>(ptr)->getFilter().cancel();
|
||||
}
|
||||
);
|
||||
}
|
||||
else if (auto p = event->getProgress()) {
|
||||
// no guarantee P and NewProgress are compatible
|
||||
// nor does it seem like the intended behavior?
|
||||
// TODO: maybe add a mapper for progress?
|
||||
}
|
||||
else if (event->isCancelled()) {
|
||||
NewTask::cancel(handle.lock());
|
||||
}
|
||||
},
|
||||
*this
|
||||
)),
|
||||
+[](void* ptr) {
|
||||
delete static_cast<EventListener<Task>*>(ptr);
|
||||
},
|
||||
+[](void* ptr) {
|
||||
static_cast<EventListener<Task>*>(ptr)->getFilter().cancel();
|
||||
}
|
||||
);
|
||||
return task;
|
||||
}
|
||||
|
||||
ListenerResult handle(std::function<Callback> fn, Event* e) {
|
||||
if (e->m_handle == m_handle && (!e->m_for || e->m_for == m_listener)) {
|
||||
fn(e);
|
||||
}
|
||||
|
@ -797,3 +904,132 @@ namespace geode {
|
|||
|
||||
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>;
|
||||
};
|
||||
|
|
|
@ -4,7 +4,8 @@
|
|||
#include <string_view>
|
||||
#include <matjson.hpp>
|
||||
#include <tuple>
|
||||
#include "../utils/Result.hpp"
|
||||
#include <Geode/Result.hpp>
|
||||
#include <fmt/format.h>
|
||||
|
||||
namespace geode {
|
||||
enum class VersionCompare {
|
||||
|
@ -186,8 +187,6 @@ namespace geode {
|
|||
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 toNonVString(bool includeTag = true) const;
|
||||
|
||||
|
@ -258,25 +257,15 @@ namespace geode {
|
|||
template <class V>
|
||||
requires std::is_same_v<V, geode::VersionInfo> || std::is_same_v<V, geode::ComparableVersionInfo>
|
||||
struct matjson::Serialize<V> {
|
||||
static matjson::Value to_json(V const& info) {
|
||||
return info.toString();
|
||||
static geode::Result<V, std::string> fromJson(Value const& value) {
|
||||
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) {
|
||||
if (json.is_string()) {
|
||||
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();
|
||||
static Value toJson(V const& value) {
|
||||
return Value(value.toNonVString());
|
||||
}
|
||||
};
|
||||
|
|
|
@ -10,22 +10,21 @@
|
|||
#include "../loader/Event.hpp"
|
||||
#include <Geode/binding/CCMenuItemSpriteExtra.hpp>
|
||||
#include <Geode/binding/CCMenuItemToggler.hpp>
|
||||
#include "MiniFunction.hpp"
|
||||
#include "../ui/Layout.hpp"
|
||||
#include "../ui/SpacerNode.hpp"
|
||||
|
||||
// support converting ccColor3B / ccColor4B to / from json
|
||||
|
||||
template <>
|
||||
struct matjson::Serialize<cocos2d::ccColor3B> {
|
||||
static matjson::Value GEODE_DLL to_json(cocos2d::ccColor3B const& color);
|
||||
static cocos2d::ccColor3B GEODE_DLL from_json(matjson::Value const& color);
|
||||
static bool GEODE_DLL is_json(matjson::Value const& json);
|
||||
static geode::Result<cocos2d::ccColor3B> GEODE_DLL fromJson(Value const& value);
|
||||
static Value GEODE_DLL toJson(cocos2d::ccColor3B const& value);
|
||||
};
|
||||
|
||||
template <>
|
||||
struct matjson::Serialize<cocos2d::ccColor4B> {
|
||||
static matjson::Value GEODE_DLL to_json(cocos2d::ccColor4B const& color);
|
||||
static cocos2d::ccColor4B GEODE_DLL from_json(matjson::Value const& color);
|
||||
static bool GEODE_DLL is_json(matjson::Value const& json);
|
||||
static geode::Result<cocos2d::ccColor4B> GEODE_DLL fromJson(Value const& value);
|
||||
static Value GEODE_DLL toJson(cocos2d::ccColor4B const& value);
|
||||
};
|
||||
|
||||
// operators for CC geometry
|
||||
|
@ -615,18 +614,6 @@ namespace geode::cocos {
|
|||
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
|
||||
* nullptr. Syntactic sugar function
|
||||
|
@ -670,7 +657,7 @@ namespace geode::cocos {
|
|||
*/
|
||||
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
|
||||
|
@ -738,7 +725,7 @@ namespace geode::cocos {
|
|||
* there is none
|
||||
*/
|
||||
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)))
|
||||
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};
|
||||
}
|
||||
|
||||
[[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
|
||||
* a leading '#'
|
||||
|
@ -887,7 +850,7 @@ namespace geode::cocos {
|
|||
* @param permissive If true, strings like "f" are considered valid
|
||||
* representations of the color white. Useful for UIs that allow entering
|
||||
* a hex color. Empty strings evaluate to pure white
|
||||
* @returns A ccColor3B if it could be succesfully parsed, or an error
|
||||
* @returns A ccColor3B if it could be successfully parsed, or an error
|
||||
* indicating the failure reason
|
||||
*/
|
||||
GEODE_DLL Result<cocos2d::ccColor3B> cc3bFromHexString(std::string const& hexValue, bool permissive = false);
|
||||
|
@ -900,7 +863,7 @@ namespace geode::cocos {
|
|||
* @param permissive If true, strings like "f" are considered valid
|
||||
* representations of the color white. Useful for UIs that allow entering
|
||||
* a hex color. Empty strings evaluate to pure white
|
||||
* @returns A ccColor4B if it could be succesfully parsed, or an error
|
||||
* @returns A ccColor4B if it could be successfully parsed, or an error
|
||||
* indicating the failure reason
|
||||
*/
|
||||
GEODE_DLL Result<cocos2d::ccColor4B> cc4bFromHexString(std::string const& hexValue, bool requireAlpha = false, bool permissive = false);
|
||||
|
@ -916,7 +879,7 @@ namespace geode::cocos {
|
|||
}
|
||||
|
||||
template <typename T, typename C, typename = std::enable_if_t<std::is_pointer_v<C>>>
|
||||
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());
|
||||
for (auto const& item : vec)
|
||||
res->addObject(convFunc(item));
|
||||
|
@ -943,7 +906,7 @@ namespace geode::cocos {
|
|||
template <
|
||||
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>>>
|
||||
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();
|
||||
for (auto const& [key, value] : map)
|
||||
res->setObject(value, convFunc(key));
|
||||
|
@ -969,10 +932,10 @@ namespace std {
|
|||
};
|
||||
|
||||
template <typename T>
|
||||
struct std::hash<geode::WeakRef<T>> {
|
||||
struct hash<geode::WeakRef<T>> {
|
||||
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
|
||||
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>
|
||||
class LambdaCallback : public cocos2d::CCObject {
|
||||
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();
|
||||
if (ret->init(std::forward<std::remove_reference_t<decltype(callback)>>(callback))) {
|
||||
if (ret->init(std::move(callback))) {
|
||||
ret->autorelease();
|
||||
return ret;
|
||||
}
|
||||
|
@ -1228,8 +1191,8 @@ namespace geode::cocos {
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
bool init(utils::MiniFunction<void(Node*)>&& callback) {
|
||||
m_callback = std::forward<std::remove_reference_t<decltype(callback)>>(callback);
|
||||
bool init(std::function<void(Node*)> callback) {
|
||||
m_callback = std::move(callback);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1240,20 +1203,20 @@ namespace geode::cocos {
|
|||
|
||||
public:
|
||||
static cocos2d::CCMenuItem* create(
|
||||
utils::MiniFunction<void(cocos2d::CCMenuItem*)>&& callback
|
||||
std::function<void(cocos2d::CCMenuItem*)> callback
|
||||
) {
|
||||
auto item = cocos2d::CCMenuItem::create();
|
||||
assignCallback(item, std::forward<std::remove_reference_t<decltype(callback)>>(callback));
|
||||
assignCallback(item, std::move(callback));
|
||||
return item;
|
||||
}
|
||||
|
||||
static cocos2d::CCMenuItemSprite* createSprite(
|
||||
cocos2d::CCNode* normalSprite,
|
||||
cocos2d::CCNode* selectedSprite,
|
||||
utils::MiniFunction<void(cocos2d::CCMenuItemSprite*)>&& callback
|
||||
std::function<void(cocos2d::CCMenuItemSprite*)> callback
|
||||
) {
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -1261,57 +1224,57 @@ namespace geode::cocos {
|
|||
cocos2d::CCNode* normalSprite,
|
||||
cocos2d::CCNode* selectedSprite,
|
||||
cocos2d::CCNode* disabledSprite,
|
||||
utils::MiniFunction<void(cocos2d::CCMenuItemSprite*)>&& callback
|
||||
std::function<void(cocos2d::CCMenuItemSprite*)> callback
|
||||
) {
|
||||
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;
|
||||
}
|
||||
|
||||
static CCMenuItemSpriteExtra* createSpriteExtra(
|
||||
cocos2d::CCNode* normalSprite,
|
||||
utils::MiniFunction<void(CCMenuItemSpriteExtra*)>&& callback
|
||||
std::function<void(CCMenuItemSpriteExtra*)> callback
|
||||
) {
|
||||
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;
|
||||
}
|
||||
|
||||
static CCMenuItemSpriteExtra* createSpriteExtraWithFilename(
|
||||
std::string_view normalSpriteName,
|
||||
float scale,
|
||||
utils::MiniFunction<void(CCMenuItemSpriteExtra*)>&& callback
|
||||
std::function<void(CCMenuItemSpriteExtra*)> callback
|
||||
) {
|
||||
auto sprite = cocos2d::CCSprite::create(normalSpriteName.data());
|
||||
sprite->setScale(scale);
|
||||
|
||||
return createSpriteExtra(sprite, std::forward<std::remove_reference_t<decltype(callback)>>(callback));
|
||||
return createSpriteExtra(sprite, std::move(callback));
|
||||
}
|
||||
|
||||
static CCMenuItemSpriteExtra* createSpriteExtraWithFrameName(
|
||||
std::string_view normalSpriteName,
|
||||
float scale,
|
||||
utils::MiniFunction<void(CCMenuItemSpriteExtra*)>&& callback
|
||||
std::function<void(CCMenuItemSpriteExtra*)> callback
|
||||
) {
|
||||
auto sprite = cocos2d::CCSprite::createWithSpriteFrameName(normalSpriteName.data());
|
||||
sprite->setScale(scale);
|
||||
|
||||
return createSpriteExtra(sprite, std::forward<std::remove_reference_t<decltype(callback)>>(callback));
|
||||
return createSpriteExtra(sprite, std::move(callback));
|
||||
}
|
||||
|
||||
static CCMenuItemToggler* createToggler(
|
||||
cocos2d::CCNode* onSprite,
|
||||
cocos2d::CCNode* offSprite,
|
||||
utils::MiniFunction<void(CCMenuItemToggler*)>&& callback
|
||||
std::function<void(CCMenuItemToggler*)> callback
|
||||
) {
|
||||
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;
|
||||
}
|
||||
|
||||
static CCMenuItemToggler* createTogglerWithStandardSprites(
|
||||
float scale,
|
||||
utils::MiniFunction<void(CCMenuItemToggler*)>&& callback
|
||||
std::function<void(CCMenuItemToggler*)> callback
|
||||
) {
|
||||
auto offSprite = cocos2d::CCSprite::createWithSpriteFrameName("GJ_checkOff_001.png");
|
||||
auto onSprite = cocos2d::CCSprite::createWithSpriteFrameName("GJ_checkOn_001.png");
|
||||
|
@ -1319,14 +1282,14 @@ namespace geode::cocos {
|
|||
offSprite->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(
|
||||
std::string_view onSpriteName,
|
||||
std::string_view offSpriteName,
|
||||
float scale,
|
||||
utils::MiniFunction<void(CCMenuItemToggler*)>&& callback
|
||||
std::function<void(CCMenuItemToggler*)> callback
|
||||
) {
|
||||
auto offSprite = cocos2d::CCSprite::create(offSpriteName.data());
|
||||
auto onSprite = cocos2d::CCSprite::create(onSpriteName.data());
|
||||
|
@ -1334,14 +1297,14 @@ namespace geode::cocos {
|
|||
offSprite->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(
|
||||
std::string_view onSpriteName,
|
||||
std::string_view offSpriteName,
|
||||
float scale,
|
||||
utils::MiniFunction<void(CCMenuItemToggler*)>&& callback
|
||||
std::function<void(CCMenuItemToggler*)> callback
|
||||
) {
|
||||
auto offSprite = cocos2d::CCSprite::createWithSpriteFrameName(offSpriteName.data());
|
||||
auto onSprite = cocos2d::CCSprite::createWithSpriteFrameName(onSpriteName.data());
|
||||
|
@ -1349,15 +1312,15 @@ namespace geode::cocos {
|
|||
offSprite->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>
|
||||
static void assignCallback(
|
||||
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->setUserObject("lambda-callback", lambda);
|
||||
}
|
||||
|
@ -1368,15 +1331,13 @@ namespace geode::cocos {
|
|||
class CallFuncExtImpl : public cocos2d::CCActionInstant {
|
||||
public:
|
||||
static CallFuncExtImpl* create(const F& func) {
|
||||
auto ret = new CallFuncExtImpl;
|
||||
ret->m_func = func;
|
||||
auto ret = new CallFuncExtImpl(func);
|
||||
ret->autorelease();
|
||||
return ret;
|
||||
}
|
||||
|
||||
static CallFuncExtImpl* create(F&& func) {
|
||||
auto ret = new CallFuncExtImpl;
|
||||
ret->m_func = std::move(func);
|
||||
auto ret = new CallFuncExtImpl(std::move(func));
|
||||
ret->autorelease();
|
||||
return ret;
|
||||
}
|
||||
|
@ -1384,8 +1345,17 @@ namespace geode::cocos {
|
|||
private:
|
||||
F m_func;
|
||||
|
||||
// F may not be default-constructible
|
||||
CallFuncExtImpl(F&& func) : m_func(std::move(func)) {}
|
||||
CallFuncExtImpl(F const& func) : m_func(func) {}
|
||||
|
||||
void update(float) override {
|
||||
if (m_func) this->m_func();
|
||||
// Make sure any `std::function`s are valid
|
||||
if constexpr (requires { static_cast<bool>(m_func); }) {
|
||||
if (m_func) m_func();
|
||||
} else {
|
||||
m_func();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include "Result.hpp"
|
||||
#include <Geode/Result.hpp>
|
||||
#include "general.hpp"
|
||||
#include "../loader/Event.hpp"
|
||||
#include "Task.hpp"
|
||||
|
@ -13,14 +13,15 @@
|
|||
|
||||
template <>
|
||||
struct matjson::Serialize<std::filesystem::path> {
|
||||
static matjson::Value to_json(std::filesystem::path const& path) {
|
||||
return path.string();
|
||||
static geode::Result<std::filesystem::path, std::string> fromJson(Value const& value)
|
||||
{
|
||||
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 bool is_json(matjson::Value const& value) {
|
||||
return value.is_string();
|
||||
|
||||
static Value toJson(std::filesystem::path const& value)
|
||||
{
|
||||
return Value(value.string());
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -32,10 +33,7 @@ namespace geode::utils::file {
|
|||
template <class T>
|
||||
Result<T> readFromJson(std::filesystem::path const& file) {
|
||||
GEODE_UNWRAP_INTO(auto json, readJson(file));
|
||||
if (!json.is<T>()) {
|
||||
return Err("JSON is not of type {}", typeid(T).name());
|
||||
}
|
||||
return Ok(json.as<T>());
|
||||
return json.as<T>();
|
||||
}
|
||||
|
||||
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
|
||||
*/
|
||||
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(
|
||||
utils::MiniFunction<void(uint32_t, uint32_t)> progressCallback,
|
||||
std::function<void(uint32_t, uint32_t)> progressCallback,
|
||||
Path const& from,
|
||||
Path const& to,
|
||||
bool deleteZipAfter = false
|
||||
|
@ -281,7 +279,7 @@ namespace geode::utils::file {
|
|||
public:
|
||||
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);
|
||||
};
|
||||
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
#pragma once
|
||||
|
||||
#include "Result.hpp"
|
||||
#include <Geode/Result.hpp>
|
||||
|
||||
#include "../DefaultInclude.hpp"
|
||||
#include <chrono>
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <filesystem>
|
||||
|
@ -13,19 +12,11 @@
|
|||
#include <charconv>
|
||||
#include <clocale>
|
||||
#include <type_traits>
|
||||
#include <fmt/format.h>
|
||||
|
||||
namespace geode {
|
||||
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>
|
||||
ByteVector toBytes(T const& a) {
|
||||
ByteVector out;
|
||||
|
@ -72,9 +63,7 @@ namespace geode {
|
|||
|
||||
template <typename T>
|
||||
std::string intToHex(T i) {
|
||||
std::stringstream stream;
|
||||
stream << std::showbase << std::setbase(16) << (uint64_t)i;
|
||||
return stream.str();
|
||||
return fmt::format("{:#x}", i);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -83,15 +72,16 @@ namespace geode {
|
|||
* @param num Number to convert to string
|
||||
* @param precision Precision of the converted number
|
||||
* @returns Number as string
|
||||
* @note Precision has no effect on integers
|
||||
*/
|
||||
template <class Num>
|
||||
std::string numToString(Num num, size_t precision = 0) {
|
||||
std::stringstream ss;
|
||||
if (precision) {
|
||||
ss << std::fixed << std::setprecision(precision);
|
||||
if constexpr (std::is_floating_point_v<Num>) {
|
||||
if (precision) {
|
||||
return fmt::format("{:.{}f}", num, precision);
|
||||
}
|
||||
}
|
||||
ss << num;
|
||||
return ss.str();
|
||||
return fmt::to_string(num);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -125,7 +115,7 @@ namespace geode {
|
|||
* @returns String as number, or Err if the string couldn't be converted
|
||||
*/
|
||||
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 defined(__cpp_lib_to_chars)
|
||||
&& false
|
||||
|
@ -168,12 +158,18 @@ namespace geode {
|
|||
*/
|
||||
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<>
|
||||
struct matjson::Serialize<geode::ByteVector> {
|
||||
static matjson::Value to_json(geode::ByteVector const& bytes) {
|
||||
return matjson::Array(bytes.begin(), bytes.end());
|
||||
static Value toJson(geode::ByteVector const& bytes) {
|
||||
return std::vector<matjson::Value>(bytes.begin(), bytes.end());
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include "Result.hpp"
|
||||
#include <Geode/Result.hpp>
|
||||
|
||||
#include <Geode/DefaultInclude.hpp>
|
||||
#include <functional>
|
||||
|
@ -20,7 +20,7 @@ namespace geode::utils::map {
|
|||
* false if not.
|
||||
*/
|
||||
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) {
|
||||
if (containFunc(r)) return true;
|
||||
}
|
||||
|
@ -39,7 +39,7 @@ namespace geode::utils::map {
|
|||
* a pointer.
|
||||
*/
|
||||
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) {
|
||||
if (selectFunc(r)) return r;
|
||||
}
|
||||
|
@ -59,7 +59,7 @@ namespace geode::utils::map {
|
|||
*/
|
||||
template <class T, class R, class H>
|
||||
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;
|
||||
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>
|
||||
std::unordered_map<T2, V2, H2> remap(
|
||||
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;
|
||||
for (auto const& [t, v] : map) {
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include <Geode/DefaultInclude.hpp>
|
||||
#include "MiniFunction.hpp"
|
||||
#include <string_view>
|
||||
|
||||
namespace geode::utils::permission {
|
||||
|
@ -21,5 +20,5 @@ namespace geode::utils::permission {
|
|||
* @param permission The permission
|
||||
* @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);
|
||||
}
|
|
@ -4,6 +4,7 @@
|
|||
#include <string>
|
||||
#include <vector>
|
||||
#include <compare>
|
||||
#include "../DefaultInclude.hpp"
|
||||
|
||||
namespace geode::utils::string {
|
||||
/**
|
||||
|
|
|
@ -71,6 +71,7 @@ namespace geode::utils {
|
|||
std::string m_msg;
|
||||
Timer<Clock> m_timer;
|
||||
|
||||
// @geode-ignore(geode-alternative)
|
||||
LogPerformance(std::string const& msg = "", std::ostream& out = std::cout) :
|
||||
m_msg(msg), m_output(out) {
|
||||
m_timer = Timer<Clock>();
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
#include <Geode/loader/Loader.hpp> // another great circular dependency fix
|
||||
#include <matjson.hpp>
|
||||
#include "Result.hpp"
|
||||
#include <Geode/Result.hpp>
|
||||
#include "Task.hpp"
|
||||
#include <chrono>
|
||||
#include <optional>
|
||||
|
@ -83,6 +83,15 @@ namespace geode::utils::web {
|
|||
|
||||
std::vector<std::string> headers() const;
|
||||
std::optional<std::string> header(std::string_view name) const;
|
||||
|
||||
/**
|
||||
* Retrieves a list of all headers from the response with a given name - there can be
|
||||
* multiple headers with the same name, such as Set-Cookie, with each cookie in a separate
|
||||
* header
|
||||
* @param name name of the header
|
||||
* @return std::optional<std::vector<std::string>>
|
||||
*/
|
||||
std::optional<std::vector<std::string>> getAllHeadersNamed(std::string_view name) const;
|
||||
};
|
||||
|
||||
class GEODE_DLL WebProgress final {
|
||||
|
@ -203,6 +212,15 @@ namespace geode::utils::web {
|
|||
*/
|
||||
WebRequest& followRedirects(bool enabled);
|
||||
|
||||
/**
|
||||
* Enables or disables ignoring the content length header.
|
||||
* The default is false.
|
||||
*
|
||||
* @param enabled
|
||||
* @return WebRequest&
|
||||
*/
|
||||
WebRequest& ignoreContentLength(bool enabled);
|
||||
|
||||
/**
|
||||
* Sets the Certificate Authority (CA) bundle content.
|
||||
* Defaults to not sending a CA bundle.
|
||||
|
@ -277,9 +295,9 @@ namespace geode::utils::web {
|
|||
/**
|
||||
* Gets the request headers
|
||||
*
|
||||
* @return std::unordered_map<std::string, std::string>
|
||||
* @return std::unordered_map<std::string, std::vector<std::string>>
|
||||
*/
|
||||
std::unordered_map<std::string, std::string> getHeaders() const;
|
||||
std::unordered_map<std::string, std::vector<std::string>> getHeaders() const;
|
||||
|
||||
/**
|
||||
* Gets the parameters inside the URL
|
||||
|
|
BIN
loader/include/link/win64/gd-libcurl.lib
Normal file
|
@ -6,6 +6,7 @@
|
|||
|
||||
struct XINPUT_STATE;
|
||||
struct XINPUT_CAPABILITIES;
|
||||
struct XINPUT_VIBRATION;
|
||||
|
||||
constexpr static auto MAX_PATH_CHARS = 32768u;
|
||||
|
||||
|
@ -41,6 +42,17 @@ extern "C" DWORD XInputGetState(DWORD dwUserIndex, XINPUT_STATE *pState) {
|
|||
return ERROR_DEVICE_NOT_CONNECTED;
|
||||
}
|
||||
|
||||
#pragma comment(linker, "/export:XInputSetState,@3")
|
||||
extern "C" DWORD XInputSetState(DWORD dwUserIndex, XINPUT_VIBRATION* pVibration) {
|
||||
static auto fp = getFP("XInputSetState");
|
||||
if (fp) {
|
||||
using FPType = decltype(&XInputSetState);
|
||||
return reinterpret_cast<FPType>(fp)(dwUserIndex, pVibration);
|
||||
}
|
||||
|
||||
return ERROR_DEVICE_NOT_CONNECTED;
|
||||
}
|
||||
|
||||
#pragma comment(linker, "/export:XInputGetCapabilities,@4")
|
||||
extern "C" DWORD XInputGetCapabilities(DWORD dwUserIndex, DWORD dwFlags, XINPUT_CAPABILITIES *pCapabilities) {
|
||||
static auto fp = getFP("XInputGetCapabilities");
|
||||
|
|
BIN
loader/resources/file-add.png
Normal file
After Width: | Height: | Size: 22 KiB |
BIN
loader/resources/grid-view.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
|
@ -85,6 +85,12 @@
|
|||
"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>"
|
||||
},
|
||||
"infinite-local-mods-list": {
|
||||
"type": "bool",
|
||||
"default": false,
|
||||
"name": "Expand Installed Mods List",
|
||||
"description": "Make the installed mods list a single infinite scrollable list instead of having pages"
|
||||
},
|
||||
"copy-mods": {
|
||||
"type": "custom:copy-mods",
|
||||
"name": ""
|
||||
|
|
BIN
loader/resources/tag-joke.png
Normal file
After Width: | Height: | Size: 18 KiB |
BIN
loader/resources/tag-modtober-long.png
Normal file
After Width: | Height: | Size: 47 KiB |
Before Width: | Height: | Size: 39 KiB After Width: | Height: | Size: 17 KiB |
BIN
loader/resources/tag-paid-long.png
Normal file
After Width: | Height: | Size: 33 KiB |
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 16 KiB |
|
@ -13,7 +13,7 @@ namespace geode::stl {
|
|||
void free();
|
||||
|
||||
char* getStorage();
|
||||
void setStorage(const std::string_view);
|
||||
void setStorage(std::string_view);
|
||||
|
||||
size_t getSize();
|
||||
void setSize(size_t);
|
||||
|
|
|
@ -100,11 +100,11 @@ namespace gd {
|
|||
bool string::operator==(string const& other) const {
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
|
@ -1026,14 +1026,6 @@ std::optional<AxisAlignment> AxisLayoutOptions::getCrossAxisAlignment() const {
|
|||
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) {
|
||||
m_impl->m_scaleLimits = { min, max };
|
||||
return this;
|
||||
|
|
|
@ -8,11 +8,15 @@ $execute {
|
|||
// this is needed because the transitions in cocos uses dynamic cast to check
|
||||
// layers, which fail on user layers due to typeinfo not matching
|
||||
|
||||
#if defined(GEODE_IS_MAC) && GEODE_COMP_GD_VERSION != 22074
|
||||
#error "Unsupported version for macOS dynamic cast fix, please update the addresses"
|
||||
#endif
|
||||
|
||||
#if defined(GEODE_IS_INTEL_MAC)
|
||||
void* dynamicCastAddr = reinterpret_cast<void*>(base::get() + 0x7dd5e7);
|
||||
void* dynamicCastAddr = reinterpret_cast<void*>(base::get() + 0x7ba1d8);
|
||||
(void) Mod::get()->hook(dynamicCastAddr, &cast::typeinfoCastInternal, "__dynamic_cast");
|
||||
#elif defined(GEODE_IS_ARM_MAC)
|
||||
void* dynamicCastAddr = reinterpret_cast<void*>(base::get() + 0x6dfb10);
|
||||
void* dynamicCastAddr = reinterpret_cast<void*>(base::get() + 0x6c8bcc);
|
||||
(void)Mod::get()->hook(dynamicCastAddr, &cast::typeinfoCastInternal, "__dynamic_cast");
|
||||
#elif defined(GEODE_IS_ANDROID)
|
||||
void* handle = dlopen("libcocos2dcpp.so", RTLD_LAZY | RTLD_NOLOAD);
|
||||
|
|
|
@ -62,10 +62,6 @@ public:
|
|||
return meta;
|
||||
}
|
||||
|
||||
FieldContainer* getFieldContainer() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
FieldContainer* getFieldContainer(char const* forClass) {
|
||||
if (!m_classFieldContainers.count(forClass)) {
|
||||
m_classFieldContainers[forClass] = new FieldContainer();
|
||||
|
@ -104,16 +100,11 @@ size_t modifier::getFieldIndexForClass(char const* 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) {
|
||||
return GeodeNodeMetadata::set(this)->getFieldContainer(forClass);
|
||||
}
|
||||
|
||||
std::string CCNode::getID() {
|
||||
const std::string& CCNode::getID() {
|
||||
return GeodeNodeMetadata::set(this)->m_id;
|
||||
}
|
||||
|
||||
|
@ -121,7 +112,11 @@ void CCNode::setID(std::string const& 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())) {
|
||||
if (child->getID() == id) {
|
||||
return child;
|
||||
|
@ -130,7 +125,7 @@ CCNode* CCNode::getChildByID(std::string const& id) {
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
CCNode* CCNode::getChildByIDRecursive(std::string const& id) {
|
||||
CCNode* CCNode::getChildByIDRecursive(std::string_view id) {
|
||||
if (auto child = this->getChildByID(id)) {
|
||||
return child;
|
||||
}
|
||||
|
@ -189,7 +184,7 @@ private:
|
|||
std::unique_ptr<NodeQuery> m_next = nullptr;
|
||||
|
||||
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()) {
|
||||
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);
|
||||
if (!res) {
|
||||
log::error("Invalid CCNode::querySelector query '{}': {}", queryStr, res.unwrapErr());
|
||||
|
@ -298,7 +293,7 @@ CCNode* CCNode::querySelector(std::string const& queryStr) {
|
|||
return query->match(this);
|
||||
}
|
||||
|
||||
void CCNode::removeChildByID(std::string const& id) {
|
||||
void CCNode::removeChildByID(std::string_view id) {
|
||||
if (auto child = this->getChildByID(id)) {
|
||||
this->removeChild(child);
|
||||
}
|
||||
|
@ -344,7 +339,7 @@ void CCNode::updateLayout(bool updateChildOrder) {
|
|||
UserObjectSetEvent::UserObjectSetEvent(CCNode* node, std::string const& id, CCObject* 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) {
|
||||
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)
|
||||
|
|
|
@ -75,6 +75,7 @@ struct CustomMenuLayer : Modify<CustomMenuLayer, MenuLayer> {
|
|||
|
||||
this->fixSocialMenu();
|
||||
|
||||
//this code doesnt run have fun figuring out why idc enough
|
||||
if (auto node = this->getChildByID("settings-gamepad-icon")) {
|
||||
node->setPositionX(
|
||||
bottomMenu->getChildByID("settings-button")->getPositionX() + winSize.width / 2
|
||||
|
@ -83,13 +84,12 @@ struct CustomMenuLayer : Modify<CustomMenuLayer, MenuLayer> {
|
|||
}
|
||||
|
||||
// show if some mods failed to load
|
||||
if (Loader::get()->getProblems().size()) {
|
||||
if (Loader::get()->getLoadProblems().size()) {
|
||||
static bool shownProblemPopup = false;
|
||||
if (!shownProblemPopup) {
|
||||
shownProblemPopup = true;
|
||||
Notification::create("There were errors - see Geode page!", NotificationIcon::Error)->show();
|
||||
}
|
||||
|
||||
if (m_fields->m_geodeButton) {
|
||||
m_fields->m_exclamation = CCSprite::createWithSpriteFrameName("exMark_001.png");
|
||||
m_fields->m_exclamation->setPosition(m_fields->m_geodeButton->getContentSize() - ccp(10, 10));
|
||||
|
|
|
@ -41,9 +41,9 @@ $execute {
|
|||
// hook them to call our own handler
|
||||
if (LoaderImpl::get()->isForwardCompatMode()) return;
|
||||
|
||||
#if GEODE_COMP_GD_VERSION == 22060
|
||||
const uintptr_t offset1 = 0x75d00; // member function in CCEGLView
|
||||
const uintptr_t offset2 = 0x75d60; // static function
|
||||
#if GEODE_COMP_GD_VERSION == 22074
|
||||
const uintptr_t offset1 = 0x75D90; // member function in CCEGLView
|
||||
const uintptr_t offset2 = 0x75DF0; // static function
|
||||
|
||||
(void) Mod::get()->hook(
|
||||
reinterpret_cast<void*>(geode::base::getCocos() + offset1),
|
||||
|
|
|
@ -37,15 +37,15 @@ static void patchCall(uintptr_t addr, uintptr_t newCall) {
|
|||
$execute {
|
||||
if (LoaderImpl::get()->isForwardCompatMode()) return;
|
||||
|
||||
#if GEODE_COMP_GD_VERSION == 22040
|
||||
// BitmapDC::~BitmapDC
|
||||
#if 0 // TODO: mat GEODE_COMP_GD_VERSION == 22040
|
||||
patchCall(0xC9A56, (uintptr_t)&RemoveFontResourceWHook);
|
||||
|
||||
// BitmapDC::setFont
|
||||
patchCall(0xCB5BC, (uintptr_t)&RemoveFontResourceWHook);
|
||||
patchCall(0xCB642, (uintptr_t)&AddFontResourceWHook);
|
||||
#else
|
||||
// #pragma message("Unsupported GD version!")
|
||||
#pragma message("Unsupported GD version!")
|
||||
#endif
|
||||
};
|
||||
|
||||
|
|
|
@ -2,10 +2,15 @@
|
|||
|
||||
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
|
||||
|
||||
#include <Geode/modify/AppDelegate.hpp>
|
||||
struct SceneSwitch : Modify<SceneSwitch, AppDelegate> {
|
||||
GEODE_FORWARD_COMPAT_DISABLE_HOOKS("persist disabled")
|
||||
void willSwitchToScene(CCScene* scene) {
|
||||
|
@ -15,8 +20,6 @@ struct SceneSwitch : Modify<SceneSwitch, AppDelegate> {
|
|||
};
|
||||
|
||||
#else
|
||||
|
||||
#include <Geode/modify/AchievementNotifier.hpp>
|
||||
struct SceneSwitch : Modify<SceneSwitch, AchievementNotifier> {
|
||||
GEODE_FORWARD_COMPAT_DISABLE_HOOKS("persist disabled")
|
||||
void willSwitchToScene(CCScene* scene) {
|
||||
|
@ -26,3 +29,5 @@ struct SceneSwitch : Modify<SceneSwitch, AchievementNotifier> {
|
|||
};
|
||||
|
||||
#endif
|
||||
|
||||
}
|
|
@ -8,7 +8,7 @@ using namespace geode::prelude;
|
|||
namespace {
|
||||
void saveModData() {
|
||||
log::info("Saving mod data...");
|
||||
log::pushNest();
|
||||
log::NestScope nest;
|
||||
|
||||
auto begin = std::chrono::high_resolution_clock::now();
|
||||
|
||||
|
@ -17,8 +17,6 @@ namespace {
|
|||
auto end = std::chrono::high_resolution_clock::now();
|
||||
auto time = std::chrono::duration_cast<std::chrono::milliseconds>(end - begin).count();
|
||||
log::info("Took {}s", static_cast<float>(time) / 1000.f);
|
||||
|
||||
log::popNest();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -46,6 +46,11 @@ $register_ids(MenuLayer) {
|
|||
}
|
||||
|
||||
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
|
||||
if (auto menu = this->getChildByType<CCMenu>(0)) {
|
||||
|
|
|
@ -2,15 +2,14 @@
|
|||
|
||||
#include <Geode/DefaultInclude.hpp>
|
||||
//#include <Geode/utils/general.hpp>
|
||||
#include <Geode/utils/MiniFunction.hpp>
|
||||
#include <filesystem>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
class FileWatcher {
|
||||
public:
|
||||
using FileWatchCallback = geode::utils::MiniFunction<void(std::filesystem::path)>;
|
||||
using ErrorCallback = geode::utils::MiniFunction<void(std::string)>;
|
||||
using FileWatchCallback = std::function<void(std::filesystem::path)>;
|
||||
using ErrorCallback = std::function<void(std::string)>;
|
||||
|
||||
protected:
|
||||
std::filesystem::path m_file;
|
||||
|
|
|
@ -20,7 +20,7 @@ protected:
|
|||
std::string content;
|
||||
std::string optionA;
|
||||
std::string optionB;
|
||||
MiniFunction<void(bool)> after;
|
||||
std::function<void(bool)> after;
|
||||
};
|
||||
|
||||
EventListener<server::ModDownloadFilter> m_download;
|
||||
|
@ -79,7 +79,7 @@ protected:
|
|||
|
||||
public:
|
||||
void start() {
|
||||
for (auto problem : Loader::get()->getProblems()) {
|
||||
for (auto problem : Loader::get()->getAllProblems()) {
|
||||
switch (problem.type) {
|
||||
// Errors where the correct solution is to just delete the invalid .geode package
|
||||
case LoadProblem::Type::InvalidFile:
|
||||
|
|
|
@ -24,7 +24,8 @@ void crashlog::printGeodeInfo(std::stringstream& stream) {
|
|||
<< "Loader Commit: " << about::getLoaderCommitHash() << "\n"
|
||||
<< "Bindings Commit: " << about::getBindingsCommitHash() << "\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) {
|
||||
|
@ -44,7 +45,8 @@ void crashlog::printMods(std::stringstream& stream) {
|
|||
stream << fmt::format("{} | [{}] {}\n",
|
||||
mod->isCurrentlyLoading() ? "o"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 :
|
||||
" "sv,
|
||||
mod->getVersion().toVString(), mod->getID()
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
#include <Geode/loader/Loader.hpp>
|
||||
#include <Geode/loader/Log.hpp>
|
||||
#include <Geode/loader/Mod.hpp>
|
||||
#include <Geode/loader/SettingEvent.hpp>
|
||||
#include <Geode/utils/JsonValidation.hpp>
|
||||
#include <loader/LogImpl.hpp>
|
||||
|
||||
|
@ -30,8 +29,7 @@ $on_mod(Loaded) {
|
|||
std::vector<matjson::Value> res;
|
||||
|
||||
auto args = *event->messageData;
|
||||
JsonChecker checker(args);
|
||||
auto root = checker.root("[ipc/list-mods]").obj();
|
||||
auto root = checkJson(args, "[ipc/list-mods]");
|
||||
|
||||
auto includeRunTimeInfo = root.has("include-runtime-info").get<bool>();
|
||||
auto dontIncludeLoader = root.has("dont-include-loader").get<bool>();
|
||||
|
@ -69,13 +67,13 @@ void tryShowForwardCompat() {
|
|||
return;
|
||||
|
||||
// TODO: change text later
|
||||
console::messageBox(
|
||||
"Forward Compatibility Warning",
|
||||
"Geode is running in a newer version of GD than Geode targets.\n"
|
||||
"UI is going to be disabled, platform console is forced on and crashes can be more common.\n"
|
||||
"However, if your game crashes, it is probably caused by an outdated mod and not Geode itself.",
|
||||
Severity::Warning
|
||||
);
|
||||
// console::messageBox(
|
||||
// "Forward Compatibility Warning",
|
||||
// "Geode is running in a newer version of GD than Geode targets.\n"
|
||||
// "UI is going to be disabled, platform console is forced on and crashes can be more common.\n"
|
||||
// "However, if your game crashes, it is probably caused by an outdated mod and not Geode itself.",
|
||||
// Severity::Warning
|
||||
// );
|
||||
|
||||
Mod::get()->setSavedValue<std::string>(
|
||||
"last-forward-compat-warn-popup-ver",
|
||||
|
@ -86,7 +84,7 @@ void tryShowForwardCompat() {
|
|||
#ifdef GEODE_IS_WINDOWS
|
||||
bool safeModeCheck() {
|
||||
// yes this is quite funny
|
||||
if (GetAsyncKeyState(VK_SHIFT) == 0) {
|
||||
if (!(GetAsyncKeyState(VK_SHIFT) & (1 << 15))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -140,17 +138,18 @@ int geodeEntry(void* platformData) {
|
|||
|
||||
// set up internal mod, settings and data
|
||||
log::info("Setting up internal mod");
|
||||
log::pushNest();
|
||||
auto internalSetupRes = LoaderImpl::get()->setupInternalMod();
|
||||
log::popNest();
|
||||
if (!internalSetupRes) {
|
||||
console::messageBox(
|
||||
"Unable to Load Geode!",
|
||||
"There was a fatal error setting up "
|
||||
"the internal mod and Geode can not be loaded: " + internalSetupRes.unwrapErr()
|
||||
);
|
||||
LoaderImpl::get()->forceReset();
|
||||
return 1;
|
||||
{
|
||||
log::NestScope nest;
|
||||
auto internalSetupRes = LoaderImpl::get()->setupInternalMod();
|
||||
if (!internalSetupRes) {
|
||||
console::messageBox(
|
||||
"Unable to Load Geode!",
|
||||
"There was a fatal error setting up "
|
||||
"the internal mod and Geode can not be loaded: " + internalSetupRes.unwrapErr()
|
||||
);
|
||||
LoaderImpl::get()->forceReset();
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
tryShowForwardCompat();
|
||||
|
@ -163,26 +162,28 @@ int geodeEntry(void* platformData) {
|
|||
|
||||
// set up loader, load mods, etc.
|
||||
log::info("Setting up loader");
|
||||
log::pushNest();
|
||||
auto setupRes = LoaderImpl::get()->setup();
|
||||
log::popNest();
|
||||
if (!setupRes) {
|
||||
console::messageBox(
|
||||
"Unable to Load Geode!",
|
||||
"There was an unknown fatal error setting up "
|
||||
"the loader and Geode can not be loaded. "
|
||||
"(" + setupRes.unwrapErr() + ")"
|
||||
);
|
||||
LoaderImpl::get()->forceReset();
|
||||
return 1;
|
||||
{
|
||||
log::NestScope nest;
|
||||
auto setupRes = LoaderImpl::get()->setup();
|
||||
if (!setupRes) {
|
||||
console::messageBox(
|
||||
"Unable to Load Geode!",
|
||||
"There was an unknown fatal error setting up "
|
||||
"the loader and Geode can not be loaded. "
|
||||
"(" + setupRes.unwrapErr() + ")"
|
||||
);
|
||||
LoaderImpl::get()->forceReset();
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
crashlog::setupPlatformHandlerPost();
|
||||
|
||||
log::debug("Setting up IPC");
|
||||
log::pushNest();
|
||||
ipc::setup();
|
||||
log::popNest();
|
||||
{
|
||||
log::NestScope nest;
|
||||
ipc::setup();
|
||||
}
|
||||
|
||||
// download and install new loader update in the background
|
||||
|
||||
|
|
|
@ -24,21 +24,20 @@ void CopyButtonSettingNode::onCopy(CCObject*) {
|
|||
});
|
||||
});
|
||||
|
||||
std::stringstream ss;
|
||||
std::string modsList;
|
||||
using namespace std::string_view_literals;
|
||||
for (int i = 0; i < mods.size(); i++) {
|
||||
auto& mod = mods[i];
|
||||
ss << fmt::format("{} | [{}] {}",
|
||||
modsList += fmt::format("{} | [{}] {}{}",
|
||||
mod->isEnabled() ? "x"sv :
|
||||
mod->hasProblems() ? "!"sv :
|
||||
mod->hasLoadProblems() ? "!"sv :
|
||||
mod->targetsOutdatedVersion() ? "*"sv :
|
||||
" "sv,
|
||||
mod->getVersion().toVString(), mod->getID()
|
||||
mod->getVersion().toVString(), mod->getID(),
|
||||
i < mods.size() ? "\n" : ""
|
||||
);
|
||||
if (i != mods.size() - 1) {
|
||||
ss << "\n";
|
||||
}
|
||||
}
|
||||
clipboard::write(ss.str());
|
||||
clipboard::write(modsList);
|
||||
|
||||
Notification::create("Mods list copied to clipboard", NotificationIcon::Info, 0.5f)->show();
|
||||
}
|
||||
|
|
|
@ -5,14 +5,14 @@ using namespace geode::prelude;
|
|||
|
||||
class CopyButtonSetting : public SettingV3 {
|
||||
public:
|
||||
static Result<std::shared_ptr<CopyButtonSetting>> parse(std::string const& key, std::string const& modID, matjson::Value const& json) {
|
||||
static Result<std::shared_ptr<SettingV3>> parse(std::string const& key, std::string const& modID, matjson::Value const& json) {
|
||||
auto res = std::make_shared<CopyButtonSetting>();
|
||||
auto root = checkJson(json, "CopyButtonSetting");
|
||||
|
||||
res->init(key, modID, root);
|
||||
res->parseNameAndDescription(root);
|
||||
|
||||
return root.ok(res);
|
||||
return root.ok(std::static_pointer_cast<SettingV3>(res));
|
||||
}
|
||||
|
||||
bool load(matjson::Value const& json) override {
|
||||
|
|
|
@ -98,7 +98,7 @@ std::string_view Hook::Impl::getDisplayName() 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["detour"] = std::to_string(reinterpret_cast<uintptr_t>(m_detour));
|
||||
json["name"] = m_displayName;
|
||||
|
|
|
@ -19,7 +19,7 @@ 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) {
|
||||
event->replyData = fn(event);
|
||||
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 reply;
|
||||
|
||||
std::string error;
|
||||
auto res = matjson::parse(buffer, error);
|
||||
if (error.size() > 0) {
|
||||
log::warn("Received IPC message that isn't valid JSON: {}", error);
|
||||
auto res = matjson::Value::parse(buffer);
|
||||
if (!res) {
|
||||
log::warn("Received IPC message that isn't valid JSON: {}", res.unwrapErr());
|
||||
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");
|
||||
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");
|
||||
return reply;
|
||||
}
|
||||
|
@ -55,6 +54,6 @@ matjson::Value ipc::processRaw(void* rawHandle, std::string const& buffer) {
|
|||
}
|
||||
// log::debug("Posting IPC event");
|
||||
// ! 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;
|
||||
}
|
||||
|
|
|
@ -68,13 +68,19 @@ std::vector<Mod*> Loader::getAllMods() {
|
|||
std::vector<LoadProblem> Loader::getAllProblems() const {
|
||||
return m_impl->getProblems();
|
||||
}
|
||||
std::vector<LoadProblem> Loader::getProblems() const {
|
||||
std::vector<LoadProblem> Loader::getLoadProblems() const {
|
||||
std::vector<LoadProblem> result;
|
||||
for (auto problem : this->getAllProblems()) {
|
||||
if (
|
||||
problem.type != LoadProblem::Type::Recommendation &&
|
||||
problem.type != LoadProblem::Type::Suggestion
|
||||
) {
|
||||
if (problem.isProblem()) {
|
||||
result.push_back(problem);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
std::vector<LoadProblem> Loader::getOutdated() const {
|
||||
std::vector<LoadProblem> result;
|
||||
for (auto problem : this->getAllProblems()) {
|
||||
if (problem.isOutdated()) {
|
||||
result.push_back(problem);
|
||||
}
|
||||
}
|
||||
|
@ -83,10 +89,7 @@ std::vector<LoadProblem> Loader::getProblems() const {
|
|||
std::vector<LoadProblem> Loader::getRecommendations() const {
|
||||
std::vector<LoadProblem> result;
|
||||
for (auto problem : this->getAllProblems()) {
|
||||
if (
|
||||
problem.type == LoadProblem::Type::Recommendation ||
|
||||
problem.type == LoadProblem::Type::Suggestion
|
||||
) {
|
||||
if (problem.isSuggestion()) {
|
||||
result.push_back(problem);
|
||||
}
|
||||
}
|
||||
|
@ -109,14 +112,14 @@ std::vector<std::string> Loader::getLaunchArgumentNames() const {
|
|||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
bool Loader::getLaunchFlag(std::string_view const name) const {
|
||||
bool Loader::getLaunchFlag(std::string_view name) const {
|
||||
return m_impl->getLaunchFlag(name);
|
||||
}
|
||||
|
|
|
@ -29,6 +29,9 @@
|
|||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#include <server/DownloadManager.hpp>
|
||||
#include <Geode/ui/Popup.hpp>
|
||||
|
||||
using namespace geode::prelude;
|
||||
|
||||
Loader::Impl* LoaderImpl::get() {
|
||||
|
@ -42,6 +45,11 @@ Loader::Impl::~Impl() = default;
|
|||
// Initialization
|
||||
|
||||
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()) {
|
||||
m_forwardCompatMode = !this->getGameVersion().empty() &&
|
||||
this->getGameVersion() != GEODE_STR(GEODE_GD_VERSION);
|
||||
|
@ -73,35 +81,32 @@ Result<> Loader::Impl::setup() {
|
|||
|
||||
if (this->supportsLaunchArguments()) {
|
||||
log::debug("Loading launch arguments");
|
||||
log::pushNest();
|
||||
log::NestScope nest;
|
||||
this->initLaunchArguments();
|
||||
log::popNest();
|
||||
}
|
||||
|
||||
// on some platforms, using the crash handler overrides more convenient native handlers
|
||||
if (!this->getLaunchFlag("disable-crash-handler")) {
|
||||
log::debug("Setting up crash handler");
|
||||
log::pushNest();
|
||||
log::NestScope nest;
|
||||
if (!crashlog::setupPlatformHandler()) {
|
||||
log::debug("Failed to set up crash handler");
|
||||
}
|
||||
log::popNest();
|
||||
} else {
|
||||
log::debug("Crash handler setup skipped");
|
||||
}
|
||||
|
||||
log::debug("Loading hooks");
|
||||
log::pushNest();
|
||||
if (!this->loadHooks()) {
|
||||
if (log::NestScope nest; !this->loadHooks()) {
|
||||
return Err("There were errors loading some hooks, see console for details");
|
||||
}
|
||||
log::popNest();
|
||||
|
||||
log::debug("Setting up directories");
|
||||
log::pushNest();
|
||||
this->createDirectories();
|
||||
this->addSearchPaths();
|
||||
log::popNest();
|
||||
{
|
||||
log::NestScope nest;
|
||||
this->createDirectories();
|
||||
this->addSearchPaths();
|
||||
}
|
||||
|
||||
// Trigger on_mod(Loaded) for the internal mod
|
||||
// this function is already on the gd thread, so this should be fine
|
||||
|
@ -121,7 +126,7 @@ void Loader::Impl::addSearchPaths() {
|
|||
|
||||
void Loader::Impl::updateResources(bool forceReload) {
|
||||
log::debug("Adding resources");
|
||||
log::pushNest();
|
||||
log::NestScope nest;
|
||||
for (auto const& [_, mod] : m_mods) {
|
||||
if (!forceReload && ModImpl::getImpl(mod)->m_resourcesLoaded)
|
||||
continue;
|
||||
|
@ -132,7 +137,6 @@ void Loader::Impl::updateResources(bool forceReload) {
|
|||
// we have to call it in both places since setup is only called once ever, but updateResources is called
|
||||
// on every texture reload
|
||||
CCFileUtils::get()->updatePaths();
|
||||
log::popNest();
|
||||
}
|
||||
|
||||
std::vector<Mod*> Loader::Impl::getAllMods() {
|
||||
|
@ -170,24 +174,22 @@ bool Loader::Impl::isModVersionSupported(VersionInfo const& target) {
|
|||
void Loader::Impl::saveData() {
|
||||
for (auto& [id, mod] : m_mods) {
|
||||
log::debug("{}", mod->getID());
|
||||
log::pushNest();
|
||||
log::NestScope nest;
|
||||
auto r = mod->saveData();
|
||||
if (!r) {
|
||||
log::warn("Unable to save data for mod \"{}\": {}", mod->getID(), r.unwrapErr());
|
||||
}
|
||||
log::popNest();
|
||||
}
|
||||
}
|
||||
|
||||
void Loader::Impl::loadData() {
|
||||
for (auto& [_, mod] : m_mods) {
|
||||
log::debug("{}", mod->getID());
|
||||
log::pushNest();
|
||||
log::NestScope nest;
|
||||
auto r = mod->loadData();
|
||||
if (!r) {
|
||||
log::warn("Unable to load data for mod \"{}\": {}", mod->getID(), r.unwrapErr());
|
||||
}
|
||||
log::popNest();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -230,7 +232,7 @@ void Loader::Impl::updateModResources(Mod* mod) {
|
|||
return;
|
||||
|
||||
log::debug("{}", mod->getID());
|
||||
log::pushNest();
|
||||
log::NestScope nest;
|
||||
|
||||
for (auto const& sheet : mod->getMetadata().getSpritesheets()) {
|
||||
log::debug("Adding sheet {}", sheet);
|
||||
|
@ -250,8 +252,6 @@ void Loader::Impl::updateModResources(Mod* mod) {
|
|||
CCSpriteFrameCache::get()->addSpriteFramesWithFile(plist.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
log::popNest();
|
||||
}
|
||||
|
||||
void Loader::Impl::addProblem(LoadProblem const& problem) {
|
||||
|
@ -267,14 +267,14 @@ void Loader::Impl::addProblem(LoadProblem const& problem) {
|
|||
void Loader::Impl::queueMods(std::vector<ModMetadata>& modQueue) {
|
||||
for (auto const& dir : m_modSearchDirectories) {
|
||||
log::debug("Searching {}", dir);
|
||||
log::pushNest();
|
||||
log::NestScope nest;
|
||||
for (auto const& entry : std::filesystem::directory_iterator(dir)) {
|
||||
if (!std::filesystem::is_regular_file(entry) ||
|
||||
entry.path().extension() != GEODE_MOD_EXTENSION)
|
||||
continue;
|
||||
|
||||
log::debug("Found {}", entry.path().filename());
|
||||
log::pushNest();
|
||||
log::NestScope nest;
|
||||
|
||||
auto res = ModMetadata::createFromGeodeFile(entry.path());
|
||||
if (!res) {
|
||||
|
@ -284,7 +284,6 @@ void Loader::Impl::queueMods(std::vector<ModMetadata>& modQueue) {
|
|||
res.unwrapErr()
|
||||
});
|
||||
log::error("Failed to queue: {}", res.unwrapErr());
|
||||
log::popNest();
|
||||
continue;
|
||||
}
|
||||
auto modMetadata = res.unwrap();
|
||||
|
@ -302,14 +301,11 @@ void Loader::Impl::queueMods(std::vector<ModMetadata>& modQueue) {
|
|||
"A mod with the same ID is already present."
|
||||
});
|
||||
log::error("Failed to queue: a mod with the same ID is already queued");
|
||||
log::popNest();
|
||||
continue;
|
||||
}
|
||||
|
||||
modQueue.push_back(modMetadata);
|
||||
log::popNest();
|
||||
}
|
||||
log::popNest();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -327,7 +323,7 @@ void Loader::Impl::populateModList(std::vector<ModMetadata>& modQueue) {
|
|||
|
||||
for (auto const& metadata : modQueue) {
|
||||
log::debug("{} {}", metadata.getID(), metadata.getVersion());
|
||||
log::pushNest();
|
||||
log::NestScope nest;
|
||||
|
||||
auto mod = new Mod(metadata);
|
||||
|
||||
|
@ -339,20 +335,17 @@ void Loader::Impl::populateModList(std::vector<ModMetadata>& modQueue) {
|
|||
res.unwrapErr()
|
||||
});
|
||||
log::error("Failed to set up: {}", res.unwrapErr());
|
||||
log::popNest();
|
||||
continue;
|
||||
}
|
||||
|
||||
m_mods.insert({metadata.getID(), mod});
|
||||
|
||||
log::popNest();
|
||||
}
|
||||
}
|
||||
|
||||
void Loader::Impl::buildModGraph() {
|
||||
for (auto const& [id, mod] : m_mods) {
|
||||
log::debug("{}", mod->getID());
|
||||
log::pushNest();
|
||||
log::NestScope nest;
|
||||
for (auto& dependency : mod->m_impl->m_metadata.m_impl->m_dependencies) {
|
||||
log::debug("{}", dependency.id);
|
||||
if (!m_mods.contains(dependency.id)) {
|
||||
|
@ -379,11 +372,38 @@ void Loader::Impl::buildModGraph() {
|
|||
incompatibility.mod =
|
||||
m_mods.contains(incompatibility.id) ? m_mods[incompatibility.id] : nullptr;
|
||||
}
|
||||
log::popNest();
|
||||
}
|
||||
}
|
||||
|
||||
void Loader::Impl::loadModGraph(Mod* node, bool early) {
|
||||
// Check version first, as it's not worth trying to load a mod with an
|
||||
// invalid target version
|
||||
// Also this makes it so that when GD updates, outdated mods get shown as
|
||||
// "Outdated" in the UI instead of "Missing Dependencies"
|
||||
auto res = node->getMetadata().checkGameVersion();
|
||||
if (!res) {
|
||||
this->addProblem({
|
||||
LoadProblem::Type::UnsupportedVersion,
|
||||
node,
|
||||
res.unwrapErr()
|
||||
});
|
||||
log::error("{}", res.unwrapErr());
|
||||
return;
|
||||
}
|
||||
|
||||
auto geodeVerRes = node->getMetadata().checkGeodeVersion();
|
||||
if (!geodeVerRes) {
|
||||
this->addProblem({
|
||||
node->getMetadata().getGeodeVersion() > this->getVersion() ?
|
||||
LoadProblem::Type::NeedsNewerGeodeVersion :
|
||||
LoadProblem::Type::UnsupportedGeodeVersion,
|
||||
node,
|
||||
geodeVerRes.unwrapErr()
|
||||
});
|
||||
log::error("{}", geodeVerRes.unwrapErr());
|
||||
return;
|
||||
}
|
||||
|
||||
if (node->hasUnresolvedDependencies()) {
|
||||
log::debug("{} {} has unresolved dependencies", node->getID(), node->getVersion());
|
||||
return;
|
||||
|
@ -393,12 +413,10 @@ void Loader::Impl::loadModGraph(Mod* node, bool early) {
|
|||
return;
|
||||
}
|
||||
|
||||
log::debug("{} {}", node->getID(), node->getVersion());
|
||||
log::pushNest();
|
||||
log::NestScope nest;
|
||||
|
||||
if (node->isEnabled()) {
|
||||
log::warn("Mod {} already loaded, this should never happen", node->getID());
|
||||
log::popNest();
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -409,6 +427,7 @@ void Loader::Impl::loadModGraph(Mod* node, bool early) {
|
|||
|
||||
auto unzipFunction = [this, node]() {
|
||||
log::debug("Unzip");
|
||||
log::NestScope nest;
|
||||
auto res = node->m_impl->unzipGeodeFile(node->getMetadata());
|
||||
return res;
|
||||
};
|
||||
|
@ -416,6 +435,7 @@ void Loader::Impl::loadModGraph(Mod* node, bool early) {
|
|||
auto loadFunction = [this, node, early]() {
|
||||
if (node->shouldLoad()) {
|
||||
log::debug("Load");
|
||||
log::NestScope nest;
|
||||
auto res = node->m_impl->loadBinary();
|
||||
if (!res) {
|
||||
this->addProblem({
|
||||
|
@ -441,36 +461,6 @@ void Loader::Impl::loadModGraph(Mod* node, bool early) {
|
|||
});
|
||||
log::error("{}", reason.value());
|
||||
m_refreshingModCount -= 1;
|
||||
log::popNest();
|
||||
return;
|
||||
}
|
||||
|
||||
auto res = node->getMetadata().checkGameVersion();
|
||||
if (!res) {
|
||||
this->addProblem({
|
||||
LoadProblem::Type::UnsupportedVersion,
|
||||
node,
|
||||
res.unwrapErr()
|
||||
});
|
||||
log::error("{}", res.unwrapErr());
|
||||
m_refreshingModCount -= 1;
|
||||
log::popNest();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this->isModVersionSupported(node->getMetadata().getGeodeVersion())) {
|
||||
this->addProblem({
|
||||
node->getMetadata().getGeodeVersion() > this->getVersion() ? LoadProblem::Type::NeedsNewerGeodeVersion : LoadProblem::Type::UnsupportedGeodeVersion,
|
||||
node,
|
||||
fmt::format(
|
||||
"Geode version {}\nis required to run this mod\n(installed: {})",
|
||||
node->getMetadata().getGeodeVersion().toVString(),
|
||||
this->getVersion().toVString()
|
||||
)
|
||||
});
|
||||
log::error("Unsupported Geode version: {}", node->getMetadata().getGeodeVersion());
|
||||
m_refreshingModCount -= 1;
|
||||
log::popNest();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -485,7 +475,6 @@ void Loader::Impl::loadModGraph(Mod* node, bool early) {
|
|||
});
|
||||
log::error("Failed to unzip: {}", res.unwrapErr());
|
||||
m_refreshingModCount -= 1;
|
||||
log::popNest();
|
||||
return;
|
||||
}
|
||||
loadFunction();
|
||||
|
@ -496,7 +485,7 @@ void Loader::Impl::loadModGraph(Mod* node, bool early) {
|
|||
thread::setName("Mod Unzip");
|
||||
log::loadNest(nest);
|
||||
auto res = unzipFunction();
|
||||
this->queueInMainThread([=, this]() {
|
||||
this->queueInMainThread([=, this, res = std::move(res)]() {
|
||||
auto prevNest = log::saveNest();
|
||||
log::loadNest(nest);
|
||||
if (!res) {
|
||||
|
@ -515,7 +504,6 @@ void Loader::Impl::loadModGraph(Mod* node, bool early) {
|
|||
});
|
||||
}).detach();
|
||||
}
|
||||
log::popNest();
|
||||
}
|
||||
|
||||
void Loader::Impl::findProblems() {
|
||||
|
@ -524,8 +512,12 @@ void Loader::Impl::findProblems() {
|
|||
log::debug("{} is not enabled", id);
|
||||
continue;
|
||||
}
|
||||
if (mod->targetsOutdatedVersion()) {
|
||||
log::debug("{} is outdated", id);
|
||||
continue;
|
||||
}
|
||||
log::debug("{}", id);
|
||||
log::pushNest();
|
||||
log::NestScope nest;
|
||||
|
||||
for (auto const& dep : mod->getMetadata().getDependencies()) {
|
||||
if (dep.mod && dep.mod->isEnabled() && dep.version.compare(dep.mod->getVersion()))
|
||||
|
@ -652,18 +644,15 @@ void Loader::Impl::findProblems() {
|
|||
});
|
||||
log::error("{} failed to load for an unknown reason", id);
|
||||
}
|
||||
|
||||
log::popNest();
|
||||
}
|
||||
}
|
||||
|
||||
void Loader::Impl::refreshModGraph() {
|
||||
log::info("Refreshing mod graph");
|
||||
log::pushNest();
|
||||
log::NestScope nest;
|
||||
|
||||
if (m_isSetup) {
|
||||
log::error("Cannot refresh mod graph after startup");
|
||||
log::popNest();
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -673,48 +662,51 @@ void Loader::Impl::refreshModGraph() {
|
|||
|
||||
m_loadingState = LoadingState::Queue;
|
||||
log::debug("Queueing mods");
|
||||
log::pushNest();
|
||||
std::vector<ModMetadata> modQueue;
|
||||
this->queueMods(modQueue);
|
||||
log::popNest();
|
||||
{
|
||||
log::NestScope nest;
|
||||
this->queueMods(modQueue);
|
||||
}
|
||||
|
||||
m_loadingState = LoadingState::List;
|
||||
log::debug("Populating mod list");
|
||||
log::pushNest();
|
||||
this->populateModList(modQueue);
|
||||
modQueue.clear();
|
||||
log::popNest();
|
||||
{
|
||||
log::NestScope nest;
|
||||
this->populateModList(modQueue);
|
||||
modQueue.clear();
|
||||
}
|
||||
|
||||
m_loadingState = LoadingState::Graph;
|
||||
log::debug("Building mod graph");
|
||||
log::pushNest();
|
||||
this->buildModGraph();
|
||||
log::popNest();
|
||||
{
|
||||
log::NestScope nest;
|
||||
this->buildModGraph();
|
||||
}
|
||||
|
||||
log::debug("Ordering mod stack");
|
||||
log::pushNest();
|
||||
this->orderModStack();
|
||||
log::popNest();
|
||||
{
|
||||
log::NestScope nest;
|
||||
this->orderModStack();
|
||||
}
|
||||
|
||||
m_loadingState = LoadingState::EarlyMods;
|
||||
log::debug("Loading early mods");
|
||||
log::pushNest();
|
||||
while (!m_modsToLoad.empty() && m_modsToLoad.front()->needsEarlyLoad()) {
|
||||
auto mod = m_modsToLoad.front();
|
||||
m_modsToLoad.pop_front();
|
||||
this->loadModGraph(mod, true);
|
||||
{
|
||||
log::NestScope nest;
|
||||
while (!m_modsToLoad.empty() && m_modsToLoad.front()->needsEarlyLoad()) {
|
||||
auto mod = m_modsToLoad.front();
|
||||
m_modsToLoad.pop_front();
|
||||
this->loadModGraph(mod, true);
|
||||
}
|
||||
}
|
||||
log::popNest();
|
||||
|
||||
auto end = std::chrono::high_resolution_clock::now();
|
||||
auto time = std::chrono::duration_cast<std::chrono::milliseconds>(end - begin).count();
|
||||
log::info("Took {}s. Continuing next frame...", static_cast<float>(time) / 1000.f);
|
||||
|
||||
log::popNest();
|
||||
|
||||
m_loadingState = LoadingState::Mods;
|
||||
|
||||
queueInMainThread([&]() {
|
||||
queueInMainThread([this]() {
|
||||
this->continueRefreshModGraph();
|
||||
});
|
||||
}
|
||||
|
@ -768,7 +760,7 @@ void Loader::Impl::orderModStack() {
|
|||
|
||||
void Loader::Impl::continueRefreshModGraph() {
|
||||
if (m_refreshingModCount != 0) {
|
||||
queueInMainThread([&]() {
|
||||
queueInMainThread([this]() {
|
||||
this->continueRefreshModGraph();
|
||||
});
|
||||
return;
|
||||
|
@ -781,28 +773,27 @@ void Loader::Impl::continueRefreshModGraph() {
|
|||
}
|
||||
|
||||
log::info("Continuing mod graph refresh...");
|
||||
log::pushNest();
|
||||
log::NestScope nest;
|
||||
|
||||
m_timerBegin = std::chrono::high_resolution_clock::now();
|
||||
|
||||
switch (m_loadingState) {
|
||||
case LoadingState::Mods:
|
||||
if (!m_modsToLoad.empty()) {
|
||||
log::debug("Loading mods");
|
||||
log::pushNest();
|
||||
auto mod = m_modsToLoad.front();
|
||||
m_modsToLoad.pop_front();
|
||||
log::debug("Loading mod {} {}", mod->getID(), mod->getVersion());
|
||||
this->loadModGraph(mod, false);
|
||||
log::popNest();
|
||||
break;
|
||||
}
|
||||
m_loadingState = LoadingState::Problems;
|
||||
[[fallthrough]];
|
||||
case LoadingState::Problems:
|
||||
log::debug("Finding problems");
|
||||
log::pushNest();
|
||||
this->findProblems();
|
||||
log::popNest();
|
||||
{
|
||||
log::NestScope nest;
|
||||
this->findProblems();
|
||||
}
|
||||
m_loadingState = LoadingState::Done;
|
||||
{
|
||||
auto end = std::chrono::high_resolution_clock::now();
|
||||
|
@ -818,12 +809,10 @@ void Loader::Impl::continueRefreshModGraph() {
|
|||
}
|
||||
|
||||
if (m_loadingState != LoadingState::Done) {
|
||||
queueInMainThread([&]() {
|
||||
queueInMainThread([this]() {
|
||||
this->continueRefreshModGraph();
|
||||
});
|
||||
}
|
||||
|
||||
log::popNest();
|
||||
}
|
||||
|
||||
std::vector<LoadProblem> Loader::Impl::getProblems() const {
|
||||
|
@ -927,11 +916,11 @@ std::vector<std::string> Loader::Impl::getLaunchArgumentNames() const {
|
|||
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();
|
||||
}
|
||||
|
||||
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));
|
||||
if (value == m_launchArgs.end()) {
|
||||
return std::nullopt;
|
||||
|
@ -939,7 +928,7 @@ std::optional<std::string> Loader::Impl::getLaunchArgument(std::string_view cons
|
|||
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);
|
||||
return arg.has_value() && arg.value() == "true";
|
||||
}
|
||||
|
@ -966,4 +955,171 @@ bool Loader::Impl::isSafeMode() const {
|
|||
|
||||
void Loader::Impl::forceSafeMode() {
|
||||
m_forceSafeMode = true;
|
||||
}
|
||||
}
|
||||
|
||||
void Loader::Impl::installModManuallyFromFile(std::filesystem::path const& path, std::function<void()> after) {
|
||||
auto res = ModMetadata::createFromGeodeFile(path);
|
||||
if (!res) {
|
||||
FLAlertLayer::create(
|
||||
"Invalid File",
|
||||
fmt::format(
|
||||
"The path <cy>'{}'</c> is not a valid Geode mod: {}",
|
||||
path.string(),
|
||||
res.unwrapErr()
|
||||
),
|
||||
"OK"
|
||||
)->show();
|
||||
return;
|
||||
}
|
||||
auto meta = res.unwrap();
|
||||
|
||||
auto check = meta.checkTargetVersions();
|
||||
if (!check) {
|
||||
FLAlertLayer::create(
|
||||
"Invalid Mod Version",
|
||||
fmt::format(
|
||||
"The mod <cy>{}</c> can not be installed: {}",
|
||||
meta.getID(),
|
||||
check.unwrapErr()
|
||||
),
|
||||
"OK"
|
||||
)->show();
|
||||
}
|
||||
|
||||
auto doInstallModFromFile = [this, path, meta, after]() {
|
||||
std::error_code ec;
|
||||
|
||||
static size_t MAX_ATTEMPTS = 10;
|
||||
|
||||
// Figure out a free path to install to
|
||||
auto installTo = dirs::getModsDir() / fmt::format("{}.geode", meta.getID());
|
||||
size_t counter = 0;
|
||||
while (std::filesystem::exists(installTo, ec) && counter < MAX_ATTEMPTS) {
|
||||
installTo = dirs::getModsDir() / fmt::format("{}-{}.geode", meta.getID(), counter);
|
||||
counter += 1;
|
||||
}
|
||||
|
||||
// This is incredibly unlikely but theoretically possible
|
||||
if (counter >= MAX_ATTEMPTS) {
|
||||
FLAlertLayer::create(
|
||||
"Unable to Install",
|
||||
fmt::format(
|
||||
"Unable to install mod <co>{}</c>: Can't find a free filename!",
|
||||
meta.getID()
|
||||
),
|
||||
"OK"
|
||||
)->show();
|
||||
return;
|
||||
}
|
||||
|
||||
// Actually copy the file over to the install directory
|
||||
std::filesystem::copy_file(path, installTo, ec);
|
||||
if (ec) {
|
||||
FLAlertLayer::create(
|
||||
"Unable to Install",
|
||||
fmt::format(
|
||||
"Unable to install mod <co>{}</c>: {} (Error code <cr>{}</c>)",
|
||||
meta.getID(), ec.message(), ec.value()
|
||||
),
|
||||
"OK"
|
||||
)->show();
|
||||
return;
|
||||
}
|
||||
|
||||
// Mark an updated mod as updated or add to the mods list
|
||||
if (m_mods.contains(meta.getID())) {
|
||||
m_mods.at(meta.getID())->m_impl->m_requestedAction = ModRequestedAction::Update;
|
||||
}
|
||||
// Otherwise add a new Mod
|
||||
// This should be safe as all of the scary stuff in setup() is only relevant
|
||||
// for mods that are actually running
|
||||
else {
|
||||
auto mod = new Mod(meta);
|
||||
auto res = mod->m_impl->setup();
|
||||
if (!res) {
|
||||
log::error("Unable to set up manually installed mod: {}", res.unwrapErr());
|
||||
}
|
||||
(void)mod->enable();
|
||||
m_mods.insert({ meta.getID(), mod });
|
||||
}
|
||||
|
||||
if (after) after();
|
||||
|
||||
// No need for the user to go and manually clean up the file
|
||||
createQuickPopup(
|
||||
"Mod Installed",
|
||||
fmt::format(
|
||||
"Mod <co>{}</c> has been successfully installed from file! "
|
||||
"<cy>Do you want to delete the original file?</c>",
|
||||
meta.getName()
|
||||
),
|
||||
"OK", "Delete File",
|
||||
[path](auto, bool btn2) {
|
||||
if (btn2) {
|
||||
std::error_code ec;
|
||||
std::filesystem::remove(path, ec);
|
||||
if (ec) {
|
||||
FLAlertLayer::create(
|
||||
"Unable to Delete",
|
||||
fmt::format(
|
||||
"Unable to delete <cy>{}</c>: {} (Error code <cr>{}</c>)",
|
||||
path, ec.message(), ec.value()
|
||||
),
|
||||
"OK"
|
||||
)->show();
|
||||
}
|
||||
// No need to show a confirmation popup if successful since that's
|
||||
// to be assumed via pressing the button on the previous popup
|
||||
}
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
if (auto existing = Loader::get()->getInstalledMod(meta.getID())) {
|
||||
createQuickPopup(
|
||||
"Already Installed",
|
||||
fmt::format(
|
||||
"The mod <cy>{}</c> <cj>{}</c> has already been installed "
|
||||
"as version <cl>{}</c>. Do you want to <co>replace the "
|
||||
"installed version with the file</c>?",
|
||||
meta.getID(), meta.getVersion(),
|
||||
existing->getVersion()
|
||||
),
|
||||
"Cancel", "Replace",
|
||||
[doInstallModFromFile, path, existing, meta](auto, bool btn2) mutable {
|
||||
std::error_code ec;
|
||||
std::filesystem::remove(existing->getPackagePath(), ec);
|
||||
if (ec) {
|
||||
FLAlertLayer::create(
|
||||
"Unable to Uninstall",
|
||||
fmt::format(
|
||||
"Unable to uninstall <cy>{}</c>: {} (Error code <cr>{}</c>)",
|
||||
existing->getID(), ec.message(), ec.value()
|
||||
),
|
||||
"OK"
|
||||
)->show();
|
||||
return;
|
||||
}
|
||||
doInstallModFromFile();
|
||||
}
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
doInstallModFromFile();
|
||||
}
|
||||
|
||||
bool Loader::Impl::isRestartRequired() const {
|
||||
for (auto mod : Loader::get()->getAllMods()) {
|
||||
if (mod->getRequestedAction() != ModRequestedAction::None) {
|
||||
return true;
|
||||
}
|
||||
if (ModSettingsManager::from(mod)->restartRequired()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (server::ModDownloadManager::get()->wantsRestart()) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|