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
|
# Geode Changelog
|
||||||
|
|
||||||
|
## v4.0.0-beta.1
|
||||||
|
* Button to manually install mods from files (e881dc5)
|
||||||
|
* Add `ModRequestedAction::Update` (e881dc5)
|
||||||
|
* Add `ModMetadata::checkGeodeVersion` and `ModMetadata::checkTargetVersions` (e881dc5)
|
||||||
|
* Add `geode::createModLogo` for creating a logo from a `.geode` package (e881dc5)
|
||||||
|
* Tags now use names provided by the server (893b03e)
|
||||||
|
* Add web support for multiple request headers with same name (#1150)
|
||||||
|
* Fix `Task::chain` using the wrong type in the impl (22a11b9)
|
||||||
|
* Fix installing mods not checking the current version (#1148)
|
||||||
|
* Fix searching for mods ignoring geode and gd version (#1153)
|
||||||
|
* Fix crash when checking tags (01807fe)
|
||||||
|
* Fix 'Outdated' label being visible while updating (6679a69)
|
||||||
|
* Fix log nesting issue (0e8d4c6)
|
||||||
|
* Remove forward compat message box as it confuses users (5592ef6)
|
||||||
|
* Fix crash on opening mod changelogs (9834cb2)
|
||||||
|
* Make `ColorPickPopup` pimpl (1a201e1)
|
||||||
|
* Fix lag issue in `ColorPickPopup` (3081164)
|
||||||
|
* Change return type of `ModSettingsManager::save` (da92090)
|
||||||
|
* Fix every misspelling of successfully (#1151)
|
||||||
|
* Allow building geode itself in debug mode (5645399)
|
||||||
|
|
||||||
|
## v4.0.0-alpha.1
|
||||||
|
* Support for the 2.2074 update
|
||||||
|
* Developers, see [this page for a migration guide](https://docs.geode-sdk.org/tutorials/migrate-v4)
|
||||||
|
* Major API breaks:
|
||||||
|
* Remove everything previously marked deprecated
|
||||||
|
* `utils::MiniFunction` removed
|
||||||
|
* Rewritten `geode::Result` class
|
||||||
|
* Rewritten matjson library
|
||||||
|
* Settings V2 completely removed, use V3 now
|
||||||
|
* `JsonChecker` removed
|
||||||
|
* Add new system for ordered hook priority, [see docs](https://docs.geode-sdk.org/tutorials/hookpriority) (673317d, 6db3084)
|
||||||
|
* C++20 coroutine support for `geode::Task`, [see docs](https://docs.geode-sdk.org/tutorials/tasks#coroutines) (e61b2c0, ab196b9)
|
||||||
|
* Add `Task::chain`, [see docs](https://docs.geode-sdk.org/tutorials/tasks#chaining-tasks) (3248831)
|
||||||
|
* Single page local mods list (efb1fbf)
|
||||||
|
* Split mod problems into load and outdated (12e8bbb, 09fa872, df2528c)
|
||||||
|
* This means mods made for outdated gd or geode versions no longer count as actual errors, resulting in less clutter in the ui
|
||||||
|
* Fix safe mode popup on windows showing up when not supposed to (038788b)
|
||||||
|
* WebRequest::ignoreContentLength (#1126)
|
||||||
|
* Lots of smaller fixes to the geode ui (c9afa75, f5f3365, 2d66279, 02845d9, 9b95301, 6d13f78, 123b3ab, 0b2fc66, f96ea5e, cad670f)
|
||||||
|
* Fix CCArrayExt::pop_back() return type (#1130)
|
||||||
|
* Add missing spanish translations to installer (#1145)
|
||||||
|
* Add hashtag symbol to CommonFilter::Any (#1131)
|
||||||
|
* Disable forward compat on android (c9e97af)
|
||||||
|
|
||||||
|
## v3.9.2
|
||||||
|
* Fix searching for mods returning unavailable mods (#1149)
|
||||||
|
|
||||||
|
## v3.9.1
|
||||||
|
* Fix mod downloads not checking version (f575187)
|
||||||
|
|
||||||
## v3.9.0
|
## v3.9.0
|
||||||
* Many changes to the settings ui (#1108)
|
* Many changes to the settings ui (#1108)
|
||||||
* Fuzzy search is now more reasonable
|
* Fuzzy search is now more reasonable
|
||||||
|
|
|
@ -189,8 +189,8 @@ include(cmake/Platform.cmake)
|
||||||
include(cmake/GeodeFile.cmake)
|
include(cmake/GeodeFile.cmake)
|
||||||
|
|
||||||
if (NOT DEFINED GEODE_GD_VERSION)
|
if (NOT DEFINED GEODE_GD_VERSION)
|
||||||
set(GEODE_GD_VERSION 2.206)
|
set(GEODE_GD_VERSION 2.2074)
|
||||||
set(GEODE_COMP_GD_VERSION 22060)
|
set(GEODE_COMP_GD_VERSION 22074)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
target_compile_definitions(
|
target_compile_definitions(
|
||||||
|
@ -236,8 +236,9 @@ if (ANDROID)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
set(MAT_JSON_AS_INTERFACE ON)
|
set(MAT_JSON_AS_INTERFACE ON)
|
||||||
CPMAddPackage("gh:geode-sdk/json#cda9807")
|
CPMAddPackage("gh:geode-sdk/result@1.2.1")
|
||||||
CPMAddPackage("gh:fmtlib/fmt#10.2.1")
|
CPMAddPackage("gh:geode-sdk/json@3.1.0")
|
||||||
|
CPMAddPackage("gh:fmtlib/fmt#11.0.2")
|
||||||
|
|
||||||
target_compile_definitions(${PROJECT_NAME} INTERFACE MAT_JSON_DYNAMIC=1)
|
target_compile_definitions(${PROJECT_NAME} INTERFACE MAT_JSON_DYNAMIC=1)
|
||||||
|
|
||||||
|
@ -266,7 +267,7 @@ if (DEFINED GEODE_TULIPHOOK_REPO_PATH)
|
||||||
message(STATUS "Using ${GEODE_TULIPHOOK_REPO_PATH} for TulipHook")
|
message(STATUS "Using ${GEODE_TULIPHOOK_REPO_PATH} for TulipHook")
|
||||||
add_subdirectory(${GEODE_TULIPHOOK_REPO_PATH} ${GEODE_TULIPHOOK_REPO_PATH}/build)
|
add_subdirectory(${GEODE_TULIPHOOK_REPO_PATH} ${GEODE_TULIPHOOK_REPO_PATH}/build)
|
||||||
else()
|
else()
|
||||||
CPMAddPackage("gh:geode-sdk/TulipHook#d1d9559")
|
CPMAddPackage("gh:geode-sdk/TulipHook@2.4.1")
|
||||||
endif()
|
endif()
|
||||||
set(CMAKE_WARN_DEPRECATED ON CACHE BOOL "" FORCE)
|
set(CMAKE_WARN_DEPRECATED ON CACHE BOOL "" FORCE)
|
||||||
|
|
||||||
|
@ -296,7 +297,7 @@ target_include_directories(GeodeBindings PUBLIC
|
||||||
${GEODE_LOADER_PATH}/include/Geode/fmod
|
${GEODE_LOADER_PATH}/include/Geode/fmod
|
||||||
)
|
)
|
||||||
target_link_directories(GeodeBindings PUBLIC ${GEODE_LOADER_PATH}/include/link)
|
target_link_directories(GeodeBindings PUBLIC ${GEODE_LOADER_PATH}/include/link)
|
||||||
target_link_libraries(GeodeBindings PUBLIC fmt TulipHookInclude mat-json)
|
target_link_libraries(GeodeBindings PUBLIC fmt TulipHookInclude mat-json GeodeResult)
|
||||||
target_link_libraries(${PROJECT_NAME} INTERFACE GeodeBindings)
|
target_link_libraries(${PROJECT_NAME} INTERFACE GeodeBindings)
|
||||||
|
|
||||||
if (NOT EXISTS ${GEODE_BIN_PATH})
|
if (NOT EXISTS ${GEODE_BIN_PATH})
|
||||||
|
|
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
|
# SPDX-FileCopyrightText: Copyright (c) 2019-2023 Lars Melchior and contributors
|
||||||
|
|
||||||
set(CPM_DOWNLOAD_VERSION 0.39.0)
|
set(CPM_DOWNLOAD_VERSION 0.40.2)
|
||||||
set(CPM_HASH_SUM "66639bcac9dd2907b2918de466783554c1334446b9874e90d38e3778d404c2ef")
|
set(CPM_HASH_SUM "c8cdc32c03816538ce22781ed72964dc864b2a34a310d3b7104812a5ca2d835d")
|
||||||
|
|
||||||
if(CPM_SOURCE_CACHE)
|
if(CPM_SOURCE_CACHE)
|
||||||
set(CPM_DOWNLOAD_LOCATION "${CPM_SOURCE_CACHE}/cpm/CPM_${CPM_DOWNLOAD_VERSION}.cmake")
|
set(CPM_DOWNLOAD_LOCATION "${CPM_SOURCE_CACHE}/cpm/CPM_${CPM_DOWNLOAD_VERSION}.cmake")
|
||||||
|
|
|
@ -72,19 +72,27 @@ elseif (GEODE_TARGET_PLATFORM STREQUAL "Win64")
|
||||||
target_link_libraries(${PROJECT_NAME} INTERFACE
|
target_link_libraries(${PROJECT_NAME} INTERFACE
|
||||||
${GEODE_LOADER_PATH}/include/link/win64/libcocos2d.lib
|
${GEODE_LOADER_PATH}/include/link/win64/libcocos2d.lib
|
||||||
${GEODE_LOADER_PATH}/include/link/win64/libExtensions.lib
|
${GEODE_LOADER_PATH}/include/link/win64/libExtensions.lib
|
||||||
${GEODE_LOADER_PATH}/include/link/win64/ssl.lib
|
|
||||||
${GEODE_LOADER_PATH}/include/link/win64/crypto.lib
|
|
||||||
${GEODE_LOADER_PATH}/include/link/win64/nghttp2.lib
|
|
||||||
${GEODE_LOADER_PATH}/include/link/win64/ngtcp2.lib
|
|
||||||
${GEODE_LOADER_PATH}/include/link/win64/nghttp3.lib
|
|
||||||
${GEODE_LOADER_PATH}/include/link/win64/ngtcp2_crypto_boringssl.lib
|
|
||||||
${GEODE_LOADER_PATH}/include/link/win64/libcurl.lib
|
|
||||||
${GEODE_LOADER_PATH}/include/link/win64/glew32.lib
|
${GEODE_LOADER_PATH}/include/link/win64/glew32.lib
|
||||||
${GEODE_LOADER_PATH}/include/link/win64/gdstring.lib
|
|
||||||
${GEODE_LOADER_PATH}/include/link/win64/fmod.lib
|
${GEODE_LOADER_PATH}/include/link/win64/fmod.lib
|
||||||
opengl32
|
opengl32
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if (PROJECT_IS_TOP_LEVEL AND CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||||
|
target_link_libraries(${PROJECT_NAME} INTERFACE
|
||||||
|
${GEODE_LOADER_PATH}/include/link/win64/gd-libcurl.lib
|
||||||
|
)
|
||||||
|
else()
|
||||||
|
target_link_libraries(${PROJECT_NAME} INTERFACE
|
||||||
|
${GEODE_LOADER_PATH}/include/link/win64/ssl.lib
|
||||||
|
${GEODE_LOADER_PATH}/include/link/win64/crypto.lib
|
||||||
|
${GEODE_LOADER_PATH}/include/link/win64/nghttp2.lib
|
||||||
|
${GEODE_LOADER_PATH}/include/link/win64/ngtcp2.lib
|
||||||
|
${GEODE_LOADER_PATH}/include/link/win64/nghttp3.lib
|
||||||
|
${GEODE_LOADER_PATH}/include/link/win64/ngtcp2_crypto_boringssl.lib
|
||||||
|
${GEODE_LOADER_PATH}/include/link/win64/libcurl.lib
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
# Windows links against .lib and not .dll
|
# Windows links against .lib and not .dll
|
||||||
set(GEODE_OUTPUT_NAME "Geode")
|
set(GEODE_OUTPUT_NAME "Geode")
|
||||||
set(GEODE_PLATFORM_BINARY "Geode.lib")
|
set(GEODE_PLATFORM_BINARY "Geode.lib")
|
||||||
|
|
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
|
!insertmacro LANGFILE_EXT SpanishInternational
|
||||||
|
|
||||||
!pragma warning disable 6030
|
!pragma warning disable 6030
|
||||||
${LangFileString} MUI_TEXT_WELCOME_INFO_TEXT "Este asistente le guiará durante la instalación de $(^NameDA) en su computadora.$\r$\n$\r$\nAntes de iniciar la instalación, asegúrese de que Geometry Dash no se está ejecutando$\r$\n$\r$\n$_CLICK"
|
${LangFileString} MUI_TEXT_WELCOME_INFO_TEXT "Este asistente le guiará durante la instalación de Geode en su computadora.$\r$\n$\r$\nAntes de iniciar la instalación, asegúrese de que Geometry Dash no se está ejecutando$\r$\n$\r$\n$_CLICK"
|
||||||
${LangFileString} MUI_UNTEXT_WELCOME_INFO_TEXT "Este asistente le guiará durante la desinstalación de $(^NameDA).$\r$\n$\r$\nAntes de iniciar la desinstalación, asegúrese de que Geometry Dash no se está ejecutando.$\r$\n$\r$\n$_CLICK"
|
${LangFileString} MUI_UNTEXT_WELCOME_INFO_TEXT "Este asistente le guiará durante la desinstalación de Geode.$\r$\n$\r$\nAntes de iniciar la desinstalación, asegúrese de que Geometry Dash no se está ejecutando.$\r$\n$\r$\n$_CLICK"
|
||||||
!pragma warning default 6030
|
!pragma warning default 6030
|
||||||
|
|
||||||
; installer
|
; installer
|
||||||
|
|
||||||
${LangFileString} GEODE_TEXT_GD_MISSING "$\r$\n$\r$\n¡Geometry Dash no está instalado en esta ruta!"
|
${LangFileString} GEODE_TEXT_GD_MISSING "$\r$\n$\r$\n¡Geometry Dash no está instalado en esta ruta!"
|
||||||
${LangFileString} GEODE_TEXT_GD_OLD "$\r$\n$\r$\nYour version of Geometry Dash is too old for this version of Geode!"
|
${LangFileString} GEODE_TEXT_GD_OLD "$\r$\n$\r$\n¡Su versión de Geometry Dash es demasiado antigua para esta versión de Geode!"
|
||||||
${LangFileString} GEODE_TEXT_MOD_LOADER_ALREADY_INSTALLED "This path has other mods installed!$\r$\nThey will be overwritten by Geode. (the dll trademark)"
|
${LangFileString} GEODE_TEXT_MOD_LOADER_ALREADY_INSTALLED "¡Esta ruta ya tiene otros mods instalados!$\r$\nVan a ser sobreescritos por Geode. (the dll trademark)"
|
||||||
|
|
||||||
; uninstaller
|
; uninstaller
|
||||||
|
|
||||||
|
|
|
@ -58,6 +58,7 @@
|
||||||
!insertmacro GEODE_LANGUAGE "Polish"
|
!insertmacro GEODE_LANGUAGE "Polish"
|
||||||
!insertmacro GEODE_LANGUAGE "Russian"
|
!insertmacro GEODE_LANGUAGE "Russian"
|
||||||
!insertmacro GEODE_LANGUAGE "PortugueseBR"
|
!insertmacro GEODE_LANGUAGE "PortugueseBR"
|
||||||
|
!insertmacro GEODE_LANGUAGE "Portuguese"
|
||||||
!insertmacro GEODE_LANGUAGE "Ukrainian"
|
!insertmacro GEODE_LANGUAGE "Ukrainian"
|
||||||
!insertmacro GEODE_LANGUAGE "Czech"
|
!insertmacro GEODE_LANGUAGE "Czech"
|
||||||
!insertmacro GEODE_LANGUAGE "Turkish"
|
!insertmacro GEODE_LANGUAGE "Turkish"
|
||||||
|
|
|
@ -5,21 +5,10 @@
|
||||||
#include <Geode/platform/platform.hpp>
|
#include <Geode/platform/platform.hpp>
|
||||||
#include <variant>
|
#include <variant>
|
||||||
|
|
||||||
#define GEODE_STATIC_PTR(type, name) \
|
#if !defined(GEODE_CONCAT)
|
||||||
static type* s_##name; \
|
#define GEODE_WRAPPER_CONCAT(x, y) x##y
|
||||||
inline type* name() { \
|
#define GEODE_CONCAT(x, y) GEODE_WRAPPER_CONCAT(x, y)
|
||||||
if (!s_##name) s_##name = new type(); \
|
#endif
|
||||||
return s_##name; \
|
|
||||||
}
|
|
||||||
|
|
||||||
#define GEODE_STATIC_VAR(type, name) \
|
|
||||||
inline type& name() { \
|
|
||||||
static type s_##name; \
|
|
||||||
return s_##name; \
|
|
||||||
}
|
|
||||||
|
|
||||||
#define GEODE_WRAPPER_CONCAT(x, y) x##y
|
|
||||||
#define GEODE_CONCAT(x, y) GEODE_WRAPPER_CONCAT(x, y)
|
|
||||||
|
|
||||||
#define GEODE_WRAPPER_STR(...) #__VA_ARGS__
|
#define GEODE_WRAPPER_STR(...) #__VA_ARGS__
|
||||||
#define GEODE_STR(...) GEODE_WRAPPER_STR(__VA_ARGS__)
|
#define GEODE_STR(...) GEODE_WRAPPER_STR(__VA_ARGS__)
|
||||||
|
|
|
@ -6,7 +6,8 @@
|
||||||
#include "ui/EnterLayerEvent.hpp"
|
#include "ui/EnterLayerEvent.hpp"
|
||||||
#include "ui/BasedButtonSprite.hpp"
|
#include "ui/BasedButtonSprite.hpp"
|
||||||
#include "ui/IconButtonSprite.hpp"
|
#include "ui/IconButtonSprite.hpp"
|
||||||
#include "ui/InputNode.hpp"
|
#include "ui/Layout.hpp"
|
||||||
|
#include "ui/SpacerNode.hpp"
|
||||||
#include "ui/General.hpp"
|
#include "ui/General.hpp"
|
||||||
#include "ui/ListView.hpp"
|
#include "ui/ListView.hpp"
|
||||||
#include "ui/MDPopup.hpp"
|
#include "ui/MDPopup.hpp"
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "DefaultInclude.hpp"
|
#include "DefaultInclude.hpp"
|
||||||
#include "utils/Result.hpp"
|
|
||||||
#include "utils/VersionInfo.hpp"
|
#include "utils/VersionInfo.hpp"
|
||||||
#include "utils/ranges.hpp"
|
#include "utils/ranges.hpp"
|
||||||
#include "utils/casts.hpp"
|
#include "utils/casts.hpp"
|
||||||
|
@ -12,5 +11,4 @@
|
||||||
#include "utils/permission.hpp"
|
#include "utils/permission.hpp"
|
||||||
#include "utils/general.hpp"
|
#include "utils/general.hpp"
|
||||||
#include "utils/timer.hpp"
|
#include "utils/timer.hpp"
|
||||||
#include "utils/MiniFunction.hpp"
|
|
||||||
#include "utils/ObjcHook.hpp"
|
#include "utils/ObjcHook.hpp"
|
||||||
|
|
|
@ -93,7 +93,7 @@ namespace gd {
|
||||||
bool empty() const;
|
bool empty() const;
|
||||||
|
|
||||||
bool operator==(string const& other) const;
|
bool operator==(string const& other) const;
|
||||||
bool operator==(std::string_view const other) const;
|
bool operator==(std::string_view other) const;
|
||||||
bool operator==(char const* other) const {
|
bool operator==(char const* other) const {
|
||||||
return *this == std::string_view(other);
|
return *this == std::string_view(other);
|
||||||
}
|
}
|
||||||
|
@ -101,7 +101,7 @@ namespace gd {
|
||||||
return *this == std::string_view(other);
|
return *this == std::string_view(other);
|
||||||
}
|
}
|
||||||
std::strong_ordering operator<=>(string const& other) const;
|
std::strong_ordering operator<=>(string const& other) const;
|
||||||
std::strong_ordering operator<=>(std::string_view const other) const;
|
std::strong_ordering operator<=>(std::string_view other) const;
|
||||||
std::strong_ordering operator<=>(char const* other) const {
|
std::strong_ordering operator<=>(char const* other) const {
|
||||||
return *this <=> std::string_view(other);
|
return *this <=> std::string_view(other);
|
||||||
}
|
}
|
||||||
|
|
63
loader/include/Geode/cocos/base_nodes/CCNode.h
vendored
|
@ -37,7 +37,6 @@
|
||||||
#include "../kazmath/include/kazmath/kazmath.h"
|
#include "../kazmath/include/kazmath/kazmath.h"
|
||||||
#include "../script_support/CCScriptSupport.h"
|
#include "../script_support/CCScriptSupport.h"
|
||||||
#include "../include/CCProtocols.h"
|
#include "../include/CCProtocols.h"
|
||||||
#include "Layout.hpp"
|
|
||||||
#include "../../loader/Event.hpp"
|
#include "../../loader/Event.hpp"
|
||||||
#include <Geode/utils/casts.hpp>
|
#include <Geode/utils/casts.hpp>
|
||||||
|
|
||||||
|
@ -45,6 +44,12 @@
|
||||||
#include <matjson.hpp>
|
#include <matjson.hpp>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
namespace geode {
|
||||||
|
class Layout;
|
||||||
|
class LayoutOptions;
|
||||||
|
enum class Anchor;
|
||||||
|
}
|
||||||
|
|
||||||
NS_CC_BEGIN
|
NS_CC_BEGIN
|
||||||
|
|
||||||
class CCCamera;
|
class CCCamera;
|
||||||
|
@ -871,31 +876,19 @@ public:
|
||||||
private:
|
private:
|
||||||
friend class geode::modifier::FieldContainer;
|
friend class geode::modifier::FieldContainer;
|
||||||
|
|
||||||
[[deprecated("Will be removed, it's an ABI break")]]
|
|
||||||
GEODE_DLL geode::modifier::FieldContainer* getFieldContainer();
|
|
||||||
GEODE_DLL geode::modifier::FieldContainer* getFieldContainer(char const* forClass);
|
GEODE_DLL geode::modifier::FieldContainer* getFieldContainer(char const* forClass);
|
||||||
GEODE_DLL void addEventListenerInternal(
|
GEODE_DLL void addEventListenerInternal(
|
||||||
std::string const& id,
|
std::string const& id,
|
||||||
geode::EventListenerProtocol* protocol
|
geode::EventListenerProtocol* protocol
|
||||||
);
|
);
|
||||||
|
|
||||||
#ifdef GEODE_EXPORTING
|
public:
|
||||||
[[deprecated("Will be removed, it's an ABI break")]]
|
|
||||||
GEODE_DLL std::optional<matjson::Value> getAttributeInternal(std::string const& attribute);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
public:
|
|
||||||
#ifdef GEODE_EXPORTING
|
|
||||||
[[deprecated("Will be removed, it's an ABI break")]]
|
|
||||||
GEODE_DLL void setAttribute(std::string const& attribute, matjson::Value const& value);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the string ID of this node
|
* Get the string ID of this node
|
||||||
* @returns The ID, or an empty string if the node has no ID.
|
* @returns The ID, or an empty string if the node has no ID.
|
||||||
* @note Geode addition
|
* @note Geode addition
|
||||||
*/
|
*/
|
||||||
GEODE_DLL std::string getID();
|
GEODE_DLL const std::string& getID();
|
||||||
/**
|
/**
|
||||||
* Set the string ID of this node. String IDs are a Geode addition
|
* Set the string ID of this node. String IDs are a Geode addition
|
||||||
* that are much safer to use to get nodes than absolute indexes
|
* that are much safer to use to get nodes than absolute indexes
|
||||||
|
@ -906,13 +899,23 @@ public:
|
||||||
*/
|
*/
|
||||||
GEODE_DLL void setID(std::string const& id);
|
GEODE_DLL void setID(std::string const& id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the string ID of this node. String IDs are a Geode addition
|
||||||
|
* that are much safer to use to get nodes than absolute indexes
|
||||||
|
* @param id The ID of the node, recommended to be in kebab case
|
||||||
|
* without any spaces or uppercase letters. If the node is added
|
||||||
|
* by a mod, use the _spr literal to append the mod ID to it
|
||||||
|
* @note Geode addition
|
||||||
|
*/
|
||||||
|
GEODE_DLL void setID(std::string&& id);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a child by its string ID
|
* Get a child by its string ID
|
||||||
* @param id ID of the child
|
* @param id ID of the child
|
||||||
* @returns The child, or nullptr if none was found
|
* @returns The child, or nullptr if none was found
|
||||||
* @note Geode addition
|
* @note Geode addition
|
||||||
*/
|
*/
|
||||||
GEODE_DLL CCNode* getChildByID(std::string const& id);
|
GEODE_DLL CCNode* getChildByID(std::string_view id);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a child by its string ID. Recursively searches all the children
|
* Get a child by its string ID. Recursively searches all the children
|
||||||
|
@ -920,7 +923,7 @@ public:
|
||||||
* @returns The child, or nullptr if none was found
|
* @returns The child, or nullptr if none was found
|
||||||
* @note Geode addition
|
* @note Geode addition
|
||||||
*/
|
*/
|
||||||
GEODE_DLL CCNode* getChildByIDRecursive(std::string const& id);
|
GEODE_DLL CCNode* getChildByIDRecursive(std::string_view id);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a child based on a query. Searches the child tree for a matching
|
* Get a child based on a query. Searches the child tree for a matching
|
||||||
|
@ -936,14 +939,14 @@ public:
|
||||||
* ->getChildByID("mod.id/epic-button")`
|
* ->getChildByID("mod.id/epic-button")`
|
||||||
* @returns The first matching node, or nullptr if none was found
|
* @returns The first matching node, or nullptr if none was found
|
||||||
*/
|
*/
|
||||||
GEODE_DLL CCNode* querySelector(std::string const& query);
|
GEODE_DLL CCNode* querySelector(std::string_view query);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes a child from the container by its ID.
|
* Removes a child from the container by its ID.
|
||||||
* @param id The ID of the node
|
* @param id The ID of the node
|
||||||
* @note Geode addition
|
* @note Geode addition
|
||||||
*/
|
*/
|
||||||
GEODE_DLL void removeChildByID(std::string const& id);
|
GEODE_DLL void removeChildByID(std::string_view id);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a child before a specified existing child
|
* Add a child before a specified existing child
|
||||||
|
@ -990,13 +993,13 @@ public:
|
||||||
* CCLayers / CCMenus, this will change where the children are located
|
* CCLayers / CCMenus, this will change where the children are located
|
||||||
* @note Geode addition
|
* @note Geode addition
|
||||||
*/
|
*/
|
||||||
GEODE_DLL void setLayout(Layout* layout, bool apply = true, bool respectAnchor = true);
|
GEODE_DLL void setLayout(geode::Layout* layout, bool apply = true, bool respectAnchor = true);
|
||||||
/**
|
/**
|
||||||
* Get the Layout for this node
|
* Get the Layout for this node
|
||||||
* @returns The current layout, or nullptr if no layout is set
|
* @returns The current layout, or nullptr if no layout is set
|
||||||
* @note Geode addition
|
* @note Geode addition
|
||||||
*/
|
*/
|
||||||
GEODE_DLL Layout* getLayout();
|
GEODE_DLL geode::Layout* getLayout();
|
||||||
/**
|
/**
|
||||||
* Update the layout of this node using the current Layout. If no layout is
|
* Update the layout of this node using the current Layout. If no layout is
|
||||||
* set, nothing happens
|
* set, nothing happens
|
||||||
|
@ -1011,13 +1014,13 @@ public:
|
||||||
* @param apply Whether to update the layout of the parent node
|
* @param apply Whether to update the layout of the parent node
|
||||||
* @note Geode addition
|
* @note Geode addition
|
||||||
*/
|
*/
|
||||||
GEODE_DLL void setLayoutOptions(LayoutOptions* options, bool apply = true);
|
GEODE_DLL void setLayoutOptions(geode::LayoutOptions* options, bool apply = true);
|
||||||
/**
|
/**
|
||||||
* Get the layout options for this node
|
* Get the layout options for this node
|
||||||
* @returns The current layout options, or nullptr if no options are set
|
* @returns The current layout options, or nullptr if no options are set
|
||||||
* @note Geode addition
|
* @note Geode addition
|
||||||
*/
|
*/
|
||||||
GEODE_DLL LayoutOptions* getLayoutOptions();
|
GEODE_DLL geode::LayoutOptions* getLayoutOptions();
|
||||||
/**
|
/**
|
||||||
* Adds a child at an anchored position with an offset. The node is placed
|
* Adds a child at an anchored position with an offset. The node is placed
|
||||||
* in its parent where the anchor specifies, and then the offset is used to
|
* in its parent where the anchor specifies, and then the offset is used to
|
||||||
|
@ -1029,7 +1032,7 @@ public:
|
||||||
* if no other layout is already specified
|
* if no other layout is already specified
|
||||||
* @note Geode addition
|
* @note Geode addition
|
||||||
*/
|
*/
|
||||||
GEODE_DLL void addChildAtPosition(CCNode* child, Anchor anchor, CCPoint const& offset = CCPointZero, bool useAnchorLayout = true);
|
GEODE_DLL void addChildAtPosition(CCNode* child, geode::Anchor anchor, CCPoint const& offset = CCPointZero, bool useAnchorLayout = true);
|
||||||
/**
|
/**
|
||||||
* Adds a child at an anchored position with an offset. The node is placed
|
* Adds a child at an anchored position with an offset. The node is placed
|
||||||
* in its parent where the anchor specifies, and then the offset is used to
|
* in its parent where the anchor specifies, and then the offset is used to
|
||||||
|
@ -1044,7 +1047,7 @@ public:
|
||||||
*/
|
*/
|
||||||
GEODE_DLL void addChildAtPosition(
|
GEODE_DLL void addChildAtPosition(
|
||||||
CCNode* child,
|
CCNode* child,
|
||||||
Anchor anchor,
|
geode::Anchor anchor,
|
||||||
CCPoint const& offset,
|
CCPoint const& offset,
|
||||||
CCPoint const& nodeAnchor,
|
CCPoint const& nodeAnchor,
|
||||||
bool useAnchorLayout = true
|
bool useAnchorLayout = true
|
||||||
|
@ -1057,7 +1060,7 @@ public:
|
||||||
* @param offset Where to place the child relative to the anchor
|
* @param offset Where to place the child relative to the anchor
|
||||||
* @note Geode addition
|
* @note Geode addition
|
||||||
*/
|
*/
|
||||||
GEODE_DLL void updateAnchoredPosition(Anchor anchor, CCPoint const& offset = CCPointZero);
|
GEODE_DLL void updateAnchoredPosition(geode::Anchor anchor, CCPoint const& offset = CCPointZero);
|
||||||
/**
|
/**
|
||||||
* Updates the anchored position of a child. Requires the child to already
|
* Updates the anchored position of a child. Requires the child to already
|
||||||
* have a parent; if the child already has AnchorLayoutOptions set, those
|
* have a parent; if the child already has AnchorLayoutOptions set, those
|
||||||
|
@ -1068,7 +1071,7 @@ public:
|
||||||
* @note Geode addition
|
* @note Geode addition
|
||||||
*/
|
*/
|
||||||
GEODE_DLL void updateAnchoredPosition(
|
GEODE_DLL void updateAnchoredPosition(
|
||||||
Anchor anchor,
|
geode::Anchor anchor,
|
||||||
CCPoint const& offset,
|
CCPoint const& offset,
|
||||||
CCPoint const& nodeAnchor
|
CCPoint const& nodeAnchor
|
||||||
);
|
);
|
||||||
|
@ -1102,7 +1105,7 @@ public:
|
||||||
template <class Filter, class... Args>
|
template <class Filter, class... Args>
|
||||||
geode::EventListenerProtocol* addEventListener(
|
geode::EventListenerProtocol* addEventListener(
|
||||||
std::string const& id,
|
std::string const& id,
|
||||||
geode::utils::MiniFunction<typename Filter::Callback> callback,
|
std::function<typename Filter::Callback> callback,
|
||||||
Args&&... args
|
Args&&... args
|
||||||
) {
|
) {
|
||||||
auto listener = new geode::EventListener<Filter>(
|
auto listener = new geode::EventListener<Filter>(
|
||||||
|
@ -1113,7 +1116,7 @@ public:
|
||||||
}
|
}
|
||||||
template <class Filter, class... Args>
|
template <class Filter, class... Args>
|
||||||
geode::EventListenerProtocol* addEventListener(
|
geode::EventListenerProtocol* addEventListener(
|
||||||
geode::utils::MiniFunction<typename Filter::Callback> callback,
|
std::function<typename Filter::Callback> callback,
|
||||||
Args&&... args
|
Args&&... args
|
||||||
) {
|
) {
|
||||||
return this->addEventListener<Filter, Args...>(
|
return this->addEventListener<Filter, Args...>(
|
||||||
|
@ -1923,7 +1926,7 @@ namespace geode {
|
||||||
std::string m_targetID;
|
std::string m_targetID;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ListenerResult handle(utils::MiniFunction<Callback> fn, UserObjectSetEvent* event);
|
ListenerResult handle(std::function<Callback> fn, UserObjectSetEvent* event);
|
||||||
|
|
||||||
AttributeSetFilter(std::string const& id);
|
AttributeSetFilter(std::string const& id);
|
||||||
};
|
};
|
||||||
|
|
|
@ -28,6 +28,8 @@
|
||||||
#include "../../include/cocos2d.h"
|
#include "../../include/cocos2d.h"
|
||||||
#include "../ExtensionMacros.h"
|
#include "../ExtensionMacros.h"
|
||||||
|
|
||||||
|
enum class GJHttpType;
|
||||||
|
|
||||||
NS_CC_EXT_BEGIN
|
NS_CC_EXT_BEGIN
|
||||||
|
|
||||||
class CC_DLL CCHttpClient;
|
class CC_DLL CCHttpClient;
|
||||||
|
@ -216,6 +218,50 @@ public:
|
||||||
return _headers;
|
return _headers;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline int getType() {
|
||||||
|
return _type;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void setType(int type) {
|
||||||
|
_type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
// @note Geode addition
|
||||||
|
inline void setType(GJHttpType type) {
|
||||||
|
_type = static_cast<int>(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool getShouldCancel() {
|
||||||
|
return _shouldCancel;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void setShouldCancel(bool shouldCancel) {
|
||||||
|
_shouldCancel = shouldCancel;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int getDownloadProgress() {
|
||||||
|
return _downloadProgress;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void setDownloadProgress(int downloadProgress) {
|
||||||
|
_downloadProgress = downloadProgress;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int getReadTimeout() {
|
||||||
|
return _readTimeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void setReadTimeout(int readTimeout) {
|
||||||
|
_readTimeout = readTimeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int getConnectTimeout() {
|
||||||
|
return _connectTimeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void setConnectTimeout(int connectTimeout) {
|
||||||
|
_connectTimeout = connectTimeout;
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// properties
|
// properties
|
||||||
|
@ -229,13 +275,15 @@ protected:
|
||||||
gd::vector<gd::string> _headers; /// custom http headers
|
gd::vector<gd::string> _headers; /// custom http headers
|
||||||
|
|
||||||
// @note RobTop Addition
|
// @note RobTop Addition
|
||||||
int _requestTypeGJ;
|
int _type;
|
||||||
// @note RobTop Addition
|
// @note RobTop Addition
|
||||||
bool _shouldCancel;
|
bool _shouldCancel;
|
||||||
// @note RobTop Addition
|
// @note RobTop Addition
|
||||||
int _downloadProgress;
|
int _downloadProgress;
|
||||||
// @note RobTop Addition
|
// @note RobTop Addition
|
||||||
int _readTimeout;
|
int _readTimeout;
|
||||||
|
// @note RobTop Addition
|
||||||
|
int _connectTimeout;
|
||||||
};
|
};
|
||||||
|
|
||||||
NS_CC_EXT_END
|
NS_CC_EXT_END
|
||||||
|
|
1
loader/include/Geode/cocos/include/cocos2d.h
vendored
|
@ -59,7 +59,6 @@ THE SOFTWARE.
|
||||||
// base_nodes
|
// base_nodes
|
||||||
#include "../base_nodes/CCNode.h"
|
#include "../base_nodes/CCNode.h"
|
||||||
#include "../base_nodes/CCAtlasNode.h"
|
#include "../base_nodes/CCAtlasNode.h"
|
||||||
#include "../base_nodes/SpacerNode.hpp"
|
|
||||||
|
|
||||||
// cocoa
|
// cocoa
|
||||||
#include "../cocoa/CCAffineTransform.h"
|
#include "../cocoa/CCAffineTransform.h"
|
||||||
|
|
|
@ -142,7 +142,7 @@ public:
|
||||||
/**
|
/**
|
||||||
* @note RobTop addition
|
* @note RobTop addition
|
||||||
*/
|
*/
|
||||||
void toggleFullScreen(bool fullscreen, bool borderless);
|
void toggleFullScreen(bool fullscreen, bool borderless, bool fix);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @note RobTop addition
|
* @note RobTop addition
|
||||||
|
@ -185,6 +185,8 @@ public:
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
static CCEGLView* s_pEglView;
|
static CCEGLView* s_pEglView;
|
||||||
|
// @note unknown members here
|
||||||
|
uint8_t m_unkPad[8];
|
||||||
bool m_bCaptured;
|
bool m_bCaptured;
|
||||||
// Robtop Removal
|
// Robtop Removal
|
||||||
// HWND m_hWnd;
|
// HWND m_hWnd;
|
||||||
|
@ -203,6 +205,8 @@ protected:
|
||||||
int m_nRetinaFactor;
|
int m_nRetinaFactor;
|
||||||
// @note RobTop Addition
|
// @note RobTop Addition
|
||||||
bool m_bCursorHidden;
|
bool m_bCursorHidden;
|
||||||
|
// @note may be before m_bCursorHidden
|
||||||
|
int m_unkSize4;
|
||||||
// Robtop Removal
|
// Robtop Removal
|
||||||
// LPCWSTR m_menu;
|
// LPCWSTR m_menu;
|
||||||
// Robtop Removal
|
// Robtop Removal
|
||||||
|
@ -225,6 +229,8 @@ public:
|
||||||
// @note RobTop Addition
|
// @note RobTop Addition
|
||||||
bool m_bIsBorderless;
|
bool m_bIsBorderless;
|
||||||
// @note RobTop Addition
|
// @note RobTop Addition
|
||||||
|
bool m_bIsFix;
|
||||||
|
// @note RobTop Addition
|
||||||
bool m_bShouldHideCursor;
|
bool m_bShouldHideCursor;
|
||||||
// @note RobTop Addition
|
// @note RobTop Addition
|
||||||
bool m_bCursorLocked;
|
bool m_bCursorLocked;
|
||||||
|
|
|
@ -208,9 +208,9 @@ namespace cocos2d
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Custom function added for geode; returns if the
|
* Custom function added for geode; returns if the
|
||||||
* zip file was succesfully decoded.
|
* zip file was successfully decoded.
|
||||||
*
|
*
|
||||||
* @return true if the zip was succesfully loaded,
|
* @return true if the zip was successfully loaded,
|
||||||
* false otherwise.
|
* false otherwise.
|
||||||
*
|
*
|
||||||
* @since geode v1.0.0
|
* @since geode v1.0.0
|
||||||
|
|
5994
loader/include/Geode/external/result/result.hpp
vendored
|
@ -53,7 +53,7 @@ namespace geode {
|
||||||
return dispatchPools()[m_id];
|
return dispatchPools()[m_id];
|
||||||
}
|
}
|
||||||
|
|
||||||
ListenerResult handle(utils::MiniFunction<Callback> fn, Ev* event) {
|
ListenerResult handle(std::function<Callback> fn, Ev* event) {
|
||||||
if (event->getID() == m_id) {
|
if (event->getID() == m_id) {
|
||||||
return std::apply(fn, event->getArgs());
|
return std::apply(fn, event->getArgs());
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "../utils/casts.hpp"
|
#include "../utils/casts.hpp"
|
||||||
#include "../utils/MiniFunction.hpp"
|
|
||||||
|
|
||||||
#include <Geode/DefaultInclude.hpp>
|
#include <Geode/DefaultInclude.hpp>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
@ -52,6 +51,7 @@ namespace geode {
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static DefaultEventListenerPool* create();
|
static DefaultEventListenerPool* create();
|
||||||
|
DefaultEventListenerPool();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
bool add(EventListenerProtocol* listener) override;
|
bool add(EventListenerProtocol* listener) override;
|
||||||
|
@ -64,10 +64,7 @@ namespace geode {
|
||||||
friend class DispatchEvent;
|
friend class DispatchEvent;
|
||||||
|
|
||||||
template <class... Args>
|
template <class... Args>
|
||||||
friend class DispatchFilter;
|
friend class DispatchFilter;
|
||||||
|
|
||||||
// todo: make this private in Geode 4.0.0
|
|
||||||
DefaultEventListenerPool();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class GEODE_DLL EventListenerProtocol {
|
class GEODE_DLL EventListenerProtocol {
|
||||||
|
@ -103,7 +100,7 @@ namespace geode {
|
||||||
using Callback = ListenerResult(T*);
|
using Callback = ListenerResult(T*);
|
||||||
using Event = T;
|
using Event = T;
|
||||||
|
|
||||||
ListenerResult handle(utils::MiniFunction<Callback> fn, T* e) {
|
ListenerResult handle(std::function<Callback> fn, T* e) {
|
||||||
return fn(e);
|
return fn(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -156,7 +153,7 @@ namespace geode {
|
||||||
this->enable();
|
this->enable();
|
||||||
}
|
}
|
||||||
|
|
||||||
EventListener(utils::MiniFunction<Callback> fn, T filter = T())
|
EventListener(std::function<Callback> fn, T filter = T())
|
||||||
: m_callback(fn), m_filter(filter)
|
: m_callback(fn), m_filter(filter)
|
||||||
{
|
{
|
||||||
m_filter.setListener(this);
|
m_filter.setListener(this);
|
||||||
|
@ -193,10 +190,7 @@ namespace geode {
|
||||||
this->enable();
|
this->enable();
|
||||||
}
|
}
|
||||||
|
|
||||||
void bind(utils::MiniFunction<Callback> const& fn) {
|
void bind(std::function<Callback> fn) {
|
||||||
m_callback = fn;
|
|
||||||
}
|
|
||||||
void bind(utils::MiniFunction<Callback>&& fn) {
|
|
||||||
m_callback = fn;
|
m_callback = fn;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -218,12 +212,12 @@ namespace geode {
|
||||||
return m_filter;
|
return m_filter;
|
||||||
}
|
}
|
||||||
|
|
||||||
utils::MiniFunction<Callback>& getCallback() {
|
std::function<Callback>& getCallback() {
|
||||||
return m_callback;
|
return m_callback;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
utils::MiniFunction<Callback> m_callback = nullptr;
|
std::function<Callback> m_callback = nullptr;
|
||||||
T m_filter;
|
T m_filter;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -57,7 +57,7 @@ namespace geode::ipc {
|
||||||
std::string m_messageID;
|
std::string m_messageID;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ListenerResult handle(utils::MiniFunction<Callback> fn, IPCEvent* event);
|
ListenerResult handle(std::function<Callback> fn, IPCEvent* event);
|
||||||
IPCFilter(
|
IPCFilter(
|
||||||
std::string const& modID,
|
std::string const& modID,
|
||||||
std::string const& messageID
|
std::string const& messageID
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include "../utils/Result.hpp"
|
#include <Geode/Result.hpp>
|
||||||
#include "../utils/MiniFunction.hpp"
|
|
||||||
#include "Log.hpp"
|
#include "Log.hpp"
|
||||||
#include "ModEvent.hpp"
|
#include "ModEvent.hpp"
|
||||||
#include "ModMetadata.hpp"
|
#include "ModMetadata.hpp"
|
||||||
|
@ -15,7 +14,7 @@
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
|
|
||||||
namespace geode {
|
namespace geode {
|
||||||
using ScheduledFunction = utils::MiniFunction<void()>;
|
using ScheduledFunction = std::function<void()>;
|
||||||
|
|
||||||
struct InvalidGeodeFile {
|
struct InvalidGeodeFile {
|
||||||
std::filesystem::path path;
|
std::filesystem::path path;
|
||||||
|
@ -47,6 +46,21 @@ namespace geode {
|
||||||
Type type;
|
Type type;
|
||||||
std::variant<std::filesystem::path, ModMetadata, Mod*> cause;
|
std::variant<std::filesystem::path, ModMetadata, Mod*> cause;
|
||||||
std::string message;
|
std::string message;
|
||||||
|
|
||||||
|
bool isSuggestion() const {
|
||||||
|
return
|
||||||
|
type == LoadProblem::Type::Recommendation ||
|
||||||
|
type == LoadProblem::Type::Suggestion;
|
||||||
|
}
|
||||||
|
bool isOutdated() const {
|
||||||
|
return
|
||||||
|
type == LoadProblem::Type::UnsupportedVersion ||
|
||||||
|
type == LoadProblem::Type::NeedsNewerGeodeVersion ||
|
||||||
|
type == LoadProblem::Type::UnsupportedGeodeVersion;
|
||||||
|
}
|
||||||
|
bool isProblem() const {
|
||||||
|
return !isSuggestion() && !isOutdated();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class LoaderImpl;
|
class LoaderImpl;
|
||||||
|
@ -92,7 +106,8 @@ namespace geode {
|
||||||
Mod* getLoadedMod(std::string const& id) const;
|
Mod* getLoadedMod(std::string const& id) const;
|
||||||
std::vector<Mod*> getAllMods();
|
std::vector<Mod*> getAllMods();
|
||||||
std::vector<LoadProblem> getAllProblems() const;
|
std::vector<LoadProblem> getAllProblems() const;
|
||||||
std::vector<LoadProblem> getProblems() const;
|
std::vector<LoadProblem> getLoadProblems() const;
|
||||||
|
std::vector<LoadProblem> getOutdated() const;
|
||||||
std::vector<LoadProblem> getRecommendations() const;
|
std::vector<LoadProblem> getRecommendations() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -103,41 +118,36 @@ namespace geode {
|
||||||
* Returns whether the specified launch argument was passed in via the command line.
|
* Returns whether the specified launch argument was passed in via the command line.
|
||||||
* @param name The argument name
|
* @param name The argument name
|
||||||
*/
|
*/
|
||||||
bool hasLaunchArgument(std::string_view const name) const;
|
bool hasLaunchArgument(std::string_view name) const;
|
||||||
/**
|
/**
|
||||||
* Get a launch argument. These are passed into the game as command-line arguments
|
* Get a launch argument. These are passed into the game as command-line arguments
|
||||||
* with the format `--geode:arg-name=value`.
|
* with the format `--geode:arg-name=value`.
|
||||||
* @param name The argument name
|
* @param name The argument name
|
||||||
* @return The value, if present
|
* @return The value, if present
|
||||||
*/
|
*/
|
||||||
std::optional<std::string> getLaunchArgument(std::string_view const name) const;
|
std::optional<std::string> getLaunchArgument(std::string_view name) const;
|
||||||
/**
|
/**
|
||||||
* Get a launch argument flag. Returns whether the argument is present and its
|
* Get a launch argument flag. Returns whether the argument is present and its
|
||||||
* value is exactly `true`.
|
* value is exactly `true`.
|
||||||
* @param name The argument name
|
* @param name The argument name
|
||||||
*/
|
*/
|
||||||
bool getLaunchFlag(std::string_view const name) const;
|
bool getLaunchFlag(std::string_view name) const;
|
||||||
/**
|
/**
|
||||||
* Get and parse a launch argument value using the setting value system.
|
* Get and parse a launch argument value using the setting value system.
|
||||||
* @param name The argument name
|
* @param name The argument name
|
||||||
*/
|
*/
|
||||||
template <class T>
|
template <class T>
|
||||||
std::optional<T> parseLaunchArgument(std::string_view const name) const {
|
Result<T> parseLaunchArgument(std::string_view name) const {
|
||||||
auto str = this->getLaunchArgument(name);
|
auto str = this->getLaunchArgument(name);
|
||||||
if (!str.has_value()) {
|
if (!str.has_value()) {
|
||||||
return std::nullopt;
|
return Err(fmt::format("Launch argument '{}' not found", name));
|
||||||
}
|
}
|
||||||
std::string parseError;
|
auto jsonOpt = matjson::Value::parse(str.value());
|
||||||
auto jsonOpt = matjson::parse(str.value(), parseError);
|
if (jsonOpt.isErr()) {
|
||||||
if (!jsonOpt.has_value()) {
|
return Err(fmt::format("Parsing launch argument '{}' failed: {}", name, jsonOpt.unwrapErr()));
|
||||||
log::debug("Parsing launch argument '{}' failed: {}", name, parseError);
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
}
|
||||||
auto value = jsonOpt.value();
|
auto value = jsonOpt.unwrap();
|
||||||
if (!value.is<T>()) {
|
return value.template as<T>();
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
return value.as<T>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void queueInMainThread(ScheduledFunction&& func);
|
void queueInMainThread(ScheduledFunction&& func);
|
||||||
|
|
|
@ -65,12 +65,6 @@ namespace std::filesystem {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace matjson {
|
|
||||||
GEODE_INLINE GEODE_HIDDEN std::string format_as(matjson::Value const& value) {
|
|
||||||
return value.dump(matjson::NO_INDENTATION);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace geode {
|
namespace geode {
|
||||||
|
|
||||||
class Mod;
|
class Mod;
|
||||||
|
@ -120,6 +114,37 @@ namespace geode {
|
||||||
popNest(getMod());
|
popNest(getMod());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct NestScope {
|
||||||
|
private:
|
||||||
|
bool m_active = true;
|
||||||
|
public:
|
||||||
|
NestScope() {
|
||||||
|
pushNest();
|
||||||
|
}
|
||||||
|
|
||||||
|
NestScope(NestScope const&) {
|
||||||
|
pushNest();
|
||||||
|
}
|
||||||
|
|
||||||
|
NestScope(NestScope&& other) {
|
||||||
|
other.m_active = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
NestScope& operator=(NestScope const&) {
|
||||||
|
pushNest();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
NestScope& operator=(NestScope&& other) {
|
||||||
|
other.m_active = false;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
~NestScope() {
|
||||||
|
if (m_active) popNest();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
class Nest final {
|
class Nest final {
|
||||||
private:
|
private:
|
||||||
class Impl;
|
class Impl;
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
#include "../DefaultInclude.hpp"
|
#include "../DefaultInclude.hpp"
|
||||||
#include "../cocos/support/zip_support/ZipUtils.h"
|
#include "../cocos/support/zip_support/ZipUtils.h"
|
||||||
#include "../utils/Result.hpp"
|
#include <Geode/Result.hpp>
|
||||||
#include "../utils/VersionInfo.hpp"
|
#include "../utils/VersionInfo.hpp"
|
||||||
#include "../utils/general.hpp"
|
#include "../utils/general.hpp"
|
||||||
|
|
||||||
|
@ -10,7 +10,6 @@
|
||||||
#include "Hook.hpp"
|
#include "Hook.hpp"
|
||||||
#include "ModMetadata.hpp"
|
#include "ModMetadata.hpp"
|
||||||
#include "Setting.hpp"
|
#include "Setting.hpp"
|
||||||
#include "SettingV3.hpp"
|
|
||||||
#include "Types.hpp"
|
#include "Types.hpp"
|
||||||
#include "Loader.hpp"
|
#include "Loader.hpp"
|
||||||
|
|
||||||
|
@ -23,9 +22,7 @@
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
namespace geode {
|
namespace geode {
|
||||||
class SettingV3;
|
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
struct HandleToSaved : public T {
|
struct HandleToSaved : public T {
|
||||||
Mod* m_mod;
|
Mod* m_mod;
|
||||||
|
@ -44,7 +41,8 @@ namespace geode {
|
||||||
Enable,
|
Enable,
|
||||||
Disable,
|
Disable,
|
||||||
Uninstall,
|
Uninstall,
|
||||||
UninstallWithSaveData
|
UninstallWithSaveData,
|
||||||
|
Update
|
||||||
};
|
};
|
||||||
|
|
||||||
static constexpr bool modRequestedActionIsToggle(ModRequestedAction action) {
|
static constexpr bool modRequestedActionIsToggle(ModRequestedAction action) {
|
||||||
|
@ -54,22 +52,6 @@ namespace geode {
|
||||||
return action == ModRequestedAction::Uninstall || action == ModRequestedAction::UninstallWithSaveData;
|
return action == ModRequestedAction::Uninstall || action == ModRequestedAction::UninstallWithSaveData;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class T>
|
|
||||||
static consteval bool typeImplementsIsJSON() {
|
|
||||||
using namespace matjson;
|
|
||||||
if constexpr (requires(const Value& json) { Serialize<std::decay_t<T>>::is_json(json); })
|
|
||||||
return true;
|
|
||||||
if constexpr (std::is_same_v<T, Value>) return true;
|
|
||||||
if constexpr (std::is_same_v<T, Array>) return true;
|
|
||||||
if constexpr (std::is_same_v<T, Object>) return true;
|
|
||||||
if constexpr (std::is_constructible_v<std::string, T>) return true;
|
|
||||||
if constexpr (std::is_integral_v<T> || std::is_floating_point_v<T>) return true;
|
|
||||||
if constexpr (std::is_same_v<T, bool>) return true;
|
|
||||||
if constexpr (std::is_same_v<T, std::nullptr_t>) return true;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
GEODE_HIDDEN Mod* takeNextLoaderMod();
|
GEODE_HIDDEN Mod* takeNextLoaderMod();
|
||||||
|
|
||||||
class ModImpl;
|
class ModImpl;
|
||||||
|
@ -108,8 +90,6 @@ namespace geode {
|
||||||
|
|
||||||
std::string getID() const;
|
std::string getID() const;
|
||||||
std::string getName() const;
|
std::string getName() const;
|
||||||
[[deprecated("Use Mod::getDevelopers instead")]]
|
|
||||||
std::string getDeveloper() const;
|
|
||||||
std::vector<std::string> getDevelopers() const;
|
std::vector<std::string> getDevelopers() const;
|
||||||
std::optional<std::string> getDescription() const;
|
std::optional<std::string> getDescription() const;
|
||||||
std::optional<std::string> getDetails() const;
|
std::optional<std::string> getDetails() const;
|
||||||
|
@ -137,17 +117,6 @@ namespace geode {
|
||||||
std::vector<Mod*> getDependants() const;
|
std::vector<Mod*> getDependants() const;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if this Mod has updates available on the mods index. If
|
|
||||||
* you're using this for automatic update checking, use
|
|
||||||
* `openInfoPopup` or `openIndexPopup` from the `ui/GeodeUI.hpp`
|
|
||||||
* header to open the Mod's page to let the user install the update
|
|
||||||
* @returns The latest available version on the index if there are
|
|
||||||
* updates for this mod
|
|
||||||
*/
|
|
||||||
[[deprecated("Use Mod::checkUpdates instead; this function always returns nullopt")]]
|
|
||||||
std::optional<VersionInfo> hasAvailableUpdate() const;
|
|
||||||
|
|
||||||
using CheckUpdatesTask = Task<Result<std::optional<VersionInfo>, std::string>>;
|
using CheckUpdatesTask = Task<Result<std::optional<VersionInfo>, std::string>>;
|
||||||
/**
|
/**
|
||||||
* Check if this Mod has updates available on the mods index. If
|
* Check if this Mod has updates available on the mods index. If
|
||||||
|
@ -187,52 +156,15 @@ namespace geode {
|
||||||
* declared in `mod.json`)
|
* declared in `mod.json`)
|
||||||
*/
|
*/
|
||||||
std::vector<std::string> getSettingKeys() const;
|
std::vector<std::string> getSettingKeys() const;
|
||||||
bool hasSetting(std::string_view const key) const;
|
bool hasSetting(std::string_view key) const;
|
||||||
|
|
||||||
// todo in v4: remove these
|
|
||||||
[[deprecated("Use Mod::getSettingV3")]]
|
|
||||||
std::optional<Setting> getSettingDefinition(std::string_view const key) const;
|
|
||||||
[[deprecated("Use Mod::getSettingV3")]]
|
|
||||||
SettingValue* getSetting(std::string_view const key) const;
|
|
||||||
|
|
||||||
// todo in v4: possibly rename this to getSetting?
|
|
||||||
/**
|
/**
|
||||||
* Get the definition of a setting, or null if the setting was not found,
|
* Get the definition of a setting, or null if the setting was not found,
|
||||||
* or if it's a custom setting that has not yet been registered using
|
* or if it's a custom setting that has not yet been registered using
|
||||||
* `Mod::registerCustomSettingType`
|
* `Mod::registerCustomSettingType`
|
||||||
* @param key The key of the setting as defined in `mod.json`
|
* @param key The key of the setting as defined in `mod.json`
|
||||||
*/
|
*/
|
||||||
std::shared_ptr<SettingV3> getSettingV3(std::string_view const key) const;
|
std::shared_ptr<Setting> getSetting(std::string_view key) const;
|
||||||
|
|
||||||
/**
|
|
||||||
* Register a custom setting's value class. See Mod::addCustomSetting
|
|
||||||
* for a convenience wrapper that creates the value in-place to avoid
|
|
||||||
* code duplication. Also see
|
|
||||||
* [the tutorial page](https://docs.geode-sdk.org/mods/settings) for
|
|
||||||
* more information about custom settings
|
|
||||||
* @param key The setting's key
|
|
||||||
* @param value The SettingValue class that shall handle this setting
|
|
||||||
* @see addCustomSetting
|
|
||||||
*/
|
|
||||||
[[deprecated("Use Mod::registerCustomSettingType")]]
|
|
||||||
void registerCustomSetting(std::string_view const key, std::unique_ptr<SettingValue> value);
|
|
||||||
/**
|
|
||||||
* Register a custom setting's value class. The new SettingValue class
|
|
||||||
* will be created in-place using `std::make_unique`. See
|
|
||||||
* [the tutorial page](https://docs.geode-sdk.org/mods/settings) for
|
|
||||||
* more information about custom settings
|
|
||||||
* @param key The setting's key
|
|
||||||
* @param value The value of the custom setting
|
|
||||||
* @example
|
|
||||||
* $on_mod(Loaded) {
|
|
||||||
* Mod::get()->addCustomSetting<MySettingValue>("setting-key", DEFAULT_VALUE);
|
|
||||||
* }
|
|
||||||
*/
|
|
||||||
template <class T, class V>
|
|
||||||
[[deprecated("Use Mod::registerCustomSettingType")]]
|
|
||||||
void addCustomSetting(std::string_view const key, V const& value) {
|
|
||||||
this->registerCustomSetting(key, std::make_unique<T>(std::string(key), this->getID(), value));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register a custom setting type. See
|
* Register a custom setting type. See
|
||||||
|
@ -248,7 +180,7 @@ namespace geode {
|
||||||
* Returns a prefixed launch argument name. See `Mod::getLaunchArgument`
|
* Returns a prefixed launch argument name. See `Mod::getLaunchArgument`
|
||||||
* for details about mod-specific launch arguments.
|
* for details about mod-specific launch arguments.
|
||||||
*/
|
*/
|
||||||
std::string getLaunchArgumentName(std::string_view const name) const;
|
std::string getLaunchArgumentName(std::string_view name) const;
|
||||||
/**
|
/**
|
||||||
* Returns the names of the available mod-specific launch arguments.
|
* Returns the names of the available mod-specific launch arguments.
|
||||||
*/
|
*/
|
||||||
|
@ -258,7 +190,7 @@ namespace geode {
|
||||||
* for details about mod-specific launch arguments.
|
* for details about mod-specific launch arguments.
|
||||||
* @param name The argument name
|
* @param name The argument name
|
||||||
*/
|
*/
|
||||||
bool hasLaunchArgument(std::string_view const name) const;
|
bool hasLaunchArgument(std::string_view name) const;
|
||||||
/**
|
/**
|
||||||
* Get a mod-specific launch argument. This is equivalent to `Loader::getLaunchArgument`
|
* Get a mod-specific launch argument. This is equivalent to `Loader::getLaunchArgument`
|
||||||
* with the argument name prefixed by the mod ID. For example, a launch argument named
|
* with the argument name prefixed by the mod ID. For example, a launch argument named
|
||||||
|
@ -266,20 +198,20 @@ namespace geode {
|
||||||
* @param name The argument name
|
* @param name The argument name
|
||||||
* @return The value, if present
|
* @return The value, if present
|
||||||
*/
|
*/
|
||||||
std::optional<std::string> getLaunchArgument(std::string_view const name) const;
|
std::optional<std::string> getLaunchArgument(std::string_view name) const;
|
||||||
/**
|
/**
|
||||||
* Equivalent to a prefixed `Loader::getLaunchFlag` call. See `Mod::getLaunchArgument`
|
* Equivalent to a prefixed `Loader::getLaunchFlag` call. See `Mod::getLaunchArgument`
|
||||||
* for details about mod-specific launch arguments.
|
* for details about mod-specific launch arguments.
|
||||||
* @param name The argument name
|
* @param name The argument name
|
||||||
*/
|
*/
|
||||||
bool getLaunchFlag(std::string_view const name) const;
|
bool getLaunchFlag(std::string_view name) const;
|
||||||
/**
|
/**
|
||||||
* Equivalent to a prefixed `Loader::parseLaunchArgument` call. See `Mod::getLaunchArgument`
|
* Equivalent to a prefixed `Loader::parseLaunchArgument` call. See `Mod::getLaunchArgument`
|
||||||
* for details about mod-specific launch arguments.
|
* for details about mod-specific launch arguments.
|
||||||
* @param name The argument name
|
* @param name The argument name
|
||||||
*/
|
*/
|
||||||
template <class T>
|
template <class T>
|
||||||
std::optional<T> parseLaunchArgument(std::string_view const name) const {
|
std::optional<T> parseLaunchArgument(std::string_view name) const {
|
||||||
return Loader::get()->parseLaunchArgument<T>(this->getLaunchArgumentName(name));
|
return Loader::get()->parseLaunchArgument<T>(this->getLaunchArgumentName(name));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -293,18 +225,18 @@ namespace geode {
|
||||||
* setting type has a `getValue` function which returns the value
|
* setting type has a `getValue` function which returns the value
|
||||||
*/
|
*/
|
||||||
template <class T>
|
template <class T>
|
||||||
T getSettingValue(std::string_view const key) const {
|
T getSettingValue(std::string_view key) const {
|
||||||
using S = typename SettingTypeForValueType<T>::SettingType;
|
using S = typename SettingTypeForValueType<T>::SettingType;
|
||||||
if (auto sett = cast::typeinfo_pointer_cast<S>(this->getSettingV3(key))) {
|
if (auto sett = cast::typeinfo_pointer_cast<S>(this->getSetting(key))) {
|
||||||
return sett->getValue();
|
return sett->getValue();
|
||||||
}
|
}
|
||||||
return T();
|
return T();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
T setSettingValue(std::string_view const key, T const& value) {
|
T setSettingValue(std::string_view key, T const& value) {
|
||||||
using S = typename SettingTypeForValueType<T>::SettingType;
|
using S = typename SettingTypeForValueType<T>::SettingType;
|
||||||
if (auto sett = cast::typeinfo_pointer_cast<S>(this->getSettingV3(key))) {
|
if (auto sett = cast::typeinfo_pointer_cast<S>(this->getSetting(key))) {
|
||||||
auto old = sett->getValue();
|
auto old = sett->getValue();
|
||||||
sett->setValue(value);
|
sett->setValue(value);
|
||||||
return old;
|
return old;
|
||||||
|
@ -312,30 +244,28 @@ namespace geode {
|
||||||
return T();
|
return T();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool hasSavedValue(std::string_view const key);
|
bool hasSavedValue(std::string_view key);
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
T getSavedValue(std::string_view const key) {
|
T getSavedValue(std::string_view key) {
|
||||||
static_assert(geode::typeImplementsIsJSON<T>(), "T must implement is_json in matjson::Serialize<T>, otherwise this always returns default value.");
|
|
||||||
auto& saved = this->getSaveContainer();
|
auto& saved = this->getSaveContainer();
|
||||||
if (saved.contains(key)) {
|
if (auto res = saved.get(key).andThen([](auto&& v) {
|
||||||
if (auto value = saved.try_get<T>(key)) {
|
return v.template as<T>();
|
||||||
return *value;
|
}); res.isOk()) {
|
||||||
}
|
return res.unwrap();
|
||||||
}
|
}
|
||||||
return T();
|
return T();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
T getSavedValue(std::string_view const key, T const& defaultValue) {
|
T getSavedValue(std::string_view key, T const& defaultValue) {
|
||||||
static_assert(geode::typeImplementsIsJSON<T>(), "T must implement is_json in matjson::Serialize<T>, otherwise this always returns default value.");
|
|
||||||
auto& saved = this->getSaveContainer();
|
auto& saved = this->getSaveContainer();
|
||||||
if (saved.contains(key)) {
|
if (auto res = saved.get(key).andThen([](auto&& v) {
|
||||||
if (auto value = saved.try_get<T>(key)) {
|
return v.template as<T>();
|
||||||
return *value;
|
}); res.isOk()) {
|
||||||
}
|
return res.unwrap();
|
||||||
}
|
}
|
||||||
saved[key] = defaultValue;
|
saved[key] = matjson::Value(defaultValue);
|
||||||
return defaultValue;
|
return defaultValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -347,7 +277,7 @@ namespace geode {
|
||||||
* @returns The old value
|
* @returns The old value
|
||||||
*/
|
*/
|
||||||
template <class T>
|
template <class T>
|
||||||
T setSavedValue(std::string_view const key, T const& value) {
|
T setSavedValue(std::string_view key, T const& value) {
|
||||||
auto& saved = this->getSaveContainer();
|
auto& saved = this->getSaveContainer();
|
||||||
auto old = this->getSavedValue<T>(key);
|
auto old = this->getSavedValue<T>(key);
|
||||||
saved[key] = value;
|
saved[key] = value;
|
||||||
|
@ -488,7 +418,7 @@ namespace geode {
|
||||||
* Check whether or not this Mod
|
* Check whether or not this Mod
|
||||||
* depends on another mod
|
* depends on another mod
|
||||||
*/
|
*/
|
||||||
bool depends(std::string_view const id) const;
|
bool depends(std::string_view id) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check whether all the required
|
* Check whether all the required
|
||||||
|
@ -517,7 +447,17 @@ namespace geode {
|
||||||
bool isLoggingEnabled() const;
|
bool isLoggingEnabled() const;
|
||||||
void setLoggingEnabled(bool enabled);
|
void setLoggingEnabled(bool enabled);
|
||||||
|
|
||||||
bool hasProblems() const;
|
/**
|
||||||
|
* If this mod is built for an outdated GD or Geode version, returns the
|
||||||
|
* `LoadProblem` describing the situation. Otherwise `nullopt` if the
|
||||||
|
* mod is made for the correct version of the game and Geode
|
||||||
|
*/
|
||||||
|
std::optional<LoadProblem> targetsOutdatedVersion() const;
|
||||||
|
/**
|
||||||
|
* @note Make sure to also call `targetsOutdatedVersion` if you want to
|
||||||
|
* make sure the mod is actually loadable
|
||||||
|
*/
|
||||||
|
bool hasLoadProblems() const;
|
||||||
std::vector<LoadProblem> getAllProblems() const;
|
std::vector<LoadProblem> getAllProblems() const;
|
||||||
std::vector<LoadProblem> getProblems() const;
|
std::vector<LoadProblem> getProblems() const;
|
||||||
std::vector<LoadProblem> getRecommendations() const;
|
std::vector<LoadProblem> getRecommendations() const;
|
||||||
|
|
|
@ -10,9 +10,6 @@ namespace geode {
|
||||||
|
|
||||||
enum class ModEventType {
|
enum class ModEventType {
|
||||||
Loaded,
|
Loaded,
|
||||||
Unloaded,
|
|
||||||
Enabled,
|
|
||||||
Disabled,
|
|
||||||
DataLoaded,
|
DataLoaded,
|
||||||
DataSaved,
|
DataSaved,
|
||||||
};
|
};
|
||||||
|
@ -43,7 +40,7 @@ namespace geode {
|
||||||
Mod* m_mod;
|
Mod* m_mod;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ListenerResult handle(utils::MiniFunction<Callback> fn, ModStateEvent* event);
|
ListenerResult handle(std::function<Callback> fn, ModStateEvent* event);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a mod state listener
|
* Create a mod state listener
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "../utils/Result.hpp"
|
#include <Geode/Result.hpp>
|
||||||
#include "../utils/VersionInfo.hpp"
|
#include "../utils/VersionInfo.hpp"
|
||||||
#include "Setting.hpp"
|
|
||||||
#include "Types.hpp"
|
#include "Types.hpp"
|
||||||
|
|
||||||
#include <matjson.hpp>
|
#include <matjson.hpp>
|
||||||
|
@ -123,13 +122,6 @@ namespace geode {
|
||||||
* character set.
|
* character set.
|
||||||
*/
|
*/
|
||||||
[[nodiscard]] std::string getName() const;
|
[[nodiscard]] std::string getName() const;
|
||||||
/**
|
|
||||||
* The name of the head developer.
|
|
||||||
* If the mod has multiple * developers, this will return the first
|
|
||||||
* developer in the list.
|
|
||||||
*/
|
|
||||||
[[nodiscard, deprecated("Use ModMetadata::getDevelopers() instead")]]
|
|
||||||
std::string getDeveloper() const;
|
|
||||||
/**
|
/**
|
||||||
* The developers of this mod
|
* The developers of this mod
|
||||||
*/
|
*/
|
||||||
|
@ -155,11 +147,6 @@ namespace geode {
|
||||||
* (see MDTextArea for more info)
|
* (see MDTextArea for more info)
|
||||||
*/
|
*/
|
||||||
[[nodiscard]] std::optional<std::string> getSupportInfo() const;
|
[[nodiscard]] std::optional<std::string> getSupportInfo() const;
|
||||||
/**
|
|
||||||
* Git Repository of the mod
|
|
||||||
*/
|
|
||||||
[[nodiscard, deprecated("Use ModMetadata::getLinks instead")]]
|
|
||||||
std::optional<std::string> getRepository() const;
|
|
||||||
/**
|
/**
|
||||||
* Get the links (related websites / servers / etc.) for this mod
|
* Get the links (related websites / servers / etc.) for this mod
|
||||||
*/
|
*/
|
||||||
|
@ -184,12 +171,7 @@ namespace geode {
|
||||||
* Mod settings
|
* Mod settings
|
||||||
* @note Not a map because insertion order must be preserved
|
* @note Not a map because insertion order must be preserved
|
||||||
*/
|
*/
|
||||||
[[nodiscard, deprecated("Use getSettingsV3")]] std::vector<std::pair<std::string, Setting>> getSettings() const;
|
[[nodiscard]] std::vector<std::pair<std::string, matjson::Value>> getSettings() const;
|
||||||
/**
|
|
||||||
* Mod settings
|
|
||||||
* @note Not a map because insertion order must be preserved
|
|
||||||
*/
|
|
||||||
[[nodiscard]] std::vector<std::pair<std::string, matjson::Value>> getSettingsV3() const;
|
|
||||||
/**
|
/**
|
||||||
* Get the tags for this mod
|
* Get the tags for this mod
|
||||||
*/
|
*/
|
||||||
|
@ -216,9 +198,19 @@ namespace geode {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if mod can be installed on the current GD version.
|
* Checks if mod can be installed on the current GD version.
|
||||||
* Returns Ok() if it can, Err otherwise.
|
* Returns Ok() if it can, Err explaining why not otherwise.
|
||||||
*/
|
*/
|
||||||
Result<> checkGameVersion() const;
|
Result<> checkGameVersion() const;
|
||||||
|
/**
|
||||||
|
* Checks if mod can be installed on the current Geode version.
|
||||||
|
* Returns Ok() if it can, Err explaining why not otherwise.
|
||||||
|
*/
|
||||||
|
Result<> checkGeodeVersion() const;
|
||||||
|
/**
|
||||||
|
* Checks if mod can be installed on the current GD & Geode version.
|
||||||
|
* Returns Ok() if it can, Err explaining why not otherwise.
|
||||||
|
*/
|
||||||
|
Result<> checkTargetVersions() const;
|
||||||
|
|
||||||
#if defined(GEODE_EXPOSE_SECRET_INTERNALS_IN_HEADERS_DO_NOT_DEFINE_PLEASE)
|
#if defined(GEODE_EXPOSE_SECRET_INTERNALS_IN_HEADERS_DO_NOT_DEFINE_PLEASE)
|
||||||
void setPath(std::filesystem::path const& value);
|
void setPath(std::filesystem::path const& value);
|
||||||
|
@ -237,8 +229,6 @@ namespace geode {
|
||||||
void setDependencies(std::vector<Dependency> const& value);
|
void setDependencies(std::vector<Dependency> const& value);
|
||||||
void setIncompatibilities(std::vector<Incompatibility> const& value);
|
void setIncompatibilities(std::vector<Incompatibility> const& value);
|
||||||
void setSpritesheets(std::vector<std::string> const& value);
|
void setSpritesheets(std::vector<std::string> const& value);
|
||||||
[[deprecated("This function does NOTHING")]]
|
|
||||||
void setSettings(std::vector<std::pair<std::string, Setting>> const& value);
|
|
||||||
void setSettings(std::vector<std::pair<std::string, matjson::Value>> const& value);
|
void setSettings(std::vector<std::pair<std::string, matjson::Value>> const& value);
|
||||||
void setTags(std::unordered_set<std::string> const& value);
|
void setTags(std::unordered_set<std::string> const& value);
|
||||||
void setNeedsEarlyLoad(bool const& value);
|
void setNeedsEarlyLoad(bool const& value);
|
||||||
|
@ -310,7 +300,8 @@ namespace geode {
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
struct matjson::Serialize<geode::ModMetadata> {
|
struct matjson::Serialize<geode::ModMetadata> {
|
||||||
static matjson::Value to_json(geode::ModMetadata const& info) {
|
static Value toJson(geode::ModMetadata const& value)
|
||||||
return info.toJSON();
|
{
|
||||||
|
return Value(value.toJSON());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <Geode/DefaultInclude.hpp>
|
#include <Geode/DefaultInclude.hpp>
|
||||||
#include "SettingV3.hpp"
|
#include "Setting.hpp"
|
||||||
|
|
||||||
namespace geode {
|
namespace geode {
|
||||||
class Mod;
|
class Mod;
|
||||||
|
@ -40,12 +40,8 @@ namespace geode {
|
||||||
* The format of the savedata will be an object with the keys being
|
* The format of the savedata will be an object with the keys being
|
||||||
* setting IDs and then the values the values of the saved settings
|
* setting IDs and then the values the values of the saved settings
|
||||||
* @note If saving a setting fails, it will log a warning to the console
|
* @note If saving a setting fails, it will log a warning to the console
|
||||||
* @warning This will overwrite the whole `json` parameter - be sure to
|
|
||||||
* pass the full settings savedata to `load()` so you can be sure that
|
|
||||||
* unregistered custom settings' saved values don't disappear!
|
|
||||||
* @todo in v4: make this return the value instead lol
|
|
||||||
*/
|
*/
|
||||||
void save(matjson::Value& json);
|
matjson::Value save();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the savedata for settings, aka the JSON object that contains all
|
* Get the savedata for settings, aka the JSON object that contains all
|
||||||
|
@ -57,12 +53,8 @@ namespace geode {
|
||||||
matjson::Value& getSaveData();
|
matjson::Value& getSaveData();
|
||||||
|
|
||||||
Result<> registerCustomSettingType(std::string_view type, SettingGenerator generator);
|
Result<> registerCustomSettingType(std::string_view type, SettingGenerator generator);
|
||||||
// todo in v4: remove this
|
|
||||||
Result<> registerLegacyCustomSetting(std::string_view key, std::unique_ptr<SettingValue>&& ptr);
|
|
||||||
|
|
||||||
std::shared_ptr<SettingV3> get(std::string_view key);
|
std::shared_ptr<Setting> get(std::string_view key);
|
||||||
std::shared_ptr<SettingValue> getLegacy(std::string_view key);
|
|
||||||
std::optional<Setting> getLegacyDefinition(std::string_view key);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if any setting with the `"restart-required"` attribute
|
* Returns true if any setting with the `"restart-required"` attribute
|
||||||
|
|
|
@ -1,324 +1,46 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "Types.hpp"
|
#include "SettingV3.hpp"
|
||||||
#include "../DefaultInclude.hpp"
|
|
||||||
#include "../utils/Result.hpp"
|
namespace geode {
|
||||||
#include "../utils/file.hpp"
|
using Setting = SettingV3;
|
||||||
#include <matjson.hpp>
|
using SettingGenerator = SettingGeneratorV3;
|
||||||
#include <optional>
|
|
||||||
#include <unordered_set>
|
template <class T, class V = T>
|
||||||
#include <cocos2d.h>
|
using SettingBaseValue = SettingBaseValueV3<T, V>;
|
||||||
|
|
||||||
#pragma warning(push)
|
using TitleSetting = TitleSettingV3;
|
||||||
#pragma warning(disable : 4275)
|
using BoolSetting = BoolSettingV3;
|
||||||
|
using IntSetting = IntSettingV3;
|
||||||
namespace geode {
|
using FloatSetting = FloatSettingV3;
|
||||||
class SettingNode;
|
using StringSetting = StringSettingV3;
|
||||||
class SettingValue;
|
using FileSetting = FileSettingV3;
|
||||||
|
using Color3BSetting = Color3BSettingV3;
|
||||||
struct JsonMaybeObject;
|
using Color4BSetting = Color4BSettingV3;
|
||||||
struct JsonMaybeValue;
|
|
||||||
|
using SettingNode = SettingNodeV3;
|
||||||
/**
|
template <class S>
|
||||||
* A Setting for a boolean value. Represented in-game as a simple toggle
|
using SettingValueNode = SettingValueNodeV3<S>;
|
||||||
*/
|
|
||||||
struct GEODE_DLL BoolSetting final {
|
using SettingChangedEvent = SettingChangedEventV3;
|
||||||
using ValueType = bool;
|
using SettingChangedFilter = SettingChangedFilterV3;
|
||||||
|
using SettingNodeSizeChangeEvent = SettingNodeSizeChangeEventV3;
|
||||||
std::optional<std::string> name;
|
using SettingNodeValueChangeEvent = SettingNodeValueChangeEventV3;
|
||||||
std::optional<std::string> description;
|
|
||||||
bool defaultValue;
|
template <class T, class Lambda>
|
||||||
|
EventListener<SettingChangedFilter>* listenForSettingChanges(std::string_view settingKey, Lambda&& callback, Mod* mod = getMod()) {
|
||||||
static Result<BoolSetting> parse(JsonMaybeObject& obj);
|
return listenForSettingChangesV3<T>(settingKey, std::forward<Lambda>(callback), mod);
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
template <class Lambda>
|
||||||
* A Setting for an integer value. The value can be limited using the min
|
EventListener<SettingChangedFilter>* listenForSettingChanges(std::string_view settingKey, Lambda&& callback, Mod* mod = getMod()) {
|
||||||
* and max options
|
return listenForSettingChangesV3(settingKey, std::forward<Lambda>(callback), mod);
|
||||||
*/
|
}
|
||||||
struct GEODE_DLL IntSetting final {
|
|
||||||
using ValueType = int64_t;
|
inline EventListener<SettingChangedFilter>* listenForAllSettingChanges(
|
||||||
|
std::function<void(std::shared_ptr<SettingV3>)> const& callback,
|
||||||
std::optional<std::string> name;
|
Mod* mod = getMod()
|
||||||
std::optional<std::string> description;
|
) {
|
||||||
ValueType defaultValue;
|
return listenForAllSettingChangesV3(callback, mod);
|
||||||
std::optional<ValueType> min;
|
}
|
||||||
std::optional<ValueType> max;
|
}
|
||||||
struct {
|
|
||||||
bool arrows = true;
|
|
||||||
bool bigArrows = false;
|
|
||||||
size_t arrowStep = 1;
|
|
||||||
size_t bigArrowStep = 5;
|
|
||||||
bool slider = true;
|
|
||||||
std::optional<ValueType> sliderStep = std::nullopt;
|
|
||||||
bool input = true;
|
|
||||||
} controls;
|
|
||||||
|
|
||||||
static Result<IntSetting> parse(JsonMaybeObject& obj);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A Setting for a float value. The value can be limited using the min
|
|
||||||
* and max options
|
|
||||||
*/
|
|
||||||
struct GEODE_DLL FloatSetting final {
|
|
||||||
using ValueType = double;
|
|
||||||
|
|
||||||
std::optional<std::string> name;
|
|
||||||
std::optional<std::string> description;
|
|
||||||
ValueType defaultValue;
|
|
||||||
std::optional<ValueType> min;
|
|
||||||
std::optional<ValueType> max;
|
|
||||||
struct {
|
|
||||||
bool arrows = true;
|
|
||||||
bool bigArrows = false;
|
|
||||||
size_t arrowStep = 1;
|
|
||||||
size_t bigArrowStep = 5;
|
|
||||||
bool slider = true;
|
|
||||||
std::optional<ValueType> sliderStep = std::nullopt;
|
|
||||||
bool input = true;
|
|
||||||
} controls;
|
|
||||||
|
|
||||||
static Result<FloatSetting> parse(JsonMaybeObject& obj);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A Setting for a string value
|
|
||||||
*/
|
|
||||||
struct GEODE_DLL StringSetting final {
|
|
||||||
using ValueType = std::string;
|
|
||||||
|
|
||||||
std::optional<std::string> name;
|
|
||||||
std::optional<std::string> description;
|
|
||||||
ValueType defaultValue;
|
|
||||||
struct Data {
|
|
||||||
/**
|
|
||||||
* A regex the string must successfully match against
|
|
||||||
*/
|
|
||||||
std::optional<std::string> match;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The CCTextInputNode's allowed character filter
|
|
||||||
*/
|
|
||||||
std::optional<std::string> filter;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A list of options the user can choose from
|
|
||||||
*/
|
|
||||||
std::optional<std::vector<std::string>> options;
|
|
||||||
};
|
|
||||||
std::unique_ptr<Data> controls;
|
|
||||||
|
|
||||||
std::array<uint8_t, sizeof(Data::match) + sizeof(Data::filter) - sizeof(controls)> m_padding;
|
|
||||||
|
|
||||||
StringSetting();
|
|
||||||
StringSetting(StringSetting const& other);
|
|
||||||
StringSetting(StringSetting&& other) noexcept;
|
|
||||||
StringSetting& operator=(StringSetting const& other);
|
|
||||||
StringSetting& operator=(StringSetting&& other) noexcept;
|
|
||||||
~StringSetting();
|
|
||||||
|
|
||||||
|
|
||||||
static Result<StringSetting> parse(JsonMaybeObject& obj);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A Setting for a file input. Lets the user select a file from their
|
|
||||||
* local device
|
|
||||||
*/
|
|
||||||
struct GEODE_DLL FileSetting final {
|
|
||||||
using ValueType = std::filesystem::path;
|
|
||||||
using Filter = utils::file::FilePickOptions::Filter;
|
|
||||||
|
|
||||||
std::optional<std::string> name;
|
|
||||||
std::optional<std::string> description;
|
|
||||||
ValueType defaultValue;
|
|
||||||
struct {
|
|
||||||
std::vector<Filter> filters;
|
|
||||||
} controls;
|
|
||||||
|
|
||||||
static Result<FileSetting> parse(JsonMaybeObject& obj);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A Setting for an RGB color. See ColorAlphaSetting for a setting that
|
|
||||||
* also allows customizing alpha
|
|
||||||
*/
|
|
||||||
struct GEODE_DLL ColorSetting final {
|
|
||||||
using ValueType = cocos2d::ccColor3B;
|
|
||||||
|
|
||||||
std::optional<std::string> name;
|
|
||||||
std::optional<std::string> description;
|
|
||||||
ValueType defaultValue;
|
|
||||||
|
|
||||||
static Result<ColorSetting> parse(JsonMaybeObject& obj);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A Setting for an RGBA color. See ColorSetting for a setting that doesn't
|
|
||||||
* have alpha
|
|
||||||
*/
|
|
||||||
struct GEODE_DLL ColorAlphaSetting final {
|
|
||||||
using ValueType = cocos2d::ccColor4B;
|
|
||||||
|
|
||||||
std::optional<std::string> name;
|
|
||||||
std::optional<std::string> description;
|
|
||||||
ValueType defaultValue;
|
|
||||||
|
|
||||||
static Result<ColorAlphaSetting> parse(JsonMaybeObject& obj);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A custom setting, defined by the mod. See
|
|
||||||
* [the tutorial page](https://docs.geode-sdk.org/mods/settings) for more
|
|
||||||
* information about how to create custom settings
|
|
||||||
*/
|
|
||||||
struct GEODE_DLL CustomSetting final {
|
|
||||||
std::shared_ptr<ModJson> json;
|
|
||||||
};
|
|
||||||
|
|
||||||
using SettingKind = std::variant<
|
|
||||||
BoolSetting,
|
|
||||||
IntSetting,
|
|
||||||
FloatSetting,
|
|
||||||
StringSetting,
|
|
||||||
FileSetting,
|
|
||||||
ColorSetting,
|
|
||||||
ColorAlphaSetting,
|
|
||||||
CustomSetting
|
|
||||||
>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents a saved value for a mod that can be customized by the user
|
|
||||||
* through an in-game UI. This class is for modeling the setting's
|
|
||||||
* definition - what values are accepted, its name etc.
|
|
||||||
* See [the tutorial page](https://docs.geode-sdk.org/mods/settings)
|
|
||||||
* for more information about how settings work
|
|
||||||
* @see SettingValue
|
|
||||||
* @see SettingNode
|
|
||||||
*/
|
|
||||||
struct GEODE_DLL Setting final {
|
|
||||||
private:
|
|
||||||
std::string m_key;
|
|
||||||
std::string m_modID;
|
|
||||||
SettingKind m_kind;
|
|
||||||
|
|
||||||
Setting() = default;
|
|
||||||
|
|
||||||
public:
|
|
||||||
static Result<Setting> parse(
|
|
||||||
std::string const& key,
|
|
||||||
std::string const& mod,
|
|
||||||
JsonMaybeValue& obj
|
|
||||||
);
|
|
||||||
Setting(
|
|
||||||
std::string const& key,
|
|
||||||
std::string const& mod,
|
|
||||||
SettingKind const& kind
|
|
||||||
);
|
|
||||||
|
|
||||||
template<class T>
|
|
||||||
std::optional<T> get() {
|
|
||||||
if (std::holds_alternative<T>(m_kind)) {
|
|
||||||
return std::get<T>(m_kind);
|
|
||||||
}
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::unique_ptr<SettingValue> createDefaultValue() const;
|
|
||||||
bool isCustom() const;
|
|
||||||
std::string getDisplayName() const;
|
|
||||||
std::optional<std::string> getDescription() const;
|
|
||||||
std::string getModID() const;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Stores the actual, current value of a Setting. See
|
|
||||||
* [the tutorial page](https://docs.geode-sdk.org/mods/settings) for more
|
|
||||||
* information, and how to create custom settings
|
|
||||||
*/
|
|
||||||
class GEODE_DLL SettingValue {
|
|
||||||
protected:
|
|
||||||
std::string m_key;
|
|
||||||
std::string m_modID;
|
|
||||||
|
|
||||||
SettingValue(std::string const& key, std::string const& mod);
|
|
||||||
|
|
||||||
void valueChanged();
|
|
||||||
|
|
||||||
public:
|
|
||||||
virtual ~SettingValue() = default;
|
|
||||||
virtual bool load(matjson::Value const& json) = 0;
|
|
||||||
virtual bool save(matjson::Value& json) const = 0;
|
|
||||||
virtual SettingNode* createNode(float width) = 0;
|
|
||||||
|
|
||||||
std::string getKey() const;
|
|
||||||
std::string getModID() const;
|
|
||||||
};
|
|
||||||
|
|
||||||
template<class T>
|
|
||||||
class GeodeSettingValue final : public SettingValue {
|
|
||||||
public:
|
|
||||||
using ValueType = typename T::ValueType;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
ValueType m_value;
|
|
||||||
T m_definition;
|
|
||||||
|
|
||||||
using Valid = std::pair<ValueType, std::optional<std::string>>;
|
|
||||||
|
|
||||||
GEODE_DLL Valid toValid(ValueType const& value) const;
|
|
||||||
|
|
||||||
public:
|
|
||||||
GeodeSettingValue(std::string const& key, std::string const& modID, T const& definition)
|
|
||||||
: SettingValue(key, modID),
|
|
||||||
m_definition(definition),
|
|
||||||
m_value(definition.defaultValue) {}
|
|
||||||
|
|
||||||
bool load(matjson::Value const& json) override;
|
|
||||||
bool save(matjson::Value& json) const;
|
|
||||||
|
|
||||||
GEODE_DLL SettingNode* createNode(float width) override;
|
|
||||||
T castDefinition() const {
|
|
||||||
return m_definition;
|
|
||||||
}
|
|
||||||
Setting getDefinition() const {
|
|
||||||
return Setting(m_key, m_modID, m_definition);
|
|
||||||
}
|
|
||||||
|
|
||||||
GEODE_DLL ValueType getValue() const;
|
|
||||||
GEODE_DLL void setValue(ValueType const& value);
|
|
||||||
GEODE_DLL Result<> validate(ValueType const& value) const;
|
|
||||||
};
|
|
||||||
|
|
||||||
using BoolSettingValue = GeodeSettingValue<BoolSetting>;
|
|
||||||
using IntSettingValue = GeodeSettingValue<IntSetting>;
|
|
||||||
using FloatSettingValue = GeodeSettingValue<FloatSetting>;
|
|
||||||
using StringSettingValue = GeodeSettingValue<StringSetting>;
|
|
||||||
using FileSettingValue = GeodeSettingValue<FileSetting>;
|
|
||||||
using ColorSettingValue = GeodeSettingValue<ColorSetting>;
|
|
||||||
using ColorAlphaSettingValue = GeodeSettingValue<ColorAlphaSetting>;
|
|
||||||
|
|
||||||
// todo: remove in v3
|
|
||||||
|
|
||||||
template<class T>
|
|
||||||
struct [[deprecated("Use SettingTypeForValueType from SettingV3 instead")]] GEODE_DLL SettingValueSetter {
|
|
||||||
static T get(SettingValue* setting);
|
|
||||||
static void set(SettingValue* setting, T const& value);
|
|
||||||
};
|
|
||||||
|
|
||||||
template<class T>
|
|
||||||
bool GeodeSettingValue<T>::load(matjson::Value const& json) {
|
|
||||||
if (!json.is<ValueType>()) return false;
|
|
||||||
m_value = json.as<ValueType>();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class T>
|
|
||||||
bool GeodeSettingValue<T>::save(matjson::Value& json) const {
|
|
||||||
json = m_value;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma warning(pop)
|
|
||||||
|
|
|
@ -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 "../DefaultInclude.hpp"
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <cocos2d.h>
|
#include <cocos2d.h>
|
||||||
// todo: remove this header in 4.0.0
|
|
||||||
#include "Setting.hpp"
|
|
||||||
#include "../utils/cocos.hpp"
|
#include "../utils/cocos.hpp"
|
||||||
|
#include "../utils/file.hpp"
|
||||||
// this unfortunately has to be included because of C++ templates
|
// this unfortunately has to be included because of C++ templates
|
||||||
#include "../utils/JsonValidation.hpp"
|
#include "../utils/JsonValidation.hpp"
|
||||||
#include "../utils/function.hpp"
|
#include "../utils/function.hpp"
|
||||||
|
|
||||||
// todo in v4: this can be removed as well as the friend decl in LegacyCustomSettingV3
|
|
||||||
class LegacyCustomSettingToV3Node;
|
|
||||||
class ModSettingsPopup;
|
class ModSettingsPopup;
|
||||||
|
|
||||||
namespace geode {
|
namespace geode {
|
||||||
class ModSettingsManager;
|
class ModSettingsManager;
|
||||||
class SettingNodeV3;
|
class SettingNodeV3;
|
||||||
// todo in v4: remove this
|
|
||||||
class SettingValue;
|
|
||||||
|
|
||||||
class GEODE_DLL SettingV3 : public std::enable_shared_from_this<SettingV3> {
|
class GEODE_DLL SettingV3 : public std::enable_shared_from_this<SettingV3> {
|
||||||
private:
|
private:
|
||||||
|
@ -125,8 +120,6 @@ namespace geode {
|
||||||
*/
|
*/
|
||||||
void markChanged();
|
void markChanged();
|
||||||
|
|
||||||
friend class ::geode::SettingValue;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
SettingV3();
|
SettingV3();
|
||||||
virtual ~SettingV3();
|
virtual ~SettingV3();
|
||||||
|
@ -183,20 +176,9 @@ namespace geode {
|
||||||
* Reset this setting's value back to its original value
|
* Reset this setting's value back to its original value
|
||||||
*/
|
*/
|
||||||
virtual void reset() = 0;
|
virtual void reset() = 0;
|
||||||
|
|
||||||
[[deprecated(
|
|
||||||
"This function will be removed alongside legacy settings in 4.0.0! "
|
|
||||||
"You should NOT be implementing it for your own custom setting classes"
|
|
||||||
)]]
|
|
||||||
virtual std::optional<Setting> convertToLegacy() const;
|
|
||||||
[[deprecated(
|
|
||||||
"This function will be removed alongside legacy settings in 4.0.0! "
|
|
||||||
"You should NOT be implementing it for your own custom setting classes"
|
|
||||||
)]]
|
|
||||||
virtual std::optional<std::shared_ptr<SettingValue>> convertToLegacyValue() const;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
using SettingGenerator = std::function<Result<std::shared_ptr<SettingV3>>(
|
using SettingGeneratorV3 = std::function<Result<std::shared_ptr<SettingV3>>(
|
||||||
std::string const& key,
|
std::string const& key,
|
||||||
std::string const& modID,
|
std::string const& modID,
|
||||||
matjson::Value const& json
|
matjson::Value const& json
|
||||||
|
@ -324,14 +306,15 @@ namespace geode {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool load(matjson::Value const& json) override {
|
bool load(matjson::Value const& json) override {
|
||||||
if (json.is<T>()) {
|
auto res = json.as<T>();
|
||||||
m_impl->value = json.as<T>();
|
if (res.isErr()) {
|
||||||
return true;
|
return false;
|
||||||
}
|
}
|
||||||
return false;
|
m_impl->value = res.unwrap();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
bool save(matjson::Value& json) const override {
|
bool save(matjson::Value& json) const override {
|
||||||
json = m_impl->value;
|
json = matjson::Value(m_impl->value);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -357,37 +340,6 @@ namespace geode {
|
||||||
void reset() override;
|
void reset() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
// todo in v4: remove this class completely
|
|
||||||
class GEODE_DLL LegacyCustomSettingV3 final : public SettingV3 {
|
|
||||||
private:
|
|
||||||
class Impl;
|
|
||||||
std::shared_ptr<Impl> m_impl;
|
|
||||||
|
|
||||||
friend class ::geode::ModSettingsManager;
|
|
||||||
friend class ::LegacyCustomSettingToV3Node;
|
|
||||||
|
|
||||||
private:
|
|
||||||
class PrivateMarker {};
|
|
||||||
friend class SettingV3;
|
|
||||||
|
|
||||||
public:
|
|
||||||
LegacyCustomSettingV3(PrivateMarker);
|
|
||||||
static Result<std::shared_ptr<LegacyCustomSettingV3>> parse(std::string const& key, std::string const& modID, matjson::Value const& json);
|
|
||||||
|
|
||||||
std::shared_ptr<SettingValue> getValue() const;
|
|
||||||
void setValue(std::shared_ptr<SettingValue> value);
|
|
||||||
|
|
||||||
bool load(matjson::Value const& json) override;
|
|
||||||
bool save(matjson::Value& json) const override;
|
|
||||||
SettingNodeV3* createNode(float width) override;
|
|
||||||
|
|
||||||
bool isDefaultValue() const override;
|
|
||||||
void reset() override;
|
|
||||||
|
|
||||||
std::optional<Setting> convertToLegacy() const override;
|
|
||||||
std::optional<std::shared_ptr<SettingValue>> convertToLegacyValue() const override;
|
|
||||||
};
|
|
||||||
|
|
||||||
class GEODE_DLL BoolSettingV3 final : public SettingBaseValueV3<bool> {
|
class GEODE_DLL BoolSettingV3 final : public SettingBaseValueV3<bool> {
|
||||||
private:
|
private:
|
||||||
class Impl;
|
class Impl;
|
||||||
|
@ -404,9 +356,6 @@ namespace geode {
|
||||||
Result<> isValid(bool value) const override;
|
Result<> isValid(bool value) const override;
|
||||||
|
|
||||||
SettingNodeV3* createNode(float width) override;
|
SettingNodeV3* createNode(float width) override;
|
||||||
|
|
||||||
std::optional<Setting> convertToLegacy() const override;
|
|
||||||
std::optional<std::shared_ptr<SettingValue>> convertToLegacyValue() const override;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class GEODE_DLL IntSettingV3 final : public SettingBaseValueV3<int64_t> {
|
class GEODE_DLL IntSettingV3 final : public SettingBaseValueV3<int64_t> {
|
||||||
|
@ -436,9 +385,6 @@ namespace geode {
|
||||||
bool isInputEnabled() const;
|
bool isInputEnabled() const;
|
||||||
|
|
||||||
SettingNodeV3* createNode(float width) override;
|
SettingNodeV3* createNode(float width) override;
|
||||||
|
|
||||||
std::optional<Setting> convertToLegacy() const override;
|
|
||||||
std::optional<std::shared_ptr<SettingValue>> convertToLegacyValue() const override;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class GEODE_DLL FloatSettingV3 final : public SettingBaseValueV3<double> {
|
class GEODE_DLL FloatSettingV3 final : public SettingBaseValueV3<double> {
|
||||||
|
@ -468,9 +414,6 @@ namespace geode {
|
||||||
bool isInputEnabled() const;
|
bool isInputEnabled() const;
|
||||||
|
|
||||||
SettingNodeV3* createNode(float width) override;
|
SettingNodeV3* createNode(float width) override;
|
||||||
|
|
||||||
std::optional<Setting> convertToLegacy() const override;
|
|
||||||
std::optional<std::shared_ptr<SettingValue>> convertToLegacyValue() const override;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class GEODE_DLL StringSettingV3 final : public SettingBaseValueV3<std::string, std::string_view> {
|
class GEODE_DLL StringSettingV3 final : public SettingBaseValueV3<std::string, std::string_view> {
|
||||||
|
@ -493,9 +436,6 @@ namespace geode {
|
||||||
std::optional<std::vector<std::string>> getEnumOptions() const;
|
std::optional<std::vector<std::string>> getEnumOptions() const;
|
||||||
|
|
||||||
SettingNodeV3* createNode(float width) override;
|
SettingNodeV3* createNode(float width) override;
|
||||||
|
|
||||||
std::optional<Setting> convertToLegacy() const override;
|
|
||||||
std::optional<std::shared_ptr<SettingValue>> convertToLegacyValue() const override;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class GEODE_DLL FileSettingV3 final : public SettingBaseValueV3<std::filesystem::path, std::filesystem::path const&> {
|
class GEODE_DLL FileSettingV3 final : public SettingBaseValueV3<std::filesystem::path, std::filesystem::path const&> {
|
||||||
|
@ -519,9 +459,6 @@ namespace geode {
|
||||||
std::optional<std::vector<utils::file::FilePickOptions::Filter>> getFilters() const;
|
std::optional<std::vector<utils::file::FilePickOptions::Filter>> getFilters() const;
|
||||||
|
|
||||||
SettingNodeV3* createNode(float width) override;
|
SettingNodeV3* createNode(float width) override;
|
||||||
|
|
||||||
std::optional<Setting> convertToLegacy() const override;
|
|
||||||
std::optional<std::shared_ptr<SettingValue>> convertToLegacyValue() const override;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class GEODE_DLL Color3BSettingV3 final : public SettingBaseValueV3<cocos2d::ccColor3B> {
|
class GEODE_DLL Color3BSettingV3 final : public SettingBaseValueV3<cocos2d::ccColor3B> {
|
||||||
|
@ -540,9 +477,6 @@ namespace geode {
|
||||||
Result<> isValid(cocos2d::ccColor3B value) const override;
|
Result<> isValid(cocos2d::ccColor3B value) const override;
|
||||||
|
|
||||||
SettingNodeV3* createNode(float width) override;
|
SettingNodeV3* createNode(float width) override;
|
||||||
|
|
||||||
std::optional<Setting> convertToLegacy() const override;
|
|
||||||
std::optional<std::shared_ptr<SettingValue>> convertToLegacyValue() const override;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class GEODE_DLL Color4BSettingV3 final : public SettingBaseValueV3<cocos2d::ccColor4B> {
|
class GEODE_DLL Color4BSettingV3 final : public SettingBaseValueV3<cocos2d::ccColor4B> {
|
||||||
|
@ -561,9 +495,6 @@ namespace geode {
|
||||||
Result<> isValid(cocos2d::ccColor4B value) const override;
|
Result<> isValid(cocos2d::ccColor4B value) const override;
|
||||||
|
|
||||||
SettingNodeV3* createNode(float width) override;
|
SettingNodeV3* createNode(float width) override;
|
||||||
|
|
||||||
std::optional<Setting> convertToLegacy() const override;
|
|
||||||
std::optional<std::shared_ptr<SettingValue>> convertToLegacyValue() const override;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class GEODE_DLL SettingNodeV3 : public cocos2d::CCNode {
|
class GEODE_DLL SettingNodeV3 : public cocos2d::CCNode {
|
||||||
|
@ -724,7 +655,7 @@ namespace geode {
|
||||||
public:
|
public:
|
||||||
using Callback = void(std::shared_ptr<SettingV3>);
|
using Callback = void(std::shared_ptr<SettingV3>);
|
||||||
|
|
||||||
ListenerResult handle(utils::MiniFunction<Callback> fn, SettingChangedEventV3* event);
|
ListenerResult handle(std::function<Callback> fn, SettingChangedEventV3* event);
|
||||||
/**
|
/**
|
||||||
* Listen to changes on a setting, or all settings
|
* Listen to changes on a setting, or all settings
|
||||||
* @param modID Mod whose settings to listen to
|
* @param modID Mod whose settings to listen to
|
||||||
|
@ -800,7 +731,7 @@ namespace geode {
|
||||||
};
|
};
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
EventListener<SettingChangedFilterV3>* listenForSettingChanges(std::string_view settingKey, auto&& callback, Mod* mod = getMod()) {
|
EventListener<SettingChangedFilterV3>* listenForSettingChangesV3(std::string_view settingKey, auto&& callback, Mod* mod = getMod()) {
|
||||||
using Ty = typename SettingTypeForValueType<T>::SettingType;
|
using Ty = typename SettingTypeForValueType<T>::SettingType;
|
||||||
return new EventListener(
|
return new EventListener(
|
||||||
[callback = std::move(callback)](std::shared_ptr<SettingV3> setting) {
|
[callback = std::move(callback)](std::shared_ptr<SettingV3> setting) {
|
||||||
|
@ -811,11 +742,11 @@ namespace geode {
|
||||||
SettingChangedFilterV3(mod, std::string(settingKey))
|
SettingChangedFilterV3(mod, std::string(settingKey))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
EventListener<SettingChangedFilterV3>* listenForSettingChanges(std::string_view settingKey, auto&& callback, Mod* mod = getMod()) {
|
EventListener<SettingChangedFilterV3>* listenForSettingChangesV3(std::string_view settingKey, auto&& callback, Mod* mod = getMod()) {
|
||||||
using T = std::remove_cvref_t<utils::function::Arg<0, decltype(callback)>>;
|
using T = std::remove_cvref_t<utils::function::Arg<0, decltype(callback)>>;
|
||||||
return listenForSettingChanges<T>(settingKey, std::move(callback), mod);
|
return listenForSettingChangesV3<T>(settingKey, std::move(callback), mod);
|
||||||
}
|
}
|
||||||
GEODE_DLL EventListener<SettingChangedFilterV3>* listenForAllSettingChanges(
|
GEODE_DLL EventListener<SettingChangedFilterV3>* listenForAllSettingChangesV3(
|
||||||
std::function<void(std::shared_ptr<SettingV3>)> const& callback,
|
std::function<void(std::shared_ptr<SettingV3>)> const& callback,
|
||||||
Mod* mod = getMod()
|
Mod* mod = getMod()
|
||||||
);
|
);
|
||||||
|
|
|
@ -2,12 +2,13 @@
|
||||||
|
|
||||||
#include <Geode/platform/platform.hpp>
|
#include <Geode/platform/platform.hpp>
|
||||||
#include <tulip/TulipHook.hpp>
|
#include <tulip/TulipHook.hpp>
|
||||||
|
#include "../Prelude.hpp"
|
||||||
|
|
||||||
namespace geode::hook {
|
namespace geode::hook {
|
||||||
/**
|
/**
|
||||||
* Create a calling convention wrapper for a function.
|
* Create a calling convention wrapper for a function.
|
||||||
*/
|
*/
|
||||||
GEODE_DLL tulip::hook::Result<void*> createWrapper(
|
GEODE_DLL Result<void*> createWrapper(
|
||||||
void* address,
|
void* address,
|
||||||
tulip::hook::WrapperMetadata const& metadata
|
tulip::hook::WrapperMetadata const& metadata
|
||||||
) noexcept;
|
) noexcept;
|
||||||
|
|
|
@ -89,14 +89,11 @@ namespace geode {
|
||||||
constexpr std::string_view GEODE_MOD_EXTENSION = ".geode";
|
constexpr std::string_view GEODE_MOD_EXTENSION = ".geode";
|
||||||
|
|
||||||
class Mod;
|
class Mod;
|
||||||
class Setting;
|
|
||||||
class Loader;
|
class Loader;
|
||||||
class Hook;
|
class Hook;
|
||||||
class VersionInfo;
|
class VersionInfo;
|
||||||
|
|
||||||
class Unknown;
|
class Unknown;
|
||||||
using unknownmemfn_t = void (Unknown::*)();
|
|
||||||
using unknownfn_t = void (*)();
|
|
||||||
|
|
||||||
namespace modifier {
|
namespace modifier {
|
||||||
template <class, class>
|
template <class, class>
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "../utils/MiniFunction.hpp"
|
|
||||||
#include "Traits.hpp"
|
#include "Traits.hpp"
|
||||||
|
|
||||||
#include <Geode/loader/Loader.hpp>
|
#include <Geode/loader/Loader.hpp>
|
||||||
|
@ -20,7 +19,7 @@ namespace geode::modifier {
|
||||||
class FieldContainer {
|
class FieldContainer {
|
||||||
private:
|
private:
|
||||||
std::vector<void*> m_containedFields;
|
std::vector<void*> m_containedFields;
|
||||||
std::vector<utils::MiniFunction<void(void*)>> m_destructorFunctions;
|
std::vector<std::function<void(void*)>> m_destructorFunctions;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
~FieldContainer() {
|
~FieldContainer() {
|
||||||
|
@ -40,9 +39,9 @@ namespace geode::modifier {
|
||||||
return m_containedFields.at(index);
|
return m_containedFields.at(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
void* setField(size_t index, size_t size, utils::MiniFunction<void(void*)> destructor) {
|
void* setField(size_t index, size_t size, std::function<void(void*)> destructor) {
|
||||||
m_containedFields.at(index) = operator new(size);
|
m_containedFields.at(index) = operator new(size);
|
||||||
m_destructorFunctions.at(index) = destructor;
|
m_destructorFunctions.at(index) = std::move(destructor);
|
||||||
return m_containedFields.at(index);
|
return m_containedFields.at(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -104,8 +104,36 @@
|
||||||
} \
|
} \
|
||||||
} while (0);
|
} while (0);
|
||||||
|
|
||||||
namespace geode::modifier {
|
namespace geode {
|
||||||
|
class Priority {
|
||||||
|
public:
|
||||||
|
static inline constexpr int32_t First = -3000;
|
||||||
|
static inline constexpr int32_t VeryEarly = -2000;
|
||||||
|
static inline constexpr int32_t Early = -1000;
|
||||||
|
static inline constexpr int32_t Normal = 0;
|
||||||
|
static inline constexpr int32_t Late = 1000;
|
||||||
|
static inline constexpr int32_t VeryLate = 2000;
|
||||||
|
static inline constexpr int32_t Last = 3000;
|
||||||
|
|
||||||
|
static inline constexpr int32_t FirstPre = First;
|
||||||
|
static inline constexpr int32_t VeryEarlyPre = VeryEarly;
|
||||||
|
static inline constexpr int32_t EarlyPre = Early;
|
||||||
|
static inline constexpr int32_t NormalPre = Normal;
|
||||||
|
static inline constexpr int32_t LatePre = Late;
|
||||||
|
static inline constexpr int32_t VeryLatePre = VeryLate;
|
||||||
|
static inline constexpr int32_t LastPre = Last;
|
||||||
|
|
||||||
|
static inline constexpr int32_t FirstPost = Last;
|
||||||
|
static inline constexpr int32_t VeryEarlyPost = VeryLate;
|
||||||
|
static inline constexpr int32_t EarlyPost = Late;
|
||||||
|
static inline constexpr int32_t NormalPost = Normal;
|
||||||
|
static inline constexpr int32_t LatePost = Early;
|
||||||
|
static inline constexpr int32_t VeryLatePost = VeryEarly;
|
||||||
|
static inline constexpr int32_t LastPost = First;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace geode::modifier {
|
||||||
template <class Derived, class Base>
|
template <class Derived, class Base>
|
||||||
class ModifyDerive;
|
class ModifyDerive;
|
||||||
|
|
||||||
|
@ -114,22 +142,179 @@ namespace geode::modifier {
|
||||||
public:
|
public:
|
||||||
std::map<std::string, std::shared_ptr<Hook>> m_hooks;
|
std::map<std::string, std::shared_ptr<Hook>> m_hooks;
|
||||||
|
|
||||||
Result<Hook*> getHook(std::string const& name) {
|
/// @brief Get a hook by name
|
||||||
if (m_hooks.find(name) == m_hooks.end()) {
|
/// @param name The name of the hook to get
|
||||||
|
/// @returns Ok if the hook was found, Err if the hook was not found
|
||||||
|
Result<Hook*> getHook(std::string_view name) {
|
||||||
|
auto key = std::string(name);
|
||||||
|
if (m_hooks.find(key) == m_hooks.end()) {
|
||||||
return Err("Hook not in this modify");
|
return Err("Hook not in this modify");
|
||||||
}
|
}
|
||||||
return Ok(m_hooks[name].get());
|
return Ok(m_hooks[key].get());
|
||||||
}
|
}
|
||||||
|
|
||||||
Result<> setHookPriority(std::string const& name, int32_t priority) {
|
/// @brief Set the priority of a hook
|
||||||
auto res = this->getHook(name);
|
/// @param name The name of the hook to set the priority of
|
||||||
if (!res) {
|
/// @param priority The priority to set the hook to
|
||||||
return Err(res.unwrapErr());
|
/// @returns Ok if the hook was found and the priority was set, Err if the hook was not found
|
||||||
}
|
Result<> setHookPriority(std::string_view name, int32_t priority = Priority::Normal) {
|
||||||
res.unwrap()->setPriority(priority);
|
GEODE_UNWRAP_INTO(auto hook, this->getHook(name));
|
||||||
|
hook->setPriority(priority);
|
||||||
return Ok();
|
return Ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @brief Set the priority of a hook
|
||||||
|
/// @param name The name of the hook to set the priority of
|
||||||
|
/// @param priority The priority to set the hook to
|
||||||
|
/// @returns Ok if the hook was found and the priority was set, Err if the hook was not found
|
||||||
|
Result<> setHookPriorityPre(std::string_view name, int32_t priority = Priority::Normal) {
|
||||||
|
return this->setHookPriority(name, priority);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Set the priority of a hook
|
||||||
|
/// @param name The name of the hook to set the priority of
|
||||||
|
/// @param priority The priority to set the hook to
|
||||||
|
/// @returns Ok if the hook was found and the priority was set, Err if the hook was not found
|
||||||
|
Result<> setHookPriorityPost(std::string_view name, int32_t priority = Priority::Normal) {
|
||||||
|
return this->setHookPriority(name, -priority);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Set the priority of a hook to be after another hook in different mods
|
||||||
|
/// @param name The name of the hook to set the priority of
|
||||||
|
/// @param after The mod to set the priority after
|
||||||
|
/// @returns Ok if the hook was found and the priority was set, Err if the hook was not found
|
||||||
|
Result<> setHookPriorityAfter(std::string_view name, Mod* mod) {
|
||||||
|
GEODE_UNWRAP_INTO(auto hook, this->getHook(name));
|
||||||
|
auto func = [=](ModStateEvent* event){
|
||||||
|
auto hooks = mod->getHooks();
|
||||||
|
for (auto modHook : hooks) {
|
||||||
|
if (modHook->getAddress() != hook->getAddress()) continue;
|
||||||
|
auto priority = modHook->getPriority();
|
||||||
|
if (hook->getPriority() <= priority) {
|
||||||
|
hook->setPriority(priority + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ListenerResult::Propagate;
|
||||||
|
};
|
||||||
|
if (mod->isEnabled()) {
|
||||||
|
func(nullptr);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
new EventListener(func, ModStateFilter(mod, ModEventType::Loaded));
|
||||||
|
}
|
||||||
|
return Ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Set the priority of a hook to be after another hook in different mods
|
||||||
|
/// @param name The name of the hook to set the priority of
|
||||||
|
/// @param after The mod id of the mod to set the priority after
|
||||||
|
/// @returns Ok if the hook was found and the priority was set, Err if the hook was not found
|
||||||
|
Result<> setHookPriorityAfter(std::string_view name, std::string_view after) {
|
||||||
|
auto mod = Loader::get()->getInstalledMod(std::string(after));
|
||||||
|
if (!mod) return Err("Mod not found");
|
||||||
|
return this->setHookPriorityAfter(name, mod);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Set the priority of a hook to be before another hook in different mods
|
||||||
|
/// @param name The name of the hook to set the priority of
|
||||||
|
/// @param before The mod to set the priority before
|
||||||
|
/// @returns Ok if the hook was found and the priority was set, Err if the hook was not found
|
||||||
|
Result<> setHookPriorityBefore(std::string_view name, Mod* mod) {
|
||||||
|
GEODE_UNWRAP_INTO(auto hook, this->getHook(name));
|
||||||
|
auto func = [=](ModStateEvent* event){
|
||||||
|
auto hooks = mod->getHooks();
|
||||||
|
for (auto modHook : hooks) {
|
||||||
|
if (modHook->getAddress() != hook->getAddress()) continue;
|
||||||
|
auto priority = modHook->getPriority();
|
||||||
|
if (hook->getPriority() >= priority) {
|
||||||
|
hook->setPriority(priority - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ListenerResult::Propagate;
|
||||||
|
};
|
||||||
|
if (mod->isEnabled()) {
|
||||||
|
func(nullptr);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
new EventListener(func, ModStateFilter(mod, ModEventType::Loaded));
|
||||||
|
}
|
||||||
|
return Ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Set the priority of a hook to be before another hook in different mods
|
||||||
|
/// @param name The name of the hook to set the priority of
|
||||||
|
/// @param before The mod id of the mod to set the priority before
|
||||||
|
/// @returns Ok if the hook was found and the priority was set, Err if the hook was not found
|
||||||
|
Result<> setHookPriorityBefore(std::string_view name, std::string_view before) {
|
||||||
|
auto mod = Loader::get()->getInstalledMod(std::string(before));
|
||||||
|
if (!mod) return Err("Mod not found");
|
||||||
|
return this->setHookPriorityBefore(name, mod);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Set the priority of a hook to be after another hook in different mods
|
||||||
|
/// @param name The name of the hook to set the priority of
|
||||||
|
/// @param after The mod id of the mod to set the priority after
|
||||||
|
/// @returns Ok if the hook was found and the priority was set, Err if the hook was not found
|
||||||
|
Result<> setHookPriorityAfterPre(std::string_view name, std::string_view after) {
|
||||||
|
return this->setHookPriorityAfter(name, after);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Set the priority of a hook to be after another hook in different mods
|
||||||
|
/// @param name The name of the hook to set the priority of
|
||||||
|
/// @param before The mod to set the priority after
|
||||||
|
/// @returns Ok if the hook was found and the priority was set, Err if the hook was not found
|
||||||
|
Result<> setHookPriorityAfterPre(std::string_view name, Mod* mod) {
|
||||||
|
return this->setHookPriorityAfter(name, mod);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Set the priority of a hook to be before another hook in different mods
|
||||||
|
/// @param name The name of the hook to set the priority of
|
||||||
|
/// @param before The mod id of the mod to set the priority before
|
||||||
|
/// @returns Ok if the hook was found and the priority was set, Err if the hook was not found
|
||||||
|
Result<> setHookPriorityBeforePre(std::string_view name, std::string_view before) {
|
||||||
|
return this->setHookPriorityBefore(name, before);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Set the priority of a hook to be before another hook in different mods
|
||||||
|
/// @param name The name of the hook to set the priority of
|
||||||
|
/// @param before The mod to set the priority before
|
||||||
|
/// @returns Ok if the hook was found and the priority was set, Err if the hook was not found
|
||||||
|
Result<> setHookPriorityBeforePre(std::string_view name, Mod* mod) {
|
||||||
|
return this->setHookPriorityBefore(name, mod);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Set the priority of a hook to be after another hook in different mods
|
||||||
|
/// @param name The name of the hook to set the priority of
|
||||||
|
/// @param after The mod id of the mod to set the priority after
|
||||||
|
/// @returns Ok if the hook was found and the priority was set, Err if the hook was not found
|
||||||
|
Result<> setHookPriorityAfterPost(std::string_view name, std::string_view after) {
|
||||||
|
return this->setHookPriorityBefore(name, after);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Set the priority of a hook to be after another hook in different mods
|
||||||
|
/// @param name The name of the hook to set the priority of
|
||||||
|
/// @param before The mod to set the priority after
|
||||||
|
/// @returns Ok if the hook was found and the priority was set, Err if the hook was not found
|
||||||
|
Result<> setHookPriorityAfterPost(std::string_view name, Mod* mod) {
|
||||||
|
return this->setHookPriorityBefore(name, mod);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Set the priority of a hook to be before another hook in different mods
|
||||||
|
/// @param name The name of the hook to set the priority of
|
||||||
|
/// @param before The mod id of the mod to set the priority before
|
||||||
|
/// @returns Ok if the hook was found and the priority was set, Err if the hook was not found
|
||||||
|
Result<> setHookPriorityBeforePost(std::string_view name, std::string_view before) {
|
||||||
|
return this->setHookPriorityAfter(name, before);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Set the priority of a hook to be before another hook in different mods
|
||||||
|
/// @param name The name of the hook to set the priority of
|
||||||
|
/// @param before The mod to set the priority before
|
||||||
|
/// @returns Ok if the hook was found and the priority was set, Err if the hook was not found
|
||||||
|
Result<> setHookPriorityBeforePost(std::string_view name, Mod* mod) {
|
||||||
|
return this->setHookPriorityAfter(name, mod);
|
||||||
|
}
|
||||||
|
|
||||||
// unordered_map<handles> idea
|
// unordered_map<handles> idea
|
||||||
ModifyBase() {
|
ModifyBase() {
|
||||||
struct EboCheck : ModifyDerived::Base {
|
struct EboCheck : ModifyDerived::Base {
|
||||||
|
@ -158,7 +343,7 @@ namespace geode::modifier {
|
||||||
for (auto& [uuid, hook] : m_hooks) {
|
for (auto& [uuid, hook] : m_hooks) {
|
||||||
auto res = Mod::get()->claimHook(hook);
|
auto res = Mod::get()->claimHook(hook);
|
||||||
if (!res) {
|
if (!res) {
|
||||||
log::error("Failed to claim hook {}: {}", hook->getDisplayName(), res.error());
|
log::error("Failed to claim hook {}: {}", hook->getDisplayName(), res.unwrapErr());
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
added.push_back(uuid);
|
added.push_back(uuid);
|
||||||
|
|
|
@ -11,6 +11,8 @@
|
||||||
|
|
||||||
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) && !defined(__CYGWIN__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) && !defined(__CYGWIN__)
|
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) && !defined(__CYGWIN__) || defined(WIN64) || defined(_WIN64) || defined(__WIN64) && !defined(__CYGWIN__)
|
||||||
#define GEODE_WINDOWS(...) __VA_ARGS__
|
#define GEODE_WINDOWS(...) __VA_ARGS__
|
||||||
|
#define GEODE_DESKTOP(...) __VA_ARGS__
|
||||||
|
#define GEODE_MOBILE(...)
|
||||||
#define GEODE_IS_WINDOWS
|
#define GEODE_IS_WINDOWS
|
||||||
#define GEODE_IS_DESKTOP
|
#define GEODE_IS_DESKTOP
|
||||||
#define GEODE_PLATFORM_NAME "Windows"
|
#define GEODE_PLATFORM_NAME "Windows"
|
||||||
|
@ -43,6 +45,8 @@
|
||||||
#define GEODE_INTEL_MAC(...)
|
#define GEODE_INTEL_MAC(...)
|
||||||
#define GEODE_ARM_MAC(...)
|
#define GEODE_ARM_MAC(...)
|
||||||
#define GEODE_IOS(...) __VA_ARGS__
|
#define GEODE_IOS(...) __VA_ARGS__
|
||||||
|
#define GEODE_DESKTOP(...)
|
||||||
|
#define GEODE_MOBILE(...) __VA_ARGS__
|
||||||
#define GEODE_IS_IOS
|
#define GEODE_IS_IOS
|
||||||
#define GEODE_IS_MOBILE
|
#define GEODE_IS_MOBILE
|
||||||
#define GEODE_PLATFORM_NAME "iOS"
|
#define GEODE_PLATFORM_NAME "iOS"
|
||||||
|
@ -53,6 +57,8 @@
|
||||||
#else
|
#else
|
||||||
#define GEODE_IOS(...)
|
#define GEODE_IOS(...)
|
||||||
#define GEODE_MACOS(...) __VA_ARGS__
|
#define GEODE_MACOS(...) __VA_ARGS__
|
||||||
|
#define GEODE_DESKTOP(...) __VA_ARGS__
|
||||||
|
#define GEODE_MOBILE(...)
|
||||||
#define GEODE_IS_MACOS
|
#define GEODE_IS_MACOS
|
||||||
#define GEODE_IS_DESKTOP
|
#define GEODE_IS_DESKTOP
|
||||||
#define GEODE_PLATFORM_EXTENSION ".dylib"
|
#define GEODE_PLATFORM_EXTENSION ".dylib"
|
||||||
|
@ -84,6 +90,8 @@
|
||||||
// Android
|
// Android
|
||||||
#if defined(__ANDROID__)
|
#if defined(__ANDROID__)
|
||||||
#define GEODE_ANDROID(...) __VA_ARGS__
|
#define GEODE_ANDROID(...) __VA_ARGS__
|
||||||
|
#define GEODE_MOBILE(...) __VA_ARGS__
|
||||||
|
#define GEODE_DESKTOP(...)
|
||||||
#define GEODE_IS_ANDROID
|
#define GEODE_IS_ANDROID
|
||||||
#define GEODE_IS_MOBILE
|
#define GEODE_IS_MOBILE
|
||||||
#define GEODE_CALL
|
#define GEODE_CALL
|
||||||
|
|
|
@ -116,14 +116,22 @@ namespace geode {
|
||||||
public:
|
public:
|
||||||
// todo in v4: make these flags and add archless Mac and Android as well as Desktop and Mobile and remove Linux
|
// todo in v4: make these flags and add archless Mac and Android as well as Desktop and Mobile and remove Linux
|
||||||
enum {
|
enum {
|
||||||
Unknown = -1,
|
Unknown = 0b000000,
|
||||||
Windows = 0,
|
Windows = 0b000001,
|
||||||
MacIntel = 1,
|
Android32 = 0b000010,
|
||||||
MacArm = 2,
|
Android64 = 0b000100,
|
||||||
iOS = 3,
|
MacIntel = 0b001000,
|
||||||
Android32 = 4,
|
MacArm = 0b010000,
|
||||||
Android64 = 5,
|
iOS = 0b100000,
|
||||||
Linux = 6,
|
Android = Android32 | Android64,
|
||||||
|
Mac = MacIntel | MacArm,
|
||||||
|
Apple = Mac | iOS,
|
||||||
|
X64 = MacIntel | Windows,
|
||||||
|
X86 = Unknown,
|
||||||
|
ArmV7 = Android32,
|
||||||
|
ArmV8 = Android64 | MacArm | iOS,
|
||||||
|
Desktop = Windows | Mac,
|
||||||
|
Mobile = Android | iOS,
|
||||||
};
|
};
|
||||||
|
|
||||||
using Type = decltype(Unknown);
|
using Type = decltype(Unknown);
|
||||||
|
@ -190,7 +198,6 @@ namespace geode {
|
||||||
case iOS: return "iOS";
|
case iOS: return "iOS";
|
||||||
case Android32: return "Android32";
|
case Android32: return "Android32";
|
||||||
case Android64: return "Android64";
|
case Android64: return "Android64";
|
||||||
case Linux: return "Linux";
|
|
||||||
default: break;
|
default: break;
|
||||||
}
|
}
|
||||||
return "Undefined";
|
return "Undefined";
|
||||||
|
@ -206,7 +213,6 @@ namespace geode {
|
||||||
case iOS: return "ios";
|
case iOS: return "ios";
|
||||||
case Android32: return ignoreArch ? "android" : "android32";
|
case Android32: return ignoreArch ? "android" : "android32";
|
||||||
case Android64: return ignoreArch ? "android" : "android64";
|
case Android64: return ignoreArch ? "android" : "android64";
|
||||||
case Linux: return "linux";
|
|
||||||
default: break;
|
default: break;
|
||||||
}
|
}
|
||||||
return "undefined";
|
return "undefined";
|
||||||
|
|
|
@ -13,7 +13,7 @@ namespace geode {
|
||||||
virtual void updateColor(cocos2d::ccColor4B const& color) {}
|
virtual void updateColor(cocos2d::ccColor4B const& color) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
// todo in v4: make this pimpl and maybe use events over the delegate?
|
// todo in v4: maybe use events over the delegate?
|
||||||
// thing with events is that if you just filter via ColorPickPopup* it
|
// thing with events is that if you just filter via ColorPickPopup* it
|
||||||
// won't work unless you automatically detach the filter when closing the
|
// won't work unless you automatically detach the filter when closing the
|
||||||
// popup (otherwise opening another popup really quickly will just be
|
// popup (otherwise opening another popup really quickly will just be
|
||||||
|
@ -24,18 +24,8 @@ namespace geode {
|
||||||
public cocos2d::extension::ColorPickerDelegate,
|
public cocos2d::extension::ColorPickerDelegate,
|
||||||
public TextInputDelegate {
|
public TextInputDelegate {
|
||||||
protected:
|
protected:
|
||||||
cocos2d::ccColor4B m_color;
|
class Impl;
|
||||||
cocos2d::ccColor4B m_originalColor;
|
std::unique_ptr<Impl> m_impl;
|
||||||
cocos2d::extension::CCControlColourPicker* m_picker;
|
|
||||||
Slider* m_opacitySlider = nullptr;
|
|
||||||
TextInput* m_rInput;
|
|
||||||
TextInput* m_gInput;
|
|
||||||
TextInput* m_bInput;
|
|
||||||
TextInput* m_hexInput;
|
|
||||||
TextInput* m_opacityInput = nullptr;
|
|
||||||
ColorPickPopupDelegate* m_delegate = nullptr;
|
|
||||||
cocos2d::CCSprite* m_newColorSpr;
|
|
||||||
CCMenuItemSpriteExtra* m_resetBtn;
|
|
||||||
|
|
||||||
static constexpr auto TAG_OPACITY_INPUT = 0;
|
static constexpr auto TAG_OPACITY_INPUT = 0;
|
||||||
static constexpr auto TAG_R_INPUT = 1;
|
static constexpr auto TAG_R_INPUT = 1;
|
||||||
|
@ -43,10 +33,13 @@ namespace geode {
|
||||||
static constexpr auto TAG_B_INPUT = 3;
|
static constexpr auto TAG_B_INPUT = 3;
|
||||||
static constexpr auto TAG_HEX_INPUT = 4;
|
static constexpr auto TAG_HEX_INPUT = 4;
|
||||||
|
|
||||||
|
ColorPickPopup();
|
||||||
|
~ColorPickPopup();
|
||||||
bool setup(cocos2d::ccColor4B const& color, bool isRGBA) override;
|
bool setup(cocos2d::ccColor4B const& color, bool isRGBA) override;
|
||||||
|
|
||||||
void onOpacitySlider(cocos2d::CCObject* sender);
|
void onOpacitySlider(cocos2d::CCObject* sender);
|
||||||
void onReset(cocos2d::CCObject* sender);
|
void onReset(cocos2d::CCObject* sender);
|
||||||
|
void onClose(cocos2d::CCObject* sender) override;
|
||||||
|
|
||||||
void textChanged(CCTextInputNode* input) override;
|
void textChanged(CCTextInputNode* input) override;
|
||||||
void colorValueChanged(cocos2d::ccColor3B color) override;
|
void colorValueChanged(cocos2d::ccColor3B color) override;
|
||||||
|
|
|
@ -30,7 +30,7 @@ namespace geode {
|
||||||
std::optional<std::string> m_targetID;
|
std::optional<std::string> m_targetID;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ListenerResult handle(utils::MiniFunction<Callback> fn, AEnterLayerEvent* event);
|
ListenerResult handle(std::function<Callback> fn, AEnterLayerEvent* event);
|
||||||
|
|
||||||
AEnterLayerFilter(
|
AEnterLayerFilter(
|
||||||
std::optional<std::string> const& id
|
std::optional<std::string> const& id
|
||||||
|
@ -63,7 +63,7 @@ namespace geode {
|
||||||
std::optional<std::string> m_targetID;
|
std::optional<std::string> m_targetID;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ListenerResult handle(utils::MiniFunction<Callback> fn, EnterLayerEvent<N>* event) {
|
ListenerResult handle(std::function<Callback> fn, EnterLayerEvent<N>* event) {
|
||||||
if (m_targetID == event->getID()) {
|
if (m_targetID == event->getID()) {
|
||||||
fn(static_cast<T*>(event));
|
fn(static_cast<T*>(event));
|
||||||
}
|
}
|
||||||
|
|
|
@ -133,11 +133,6 @@ namespace geode {
|
||||||
*/
|
*/
|
||||||
GEODE_DLL void openSupportPopup(Mod* mod);
|
GEODE_DLL void openSupportPopup(Mod* mod);
|
||||||
GEODE_DLL void openSupportPopup(ModMetadata const& metadata);
|
GEODE_DLL void openSupportPopup(ModMetadata const& metadata);
|
||||||
/**
|
|
||||||
* Open the store page for a mod (if it exists)
|
|
||||||
*/
|
|
||||||
[[deprecated("Will be removed, use openInfoPopup instead")]]
|
|
||||||
GEODE_DLL void openIndexPopup(Mod* mod);
|
|
||||||
/**
|
/**
|
||||||
* Open the settings popup for a mod (if it has any settings)
|
* Open the settings popup for a mod (if it has any settings)
|
||||||
*/
|
*/
|
||||||
|
@ -160,6 +155,10 @@ namespace geode {
|
||||||
* Create a logo sprite for a mod
|
* Create a logo sprite for a mod
|
||||||
*/
|
*/
|
||||||
GEODE_DLL cocos2d::CCNode* createModLogo(Mod* mod);
|
GEODE_DLL cocos2d::CCNode* createModLogo(Mod* mod);
|
||||||
|
/**
|
||||||
|
* Create a logo sprite for a mod from a .geode file
|
||||||
|
*/
|
||||||
|
GEODE_DLL cocos2d::CCNode* createModLogo(std::filesystem::path const& geodePackage);
|
||||||
/**
|
/**
|
||||||
* Create a logo sprite for a mod downloaded from the Geode servers. The
|
* Create a logo sprite for a mod downloaded from the Geode servers. The
|
||||||
* logo is initially a loading circle, with the actual sprite downloaded
|
* logo is initially a loading circle, with the actual sprite downloaded
|
||||||
|
|
|
@ -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
|
#pragma once
|
||||||
|
|
||||||
#include "../include/ccMacros.h"
|
#include <cocos2d.h>
|
||||||
#include "../cocoa/CCAffineTransform.h"
|
#include <Geode/platform/platform.hpp>
|
||||||
#include "../cocoa/CCArray.h"
|
#include <optional>
|
||||||
#include <Geode/platform/platform.hpp>
|
#include <memory>
|
||||||
#include <optional>
|
|
||||||
#include <memory>
|
namespace geode {
|
||||||
|
|
||||||
NS_CC_BEGIN
|
#pragma warning(push)
|
||||||
|
#pragma warning(disable: 4275)
|
||||||
class CCNode;
|
|
||||||
|
/**
|
||||||
#pragma warning(push)
|
* Layouts automatically handle the positioning of nodes. Use CCNode::setLayout
|
||||||
#pragma warning(disable: 4275)
|
* to apply a layout to a node, and then use CCNode::updateLayout to apply
|
||||||
|
* the layout's positioning. Geode comes with a few default layouts like
|
||||||
/**
|
* RowLayout, ColumnLayout, and GridLayout, but if you need a different kind
|
||||||
* Layouts automatically handle the positioning of nodes. Use CCNode::setLayout
|
* of layout you can inherit from the Layout class.
|
||||||
* to apply a layout to a node, and then use CCNode::updateLayout to apply
|
*/
|
||||||
* the layout's positioning. Geode comes with a few default layouts like
|
class GEODE_DLL Layout : public cocos2d::CCObject {
|
||||||
* RowLayout, ColumnLayout, and GridLayout, but if you need a different kind
|
protected:
|
||||||
* of layout you can inherit from the Layout class.
|
cocos2d::CCArray* getNodesToPosition(cocos2d::CCNode* forNode) const;
|
||||||
*/
|
|
||||||
class GEODE_DLL Layout : public CCObject {
|
bool m_ignoreInvisibleChildren = false;
|
||||||
protected:
|
|
||||||
CCArray* getNodesToPosition(CCNode* forNode) const;
|
public:
|
||||||
|
/**
|
||||||
bool m_ignoreInvisibleChildren = false;
|
* Automatically apply the layout's positioning on a set of nodes
|
||||||
|
* @param on Node to apply the layout on. Position's the node's children
|
||||||
public:
|
* according to the layout. The content size of the node should be
|
||||||
/**
|
* respected as a boundary the layout shouldn't overflow. The node may be
|
||||||
* Automatically apply the layout's positioning on a set of nodes
|
* rescaled to better fit its contents
|
||||||
* @param on Node to apply the layout on. Position's the node's children
|
*/
|
||||||
* according to the layout. The content size of the node should be
|
virtual void apply(cocos2d::CCNode* on) = 0;
|
||||||
* respected as a boundary the layout shouldn't overflow. The node may be
|
|
||||||
* rescaled to better fit its contents
|
/**
|
||||||
*/
|
* Get how much space this layout would like to take up for a given target
|
||||||
virtual void apply(CCNode* on) = 0;
|
*/
|
||||||
|
virtual cocos2d::CCSize getSizeHint(cocos2d::CCNode* on) const = 0;
|
||||||
/**
|
|
||||||
* Get how much space this layout would like to take up for a given target
|
void ignoreInvisibleChildren(bool ignore);
|
||||||
*/
|
bool isIgnoreInvisibleChildren() const;
|
||||||
virtual CCSize getSizeHint(CCNode* on) const = 0;
|
|
||||||
|
virtual ~Layout() = default;
|
||||||
void ignoreInvisibleChildren(bool ignore);
|
};
|
||||||
bool isIgnoreInvisibleChildren() const;
|
|
||||||
|
class GEODE_DLL LayoutOptions : public cocos2d::CCObject {
|
||||||
virtual ~Layout() = default;
|
public:
|
||||||
};
|
virtual ~LayoutOptions() = default;
|
||||||
|
};
|
||||||
class GEODE_DLL LayoutOptions : public CCObject {
|
|
||||||
public:
|
/**
|
||||||
virtual ~LayoutOptions() = default;
|
* The direction of an AxisLayout
|
||||||
};
|
*/
|
||||||
|
enum class Axis {
|
||||||
/**
|
Row,
|
||||||
* The direction of an AxisLayout
|
Column,
|
||||||
*/
|
};
|
||||||
enum class Axis {
|
|
||||||
Row,
|
/**
|
||||||
Column,
|
* Specifies the alignment of something in an AxisLayout
|
||||||
};
|
*/
|
||||||
|
enum class AxisAlignment {
|
||||||
/**
|
// Align items to the start
|
||||||
* Specifies the alignment of something in an AxisLayout
|
// |ooo......|
|
||||||
*/
|
Start,
|
||||||
enum class AxisAlignment {
|
// All items are centered
|
||||||
// Align items to the start
|
// |...ooo...|
|
||||||
// |ooo......|
|
Center,
|
||||||
Start,
|
// Align items to the end
|
||||||
// All items are centered
|
// |......ooo|
|
||||||
// |...ooo...|
|
End,
|
||||||
Center,
|
// Each item gets the same portion from the layout (disregards gap)
|
||||||
// Align items to the end
|
// |.o..o..o.|
|
||||||
// |......ooo|
|
Even,
|
||||||
End,
|
// Space between each item is the same (disregards gap)
|
||||||
// Each item gets the same portion from the layout (disregards gap)
|
// |o...o...o|
|
||||||
// |.o..o..o.|
|
Between,
|
||||||
Even,
|
};
|
||||||
// Space between each item is the same (disregards gap)
|
|
||||||
// |o...o...o|
|
constexpr float AXISLAYOUT_DEFAULT_MIN_SCALE = 0.65f;
|
||||||
Between,
|
constexpr int AXISLAYOUT_DEFAULT_PRIORITY = 0;
|
||||||
};
|
|
||||||
|
/**
|
||||||
constexpr float AXISLAYOUT_DEFAULT_MIN_SCALE = 0.65f;
|
* Options for controlling the behaviour of individual nodes in an AxisLayout
|
||||||
constexpr int AXISLAYOUT_DEFAULT_PRIORITY = 0;
|
* @example
|
||||||
|
* auto node = CCNode::create();
|
||||||
/**
|
* // this node will have 10 units of spacing between it and the next one
|
||||||
* Options for controlling the behaviour of individual nodes in an AxisLayout
|
* node->setLayoutOptions(
|
||||||
* @example
|
* AxisLayoutOptions::create()
|
||||||
* auto node = CCNode::create();
|
* ->setNextGap(10.f)
|
||||||
* // this node will have 10 units of spacing between it and the next one
|
* );
|
||||||
* node->setLayoutOptions(
|
* someNodeWithALayout->addChild(node);
|
||||||
* AxisLayoutOptions::create()
|
*/
|
||||||
* ->setNextGap(10.f)
|
class GEODE_DLL AxisLayoutOptions : public LayoutOptions {
|
||||||
* );
|
protected:
|
||||||
* someNodeWithALayout->addChild(node);
|
class Impl;
|
||||||
*/
|
|
||||||
class GEODE_DLL AxisLayoutOptions : public LayoutOptions {
|
std::unique_ptr<Impl> m_impl;
|
||||||
protected:
|
|
||||||
class Impl;
|
AxisLayoutOptions();
|
||||||
|
|
||||||
std::unique_ptr<Impl> m_impl;
|
public:
|
||||||
|
static AxisLayoutOptions* create();
|
||||||
AxisLayoutOptions();
|
|
||||||
|
virtual ~AxisLayoutOptions();
|
||||||
public:
|
|
||||||
static AxisLayoutOptions* create();
|
std::optional<bool> getAutoScale() const;
|
||||||
|
// @note Use hasExplicitMaxScale to know if the default scale has been overwritten
|
||||||
virtual ~AxisLayoutOptions();
|
float getMaxScale() const;
|
||||||
|
// @note Use hasExplicitMinScale to know if the default scale has been overwritten
|
||||||
std::optional<bool> getAutoScale() const;
|
float getMinScale() const;
|
||||||
// @note Use hasExplicitMaxScale to know if the default scale has been overwritten
|
bool hasExplicitMaxScale() const;
|
||||||
float getMaxScale() const;
|
bool hasExplicitMinScale() const;
|
||||||
// @note Use hasExplicitMinScale to know if the default scale has been overwritten
|
float getRelativeScale() const;
|
||||||
float getMinScale() const;
|
std::optional<float> getLength() const;
|
||||||
bool hasExplicitMaxScale() const;
|
std::optional<float> getPrevGap() const;
|
||||||
bool hasExplicitMinScale() const;
|
std::optional<float> getNextGap() const;
|
||||||
float getRelativeScale() const;
|
bool getBreakLine() const;
|
||||||
std::optional<float> getLength() const;
|
bool getSameLine() const;
|
||||||
std::optional<float> getPrevGap() const;
|
int getScalePriority() const;
|
||||||
std::optional<float> getNextGap() const;
|
std::optional<AxisAlignment> getCrossAxisAlignment() const;
|
||||||
bool getBreakLine() const;
|
|
||||||
bool getSameLine() const;
|
/**
|
||||||
int getScalePriority() const;
|
* Set the limits to what the node can be scaled to. Passing `std::nullopt`
|
||||||
std::optional<AxisAlignment> getCrossAxisAlignment() const;
|
* uses the parent layout's default min / max scales
|
||||||
|
*/
|
||||||
/**
|
AxisLayoutOptions* setScaleLimits(std::optional<float> min, std::optional<float> max);
|
||||||
* Set the maximum scale this node can be if it's contained in an
|
|
||||||
* auto-scaled layout. Default is 1
|
/**
|
||||||
*/
|
* Set the relative scale of this node compared to other nodes if it's
|
||||||
[[deprecated("Use AxisLayoutOptions::setScaleLimits")]]
|
* contained in an auto-scaled layout. Default is 1
|
||||||
AxisLayoutOptions* setMaxScale(float scale);
|
*/
|
||||||
|
AxisLayoutOptions* setRelativeScale(float scale);
|
||||||
/**
|
|
||||||
* Set the minimum scale this node can be if it's contained in an
|
/**
|
||||||
* auto-scaled layout. Default is AXISLAYOUT_DEFAULT_MIN_SCALE
|
* Set auto-scaling for this node, overriding the layout's auto-scale
|
||||||
*/
|
* setting. If nullopt, the layout's auto-scale options will be used
|
||||||
[[deprecated("Use AxisLayoutOptions::setScaleLimits")]]
|
*/
|
||||||
AxisLayoutOptions* setMinScale(float scale);
|
AxisLayoutOptions* setAutoScale(std::optional<bool> enabled);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the limits to what the node can be scaled to. Passing `std::nullopt`
|
* Set an absolute length for this node. If nullopt, the length will be
|
||||||
* uses the parent layout's default min / max scales
|
* dynamically calculated based on content size
|
||||||
*/
|
*/
|
||||||
AxisLayoutOptions* setScaleLimits(std::optional<float> min, std::optional<float> max);
|
AxisLayoutOptions* setLength(std::optional<float> length);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the relative scale of this node compared to other nodes if it's
|
* Override the default gap in the layout between this node and the
|
||||||
* contained in an auto-scaled layout. Default is 1
|
* previous one. If nullopt, the default gap of the layout will be used
|
||||||
*/
|
*/
|
||||||
AxisLayoutOptions* setRelativeScale(float scale);
|
AxisLayoutOptions* setPrevGap(std::optional<float> gap);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set auto-scaling for this node, overriding the layout's auto-scale
|
* Override the default gap in the layout between this node and the next
|
||||||
* setting. If nullopt, the layout's auto-scale options will be used
|
* one. If nullopt, the default gap of the layout will be used
|
||||||
*/
|
*/
|
||||||
AxisLayoutOptions* setAutoScale(std::optional<bool> enabled);
|
AxisLayoutOptions* setNextGap(std::optional<float> gap);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set an absolute length for this node. If nullopt, the length will be
|
* If enabled, the node will always cause a growable axis layout to break
|
||||||
* dynamically calculated based on content size
|
* into a new line even if the current line could've fit the next node
|
||||||
*/
|
*/
|
||||||
AxisLayoutOptions* setLength(std::optional<float> length);
|
AxisLayoutOptions* setBreakLine(bool enable);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Override the default gap in the layout between this node and the
|
* If enabled, the node will be forced to be on the same line as the
|
||||||
* previous one. If nullopt, the default gap of the layout will be used
|
* previous node even if doing this would overflow
|
||||||
*/
|
*/
|
||||||
AxisLayoutOptions* setPrevGap(std::optional<float> gap);
|
AxisLayoutOptions* setSameLine(bool enable);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Override the default gap in the layout between this node and the next
|
* Set the scale priority of this node. Nodes with higher priority will be
|
||||||
* one. If nullopt, the default gap of the layout will be used
|
* scaled down first before nodes with lower priority when an auto-scaled
|
||||||
*/
|
* layout attempts to fit its contents. Default is
|
||||||
AxisLayoutOptions* setNextGap(std::optional<float> gap);
|
* AXISLAYOUT_DEFAULT_PRIORITY
|
||||||
|
* @note For optimal performance, the priorities should all be close to
|
||||||
/**
|
* each other with no gaps
|
||||||
* If enabled, the node will always cause a growable axis layout to break
|
*/
|
||||||
* into a new line even if the current line could've fit the next node
|
AxisLayoutOptions* setScalePriority(int priority);
|
||||||
*/
|
|
||||||
AxisLayoutOptions* setBreakLine(bool enable);
|
/**
|
||||||
|
* Override the cross axis alignment for this node in the layout
|
||||||
/**
|
*/
|
||||||
* If enabled, the node will be forced to be on the same line as the
|
AxisLayoutOptions* setCrossAxisAlignment(std::optional<AxisAlignment> alignment);
|
||||||
* previous node even if doing this would overflow
|
};
|
||||||
*/
|
|
||||||
AxisLayoutOptions* setSameLine(bool enable);
|
/**
|
||||||
|
* A multi-purpose dynamic layout for arranging nodes along an axis. Can be
|
||||||
/**
|
* used to arrange nodes in a single line, a grid, or a flex layout. The
|
||||||
* Set the scale priority of this node. Nodes with higher priority will be
|
* RowLayout and ColumnLayout classes function as simple thin wrappers over
|
||||||
* scaled down first before nodes with lower priority when an auto-scaled
|
* AxisLayout. The positioning of individual nodes in the layout can be
|
||||||
* layout attempts to fit its contents. Default is
|
* further controlled using AxisLayoutOptions
|
||||||
* AXISLAYOUT_DEFAULT_PRIORITY
|
* @warning Calculating layouts can get increasingly expensive for large
|
||||||
* @note For optimal performance, the priorities should all be close to
|
* amounts of child nodes being fit into a small space - while this should
|
||||||
* each other with no gaps
|
* never prove a real performance concern as most layouts only have a few
|
||||||
*/
|
* hundred children at the very most, be aware that you probably shouldn't
|
||||||
AxisLayoutOptions* setScalePriority(int priority);
|
* call CCNode::updateLayout every frame for a menu with thousands of children
|
||||||
|
* @example
|
||||||
/**
|
* auto menu = CCMenu::create();
|
||||||
* Override the cross axis alignment for this node in the layout
|
* // The menu's children will be arranged horizontally, unless they overflow
|
||||||
*/
|
* // the content size width in which case a new line will be inserted and
|
||||||
AxisLayoutOptions* setCrossAxisAlignment(std::optional<AxisAlignment> alignment);
|
* // aligned to the left. The menu automatically will automatically grow in
|
||||||
};
|
* // height to fit all the rows
|
||||||
|
* menu->setLayout(
|
||||||
/**
|
* RowLayout::create()
|
||||||
* A multi-purpose dynamic layout for arranging nodes along an axis. Can be
|
* ->setGap(10.f)
|
||||||
* used to arrange nodes in a single line, a grid, or a flex layout. The
|
* ->setGrowCrossAxis(true)
|
||||||
* RowLayout and ColumnLayout classes function as simple thin wrappers over
|
* ->setAxisAlignment(AxisAlignment::Start)
|
||||||
* AxisLayout. The positioning of individual nodes in the layout can be
|
* );
|
||||||
* further controlled using AxisLayoutOptions
|
* menu->setContentSize({ 200.f, 0.f });
|
||||||
* @warning Calculating layouts can get increasingly expensive for large
|
* menu->addChild(...);
|
||||||
* amounts of child nodes being fit into a small space - while this should
|
* menu->updateLayout();
|
||||||
* never prove a real performance concern as most layouts only have a few
|
*/
|
||||||
* hundred children at the very most, be aware that you probably shouldn't
|
class GEODE_DLL AxisLayout : public Layout {
|
||||||
* call CCNode::updateLayout every frame for a menu with thousands of children
|
protected:
|
||||||
* @example
|
class Impl;
|
||||||
* auto menu = CCMenu::create();
|
|
||||||
* // The menu's children will be arranged horizontally, unless they overflow
|
std::unique_ptr<Impl> m_impl;
|
||||||
* // the content size width in which case a new line will be inserted and
|
|
||||||
* // aligned to the left. The menu automatically will automatically grow in
|
AxisLayout(Axis);
|
||||||
* // height to fit all the rows
|
|
||||||
* menu->setLayout(
|
public:
|
||||||
* RowLayout::create()
|
/**
|
||||||
* ->setGap(10.f)
|
* Create a new AxisLayout. Note that this class is not automatically
|
||||||
* ->setGrowCrossAxis(true)
|
* managed by default, so you must assign it to a CCNode or manually
|
||||||
* ->setAxisAlignment(AxisAlignment::Start)
|
* manage the memory yourself. See the chainable setters on AxisLayout for
|
||||||
* );
|
* what options you can customize for the layout
|
||||||
* menu->setContentSize({ 200.f, 0.f });
|
* @param axis The direction of the layout
|
||||||
* menu->addChild(...);
|
* @note For convenience, you can use the RowLayout and ColumnLayout
|
||||||
* menu->updateLayout();
|
* classes, which are just thin wrappers over AxisLayout
|
||||||
*/
|
* @returns Created AxisLayout
|
||||||
class GEODE_DLL AxisLayout : public Layout {
|
*/
|
||||||
protected:
|
static AxisLayout* create(Axis axis = Axis::Row);
|
||||||
class Impl;
|
|
||||||
|
virtual ~AxisLayout();
|
||||||
std::unique_ptr<Impl> m_impl;
|
|
||||||
|
void apply(cocos2d::CCNode* on) override;
|
||||||
AxisLayout(Axis);
|
cocos2d::CCSize getSizeHint(cocos2d::CCNode* on) const override;
|
||||||
|
|
||||||
public:
|
Axis getAxis() const;
|
||||||
/**
|
AxisAlignment getAxisAlignment() const;
|
||||||
* Create a new AxisLayout. Note that this class is not automatically
|
AxisAlignment getCrossAxisAlignment() const;
|
||||||
* managed by default, so you must assign it to a CCNode or manually
|
AxisAlignment getCrossAxisLineAlignment() const;
|
||||||
* manage the memory yourself. See the chainable setters on AxisLayout for
|
float getGap() const;
|
||||||
* what options you can customize for the layout
|
bool getAxisReverse() const;
|
||||||
* @param axis The direction of the layout
|
bool getCrossAxisReverse() const;
|
||||||
* @note For convenience, you can use the RowLayout and ColumnLayout
|
bool getAutoScale() const;
|
||||||
* classes, which are just thin wrappers over AxisLayout
|
bool getGrowCrossAxis() const;
|
||||||
* @returns Created AxisLayout
|
bool getCrossAxisOverflow() const;
|
||||||
*/
|
std::optional<float> getAutoGrowAxis() const;
|
||||||
static AxisLayout* create(Axis axis = Axis::Row);
|
float getDefaultMinScale() const;
|
||||||
|
float getDefaultMaxScale() const;
|
||||||
virtual ~AxisLayout();
|
|
||||||
|
AxisLayout* setAxis(Axis axis);
|
||||||
void apply(CCNode* on) override;
|
/**
|
||||||
CCSize getSizeHint(CCNode* on) const override;
|
* Sets where to align the target node's children on the main axis (X-axis
|
||||||
|
* for Row, Y-axis for Column)
|
||||||
Axis getAxis() const;
|
*/
|
||||||
AxisAlignment getAxisAlignment() const;
|
AxisLayout* setAxisAlignment(AxisAlignment align);
|
||||||
AxisAlignment getCrossAxisAlignment() const;
|
/**
|
||||||
AxisAlignment getCrossAxisLineAlignment() const;
|
* Sets where to align the target node's children on the cross-axis (Y-axis
|
||||||
float getGap() const;
|
* for Row, X-axis for Column)
|
||||||
bool getAxisReverse() const;
|
*/
|
||||||
bool getCrossAxisReverse() const;
|
AxisLayout* setCrossAxisAlignment(AxisAlignment align);
|
||||||
bool getAutoScale() const;
|
/**
|
||||||
bool getGrowCrossAxis() const;
|
* Sets where to align the target node's children on the cross-axis for
|
||||||
bool getCrossAxisOverflow() const;
|
* each row (Y-axis for Row, X-axis for Column)
|
||||||
std::optional<float> getAutoGrowAxis() const;
|
*/
|
||||||
float getDefaultMinScale() const;
|
AxisLayout* setCrossAxisLineAlignment(AxisAlignment align);
|
||||||
float getDefaultMaxScale() const;
|
/**
|
||||||
|
* The spacing between the children of the node this layout applies to.
|
||||||
AxisLayout* setAxis(Axis axis);
|
* Measured as the space between their edges, not centres. Does not apply
|
||||||
/**
|
* on the main / cross axis if their alignment is AxisAlignment::Even
|
||||||
* Sets where to align the target node's children on the main axis (X-axis
|
*/
|
||||||
* for Row, Y-axis for Column)
|
AxisLayout* setGap(float gap);
|
||||||
*/
|
/**
|
||||||
AxisLayout* setAxisAlignment(AxisAlignment align);
|
* Whether to reverse the direction of the children in this layout or not
|
||||||
/**
|
*/
|
||||||
* Sets where to align the target node's children on the cross-axis (Y-axis
|
AxisLayout* setAxisReverse(bool reverse);
|
||||||
* for Row, X-axis for Column)
|
/**
|
||||||
*/
|
* Whether to reverse the direction of the rows on the cross-axis or not
|
||||||
AxisLayout* setCrossAxisAlignment(AxisAlignment align);
|
*/
|
||||||
/**
|
AxisLayout* setCrossAxisReverse(bool reverse);
|
||||||
* Sets where to align the target node's children on the cross-axis for
|
/**
|
||||||
* each row (Y-axis for Row, X-axis for Column)
|
* If enabled, then the layout may scale the target's children if they are
|
||||||
*/
|
* about to overflow. Assumes that all the childrens' intended scale is 1
|
||||||
AxisLayout* setCrossAxisLineAlignment(AxisAlignment align);
|
*/
|
||||||
/**
|
AxisLayout* setAutoScale(bool enable);
|
||||||
* The spacing between the children of the node this layout applies to.
|
/**
|
||||||
* Measured as the space between their edges, not centres. Does not apply
|
* If true, if the main axis overflows extra nodes will be placed on new
|
||||||
* on the main / cross axis if their alignment is AxisAlignment::Even
|
* rows/columns on the cross-axis
|
||||||
*/
|
*/
|
||||||
AxisLayout* setGap(float gap);
|
AxisLayout* setGrowCrossAxis(bool expand);
|
||||||
/**
|
/**
|
||||||
* Whether to reverse the direction of the children in this layout or not
|
* If true, the cross-axis content size of the target node will be
|
||||||
*/
|
* automatically adjusted to fit the children
|
||||||
AxisLayout* setAxisReverse(bool reverse);
|
*/
|
||||||
/**
|
AxisLayout* setCrossAxisOverflow(bool allow);
|
||||||
* Whether to reverse the direction of the rows on the cross-axis or not
|
/**
|
||||||
*/
|
* If not `std::nullopt`, then the axis will be automatically extended to
|
||||||
AxisLayout* setCrossAxisReverse(bool reverse);
|
* fit all items in a single row whose minimum length is the specified.
|
||||||
/**
|
* Useful for scrollable list layer contents
|
||||||
* If enabled, then the layout may scale the target's children if they are
|
*/
|
||||||
* about to overflow. Assumes that all the childrens' intended scale is 1
|
AxisLayout* setAutoGrowAxis(std::optional<float> allowAndMinLength);
|
||||||
*/
|
/**
|
||||||
AxisLayout* setAutoScale(bool enable);
|
* Set the default minimum/maximum scales for nodes in the layout
|
||||||
/**
|
*/
|
||||||
* If true, if the main axis overflows extra nodes will be placed on new
|
AxisLayout* setDefaultScaleLimits(float min, float max);
|
||||||
* rows/columns on the cross-axis
|
};
|
||||||
*/
|
|
||||||
AxisLayout* setGrowCrossAxis(bool expand);
|
/**
|
||||||
/**
|
* Simple layout for arranging nodes in a row (horizontal line)
|
||||||
* If true, the cross-axis content size of the target node will be
|
*/
|
||||||
* automatically adjusted to fit the children
|
class GEODE_DLL RowLayout : public AxisLayout {
|
||||||
*/
|
protected:
|
||||||
AxisLayout* setCrossAxisOverflow(bool allow);
|
RowLayout();
|
||||||
/**
|
|
||||||
* If not `std::nullopt`, then the axis will be automatically extended to
|
public:
|
||||||
* fit all items in a single row whose minimum length is the specified.
|
/**
|
||||||
* Useful for scrollable list layer contents
|
* Create a new RowLayout. See the chainable setters on RowLayout for
|
||||||
*/
|
* what options you can customize for the layout
|
||||||
AxisLayout* setAutoGrowAxis(std::optional<float> allowAndMinLength);
|
* @returns Created RowLayout
|
||||||
/**
|
*/
|
||||||
* Set the default minimum/maximum scales for nodes in the layout
|
static RowLayout* create();
|
||||||
*/
|
};
|
||||||
AxisLayout* setDefaultScaleLimits(float min, float max);
|
|
||||||
};
|
/**
|
||||||
|
* Simple layout for arranging nodes in a column (vertical line)
|
||||||
/**
|
*/
|
||||||
* Simple layout for arranging nodes in a row (horizontal line)
|
class GEODE_DLL ColumnLayout : public AxisLayout {
|
||||||
*/
|
protected:
|
||||||
class GEODE_DLL RowLayout : public AxisLayout {
|
ColumnLayout();
|
||||||
protected:
|
|
||||||
RowLayout();
|
public:
|
||||||
|
/**
|
||||||
public:
|
* Create a new ColumnLayout. See the chainable setters on RowLayout for
|
||||||
/**
|
* what options you can customize for the layout
|
||||||
* Create a new RowLayout. See the chainable setters on RowLayout for
|
* @returns Created ColumnLayout
|
||||||
* what options you can customize for the layout
|
*/
|
||||||
* @returns Created RowLayout
|
static ColumnLayout* create();
|
||||||
*/
|
};
|
||||||
static RowLayout* create();
|
|
||||||
};
|
/**
|
||||||
|
* The relative position of a node to its parent in an AnchorLayout
|
||||||
/**
|
*/
|
||||||
* Simple layout for arranging nodes in a column (vertical line)
|
enum class Anchor {
|
||||||
*/
|
Center,
|
||||||
class GEODE_DLL ColumnLayout : public AxisLayout {
|
TopLeft,
|
||||||
protected:
|
Top,
|
||||||
ColumnLayout();
|
TopRight,
|
||||||
|
Right,
|
||||||
public:
|
BottomRight,
|
||||||
/**
|
Bottom,
|
||||||
* Create a new ColumnLayout. See the chainable setters on RowLayout for
|
BottomLeft,
|
||||||
* what options you can customize for the layout
|
Left,
|
||||||
* @returns Created ColumnLayout
|
};
|
||||||
*/
|
|
||||||
static ColumnLayout* create();
|
/**
|
||||||
};
|
* Options for customizing a node's position in an AnchorLayout
|
||||||
|
*/
|
||||||
/**
|
class GEODE_DLL AnchorLayoutOptions : public LayoutOptions {
|
||||||
* The relative position of a node to its parent in an AnchorLayout
|
protected:
|
||||||
*/
|
Anchor m_anchor = Anchor::Center;
|
||||||
enum class Anchor {
|
cocos2d::CCPoint m_offset = cocos2d::CCPointZero;
|
||||||
Center,
|
|
||||||
TopLeft,
|
public:
|
||||||
Top,
|
static AnchorLayoutOptions* create();
|
||||||
TopRight,
|
|
||||||
Right,
|
Anchor getAnchor() const;
|
||||||
BottomRight,
|
cocos2d::CCPoint getOffset() const;
|
||||||
Bottom,
|
|
||||||
BottomLeft,
|
AnchorLayoutOptions* setAnchor(Anchor anchor);
|
||||||
Left,
|
AnchorLayoutOptions* setOffset(cocos2d::CCPoint const& offset);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Options for customizing a node's position in an AnchorLayout
|
* A layout for positioning nodes at specific positions relative to their
|
||||||
*/
|
* parent's content size. See `Anchor` for available anchoring options. Useful
|
||||||
class GEODE_DLL AnchorLayoutOptions : public LayoutOptions {
|
* for example for popups, where a popup using `AnchorLayout` can be
|
||||||
protected:
|
* automatically resized without needing to manually shuffle nodes around
|
||||||
Anchor m_anchor = Anchor::Center;
|
*/
|
||||||
CCPoint m_offset = CCPointZero;
|
class GEODE_DLL AnchorLayout : public Layout {
|
||||||
|
public:
|
||||||
public:
|
static AnchorLayout* create();
|
||||||
static AnchorLayoutOptions* create();
|
|
||||||
|
void apply(cocos2d::CCNode* on) override;
|
||||||
Anchor getAnchor() const;
|
cocos2d::CCSize getSizeHint(cocos2d::CCNode* on) const override;
|
||||||
CCPoint getOffset() const;
|
|
||||||
|
/**
|
||||||
AnchorLayoutOptions* setAnchor(Anchor anchor);
|
* Get a position according to anchoring rules, with the same algorithm as
|
||||||
AnchorLayoutOptions* setOffset(CCPoint const& offset);
|
* `AnchorLayout` uses to position its nodes
|
||||||
};
|
* @param in The node whose content size to use as a reference
|
||||||
|
* @param anchor The anchor position
|
||||||
/**
|
* @param offset Offset from the anchor
|
||||||
* A layout for positioning nodes at specific positions relative to their
|
* @returns A position in `in` for the anchored and offsetted location
|
||||||
* parent's content size. See `Anchor` for available anchoring options. Useful
|
*/
|
||||||
* for example for popups, where a popup using `AnchorLayout` can be
|
static cocos2d::CCPoint getAnchoredPosition(cocos2d::CCNode* in, Anchor anchor, cocos2d::CCPoint const& offset);
|
||||||
* automatically resized without needing to manually shuffle nodes around
|
};
|
||||||
*/
|
|
||||||
class GEODE_DLL AnchorLayout : public Layout {
|
/**
|
||||||
public:
|
* A layout for automatically copying the content size of a node to other nodes.
|
||||||
static AnchorLayout* create();
|
* Basically main use case is for FLAlertLayers (setting the size of the
|
||||||
|
* background and `m_buttonMenu` based on `m_mainLayer`)
|
||||||
void apply(CCNode* on) override;
|
*/
|
||||||
CCSize getSizeHint(CCNode* on) const override;
|
class GEODE_DLL CopySizeLayout : public AnchorLayout {
|
||||||
|
protected:
|
||||||
/**
|
cocos2d::CCArray* m_targets;
|
||||||
* Get a position according to anchoring rules, with the same algorithm as
|
|
||||||
* `AnchorLayout` uses to position its nodes
|
public:
|
||||||
* @param in The node whose content size to use as a reference
|
static CopySizeLayout* create();
|
||||||
* @param anchor The anchor position
|
virtual ~CopySizeLayout();
|
||||||
* @param offset Offset from the anchor
|
|
||||||
* @returns A position in `in` for the anchored and offsetted location
|
/**
|
||||||
*/
|
* Add a target to be automatically resized. Any targets' layouts will
|
||||||
static CCPoint getAnchoredPosition(CCNode* in, Anchor anchor, CCPoint const& offset);
|
* also be updated when this layout is updated
|
||||||
};
|
*/
|
||||||
|
CopySizeLayout* add(cocos2d::CCNode* target);
|
||||||
/**
|
/**
|
||||||
* A layout for automatically copying the content size of a node to other nodes.
|
* Remove a target from being automatically resized
|
||||||
* Basically main use case is for FLAlertLayers (setting the size of the
|
*/
|
||||||
* background and `m_buttonMenu` based on `m_mainLayer`)
|
CopySizeLayout* remove(cocos2d::CCNode* target);
|
||||||
*/
|
|
||||||
class GEODE_DLL CopySizeLayout : public cocos2d::AnchorLayout {
|
void apply(cocos2d::CCNode* in) override;
|
||||||
protected:
|
cocos2d::CCSize getSizeHint(cocos2d::CCNode* in) const override;
|
||||||
cocos2d::CCArray* m_targets;
|
};
|
||||||
|
|
||||||
public:
|
#pragma warning(pop)
|
||||||
static CopySizeLayout* create();
|
|
||||||
virtual ~CopySizeLayout();
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Add a target to be automatically resized. Any targets' layouts will
|
|
||||||
* also be updated when this layout is updated
|
|
||||||
*/
|
|
||||||
CopySizeLayout* add(cocos2d::CCNode* target);
|
|
||||||
/**
|
|
||||||
* Remove a target from being automatically resized
|
|
||||||
*/
|
|
||||||
CopySizeLayout* remove(cocos2d::CCNode* target);
|
|
||||||
|
|
||||||
void apply(cocos2d::CCNode* in) override;
|
|
||||||
cocos2d::CCSize getSizeHint(cocos2d::CCNode* in) const override;
|
|
||||||
};
|
|
||||||
|
|
||||||
#pragma warning(pop)
|
|
||||||
|
|
||||||
NS_CC_END
|
|
|
@ -11,13 +11,13 @@ namespace geode {
|
||||||
class GEODE_DLL MDPopup :
|
class GEODE_DLL MDPopup :
|
||||||
public Popup<
|
public Popup<
|
||||||
std::string const&, std::string const&, char const*, char const*,
|
std::string const&, std::string const&, char const*, char const*,
|
||||||
utils::MiniFunction<void(bool)>> {
|
std::function<void(bool)>> {
|
||||||
protected:
|
protected:
|
||||||
utils::MiniFunction<void(bool)> m_onClick = nullptr;
|
std::function<void(bool)> m_onClick = nullptr;
|
||||||
|
|
||||||
bool setup(
|
bool setup(
|
||||||
std::string const& title, std::string const& info, char const* btn1, char const* btn2,
|
std::string const& title, std::string const& info, char const* btn1, char const* btn2,
|
||||||
utils::MiniFunction<void(bool)> onClick
|
std::function<void(bool)> onClick
|
||||||
) override;
|
) override;
|
||||||
|
|
||||||
void onBtn(CCObject*);
|
void onBtn(CCObject*);
|
||||||
|
@ -27,7 +27,7 @@ namespace geode {
|
||||||
public:
|
public:
|
||||||
static MDPopup* create(
|
static MDPopup* create(
|
||||||
std::string const& title, std::string const& content, char const* btn1,
|
std::string const& title, std::string const& content, char const* btn1,
|
||||||
char const* btn2 = nullptr, utils::MiniFunction<void(bool)> onClick = nullptr
|
char const* btn2 = nullptr, std::function<void(bool)> onClick = nullptr
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,8 +2,8 @@
|
||||||
|
|
||||||
#include <Geode/binding/CCMenuItemSpriteExtra.hpp>
|
#include <Geode/binding/CCMenuItemSpriteExtra.hpp>
|
||||||
#include <Geode/binding/FLAlertLayer.hpp>
|
#include <Geode/binding/FLAlertLayer.hpp>
|
||||||
#include <Geode/utils/MiniFunction.hpp>
|
|
||||||
#include <Geode/utils/cocos.hpp>
|
#include <Geode/utils/cocos.hpp>
|
||||||
|
#include <Geode/ui/Layout.hpp>
|
||||||
|
|
||||||
namespace geode {
|
namespace geode {
|
||||||
template <class... InitArgs>
|
template <class... InitArgs>
|
||||||
|
@ -51,7 +51,7 @@ namespace geode {
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ListenerResult handle(utils::MiniFunction<Callback> fn, CloseEvent* event) {
|
ListenerResult handle(std::function<Callback> fn, CloseEvent* event) {
|
||||||
if (event->getPopup() == m_impl->popup) {
|
if (event->getPopup() == m_impl->popup) {
|
||||||
fn(event);
|
fn(event);
|
||||||
}
|
}
|
||||||
|
@ -104,7 +104,7 @@ namespace geode {
|
||||||
m_mainLayer->setPosition(winSize / 2);
|
m_mainLayer->setPosition(winSize / 2);
|
||||||
m_mainLayer->setContentSize(m_size);
|
m_mainLayer->setContentSize(m_size);
|
||||||
m_mainLayer->setLayout(
|
m_mainLayer->setLayout(
|
||||||
cocos2d::CopySizeLayout::create()
|
geode::CopySizeLayout::create()
|
||||||
->add(m_buttonMenu)
|
->add(m_buttonMenu)
|
||||||
->add(m_bgSprite)
|
->add(m_bgSprite)
|
||||||
);
|
);
|
||||||
|
@ -119,7 +119,7 @@ namespace geode {
|
||||||
closeSpr, this, (cocos2d::SEL_MenuHandler)(&Popup::onClose)
|
closeSpr, this, (cocos2d::SEL_MenuHandler)(&Popup::onClose)
|
||||||
);
|
);
|
||||||
if (dynamic) {
|
if (dynamic) {
|
||||||
m_buttonMenu->addChildAtPosition(m_closeBtn, cocos2d::Anchor::TopLeft, { 3.f, -3.f });
|
m_buttonMenu->addChildAtPosition(m_closeBtn, geode::Anchor::TopLeft, { 3.f, -3.f });
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
m_closeBtn->setPosition(-m_size.width / 2 + 3.f, m_size.height / 2 - 3.f);
|
m_closeBtn->setPosition(-m_size.width / 2 + 3.f, m_size.height / 2 - 3.f);
|
||||||
|
@ -137,14 +137,6 @@ namespace geode {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
[[deprecated("Use Popup::initAnchored instead, as it has more reasonable menu and layer content sizes")]]
|
|
||||||
bool init(
|
|
||||||
float width, float height, InitArgs... args, char const* bg = "GJ_square01.png",
|
|
||||||
cocos2d::CCRect bgRect = { 0, 0, 80, 80 }
|
|
||||||
) {
|
|
||||||
return this->initBase(width, height, std::forward<InitArgs>(args)..., bg, bgRect, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Init with AnchorLayout and the content size of `m_buttonMenu` and
|
* Init with AnchorLayout and the content size of `m_buttonMenu` and
|
||||||
* `m_bgSprite` being tied to the size of `m_mainLayer` (rather than
|
* `m_bgSprite` being tied to the size of `m_mainLayer` (rather than
|
||||||
|
@ -185,7 +177,7 @@ namespace geode {
|
||||||
m_title = cocos2d::CCLabelBMFont::create(title.c_str(), font);
|
m_title = cocos2d::CCLabelBMFont::create(title.c_str(), font);
|
||||||
m_title->setZOrder(2);
|
m_title->setZOrder(2);
|
||||||
if (m_dynamic) {
|
if (m_dynamic) {
|
||||||
m_mainLayer->addChildAtPosition(m_title, cocos2d::Anchor::Top, ccp(0, -offset));
|
m_mainLayer->addChildAtPosition(m_title, geode::Anchor::Top, ccp(0, -offset));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
auto winSize = cocos2d::CCDirector::get()->getWinSize();
|
auto winSize = cocos2d::CCDirector::get()->getWinSize();
|
||||||
|
@ -221,21 +213,21 @@ namespace geode {
|
||||||
|
|
||||||
GEODE_DLL FLAlertLayer* createQuickPopup(
|
GEODE_DLL FLAlertLayer* createQuickPopup(
|
||||||
char const* title, std::string const& content, char const* btn1, char const* btn2,
|
char const* title, std::string const& content, char const* btn1, char const* btn2,
|
||||||
utils::MiniFunction<void(FLAlertLayer*, bool)> selected, bool doShow = true
|
std::function<void(FLAlertLayer*, bool)> selected, bool doShow = true
|
||||||
);
|
);
|
||||||
|
|
||||||
GEODE_DLL FLAlertLayer* createQuickPopup(
|
GEODE_DLL FLAlertLayer* createQuickPopup(
|
||||||
char const* title, std::string const& content, char const* btn1, char const* btn2,
|
char const* title, std::string const& content, char const* btn1, char const* btn2,
|
||||||
float width, utils::MiniFunction<void(FLAlertLayer*, bool)> selected, bool doShow = true
|
float width, std::function<void(FLAlertLayer*, bool)> selected, bool doShow = true
|
||||||
);
|
);
|
||||||
|
|
||||||
GEODE_DLL FLAlertLayer* createQuickPopup(
|
GEODE_DLL FLAlertLayer* createQuickPopup(
|
||||||
char const* title, std::string const& content, char const* btn1, char const* btn2,
|
char const* title, std::string const& content, char const* btn1, char const* btn2,
|
||||||
utils::MiniFunction<void(FLAlertLayer*, bool)> selected, bool doShow, bool cancelledByEscape
|
std::function<void(FLAlertLayer*, bool)> selected, bool doShow, bool cancelledByEscape
|
||||||
);
|
);
|
||||||
|
|
||||||
GEODE_DLL FLAlertLayer* createQuickPopup(
|
GEODE_DLL FLAlertLayer* createQuickPopup(
|
||||||
char const* title, std::string const& content, char const* btn1, char const* btn2,
|
char const* title, std::string const& content, char const* btn1, char const* btn2,
|
||||||
float width, utils::MiniFunction<void(FLAlertLayer*, bool)> selected, bool doShow, bool cancelledByEscape
|
float width, std::function<void(FLAlertLayer*, bool)> selected, bool doShow, bool cancelledByEscape
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,8 @@
|
||||||
#include <Geode/utils/cocos.hpp>
|
#include <Geode/utils/cocos.hpp>
|
||||||
|
|
||||||
namespace geode {
|
namespace geode {
|
||||||
|
struct SceneSwitch;
|
||||||
|
|
||||||
class GEODE_DLL SceneManager final {
|
class GEODE_DLL SceneManager final {
|
||||||
protected:
|
protected:
|
||||||
std::vector<Ref<cocos2d::CCNode>> m_persistedNodes;
|
std::vector<Ref<cocos2d::CCNode>> m_persistedNodes;
|
||||||
|
@ -15,6 +17,10 @@ namespace geode {
|
||||||
|
|
||||||
virtual ~SceneManager();
|
virtual ~SceneManager();
|
||||||
|
|
||||||
|
void willSwitchToScene(cocos2d::CCScene* scene);
|
||||||
|
|
||||||
|
friend struct SceneSwitch;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static SceneManager* get();
|
static SceneManager* get();
|
||||||
|
|
||||||
|
@ -34,9 +40,5 @@ namespace geode {
|
||||||
* Gets a span of the persisted nodes. To add new nodes to the list, use keepAcrossScenes.
|
* Gets a span of the persisted nodes. To add new nodes to the list, use keepAcrossScenes.
|
||||||
*/
|
*/
|
||||||
std::span<Ref<cocos2d::CCNode> const> getPersistedNodes();
|
std::span<Ref<cocos2d::CCNode> const> getPersistedNodes();
|
||||||
|
|
||||||
// This method should only be called by geode itself
|
|
||||||
// TODO(v4): hide this
|
|
||||||
void willSwitchToScene(cocos2d::CCScene* scene);
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,13 +14,13 @@ namespace geode {
|
||||||
protected:
|
protected:
|
||||||
std::vector<T> m_list;
|
std::vector<T> m_list;
|
||||||
size_t m_index = 0;
|
size_t m_index = 0;
|
||||||
utils::MiniFunction<void(T const&, size_t)> m_onChange;
|
std::function<void(T const&, size_t)> m_onChange;
|
||||||
cocos2d::CCLabelBMFont* m_label;
|
cocos2d::CCLabelBMFont* m_label;
|
||||||
CCMenuItemSpriteExtra* m_prevBtn;
|
CCMenuItemSpriteExtra* m_prevBtn;
|
||||||
CCMenuItemSpriteExtra* m_nextBtn;
|
CCMenuItemSpriteExtra* m_nextBtn;
|
||||||
|
|
||||||
bool init(
|
bool init(
|
||||||
float width, std::vector<T> const& list, utils::MiniFunction<void(T const&, size_t)> onChange
|
float width, std::vector<T> const& list, std::function<void(T const&, size_t)> onChange
|
||||||
) {
|
) {
|
||||||
if (!cocos2d::CCMenu::init()) return false;
|
if (!cocos2d::CCMenu::init()) return false;
|
||||||
|
|
||||||
|
@ -94,7 +94,7 @@ namespace geode {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static SelectList* create(
|
static SelectList* create(
|
||||||
float width, std::vector<T> const& list, utils::MiniFunction<void(T const&, size_t)> onChange
|
float width, std::vector<T> const& list, std::function<void(T const&, size_t)> onChange
|
||||||
) {
|
) {
|
||||||
auto ret = new SelectList();
|
auto ret = new SelectList();
|
||||||
if (ret->init(width, list, onChange)) {
|
if (ret->init(width, list, onChange)) {
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "CCNode.h"
|
#include <cocos2d.h>
|
||||||
|
|
||||||
NS_CC_BEGIN
|
namespace geode {
|
||||||
|
|
||||||
#pragma warning(push)
|
#pragma warning(push)
|
||||||
#pragma warning(disable: 4275)
|
#pragma warning(disable: 4275)
|
||||||
|
@ -22,7 +22,7 @@ NS_CC_BEGIN
|
||||||
* @note If you want to specify a minimum width for a SpacerNode, add
|
* @note If you want to specify a minimum width for a SpacerNode, add
|
||||||
* AxisLayoutOptions for it and use setLength
|
* AxisLayoutOptions for it and use setLength
|
||||||
*/
|
*/
|
||||||
class GEODE_DLL SpacerNode : public CCNode {
|
class GEODE_DLL SpacerNode : public cocos2d::CCNode {
|
||||||
protected:
|
protected:
|
||||||
size_t m_grow;
|
size_t m_grow;
|
||||||
|
|
||||||
|
@ -61,9 +61,9 @@ public:
|
||||||
*/
|
*/
|
||||||
class GEODE_DLL SpacerNodeChild : public SpacerNode {
|
class GEODE_DLL SpacerNodeChild : public SpacerNode {
|
||||||
protected:
|
protected:
|
||||||
CCNode* m_child = nullptr;
|
cocos2d::CCNode* m_child = nullptr;
|
||||||
|
|
||||||
bool init(CCNode* child, size_t grow);
|
bool init(cocos2d::CCNode* child, size_t grow);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
|
@ -73,11 +73,11 @@ public:
|
||||||
* factors (akin to CSS flew grow)
|
* factors (akin to CSS flew grow)
|
||||||
* @param grow The grow factor for this node. Default is 1
|
* @param grow The grow factor for this node. Default is 1
|
||||||
*/
|
*/
|
||||||
static SpacerNodeChild* create(CCNode* child, size_t grow = 1);
|
static SpacerNodeChild* create(cocos2d::CCNode* child, size_t grow = 1);
|
||||||
|
|
||||||
void setContentSize(CCSize const& size) override;
|
void setContentSize(cocos2d::CCSize const& size) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
#pragma warning(pop)
|
#pragma warning(pop)
|
||||||
|
|
||||||
NS_CC_END
|
}
|
|
@ -24,34 +24,34 @@ namespace geode {
|
||||||
*/
|
*/
|
||||||
class GEODE_DLL SimpleTextArea : public cocos2d::CCNode {
|
class GEODE_DLL SimpleTextArea : public cocos2d::CCNode {
|
||||||
public:
|
public:
|
||||||
static SimpleTextArea* create(const std::string& text, const std::string& font = "chatFont.fnt", const float scale = 1);
|
static SimpleTextArea* create(const std::string& text, const std::string& font = "chatFont.fnt", float scale = 1.0f);
|
||||||
static SimpleTextArea* create(const std::string& text, const std::string& font, const float scale, const float width);
|
static SimpleTextArea* create(const std::string& text, const std::string& font, float scale, float width);
|
||||||
|
|
||||||
void setFont(const std::string& font);
|
void setFont(const std::string& font);
|
||||||
std::string getFont();
|
std::string getFont();
|
||||||
void setColor(const cocos2d::ccColor4B& color);
|
void setColor(const cocos2d::ccColor4B& color);
|
||||||
cocos2d::ccColor4B getColor();
|
cocos2d::ccColor4B getColor();
|
||||||
void setAlignment(const cocos2d::CCTextAlignment alignment);
|
void setAlignment(cocos2d::CCTextAlignment alignment);
|
||||||
cocos2d::CCTextAlignment getAlignment();
|
cocos2d::CCTextAlignment getAlignment();
|
||||||
void setWrappingMode(const WrappingMode mode);
|
void setWrappingMode(WrappingMode mode);
|
||||||
WrappingMode getWrappingMode();
|
WrappingMode getWrappingMode();
|
||||||
void setText(const std::string& text);
|
void setText(const std::string& text);
|
||||||
std::string getText();
|
std::string getText();
|
||||||
void setMaxLines(const size_t maxLines);
|
void setMaxLines(size_t maxLines);
|
||||||
size_t getMaxLines();
|
size_t getMaxLines();
|
||||||
void setWidth(const float width);
|
void setWidth(float width);
|
||||||
float getWidth();
|
float getWidth();
|
||||||
void setScale(const float scale) override;
|
void setScale(float scale) override;
|
||||||
float getScale() override;
|
float getScale() override;
|
||||||
void setLinePadding(const float padding);
|
void setLinePadding(float padding);
|
||||||
float getLinePadding();
|
float getLinePadding();
|
||||||
std::vector<cocos2d::CCLabelBMFont*> getLines();
|
std::vector<cocos2d::CCLabelBMFont*> getLines();
|
||||||
float getHeight();
|
float getHeight();
|
||||||
float getLineHeight();
|
float getLineHeight();
|
||||||
private:
|
private:
|
||||||
static SimpleTextArea* create(const std::string& font, const std::string& text, const float scale, const float width, const bool artificialWidth);
|
static SimpleTextArea* create(const std::string& font, const std::string& text, float scale, float width, const bool artificialWidth);
|
||||||
|
|
||||||
bool init(const std::string& font, const std::string& text, const float scale, const float width, const bool artificialWidth);
|
bool init(const std::string& font, const std::string& text, float scale, float width, const bool artificialWidth);
|
||||||
|
|
||||||
bool m_shouldUpdate = false;
|
bool m_shouldUpdate = false;
|
||||||
bool m_artificialWidth = false;
|
bool m_artificialWidth = false;
|
||||||
|
@ -67,9 +67,9 @@ namespace geode {
|
||||||
float m_lineHeight = 0.f;
|
float m_lineHeight = 0.f;
|
||||||
float m_linePadding = 0.f;
|
float m_linePadding = 0.f;
|
||||||
|
|
||||||
cocos2d::CCLabelBMFont* createLabel(const std::string& text, const float top);
|
cocos2d::CCLabelBMFont* createLabel(const std::string& text, float top);
|
||||||
float calculateOffset(cocos2d::CCLabelBMFont* label);
|
float calculateOffset(cocos2d::CCLabelBMFont* label);
|
||||||
void charIteration(const std::function<cocos2d::CCLabelBMFont*(cocos2d::CCLabelBMFont* line, const char c, const float top)>& overflowHandling);
|
void charIteration(const std::function<cocos2d::CCLabelBMFont*(cocos2d::CCLabelBMFont* line, char c, float top)>& overflowHandling);
|
||||||
void updateLinesNoWrap();
|
void updateLinesNoWrap();
|
||||||
void updateLinesWordWrap(bool spaceWrap);
|
void updateLinesWordWrap(bool spaceWrap);
|
||||||
void updateLinesCutoffWrap();
|
void updateLinesCutoffWrap();
|
||||||
|
|
|
@ -122,7 +122,7 @@ namespace geode {
|
||||||
* to distinguish between bold, italic and
|
* to distinguish between bold, italic and
|
||||||
* regular text.
|
* regular text.
|
||||||
*/
|
*/
|
||||||
using Font = utils::MiniFunction<Label(int)>;
|
using Font = std::function<Label(int)>;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
cocos2d::CCPoint m_origin = cocos2d::CCPointZero;
|
cocos2d::CCPoint m_origin = cocos2d::CCPointZero;
|
||||||
|
|
|
@ -21,7 +21,7 @@ namespace geode {
|
||||||
std::string m_id;
|
std::string m_id;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ListenerResult handle(utils::MiniFunction<Callback> fn, ColorProvidedEvent* event);
|
ListenerResult handle(std::function<Callback> fn, ColorProvidedEvent* event);
|
||||||
|
|
||||||
ColorProvidedFilter(std::string const& id);
|
ColorProvidedFilter(std::string const& id);
|
||||||
};
|
};
|
||||||
|
|
|
@ -4,8 +4,7 @@
|
||||||
#include "../loader/Log.hpp"
|
#include "../loader/Log.hpp"
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <variant>
|
#include <variant>
|
||||||
#include <Geode/utils/MiniFunction.hpp>
|
#include <Geode/Result.hpp>
|
||||||
#include <Geode/utils/Result.hpp>
|
|
||||||
|
|
||||||
namespace geode {
|
namespace geode {
|
||||||
struct JsonChecker;
|
struct JsonChecker;
|
||||||
|
@ -73,231 +72,11 @@ namespace geode {
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
using JsonValueValidator = utils::MiniFunction<bool(T const&)>;
|
using JsonValueValidator = std::function<bool(T const&)>;
|
||||||
|
|
||||||
struct JsonMaybeObject;
|
struct JsonMaybeObject;
|
||||||
struct JsonMaybeValue;
|
struct JsonMaybeValue;
|
||||||
|
|
||||||
struct GEODE_DLL
|
|
||||||
[[deprecated("Use JsonExpectedValue via the checkJson function instead")]]
|
|
||||||
JsonMaybeSomething {
|
|
||||||
protected:
|
|
||||||
JsonChecker& m_checker;
|
|
||||||
matjson::Value& m_json;
|
|
||||||
std::string m_hierarchy;
|
|
||||||
bool m_hasValue;
|
|
||||||
|
|
||||||
friend struct JsonMaybeObject;
|
|
||||||
friend struct JsonMaybeValue;
|
|
||||||
|
|
||||||
void setError(std::string const& error);
|
|
||||||
|
|
||||||
public:
|
|
||||||
matjson::Value& json();
|
|
||||||
|
|
||||||
JsonMaybeSomething(
|
|
||||||
JsonChecker& checker, matjson::Value& json, std::string const& hierarchy, bool hasValue
|
|
||||||
);
|
|
||||||
|
|
||||||
bool isError() const;
|
|
||||||
std::string getError() const;
|
|
||||||
|
|
||||||
operator bool() const;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct GEODE_DLL
|
|
||||||
[[deprecated("Use JsonExpectedValue via the checkJson function instead")]]
|
|
||||||
JsonMaybeValue : public JsonMaybeSomething {
|
|
||||||
bool m_inferType = true;
|
|
||||||
|
|
||||||
JsonMaybeValue(
|
|
||||||
JsonChecker& checker, matjson::Value& json, std::string const& hierarchy, bool hasValue
|
|
||||||
);
|
|
||||||
|
|
||||||
JsonMaybeSomething& self();
|
|
||||||
|
|
||||||
template <matjson::Type T>
|
|
||||||
JsonMaybeValue& as() {
|
|
||||||
if (this->isError()) return *this;
|
|
||||||
if (!jsonConvertibleTo(self().m_json.type(), T)) {
|
|
||||||
this->setError(
|
|
||||||
self().m_hierarchy + ": Invalid type \"" + jsonValueTypeToString(self().m_json.type()) +
|
|
||||||
"\", expected \"" + jsonValueTypeToString(T) + "\""
|
|
||||||
);
|
|
||||||
}
|
|
||||||
m_inferType = false;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
JsonMaybeValue& array();
|
|
||||||
|
|
||||||
template <matjson::Type... T>
|
|
||||||
JsonMaybeValue& asOneOf() {
|
|
||||||
if (this->isError()) return *this;
|
|
||||||
bool isOneOf = (... || jsonConvertibleTo(self().m_json.type(), T));
|
|
||||||
if (!isOneOf) {
|
|
||||||
this->setError(
|
|
||||||
self().m_hierarchy + ": Invalid type \"" + jsonValueTypeToString(self().m_json.type()) +
|
|
||||||
"\", expected one of \"" + (jsonValueTypeToString(T), ...) + "\""
|
|
||||||
);
|
|
||||||
}
|
|
||||||
m_inferType = false;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class T>
|
|
||||||
bool is() {
|
|
||||||
if (this->isError()) return false;
|
|
||||||
return self().m_json.is<T>();
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class T>
|
|
||||||
JsonMaybeValue& validate(JsonValueValidator<T> validator) {
|
|
||||||
if (this->isError()) return *this;
|
|
||||||
if (self().m_json.is<T>()) {
|
|
||||||
if (!validator(self().m_json.as<T>())) {
|
|
||||||
this->setError(self().m_hierarchy + ": Invalid value format");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
this->setError(
|
|
||||||
self().m_hierarchy + ": Invalid type \"" +
|
|
||||||
std::string(jsonValueTypeToString(self().m_json.type())) + "\""
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class T>
|
|
||||||
JsonMaybeValue& inferType() {
|
|
||||||
if (this->isError() || !m_inferType) return *this;
|
|
||||||
return this->as<getJsonType<T>()>();
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class T>
|
|
||||||
JsonMaybeValue& intoRaw(T& target) {
|
|
||||||
if (this->isError()) return *this;
|
|
||||||
target = self().m_json;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class T>
|
|
||||||
JsonMaybeValue& into(T& target) {
|
|
||||||
return this->intoAs<T, T>(target);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class T>
|
|
||||||
JsonMaybeValue& into(std::optional<T>& target) {
|
|
||||||
return this->intoAs<T, std::optional<T>>(target);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class A, class T>
|
|
||||||
JsonMaybeValue& intoAs(T& target) {
|
|
||||||
this->inferType<A>();
|
|
||||||
if (this->isError()) return *this;
|
|
||||||
|
|
||||||
if (self().m_json.is<A>()) {
|
|
||||||
try {
|
|
||||||
target = self().m_json.as<A>();
|
|
||||||
}
|
|
||||||
catch(matjson::JsonException const& e) {
|
|
||||||
this->setError(
|
|
||||||
self().m_hierarchy + ": Error parsing JSON: " + std::string(e.what())
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
this->setError(
|
|
||||||
self().m_hierarchy + ": Invalid type \"" +
|
|
||||||
std::string(jsonValueTypeToString(self().m_json.type())) + "\""
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class T>
|
|
||||||
T get() {
|
|
||||||
this->inferType<T>();
|
|
||||||
if (this->isError()) return T();
|
|
||||||
if (self().m_json.is<T>()) {
|
|
||||||
return self().m_json.as<T>();
|
|
||||||
}
|
|
||||||
return T();
|
|
||||||
}
|
|
||||||
|
|
||||||
JsonMaybeObject obj();
|
|
||||||
|
|
||||||
template <class T>
|
|
||||||
struct Iterator {
|
|
||||||
std::vector<T> m_values;
|
|
||||||
|
|
||||||
using iterator = typename std::vector<T>::iterator;
|
|
||||||
using const_iterator = typename std::vector<T>::const_iterator;
|
|
||||||
|
|
||||||
iterator begin() {
|
|
||||||
return m_values.begin();
|
|
||||||
}
|
|
||||||
|
|
||||||
iterator end() {
|
|
||||||
return m_values.end();
|
|
||||||
}
|
|
||||||
|
|
||||||
const_iterator begin() const {
|
|
||||||
return m_values.begin();
|
|
||||||
}
|
|
||||||
|
|
||||||
const_iterator end() const {
|
|
||||||
return m_values.end();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
JsonMaybeValue at(size_t i);
|
|
||||||
|
|
||||||
Iterator<JsonMaybeValue> iterate();
|
|
||||||
|
|
||||||
Iterator<std::pair<std::string, JsonMaybeValue>> items();
|
|
||||||
};
|
|
||||||
|
|
||||||
struct
|
|
||||||
[[deprecated("Use JsonExpectedValue via the checkJson function instead")]]
|
|
||||||
GEODE_DLL JsonMaybeObject : JsonMaybeSomething {
|
|
||||||
std::set<std::string> m_knownKeys;
|
|
||||||
|
|
||||||
JsonMaybeObject(
|
|
||||||
JsonChecker& checker, matjson::Value& json, std::string const& hierarchy, bool hasValue
|
|
||||||
);
|
|
||||||
|
|
||||||
JsonMaybeSomething& self();
|
|
||||||
|
|
||||||
void addKnownKey(std::string const& key);
|
|
||||||
|
|
||||||
matjson::Value& json();
|
|
||||||
|
|
||||||
JsonMaybeValue emptyValue();
|
|
||||||
|
|
||||||
JsonMaybeValue has(std::string const& key);
|
|
||||||
|
|
||||||
JsonMaybeValue needs(std::string const& key);
|
|
||||||
|
|
||||||
void checkUnknownKeys();
|
|
||||||
};
|
|
||||||
|
|
||||||
struct
|
|
||||||
[[deprecated("Use JsonExpectedValue via the checkJson function instead")]]
|
|
||||||
GEODE_DLL JsonChecker {
|
|
||||||
std::variant<std::monostate, std::string> m_result;
|
|
||||||
matjson::Value& m_json;
|
|
||||||
|
|
||||||
JsonChecker(matjson::Value& json);
|
|
||||||
|
|
||||||
bool isError() const;
|
|
||||||
|
|
||||||
std::string getError() const;
|
|
||||||
|
|
||||||
JsonMaybeValue root(std::string const& hierarchy);
|
|
||||||
};
|
|
||||||
|
|
||||||
class GEODE_DLL JsonExpectedValue final {
|
class GEODE_DLL JsonExpectedValue final {
|
||||||
protected:
|
protected:
|
||||||
class Impl;
|
class Impl;
|
||||||
|
@ -325,21 +104,14 @@ namespace geode {
|
||||||
return this->getJSONRef();
|
return this->getJSONRef();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
try {
|
auto res = this->getJSONRef().as<T>();
|
||||||
if (this->getJSONRef().is<T>()) {
|
if (res) {
|
||||||
return this->getJSONRef().as<T>();
|
return res.unwrap();
|
||||||
}
|
|
||||||
else {
|
|
||||||
this->setError(
|
|
||||||
"unexpected type {}",
|
|
||||||
this->matJsonTypeToString(this->getJSONRef().type())
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// matjson can throw variant exceptions too so you need to do this
|
|
||||||
catch(std::exception const& e) {
|
|
||||||
this->setError("unable to parse json: {}", e);
|
|
||||||
}
|
}
|
||||||
|
this->setError(
|
||||||
|
"unexpected type {}",
|
||||||
|
this->matJsonTypeToString(this->getJSONRef().type())
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
@ -451,6 +223,13 @@ namespace geode {
|
||||||
* @returns The key, which is a no-op value if it didn't exist
|
* @returns The key, which is a no-op value if it didn't exist
|
||||||
*/
|
*/
|
||||||
JsonExpectedValue has(std::string_view key);
|
JsonExpectedValue has(std::string_view key);
|
||||||
|
/**
|
||||||
|
* Check if this object has an optional key. Asserts that this JSON
|
||||||
|
* value is an object. If the key doesn't exist, or the value is null, returns a
|
||||||
|
* `JsonExpectValue` that does nothing
|
||||||
|
* @returns The key, which is a no-op value if it didn't exist, or was null
|
||||||
|
*/
|
||||||
|
JsonExpectedValue hasNullable(std::string_view key);
|
||||||
/**
|
/**
|
||||||
* Check if this object has an optional key. Asserts that this JSON
|
* Check if this object has an optional key. Asserts that this JSON
|
||||||
* value is an object. If the key doesn't exist, sets an error and
|
* value is an object. If the key doesn't exist, sets an error and
|
||||||
|
|
|
@ -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"
|
#include "cocos.hpp"
|
||||||
|
|
||||||
namespace geode::node_ids {
|
namespace geode::node_ids {
|
||||||
using namespace cocos2d;
|
using namespace cocos2d;
|
||||||
|
|
||||||
static constexpr int32_t GEODE_ID_PRIORITY = 0x100000;
|
static constexpr int32_t GEODE_ID_PRIORITY = Priority::VeryEarlyPost;
|
||||||
|
|
||||||
template <class T = CCNode>
|
template <class T = CCNode>
|
||||||
requires std::is_base_of_v<CCNode, T>
|
requires std::is_base_of_v<CCNode, T>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "../loader/Hook.hpp"
|
#include "../loader/Hook.hpp"
|
||||||
#include "Result.hpp"
|
#include <Geode/Result.hpp>
|
||||||
|
|
||||||
namespace geode {
|
namespace geode {
|
||||||
namespace hook {
|
namespace hook {
|
||||||
|
|
|
@ -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
|
#pragma once
|
||||||
|
|
||||||
#include "general.hpp"
|
#include "general.hpp"
|
||||||
#include "MiniFunction.hpp"
|
|
||||||
#include "../loader/Event.hpp"
|
#include "../loader/Event.hpp"
|
||||||
#include "../loader/Loader.hpp"
|
#include "../loader/Loader.hpp"
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
|
#include <coroutine>
|
||||||
|
|
||||||
namespace geode {
|
namespace geode {
|
||||||
|
namespace geode_internal {
|
||||||
|
template <class T, class P>
|
||||||
|
struct TaskPromise;
|
||||||
|
|
||||||
|
template <class T, class P>
|
||||||
|
struct TaskAwaiter;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tasks represent an asynchronous operation that will be finished at some
|
* Tasks represent an asynchronous operation that will be finished at some
|
||||||
* unknown point in the future. Tasks can report their progress, and will
|
* unknown point in the future. Tasks can report their progress, and will
|
||||||
|
@ -87,7 +95,7 @@ namespace geode {
|
||||||
enum class Status {
|
enum class Status {
|
||||||
/// The task is still running or waiting to start
|
/// The task is still running or waiting to start
|
||||||
Pending,
|
Pending,
|
||||||
/// The task has succesfully finished
|
/// The task has successfully finished
|
||||||
Finished,
|
Finished,
|
||||||
/// The task has been cancelled
|
/// The task has been cancelled
|
||||||
Cancelled,
|
Cancelled,
|
||||||
|
@ -141,7 +149,7 @@ namespace geode {
|
||||||
|
|
||||||
class PrivateMarker final {};
|
class PrivateMarker final {};
|
||||||
|
|
||||||
static std::shared_ptr<Handle> create(std::string_view const name) {
|
static std::shared_ptr<Handle> create(std::string_view name) {
|
||||||
return std::make_shared<Handle>(PrivateMarker(), name);
|
return std::make_shared<Handle>(PrivateMarker(), name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -153,8 +161,14 @@ namespace geode {
|
||||||
template <std::move_constructible T2, std::move_constructible P2>
|
template <std::move_constructible T2, std::move_constructible P2>
|
||||||
friend class Task;
|
friend class Task;
|
||||||
|
|
||||||
|
template <class, class>
|
||||||
|
friend struct geode_internal::TaskPromise;
|
||||||
|
|
||||||
|
template <class, class>
|
||||||
|
friend struct geode_internal::TaskAwaiter;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Handle(PrivateMarker, std::string_view const name) : m_name(name) {}
|
Handle(PrivateMarker, std::string_view name) : m_name(name) {}
|
||||||
~Handle() {
|
~Handle() {
|
||||||
// If this Task was still pending when the Handle was destroyed,
|
// If this Task was still pending when the Handle was destroyed,
|
||||||
// it can no longer be listened to so just cancel and cleanup
|
// it can no longer be listened to so just cancel and cleanup
|
||||||
|
@ -249,11 +263,11 @@ namespace geode {
|
||||||
|
|
||||||
using Value = T;
|
using Value = T;
|
||||||
using Progress = P;
|
using Progress = P;
|
||||||
using PostResult = utils::MiniFunction<void(Result)>;
|
using PostResult = std::function<void(Result&&)>;
|
||||||
using PostProgress = utils::MiniFunction<void(P)>;
|
using PostProgress = std::function<void(P)>;
|
||||||
using HasBeenCancelled = utils::MiniFunction<bool()>;
|
using HasBeenCancelled = std::function<bool()>;
|
||||||
using Run = utils::MiniFunction<Result(PostProgress, HasBeenCancelled)>;
|
using Run = std::function<Result(PostProgress, HasBeenCancelled)>;
|
||||||
using RunWithCallback = utils::MiniFunction<void(PostResult, PostProgress, HasBeenCancelled)>;
|
using RunWithCallback = std::function<void(PostResult, PostProgress, HasBeenCancelled)>;
|
||||||
|
|
||||||
using Callback = void(Event*);
|
using Callback = void(Event*);
|
||||||
|
|
||||||
|
@ -308,6 +322,12 @@ namespace geode {
|
||||||
template <std::move_constructible T2, std::move_constructible P2>
|
template <std::move_constructible T2, std::move_constructible P2>
|
||||||
friend class Task;
|
friend class Task;
|
||||||
|
|
||||||
|
template <class, class>
|
||||||
|
friend struct geode_internal::TaskPromise;
|
||||||
|
|
||||||
|
template <class, class>
|
||||||
|
friend struct geode_internal::TaskAwaiter;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// Allow default-construction
|
// Allow default-construction
|
||||||
Task() : m_handle(nullptr) {}
|
Task() : m_handle(nullptr) {}
|
||||||
|
@ -394,7 +414,7 @@ namespace geode {
|
||||||
* Create a new Task that is immediately cancelled
|
* Create a new Task that is immediately cancelled
|
||||||
* @param name The name of the Task; used for debugging
|
* @param name The name of the Task; used for debugging
|
||||||
*/
|
*/
|
||||||
static Task cancelled(std::string_view const name = "<Cancelled Task>") {
|
static Task cancelled(std::string_view name = "<Cancelled Task>") {
|
||||||
auto task = Task(Handle::create(name));
|
auto task = Task(Handle::create(name));
|
||||||
Task::cancel(task.m_handle);
|
Task::cancel(task.m_handle);
|
||||||
return task;
|
return task;
|
||||||
|
@ -405,7 +425,7 @@ namespace geode {
|
||||||
* @param value The value the Task shall be finished with
|
* @param value The value the Task shall be finished with
|
||||||
* @param name The name of the Task; used for debugging
|
* @param name The name of the Task; used for debugging
|
||||||
*/
|
*/
|
||||||
static Task immediate(T value, std::string_view const name = "<Immediate Task>") {
|
static Task immediate(T value, std::string_view name = "<Immediate Task>") {
|
||||||
auto task = Task(Handle::create(name));
|
auto task = Task(Handle::create(name));
|
||||||
Task::finish(task.m_handle, std::move(value));
|
Task::finish(task.m_handle, std::move(value));
|
||||||
return task;
|
return task;
|
||||||
|
@ -417,7 +437,7 @@ namespace geode {
|
||||||
* function MUST be synchronous - Task creates the thread for you!
|
* function MUST be synchronous - Task creates the thread for you!
|
||||||
* @param name The name of the Task; used for debugging
|
* @param name The name of the Task; used for debugging
|
||||||
*/
|
*/
|
||||||
static Task run(Run&& body, std::string_view const name = "<Task>") {
|
static Task run(Run&& body, std::string_view name = "<Task>") {
|
||||||
auto task = Task(Handle::create(name));
|
auto task = Task(Handle::create(name));
|
||||||
std::thread([handle = std::weak_ptr(task.m_handle), name = std::string(name), body = std::move(body)] {
|
std::thread([handle = std::weak_ptr(task.m_handle), name = std::string(name), body = std::move(body)] {
|
||||||
utils::thread::setName(fmt::format("Task '{}'", name));
|
utils::thread::setName(fmt::format("Task '{}'", name));
|
||||||
|
@ -450,7 +470,7 @@ namespace geode {
|
||||||
* calls will always be ignored
|
* calls will always be ignored
|
||||||
* @param name The name of the Task; used for debugging
|
* @param name The name of the Task; used for debugging
|
||||||
*/
|
*/
|
||||||
static Task runWithCallback(RunWithCallback&& body, std::string_view const name = "<Callback Task>") {
|
static Task runWithCallback(RunWithCallback&& body, std::string_view name = "<Callback Task>") {
|
||||||
auto task = Task(Handle::create(name));
|
auto task = Task(Handle::create(name));
|
||||||
std::thread([handle = std::weak_ptr(task.m_handle), name = std::string(name), body = std::move(body)] {
|
std::thread([handle = std::weak_ptr(task.m_handle), name = std::string(name), body = std::move(body)] {
|
||||||
utils::thread::setName(fmt::format("Task '{}'", name));
|
utils::thread::setName(fmt::format("Task '{}'", name));
|
||||||
|
@ -485,7 +505,7 @@ namespace geode {
|
||||||
* were cancelled!
|
* were cancelled!
|
||||||
*/
|
*/
|
||||||
template <std::move_constructible NP>
|
template <std::move_constructible NP>
|
||||||
static Task<std::vector<T*>, std::monostate> all(std::vector<Task<T, NP>>&& tasks, std::string_view const name = "<Multiple Tasks>") {
|
static Task<std::vector<T*>, std::monostate> all(std::vector<Task<T, NP>>&& tasks, std::string_view name = "<Multiple Tasks>") {
|
||||||
using AllTask = Task<std::vector<T*>, std::monostate>;
|
using AllTask = Task<std::vector<T*>, std::monostate>;
|
||||||
|
|
||||||
// If there are no tasks, return an immediate task that does nothing
|
// If there are no tasks, return an immediate task that does nothing
|
||||||
|
@ -582,7 +602,7 @@ namespace geode {
|
||||||
* the mapped task is appended to the end
|
* the mapped task is appended to the end
|
||||||
*/
|
*/
|
||||||
template <class ResultMapper, class ProgressMapper, class OnCancelled>
|
template <class ResultMapper, class ProgressMapper, class OnCancelled>
|
||||||
auto map(ResultMapper&& resultMapper, ProgressMapper&& progressMapper, OnCancelled&& onCancelled, std::string_view const name = "<Mapping Task>") const {
|
auto map(ResultMapper&& resultMapper, ProgressMapper&& progressMapper, OnCancelled&& onCancelled, std::string_view name = "<Mapping Task>") const {
|
||||||
using T2 = decltype(resultMapper(std::declval<T*>()));
|
using T2 = decltype(resultMapper(std::declval<T*>()));
|
||||||
using P2 = decltype(progressMapper(std::declval<P*>()));
|
using P2 = decltype(progressMapper(std::declval<P*>()));
|
||||||
|
|
||||||
|
@ -652,7 +672,7 @@ namespace geode {
|
||||||
* @param name The name of the Task; used for debugging. The name of
|
* @param name The name of the Task; used for debugging. The name of
|
||||||
* the mapped task is appended to the end
|
* the mapped task is appended to the end
|
||||||
*/ template <class ResultMapper, class ProgressMapper>
|
*/ template <class ResultMapper, class ProgressMapper>
|
||||||
auto map(ResultMapper&& resultMapper, ProgressMapper&& progressMapper, std::string_view const name = "<Mapping Task>") const {
|
auto map(ResultMapper&& resultMapper, ProgressMapper&& progressMapper, std::string_view name = "<Mapping Task>") const {
|
||||||
return this->map(std::move(resultMapper), std::move(progressMapper), +[]() {}, name);
|
return this->map(std::move(resultMapper), std::move(progressMapper), +[]() {}, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -670,7 +690,7 @@ namespace geode {
|
||||||
* the mapped task is appended to the end
|
* the mapped task is appended to the end
|
||||||
*/ template <class ResultMapper>
|
*/ template <class ResultMapper>
|
||||||
requires std::copy_constructible<P>
|
requires std::copy_constructible<P>
|
||||||
auto map(ResultMapper&& resultMapper, std::string_view const name = "<Mapping Task>") const {
|
auto map(ResultMapper&& resultMapper, std::string_view name = "<Mapping Task>") const {
|
||||||
return this->map(std::move(resultMapper), +[](P* p) -> P { return *p; }, name);
|
return this->map(std::move(resultMapper), +[](P* p) -> P { return *p; }, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -752,7 +772,94 @@ namespace geode {
|
||||||
this->listen(std::move(onResult), [](auto const&) {}, [] {});
|
this->listen(std::move(onResult), [](auto const&) {}, [] {});
|
||||||
}
|
}
|
||||||
|
|
||||||
ListenerResult handle(utils::MiniFunction<Callback> fn, Event* e) {
|
/**
|
||||||
|
* Create a new Task that listens to this Task and maps the values using
|
||||||
|
* the provided function. The new Task will only start when this Task finishes.
|
||||||
|
* @param mapper Function that makes a new task given the finished value of this task.
|
||||||
|
* The function signature should be `Task<NewType, NewProgress>(T*)`, and it will be executed
|
||||||
|
* on the main thread.
|
||||||
|
* @param name The name of the Task; used for debugging.
|
||||||
|
* @return The new Task that will run when this Task finishes.
|
||||||
|
* @note Progress from this task is not sent through, only progress from the new task is.
|
||||||
|
*/
|
||||||
|
template <std::invocable<T*> Mapper>
|
||||||
|
auto chain(Mapper mapper, std::string_view name = "<Chained Task>") const -> decltype(mapper(std::declval<T*>())) {
|
||||||
|
using NewTask = decltype(mapper(std::declval<T*>()));
|
||||||
|
using NewType = typename NewTask::Value;
|
||||||
|
using NewProgress = typename NewTask::Progress;
|
||||||
|
|
||||||
|
std::unique_lock<std::recursive_mutex> lock(m_handle->m_mutex);
|
||||||
|
|
||||||
|
if (m_handle->m_status == Status::Cancelled) {
|
||||||
|
// if the current task has been cancelled already, make an immediate cancelled task
|
||||||
|
return NewTask::cancelled();
|
||||||
|
}
|
||||||
|
else if (m_handle->m_status == Status::Finished) {
|
||||||
|
// if the current task is already done, we can just call the mapper directly
|
||||||
|
return mapper(&*m_handle->m_resultValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
// otherwise, make a wrapper task that waits for the current task to finish,
|
||||||
|
// and then runs the mapper on the result. this new task will also wait for the task
|
||||||
|
// created by the mapper to finish, and will just forward the values through.
|
||||||
|
// do this because we cant really change the handle of the task we already returned
|
||||||
|
|
||||||
|
NewTask task = NewTask::Handle::create(fmt::format("{} <- {}", name, m_handle->m_name));
|
||||||
|
|
||||||
|
task.m_handle->m_extraData = std::make_unique<typename NewTask::Handle::ExtraData>(
|
||||||
|
// make the first event listener that waits for the current task
|
||||||
|
static_cast<void*>(new EventListener<Task>(
|
||||||
|
[handle = std::weak_ptr(task.m_handle), mapper = std::move(mapper)](Event* event) mutable {
|
||||||
|
if (auto v = event->getValue()) {
|
||||||
|
auto newInnerTask = mapper(v);
|
||||||
|
// this is scary.. but it doesn't seem to crash lol
|
||||||
|
handle.lock()->m_extraData = std::make_unique<typename NewTask::Handle::ExtraData>(
|
||||||
|
// make the second event listener that waits for the mapper's task
|
||||||
|
// and just forwards everything through
|
||||||
|
static_cast<void*>(new EventListener<NewTask>(
|
||||||
|
[handle](typename NewTask::Event* event) mutable {
|
||||||
|
if (auto v = event->getValue()) {
|
||||||
|
NewTask::finish(handle.lock(), std::move(*v));
|
||||||
|
}
|
||||||
|
else if (auto p = event->getProgress()) {
|
||||||
|
NewTask::progress(handle.lock(), std::move(*p));
|
||||||
|
}
|
||||||
|
else if (event->isCancelled()) {
|
||||||
|
NewTask::cancel(handle.lock());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
std::move(newInnerTask)
|
||||||
|
)),
|
||||||
|
+[](void* ptr) {
|
||||||
|
delete static_cast<EventListener<NewTask>*>(ptr);
|
||||||
|
},
|
||||||
|
+[](void* ptr) {
|
||||||
|
static_cast<EventListener<NewTask>*>(ptr)->getFilter().cancel();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else if (auto p = event->getProgress()) {
|
||||||
|
// no guarantee P and NewProgress are compatible
|
||||||
|
// nor does it seem like the intended behavior?
|
||||||
|
// TODO: maybe add a mapper for progress?
|
||||||
|
}
|
||||||
|
else if (event->isCancelled()) {
|
||||||
|
NewTask::cancel(handle.lock());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
*this
|
||||||
|
)),
|
||||||
|
+[](void* ptr) {
|
||||||
|
delete static_cast<EventListener<Task>*>(ptr);
|
||||||
|
},
|
||||||
|
+[](void* ptr) {
|
||||||
|
static_cast<EventListener<Task>*>(ptr)->getFilter().cancel();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
return task;
|
||||||
|
}
|
||||||
|
|
||||||
|
ListenerResult handle(std::function<Callback> fn, Event* e) {
|
||||||
if (e->m_handle == m_handle && (!e->m_for || e->m_for == m_listener)) {
|
if (e->m_handle == m_handle && (!e->m_for || e->m_for == m_listener)) {
|
||||||
fn(e);
|
fn(e);
|
||||||
}
|
}
|
||||||
|
@ -797,3 +904,132 @@ namespace geode {
|
||||||
|
|
||||||
static_assert(is_filter<Task<int>>, "The Task class must be a valid event filter!");
|
static_assert(is_filter<Task<int>>, "The Task class must be a valid event filter!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// - C++20 coroutine support for Task - //
|
||||||
|
|
||||||
|
// Example usage (function must return a Task):
|
||||||
|
// ```
|
||||||
|
// Task<int> someTask() {
|
||||||
|
// auto response = co_await web::WebRequest().get("https://example.com");
|
||||||
|
// co_return response.code();
|
||||||
|
// }
|
||||||
|
// ```
|
||||||
|
// This will create a Task that will finish with the response code of the
|
||||||
|
// web request.
|
||||||
|
//
|
||||||
|
// Note: If the Task the coroutine is waiting on is cancelled, the coroutine
|
||||||
|
// will be destroyed and the Task will be cancelled as well. If the Task returned
|
||||||
|
// by the coroutine is cancelled, the coroutine will be destroyed as well and execution
|
||||||
|
// stops as soon as possible.
|
||||||
|
//
|
||||||
|
// The body of the coroutine is ran in whatever thread it got called in.
|
||||||
|
// TODO: maybe guarantee main thread?
|
||||||
|
//
|
||||||
|
// The coroutine can also yield progress values using `co_yield`:
|
||||||
|
// ```
|
||||||
|
// Task<std::string, int> someTask() {
|
||||||
|
// for (int i = 0; i < 10; i++) {
|
||||||
|
// co_yield i;
|
||||||
|
// }
|
||||||
|
// co_return "done!";
|
||||||
|
// }
|
||||||
|
// ```
|
||||||
|
|
||||||
|
namespace geode {
|
||||||
|
namespace geode_internal {
|
||||||
|
template <class T, class P>
|
||||||
|
struct TaskPromise {
|
||||||
|
using MyTask = Task<T, P>;
|
||||||
|
std::weak_ptr<typename MyTask::Handle> m_handle;
|
||||||
|
|
||||||
|
~TaskPromise() {
|
||||||
|
// does nothing if its not pending
|
||||||
|
MyTask::cancel(m_handle.lock());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::suspend_never initial_suspend() noexcept { return {}; }
|
||||||
|
std::suspend_never final_suspend() noexcept { return {}; }
|
||||||
|
// TODO: do something here?
|
||||||
|
void unhandled_exception() {}
|
||||||
|
|
||||||
|
MyTask get_return_object() {
|
||||||
|
auto handle = MyTask::Handle::create("<Coroutine Task>");
|
||||||
|
m_handle = handle;
|
||||||
|
return handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
void return_value(T x) {
|
||||||
|
MyTask::finish(m_handle.lock(), std::move(x));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::suspend_never yield_value(P value) {
|
||||||
|
MyTask::progress(m_handle.lock(), std::move(value));
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isCancelled() {
|
||||||
|
if (auto p = m_handle.lock()) {
|
||||||
|
return p->is(MyTask::Status::Cancelled);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class T, class P>
|
||||||
|
struct TaskAwaiter {
|
||||||
|
Task<T, P> task;
|
||||||
|
|
||||||
|
bool await_ready() {
|
||||||
|
return task.isFinished();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class U, class V>
|
||||||
|
void await_suspend(std::coroutine_handle<TaskPromise<U, V>> handle) {
|
||||||
|
if (handle.promise().isCancelled()) {
|
||||||
|
handle.destroy();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// this should be fine because the parent task can only have
|
||||||
|
// one pending task at a time
|
||||||
|
auto parentHandle = handle.promise().m_handle.lock();
|
||||||
|
if (!parentHandle) {
|
||||||
|
handle.destroy();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
parentHandle->m_extraData = std::make_unique<typename Task<U, V>::Handle::ExtraData>(
|
||||||
|
static_cast<void*>(new EventListener<Task<T, P>>(
|
||||||
|
[handle](auto* event) {
|
||||||
|
if (event->getValue()) {
|
||||||
|
handle.resume();
|
||||||
|
}
|
||||||
|
if (event->isCancelled()) {
|
||||||
|
handle.destroy();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
task
|
||||||
|
)),
|
||||||
|
+[](void* ptr) {
|
||||||
|
delete static_cast<EventListener<Task<T, P>>*>(ptr);
|
||||||
|
},
|
||||||
|
+[](void* ptr) {
|
||||||
|
static_cast<EventListener<Task<T, P>>*>(ptr)->getFilter().cancel();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
T await_resume() {
|
||||||
|
return std::move(*task.getFinishedValue());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T, class P>
|
||||||
|
auto operator co_await(geode::Task<T, P> task) {
|
||||||
|
return geode::geode_internal::TaskAwaiter<T, P>{task};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T, class P, class... Args>
|
||||||
|
struct std::coroutine_traits<geode::Task<T, P>, Args...> {
|
||||||
|
using promise_type = geode::geode_internal::TaskPromise<T, P>;
|
||||||
|
};
|
||||||
|
|
|
@ -4,7 +4,8 @@
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
#include <matjson.hpp>
|
#include <matjson.hpp>
|
||||||
#include <tuple>
|
#include <tuple>
|
||||||
#include "../utils/Result.hpp"
|
#include <Geode/Result.hpp>
|
||||||
|
#include <fmt/format.h>
|
||||||
|
|
||||||
namespace geode {
|
namespace geode {
|
||||||
enum class VersionCompare {
|
enum class VersionCompare {
|
||||||
|
@ -186,8 +187,6 @@ namespace geode {
|
||||||
std::tie(other.m_major, other.m_minor, other.m_patch, other.m_tag);
|
std::tie(other.m_major, other.m_minor, other.m_patch, other.m_tag);
|
||||||
}
|
}
|
||||||
|
|
||||||
[[deprecated("Use toNonVString or toVString instead")]]
|
|
||||||
std::string toString(bool includeTag = true) const;
|
|
||||||
std::string toVString(bool includeTag = true) const;
|
std::string toVString(bool includeTag = true) const;
|
||||||
std::string toNonVString(bool includeTag = true) const;
|
std::string toNonVString(bool includeTag = true) const;
|
||||||
|
|
||||||
|
@ -258,25 +257,15 @@ namespace geode {
|
||||||
template <class V>
|
template <class V>
|
||||||
requires std::is_same_v<V, geode::VersionInfo> || std::is_same_v<V, geode::ComparableVersionInfo>
|
requires std::is_same_v<V, geode::VersionInfo> || std::is_same_v<V, geode::ComparableVersionInfo>
|
||||||
struct matjson::Serialize<V> {
|
struct matjson::Serialize<V> {
|
||||||
static matjson::Value to_json(V const& info) {
|
static geode::Result<V, std::string> fromJson(Value const& value) {
|
||||||
return info.toString();
|
GEODE_UNWRAP_INTO(auto str, value.asString());
|
||||||
|
GEODE_UNWRAP_INTO(auto version, V::parse(str).mapErr([](auto&& err) {
|
||||||
|
return fmt::format("Invalid version format: {}", err);
|
||||||
|
}));
|
||||||
|
return geode::Ok(version);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool is_json(matjson::Value const& json) {
|
static Value toJson(V const& value) {
|
||||||
if (json.is_string()) {
|
return Value(value.toNonVString());
|
||||||
auto ver = V::parse(json.as_string());
|
|
||||||
return !ver.isErr();
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static V from_json(matjson::Value const& json) {
|
|
||||||
auto ver = V::parse(json.as_string());
|
|
||||||
if (!ver) {
|
|
||||||
throw matjson::JsonException(
|
|
||||||
"Invalid version format: " + ver.unwrapErr()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return ver.unwrap();
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -10,22 +10,21 @@
|
||||||
#include "../loader/Event.hpp"
|
#include "../loader/Event.hpp"
|
||||||
#include <Geode/binding/CCMenuItemSpriteExtra.hpp>
|
#include <Geode/binding/CCMenuItemSpriteExtra.hpp>
|
||||||
#include <Geode/binding/CCMenuItemToggler.hpp>
|
#include <Geode/binding/CCMenuItemToggler.hpp>
|
||||||
#include "MiniFunction.hpp"
|
#include "../ui/Layout.hpp"
|
||||||
|
#include "../ui/SpacerNode.hpp"
|
||||||
|
|
||||||
// support converting ccColor3B / ccColor4B to / from json
|
// support converting ccColor3B / ccColor4B to / from json
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
struct matjson::Serialize<cocos2d::ccColor3B> {
|
struct matjson::Serialize<cocos2d::ccColor3B> {
|
||||||
static matjson::Value GEODE_DLL to_json(cocos2d::ccColor3B const& color);
|
static geode::Result<cocos2d::ccColor3B> GEODE_DLL fromJson(Value const& value);
|
||||||
static cocos2d::ccColor3B GEODE_DLL from_json(matjson::Value const& color);
|
static Value GEODE_DLL toJson(cocos2d::ccColor3B const& value);
|
||||||
static bool GEODE_DLL is_json(matjson::Value const& json);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
struct matjson::Serialize<cocos2d::ccColor4B> {
|
struct matjson::Serialize<cocos2d::ccColor4B> {
|
||||||
static matjson::Value GEODE_DLL to_json(cocos2d::ccColor4B const& color);
|
static geode::Result<cocos2d::ccColor4B> GEODE_DLL fromJson(Value const& value);
|
||||||
static cocos2d::ccColor4B GEODE_DLL from_json(matjson::Value const& color);
|
static Value GEODE_DLL toJson(cocos2d::ccColor4B const& value);
|
||||||
static bool GEODE_DLL is_json(matjson::Value const& json);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// operators for CC geometry
|
// operators for CC geometry
|
||||||
|
@ -615,18 +614,6 @@ namespace geode::cocos {
|
||||||
return static_cast<T*>(x->getChildren()->objectAtIndex(i));
|
return static_cast<T*>(x->getChildren()->objectAtIndex(i));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get nth child that is a given type. Checks bounds.
|
|
||||||
* @returns Child at index cast to the given type,
|
|
||||||
* or nullptr if index exceeds bounds
|
|
||||||
*/
|
|
||||||
|
|
||||||
template <class Type = cocos2d::CCNode>
|
|
||||||
[[deprecated("Use CCNode::getChildByType instead")]]
|
|
||||||
static Type* getChildOfType(cocos2d::CCNode* node, int index) {
|
|
||||||
return node->getChildByType<Type>(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return a node, or create a default one if it's
|
* Return a node, or create a default one if it's
|
||||||
* nullptr. Syntactic sugar function
|
* nullptr. Syntactic sugar function
|
||||||
|
@ -670,7 +657,7 @@ namespace geode::cocos {
|
||||||
*/
|
*/
|
||||||
GEODE_DLL cocos2d::CCScene* switchToScene(cocos2d::CCLayer* layer);
|
GEODE_DLL cocos2d::CCScene* switchToScene(cocos2d::CCLayer* layer);
|
||||||
|
|
||||||
using CreateLayerFunc = utils::MiniFunction<cocos2d::CCLayer*()>;
|
using CreateLayerFunc = std::function<cocos2d::CCLayer*()>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reload textures, overwriting the scene to return to after the loading
|
* Reload textures, overwriting the scene to return to after the loading
|
||||||
|
@ -738,7 +725,7 @@ namespace geode::cocos {
|
||||||
* there is none
|
* there is none
|
||||||
*/
|
*/
|
||||||
template <class Type = cocos2d::CCNode>
|
template <class Type = cocos2d::CCNode>
|
||||||
Type* findFirstChildRecursive(cocos2d::CCNode* node, utils::MiniFunction<bool(Type*)> predicate) {
|
Type* findFirstChildRecursive(cocos2d::CCNode* node, std::function<bool(Type*)> predicate) {
|
||||||
if (cast::typeinfo_cast<Type*>(node) && predicate(static_cast<Type*>(node)))
|
if (cast::typeinfo_cast<Type*>(node) && predicate(static_cast<Type*>(node)))
|
||||||
return static_cast<Type*>(node);
|
return static_cast<Type*>(node);
|
||||||
|
|
||||||
|
@ -856,30 +843,6 @@ namespace geode::cocos {
|
||||||
return {color.r / 255.f, color.g / 255.f, color.b / 255.f, color.a / 255.f};
|
return {color.r / 255.f, color.g / 255.f, color.b / 255.f, color.a / 255.f};
|
||||||
}
|
}
|
||||||
|
|
||||||
[[deprecated("This function may have unintended behavior, use cc3bFromHexString or manually expand the color instead")]]
|
|
||||||
constexpr cocos2d::ccColor3B cc3x(int hexValue) {
|
|
||||||
if (hexValue <= 0xf)
|
|
||||||
return cocos2d::ccColor3B{
|
|
||||||
static_cast<GLubyte>(hexValue * 17),
|
|
||||||
static_cast<GLubyte>(hexValue * 17),
|
|
||||||
static_cast<GLubyte>(hexValue * 17)};
|
|
||||||
if (hexValue <= 0xff)
|
|
||||||
return cocos2d::ccColor3B{
|
|
||||||
static_cast<GLubyte>(hexValue),
|
|
||||||
static_cast<GLubyte>(hexValue),
|
|
||||||
static_cast<GLubyte>(hexValue)};
|
|
||||||
if (hexValue <= 0xfff)
|
|
||||||
return cocos2d::ccColor3B{
|
|
||||||
static_cast<GLubyte>((hexValue >> 8 & 0xf) * 17),
|
|
||||||
static_cast<GLubyte>((hexValue >> 4 & 0xf) * 17),
|
|
||||||
static_cast<GLubyte>((hexValue >> 0 & 0xf) * 17)};
|
|
||||||
else
|
|
||||||
return cocos2d::ccColor3B{
|
|
||||||
static_cast<GLubyte>(hexValue >> 16 & 0xff),
|
|
||||||
static_cast<GLubyte>(hexValue >> 8 & 0xff),
|
|
||||||
static_cast<GLubyte>(hexValue >> 0 & 0xff)};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse a ccColor3B from a hexadecimal string. The string may contain
|
* Parse a ccColor3B from a hexadecimal string. The string may contain
|
||||||
* a leading '#'
|
* a leading '#'
|
||||||
|
@ -887,7 +850,7 @@ namespace geode::cocos {
|
||||||
* @param permissive If true, strings like "f" are considered valid
|
* @param permissive If true, strings like "f" are considered valid
|
||||||
* representations of the color white. Useful for UIs that allow entering
|
* representations of the color white. Useful for UIs that allow entering
|
||||||
* a hex color. Empty strings evaluate to pure white
|
* a hex color. Empty strings evaluate to pure white
|
||||||
* @returns A ccColor3B if it could be succesfully parsed, or an error
|
* @returns A ccColor3B if it could be successfully parsed, or an error
|
||||||
* indicating the failure reason
|
* indicating the failure reason
|
||||||
*/
|
*/
|
||||||
GEODE_DLL Result<cocos2d::ccColor3B> cc3bFromHexString(std::string const& hexValue, bool permissive = false);
|
GEODE_DLL Result<cocos2d::ccColor3B> cc3bFromHexString(std::string const& hexValue, bool permissive = false);
|
||||||
|
@ -900,7 +863,7 @@ namespace geode::cocos {
|
||||||
* @param permissive If true, strings like "f" are considered valid
|
* @param permissive If true, strings like "f" are considered valid
|
||||||
* representations of the color white. Useful for UIs that allow entering
|
* representations of the color white. Useful for UIs that allow entering
|
||||||
* a hex color. Empty strings evaluate to pure white
|
* a hex color. Empty strings evaluate to pure white
|
||||||
* @returns A ccColor4B if it could be succesfully parsed, or an error
|
* @returns A ccColor4B if it could be successfully parsed, or an error
|
||||||
* indicating the failure reason
|
* indicating the failure reason
|
||||||
*/
|
*/
|
||||||
GEODE_DLL Result<cocos2d::ccColor4B> cc4bFromHexString(std::string const& hexValue, bool requireAlpha = false, bool permissive = false);
|
GEODE_DLL Result<cocos2d::ccColor4B> cc4bFromHexString(std::string const& hexValue, bool requireAlpha = false, bool permissive = false);
|
||||||
|
@ -916,7 +879,7 @@ namespace geode::cocos {
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T, typename C, typename = std::enable_if_t<std::is_pointer_v<C>>>
|
template <typename T, typename C, typename = std::enable_if_t<std::is_pointer_v<C>>>
|
||||||
static cocos2d::CCArray* vectorToCCArray(std::vector<T> const& vec, utils::MiniFunction<C(T)> convFunc) {
|
static cocos2d::CCArray* vectorToCCArray(std::vector<T> const& vec, std::function<C(T)> convFunc) {
|
||||||
auto res = cocos2d::CCArray::createWithCapacity(vec.size());
|
auto res = cocos2d::CCArray::createWithCapacity(vec.size());
|
||||||
for (auto const& item : vec)
|
for (auto const& item : vec)
|
||||||
res->addObject(convFunc(item));
|
res->addObject(convFunc(item));
|
||||||
|
@ -943,7 +906,7 @@ namespace geode::cocos {
|
||||||
template <
|
template <
|
||||||
typename K, typename V, typename C,
|
typename K, typename V, typename C,
|
||||||
typename = std::enable_if_t<std::is_same_v<C, std::string> || std::is_same_v<C, intptr_t>>>
|
typename = std::enable_if_t<std::is_same_v<C, std::string> || std::is_same_v<C, intptr_t>>>
|
||||||
static cocos2d::CCDictionary* mapToCCDict(std::map<K, V> const& map, utils::MiniFunction<C(K)> convFunc) {
|
static cocos2d::CCDictionary* mapToCCDict(std::map<K, V> const& map, std::function<C(K)> convFunc) {
|
||||||
auto res = cocos2d::CCDictionary::create();
|
auto res = cocos2d::CCDictionary::create();
|
||||||
for (auto const& [key, value] : map)
|
for (auto const& [key, value] : map)
|
||||||
res->setObject(value, convFunc(key));
|
res->setObject(value, convFunc(key));
|
||||||
|
@ -969,10 +932,10 @@ namespace std {
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
struct std::hash<geode::WeakRef<T>> {
|
struct hash<geode::WeakRef<T>> {
|
||||||
size_t operator()(geode::WeakRef<T> const& ref) const {
|
size_t operator()(geode::WeakRef<T> const& ref) const {
|
||||||
// the explicit template argument is needed here because it would otherwise cast to WeakRef and recurse
|
// the explicit template argument is needed here because it would otherwise cast to WeakRef and recurse
|
||||||
return hash<std::shared_ptr<geode::WeakRefController>>{}(ref.m_controller);
|
return std::hash<std::shared_ptr<geode::WeakRefController>>{}(ref.m_controller);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -1216,11 +1179,11 @@ namespace geode::cocos {
|
||||||
template <class Node>
|
template <class Node>
|
||||||
class LambdaCallback : public cocos2d::CCObject {
|
class LambdaCallback : public cocos2d::CCObject {
|
||||||
public:
|
public:
|
||||||
utils::MiniFunction<void(Node*)> m_callback;
|
std::function<void(Node*)> m_callback;
|
||||||
|
|
||||||
static LambdaCallback* create(utils::MiniFunction<void(Node*)>&& callback) {
|
static LambdaCallback* create(std::function<void(Node*)> callback) {
|
||||||
auto ret = new (std::nothrow) LambdaCallback();
|
auto ret = new (std::nothrow) LambdaCallback();
|
||||||
if (ret->init(std::forward<std::remove_reference_t<decltype(callback)>>(callback))) {
|
if (ret->init(std::move(callback))) {
|
||||||
ret->autorelease();
|
ret->autorelease();
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -1228,8 +1191,8 @@ namespace geode::cocos {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool init(utils::MiniFunction<void(Node*)>&& callback) {
|
bool init(std::function<void(Node*)> callback) {
|
||||||
m_callback = std::forward<std::remove_reference_t<decltype(callback)>>(callback);
|
m_callback = std::move(callback);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1240,20 +1203,20 @@ namespace geode::cocos {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static cocos2d::CCMenuItem* create(
|
static cocos2d::CCMenuItem* create(
|
||||||
utils::MiniFunction<void(cocos2d::CCMenuItem*)>&& callback
|
std::function<void(cocos2d::CCMenuItem*)> callback
|
||||||
) {
|
) {
|
||||||
auto item = cocos2d::CCMenuItem::create();
|
auto item = cocos2d::CCMenuItem::create();
|
||||||
assignCallback(item, std::forward<std::remove_reference_t<decltype(callback)>>(callback));
|
assignCallback(item, std::move(callback));
|
||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
|
|
||||||
static cocos2d::CCMenuItemSprite* createSprite(
|
static cocos2d::CCMenuItemSprite* createSprite(
|
||||||
cocos2d::CCNode* normalSprite,
|
cocos2d::CCNode* normalSprite,
|
||||||
cocos2d::CCNode* selectedSprite,
|
cocos2d::CCNode* selectedSprite,
|
||||||
utils::MiniFunction<void(cocos2d::CCMenuItemSprite*)>&& callback
|
std::function<void(cocos2d::CCMenuItemSprite*)> callback
|
||||||
) {
|
) {
|
||||||
auto item = cocos2d::CCMenuItemSprite::create(normalSprite, selectedSprite);
|
auto item = cocos2d::CCMenuItemSprite::create(normalSprite, selectedSprite);
|
||||||
assignCallback(item, std::forward<std::remove_reference_t<decltype(callback)>>(callback));
|
assignCallback(item, std::move(callback));
|
||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1261,57 +1224,57 @@ namespace geode::cocos {
|
||||||
cocos2d::CCNode* normalSprite,
|
cocos2d::CCNode* normalSprite,
|
||||||
cocos2d::CCNode* selectedSprite,
|
cocos2d::CCNode* selectedSprite,
|
||||||
cocos2d::CCNode* disabledSprite,
|
cocos2d::CCNode* disabledSprite,
|
||||||
utils::MiniFunction<void(cocos2d::CCMenuItemSprite*)>&& callback
|
std::function<void(cocos2d::CCMenuItemSprite*)> callback
|
||||||
) {
|
) {
|
||||||
auto item = cocos2d::CCMenuItemSprite::create(normalSprite, selectedSprite, disabledSprite);
|
auto item = cocos2d::CCMenuItemSprite::create(normalSprite, selectedSprite, disabledSprite);
|
||||||
assignCallback(item, std::forward<std::remove_reference_t<decltype(callback)>>(callback));
|
assignCallback(item, std::move(callback));
|
||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
|
|
||||||
static CCMenuItemSpriteExtra* createSpriteExtra(
|
static CCMenuItemSpriteExtra* createSpriteExtra(
|
||||||
cocos2d::CCNode* normalSprite,
|
cocos2d::CCNode* normalSprite,
|
||||||
utils::MiniFunction<void(CCMenuItemSpriteExtra*)>&& callback
|
std::function<void(CCMenuItemSpriteExtra*)> callback
|
||||||
) {
|
) {
|
||||||
auto item = CCMenuItemSpriteExtra::create(normalSprite, nullptr, nullptr);
|
auto item = CCMenuItemSpriteExtra::create(normalSprite, nullptr, nullptr);
|
||||||
assignCallback(item, std::forward<std::remove_reference_t<decltype(callback)>>(callback));
|
assignCallback(item, std::move(callback));
|
||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
|
|
||||||
static CCMenuItemSpriteExtra* createSpriteExtraWithFilename(
|
static CCMenuItemSpriteExtra* createSpriteExtraWithFilename(
|
||||||
std::string_view normalSpriteName,
|
std::string_view normalSpriteName,
|
||||||
float scale,
|
float scale,
|
||||||
utils::MiniFunction<void(CCMenuItemSpriteExtra*)>&& callback
|
std::function<void(CCMenuItemSpriteExtra*)> callback
|
||||||
) {
|
) {
|
||||||
auto sprite = cocos2d::CCSprite::create(normalSpriteName.data());
|
auto sprite = cocos2d::CCSprite::create(normalSpriteName.data());
|
||||||
sprite->setScale(scale);
|
sprite->setScale(scale);
|
||||||
|
|
||||||
return createSpriteExtra(sprite, std::forward<std::remove_reference_t<decltype(callback)>>(callback));
|
return createSpriteExtra(sprite, std::move(callback));
|
||||||
}
|
}
|
||||||
|
|
||||||
static CCMenuItemSpriteExtra* createSpriteExtraWithFrameName(
|
static CCMenuItemSpriteExtra* createSpriteExtraWithFrameName(
|
||||||
std::string_view normalSpriteName,
|
std::string_view normalSpriteName,
|
||||||
float scale,
|
float scale,
|
||||||
utils::MiniFunction<void(CCMenuItemSpriteExtra*)>&& callback
|
std::function<void(CCMenuItemSpriteExtra*)> callback
|
||||||
) {
|
) {
|
||||||
auto sprite = cocos2d::CCSprite::createWithSpriteFrameName(normalSpriteName.data());
|
auto sprite = cocos2d::CCSprite::createWithSpriteFrameName(normalSpriteName.data());
|
||||||
sprite->setScale(scale);
|
sprite->setScale(scale);
|
||||||
|
|
||||||
return createSpriteExtra(sprite, std::forward<std::remove_reference_t<decltype(callback)>>(callback));
|
return createSpriteExtra(sprite, std::move(callback));
|
||||||
}
|
}
|
||||||
|
|
||||||
static CCMenuItemToggler* createToggler(
|
static CCMenuItemToggler* createToggler(
|
||||||
cocos2d::CCNode* onSprite,
|
cocos2d::CCNode* onSprite,
|
||||||
cocos2d::CCNode* offSprite,
|
cocos2d::CCNode* offSprite,
|
||||||
utils::MiniFunction<void(CCMenuItemToggler*)>&& callback
|
std::function<void(CCMenuItemToggler*)> callback
|
||||||
) {
|
) {
|
||||||
auto item = CCMenuItemToggler::create(offSprite, onSprite, nullptr, nullptr);
|
auto item = CCMenuItemToggler::create(offSprite, onSprite, nullptr, nullptr);
|
||||||
assignCallback(item, std::forward<std::remove_reference_t<decltype(callback)>>(callback));
|
assignCallback(item, std::move(callback));
|
||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
|
|
||||||
static CCMenuItemToggler* createTogglerWithStandardSprites(
|
static CCMenuItemToggler* createTogglerWithStandardSprites(
|
||||||
float scale,
|
float scale,
|
||||||
utils::MiniFunction<void(CCMenuItemToggler*)>&& callback
|
std::function<void(CCMenuItemToggler*)> callback
|
||||||
) {
|
) {
|
||||||
auto offSprite = cocos2d::CCSprite::createWithSpriteFrameName("GJ_checkOff_001.png");
|
auto offSprite = cocos2d::CCSprite::createWithSpriteFrameName("GJ_checkOff_001.png");
|
||||||
auto onSprite = cocos2d::CCSprite::createWithSpriteFrameName("GJ_checkOn_001.png");
|
auto onSprite = cocos2d::CCSprite::createWithSpriteFrameName("GJ_checkOn_001.png");
|
||||||
|
@ -1319,14 +1282,14 @@ namespace geode::cocos {
|
||||||
offSprite->setScale(scale);
|
offSprite->setScale(scale);
|
||||||
onSprite->setScale(scale);
|
onSprite->setScale(scale);
|
||||||
|
|
||||||
return createToggler(onSprite, offSprite, std::forward<std::remove_reference_t<decltype(callback)>>(callback));
|
return createToggler(onSprite, offSprite, std::move(callback));
|
||||||
}
|
}
|
||||||
|
|
||||||
static CCMenuItemToggler* createTogglerWithFilename(
|
static CCMenuItemToggler* createTogglerWithFilename(
|
||||||
std::string_view onSpriteName,
|
std::string_view onSpriteName,
|
||||||
std::string_view offSpriteName,
|
std::string_view offSpriteName,
|
||||||
float scale,
|
float scale,
|
||||||
utils::MiniFunction<void(CCMenuItemToggler*)>&& callback
|
std::function<void(CCMenuItemToggler*)> callback
|
||||||
) {
|
) {
|
||||||
auto offSprite = cocos2d::CCSprite::create(offSpriteName.data());
|
auto offSprite = cocos2d::CCSprite::create(offSpriteName.data());
|
||||||
auto onSprite = cocos2d::CCSprite::create(onSpriteName.data());
|
auto onSprite = cocos2d::CCSprite::create(onSpriteName.data());
|
||||||
|
@ -1334,14 +1297,14 @@ namespace geode::cocos {
|
||||||
offSprite->setScale(scale);
|
offSprite->setScale(scale);
|
||||||
onSprite->setScale(scale);
|
onSprite->setScale(scale);
|
||||||
|
|
||||||
return createToggler(onSprite, offSprite, std::forward<std::remove_reference_t<decltype(callback)>>(callback));
|
return createToggler(onSprite, offSprite, std::move(callback));
|
||||||
}
|
}
|
||||||
|
|
||||||
static CCMenuItemToggler* createTogglerWithFrameName(
|
static CCMenuItemToggler* createTogglerWithFrameName(
|
||||||
std::string_view onSpriteName,
|
std::string_view onSpriteName,
|
||||||
std::string_view offSpriteName,
|
std::string_view offSpriteName,
|
||||||
float scale,
|
float scale,
|
||||||
utils::MiniFunction<void(CCMenuItemToggler*)>&& callback
|
std::function<void(CCMenuItemToggler*)> callback
|
||||||
) {
|
) {
|
||||||
auto offSprite = cocos2d::CCSprite::createWithSpriteFrameName(offSpriteName.data());
|
auto offSprite = cocos2d::CCSprite::createWithSpriteFrameName(offSpriteName.data());
|
||||||
auto onSprite = cocos2d::CCSprite::createWithSpriteFrameName(onSpriteName.data());
|
auto onSprite = cocos2d::CCSprite::createWithSpriteFrameName(onSpriteName.data());
|
||||||
|
@ -1349,15 +1312,15 @@ namespace geode::cocos {
|
||||||
offSprite->setScale(scale);
|
offSprite->setScale(scale);
|
||||||
onSprite->setScale(scale);
|
onSprite->setScale(scale);
|
||||||
|
|
||||||
return createToggler(onSprite, offSprite, std::forward<std::remove_reference_t<decltype(callback)>>(callback));
|
return createToggler(onSprite, offSprite, std::move(callback));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class Node>
|
template <class Node>
|
||||||
static void assignCallback(
|
static void assignCallback(
|
||||||
cocos2d::CCMenuItem* item,
|
cocos2d::CCMenuItem* item,
|
||||||
utils::MiniFunction<void(Node*)>&& callback
|
std::function<void(Node*)> callback
|
||||||
) {
|
) {
|
||||||
auto lambda = LambdaCallback<Node>::create(std::forward<std::remove_reference_t<decltype(callback)>>(callback));
|
auto lambda = LambdaCallback<Node>::create(std::move(callback));
|
||||||
item->setTarget(lambda, menu_selector(LambdaCallback<Node>::execute));
|
item->setTarget(lambda, menu_selector(LambdaCallback<Node>::execute));
|
||||||
item->setUserObject("lambda-callback", lambda);
|
item->setUserObject("lambda-callback", lambda);
|
||||||
}
|
}
|
||||||
|
@ -1368,15 +1331,13 @@ namespace geode::cocos {
|
||||||
class CallFuncExtImpl : public cocos2d::CCActionInstant {
|
class CallFuncExtImpl : public cocos2d::CCActionInstant {
|
||||||
public:
|
public:
|
||||||
static CallFuncExtImpl* create(const F& func) {
|
static CallFuncExtImpl* create(const F& func) {
|
||||||
auto ret = new CallFuncExtImpl;
|
auto ret = new CallFuncExtImpl(func);
|
||||||
ret->m_func = func;
|
|
||||||
ret->autorelease();
|
ret->autorelease();
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static CallFuncExtImpl* create(F&& func) {
|
static CallFuncExtImpl* create(F&& func) {
|
||||||
auto ret = new CallFuncExtImpl;
|
auto ret = new CallFuncExtImpl(std::move(func));
|
||||||
ret->m_func = std::move(func);
|
|
||||||
ret->autorelease();
|
ret->autorelease();
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -1384,8 +1345,17 @@ namespace geode::cocos {
|
||||||
private:
|
private:
|
||||||
F m_func;
|
F m_func;
|
||||||
|
|
||||||
|
// F may not be default-constructible
|
||||||
|
CallFuncExtImpl(F&& func) : m_func(std::move(func)) {}
|
||||||
|
CallFuncExtImpl(F const& func) : m_func(func) {}
|
||||||
|
|
||||||
void update(float) override {
|
void update(float) override {
|
||||||
if (m_func) this->m_func();
|
// Make sure any `std::function`s are valid
|
||||||
|
if constexpr (requires { static_cast<bool>(m_func); }) {
|
||||||
|
if (m_func) m_func();
|
||||||
|
} else {
|
||||||
|
m_func();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "Result.hpp"
|
#include <Geode/Result.hpp>
|
||||||
#include "general.hpp"
|
#include "general.hpp"
|
||||||
#include "../loader/Event.hpp"
|
#include "../loader/Event.hpp"
|
||||||
#include "Task.hpp"
|
#include "Task.hpp"
|
||||||
|
@ -13,14 +13,15 @@
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
struct matjson::Serialize<std::filesystem::path> {
|
struct matjson::Serialize<std::filesystem::path> {
|
||||||
static matjson::Value to_json(std::filesystem::path const& path) {
|
static geode::Result<std::filesystem::path, std::string> fromJson(Value const& value)
|
||||||
return path.string();
|
{
|
||||||
|
GEODE_UNWRAP_INTO(auto str, value.asString());
|
||||||
|
return geode::Ok(std::filesystem::path(str).make_preferred());
|
||||||
}
|
}
|
||||||
static std::filesystem::path from_json(matjson::Value const& value) {
|
|
||||||
return std::filesystem::path(value.as_string()).make_preferred();
|
static Value toJson(std::filesystem::path const& value)
|
||||||
}
|
{
|
||||||
static bool is_json(matjson::Value const& value) {
|
return Value(value.string());
|
||||||
return value.is_string();
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -32,10 +33,7 @@ namespace geode::utils::file {
|
||||||
template <class T>
|
template <class T>
|
||||||
Result<T> readFromJson(std::filesystem::path const& file) {
|
Result<T> readFromJson(std::filesystem::path const& file) {
|
||||||
GEODE_UNWRAP_INTO(auto json, readJson(file));
|
GEODE_UNWRAP_INTO(auto json, readJson(file));
|
||||||
if (!json.is<T>()) {
|
return json.as<T>();
|
||||||
return Err("JSON is not of type {}", typeid(T).name());
|
|
||||||
}
|
|
||||||
return Ok(json.as<T>());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
GEODE_DLL Result<> writeString(std::filesystem::path const& path, std::string const& data);
|
GEODE_DLL Result<> writeString(std::filesystem::path const& path, std::string const& data);
|
||||||
|
@ -162,7 +160,7 @@ namespace geode::utils::file {
|
||||||
* @param callback Callback to call with the progress of the unzip operation
|
* @param callback Callback to call with the progress of the unzip operation
|
||||||
*/
|
*/
|
||||||
void setProgressCallback(
|
void setProgressCallback(
|
||||||
utils::MiniFunction<void(uint32_t, uint32_t)> callback
|
std::function<void(uint32_t, uint32_t)> callback
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -213,7 +211,7 @@ namespace geode::utils::file {
|
||||||
);
|
);
|
||||||
|
|
||||||
static Result<> intoDir(
|
static Result<> intoDir(
|
||||||
utils::MiniFunction<void(uint32_t, uint32_t)> progressCallback,
|
std::function<void(uint32_t, uint32_t)> progressCallback,
|
||||||
Path const& from,
|
Path const& from,
|
||||||
Path const& to,
|
Path const& to,
|
||||||
bool deleteZipAfter = false
|
bool deleteZipAfter = false
|
||||||
|
@ -281,7 +279,7 @@ namespace geode::utils::file {
|
||||||
public:
|
public:
|
||||||
using Callback = void(FileWatchEvent*);
|
using Callback = void(FileWatchEvent*);
|
||||||
|
|
||||||
ListenerResult handle(utils::MiniFunction<Callback> callback, FileWatchEvent* event);
|
ListenerResult handle(std::function<Callback> callback, FileWatchEvent* event);
|
||||||
FileWatchFilter(std::filesystem::path const& path);
|
FileWatchFilter(std::filesystem::path const& path);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "Result.hpp"
|
#include <Geode/Result.hpp>
|
||||||
|
|
||||||
#include "../DefaultInclude.hpp"
|
#include "../DefaultInclude.hpp"
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
#include <sstream>
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
|
@ -13,19 +12,11 @@
|
||||||
#include <charconv>
|
#include <charconv>
|
||||||
#include <clocale>
|
#include <clocale>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
#include <fmt/format.h>
|
||||||
|
|
||||||
namespace geode {
|
namespace geode {
|
||||||
using ByteVector = std::vector<uint8_t>;
|
using ByteVector = std::vector<uint8_t>;
|
||||||
|
|
||||||
// todo in v4: remove this
|
|
||||||
template <typename T>
|
|
||||||
[[deprecated("Use geode::toBytes instead")]]
|
|
||||||
ByteVector toByteArray(T const& a) {
|
|
||||||
ByteVector out;
|
|
||||||
out.resize(sizeof(T));
|
|
||||||
std::memcpy(out.data(), &a, sizeof(T));
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
ByteVector toBytes(T const& a) {
|
ByteVector toBytes(T const& a) {
|
||||||
ByteVector out;
|
ByteVector out;
|
||||||
|
@ -72,9 +63,7 @@ namespace geode {
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
std::string intToHex(T i) {
|
std::string intToHex(T i) {
|
||||||
std::stringstream stream;
|
return fmt::format("{:#x}", i);
|
||||||
stream << std::showbase << std::setbase(16) << (uint64_t)i;
|
|
||||||
return stream.str();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -83,15 +72,16 @@ namespace geode {
|
||||||
* @param num Number to convert to string
|
* @param num Number to convert to string
|
||||||
* @param precision Precision of the converted number
|
* @param precision Precision of the converted number
|
||||||
* @returns Number as string
|
* @returns Number as string
|
||||||
|
* @note Precision has no effect on integers
|
||||||
*/
|
*/
|
||||||
template <class Num>
|
template <class Num>
|
||||||
std::string numToString(Num num, size_t precision = 0) {
|
std::string numToString(Num num, size_t precision = 0) {
|
||||||
std::stringstream ss;
|
if constexpr (std::is_floating_point_v<Num>) {
|
||||||
if (precision) {
|
if (precision) {
|
||||||
ss << std::fixed << std::setprecision(precision);
|
return fmt::format("{:.{}f}", num, precision);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ss << num;
|
return fmt::to_string(num);
|
||||||
return ss.str();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -125,7 +115,7 @@ namespace geode {
|
||||||
* @returns String as number, or Err if the string couldn't be converted
|
* @returns String as number, or Err if the string couldn't be converted
|
||||||
*/
|
*/
|
||||||
template <class Num>
|
template <class Num>
|
||||||
Result<Num> numFromString(std::string_view const str, int base = 10) {
|
Result<Num> numFromString(std::string_view str, int base = 10) {
|
||||||
if constexpr (std::is_floating_point_v<Num>
|
if constexpr (std::is_floating_point_v<Num>
|
||||||
#if defined(__cpp_lib_to_chars)
|
#if defined(__cpp_lib_to_chars)
|
||||||
&& false
|
&& false
|
||||||
|
@ -168,12 +158,18 @@ namespace geode {
|
||||||
*/
|
*/
|
||||||
GEODE_DLL float getDisplayFactor();
|
GEODE_DLL float getDisplayFactor();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <class... Args>
|
||||||
|
requires (sizeof...(Args) > 0)
|
||||||
|
constexpr auto Err(fmt::format_string<Args...> fmt, Args&&... args) {
|
||||||
|
return Err(fmt::format(fmt, std::forward<Args>(args)...));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
struct matjson::Serialize<geode::ByteVector> {
|
struct matjson::Serialize<geode::ByteVector> {
|
||||||
static matjson::Value to_json(geode::ByteVector const& bytes) {
|
static Value toJson(geode::ByteVector const& bytes) {
|
||||||
return matjson::Array(bytes.begin(), bytes.end());
|
return std::vector<matjson::Value>(bytes.begin(), bytes.end());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "Result.hpp"
|
#include <Geode/Result.hpp>
|
||||||
|
|
||||||
#include <Geode/DefaultInclude.hpp>
|
#include <Geode/DefaultInclude.hpp>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
@ -20,7 +20,7 @@ namespace geode::utils::map {
|
||||||
* false if not.
|
* false if not.
|
||||||
*/
|
*/
|
||||||
template <typename T, typename R, typename H>
|
template <typename T, typename R, typename H>
|
||||||
bool contains(std::unordered_map<T, R, H> const& map, utils::MiniFunction<bool(R)> containFunc) {
|
bool contains(std::unordered_map<T, R, H> const& map, std::function<bool(R)> containFunc) {
|
||||||
for (auto const& [_, r] : map) {
|
for (auto const& [_, r] : map) {
|
||||||
if (containFunc(r)) return true;
|
if (containFunc(r)) return true;
|
||||||
}
|
}
|
||||||
|
@ -39,7 +39,7 @@ namespace geode::utils::map {
|
||||||
* a pointer.
|
* a pointer.
|
||||||
*/
|
*/
|
||||||
template <class T, class R, class H>
|
template <class T, class R, class H>
|
||||||
R select(std::unordered_map<T, R, H> const& map, utils::MiniFunction<bool(R)> selectFunc) {
|
R select(std::unordered_map<T, R, H> const& map, std::function<bool(R)> selectFunc) {
|
||||||
for (auto const& [_, r] : map) {
|
for (auto const& [_, r] : map) {
|
||||||
if (selectFunc(r)) return r;
|
if (selectFunc(r)) return r;
|
||||||
}
|
}
|
||||||
|
@ -59,7 +59,7 @@ namespace geode::utils::map {
|
||||||
*/
|
*/
|
||||||
template <class T, class R, class H>
|
template <class T, class R, class H>
|
||||||
std::vector<R> selectAll(
|
std::vector<R> selectAll(
|
||||||
std::unordered_map<T, R, H> const& map, utils::MiniFunction<bool(R)> selectFunc
|
std::unordered_map<T, R, H> const& map, std::function<bool(R)> selectFunc
|
||||||
) {
|
) {
|
||||||
std::vector<R> res;
|
std::vector<R> res;
|
||||||
for (auto const& [_, r] : map) {
|
for (auto const& [_, r] : map) {
|
||||||
|
@ -111,7 +111,7 @@ namespace geode::utils::map {
|
||||||
template <class T1, class V1, class H1, class T2, class V2, class H2>
|
template <class T1, class V1, class H1, class T2, class V2, class H2>
|
||||||
std::unordered_map<T2, V2, H2> remap(
|
std::unordered_map<T2, V2, H2> remap(
|
||||||
std::unordered_map<T1, V1, H1> const& map,
|
std::unordered_map<T1, V1, H1> const& map,
|
||||||
utils::MiniFunction<std::pair<T2, V2>(std::pair<T1, V1>)> remapFunc
|
std::function<std::pair<T2, V2>(std::pair<T1, V1>)> remapFunc
|
||||||
) {
|
) {
|
||||||
std::unordered_map<T2, V2, H2> res;
|
std::unordered_map<T2, V2, H2> res;
|
||||||
for (auto const& [t, v] : map) {
|
for (auto const& [t, v] : map) {
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <Geode/DefaultInclude.hpp>
|
#include <Geode/DefaultInclude.hpp>
|
||||||
#include "MiniFunction.hpp"
|
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
|
|
||||||
namespace geode::utils::permission {
|
namespace geode::utils::permission {
|
||||||
|
@ -21,5 +20,5 @@ namespace geode::utils::permission {
|
||||||
* @param permission The permission
|
* @param permission The permission
|
||||||
* @param callback The callback, passed value is 'true' if permission was granted and 'false' otherwise.
|
* @param callback The callback, passed value is 'true' if permission was granted and 'false' otherwise.
|
||||||
*/
|
*/
|
||||||
void GEODE_DLL requestPermission(Permission permission, utils::MiniFunction<void(bool)> callback);
|
void GEODE_DLL requestPermission(Permission permission, std::function<void(bool)> callback);
|
||||||
}
|
}
|
|
@ -4,6 +4,7 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <compare>
|
#include <compare>
|
||||||
|
#include "../DefaultInclude.hpp"
|
||||||
|
|
||||||
namespace geode::utils::string {
|
namespace geode::utils::string {
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -71,6 +71,7 @@ namespace geode::utils {
|
||||||
std::string m_msg;
|
std::string m_msg;
|
||||||
Timer<Clock> m_timer;
|
Timer<Clock> m_timer;
|
||||||
|
|
||||||
|
// @geode-ignore(geode-alternative)
|
||||||
LogPerformance(std::string const& msg = "", std::ostream& out = std::cout) :
|
LogPerformance(std::string const& msg = "", std::ostream& out = std::cout) :
|
||||||
m_msg(msg), m_output(out) {
|
m_msg(msg), m_output(out) {
|
||||||
m_timer = Timer<Clock>();
|
m_timer = Timer<Clock>();
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
#include <Geode/loader/Loader.hpp> // another great circular dependency fix
|
#include <Geode/loader/Loader.hpp> // another great circular dependency fix
|
||||||
#include <matjson.hpp>
|
#include <matjson.hpp>
|
||||||
#include "Result.hpp"
|
#include <Geode/Result.hpp>
|
||||||
#include "Task.hpp"
|
#include "Task.hpp"
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
@ -83,6 +83,15 @@ namespace geode::utils::web {
|
||||||
|
|
||||||
std::vector<std::string> headers() const;
|
std::vector<std::string> headers() const;
|
||||||
std::optional<std::string> header(std::string_view name) const;
|
std::optional<std::string> header(std::string_view name) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves a list of all headers from the response with a given name - there can be
|
||||||
|
* multiple headers with the same name, such as Set-Cookie, with each cookie in a separate
|
||||||
|
* header
|
||||||
|
* @param name name of the header
|
||||||
|
* @return std::optional<std::vector<std::string>>
|
||||||
|
*/
|
||||||
|
std::optional<std::vector<std::string>> getAllHeadersNamed(std::string_view name) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
class GEODE_DLL WebProgress final {
|
class GEODE_DLL WebProgress final {
|
||||||
|
@ -203,6 +212,15 @@ namespace geode::utils::web {
|
||||||
*/
|
*/
|
||||||
WebRequest& followRedirects(bool enabled);
|
WebRequest& followRedirects(bool enabled);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enables or disables ignoring the content length header.
|
||||||
|
* The default is false.
|
||||||
|
*
|
||||||
|
* @param enabled
|
||||||
|
* @return WebRequest&
|
||||||
|
*/
|
||||||
|
WebRequest& ignoreContentLength(bool enabled);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the Certificate Authority (CA) bundle content.
|
* Sets the Certificate Authority (CA) bundle content.
|
||||||
* Defaults to not sending a CA bundle.
|
* Defaults to not sending a CA bundle.
|
||||||
|
@ -277,9 +295,9 @@ namespace geode::utils::web {
|
||||||
/**
|
/**
|
||||||
* Gets the request headers
|
* Gets the request headers
|
||||||
*
|
*
|
||||||
* @return std::unordered_map<std::string, std::string>
|
* @return std::unordered_map<std::string, std::vector<std::string>>
|
||||||
*/
|
*/
|
||||||
std::unordered_map<std::string, std::string> getHeaders() const;
|
std::unordered_map<std::string, std::vector<std::string>> getHeaders() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the parameters inside the URL
|
* Gets the parameters inside the URL
|
||||||
|
|
BIN
loader/include/link/win64/gd-libcurl.lib
Normal file
|
@ -6,6 +6,7 @@
|
||||||
|
|
||||||
struct XINPUT_STATE;
|
struct XINPUT_STATE;
|
||||||
struct XINPUT_CAPABILITIES;
|
struct XINPUT_CAPABILITIES;
|
||||||
|
struct XINPUT_VIBRATION;
|
||||||
|
|
||||||
constexpr static auto MAX_PATH_CHARS = 32768u;
|
constexpr static auto MAX_PATH_CHARS = 32768u;
|
||||||
|
|
||||||
|
@ -41,6 +42,17 @@ extern "C" DWORD XInputGetState(DWORD dwUserIndex, XINPUT_STATE *pState) {
|
||||||
return ERROR_DEVICE_NOT_CONNECTED;
|
return ERROR_DEVICE_NOT_CONNECTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#pragma comment(linker, "/export:XInputSetState,@3")
|
||||||
|
extern "C" DWORD XInputSetState(DWORD dwUserIndex, XINPUT_VIBRATION* pVibration) {
|
||||||
|
static auto fp = getFP("XInputSetState");
|
||||||
|
if (fp) {
|
||||||
|
using FPType = decltype(&XInputSetState);
|
||||||
|
return reinterpret_cast<FPType>(fp)(dwUserIndex, pVibration);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ERROR_DEVICE_NOT_CONNECTED;
|
||||||
|
}
|
||||||
|
|
||||||
#pragma comment(linker, "/export:XInputGetCapabilities,@4")
|
#pragma comment(linker, "/export:XInputGetCapabilities,@4")
|
||||||
extern "C" DWORD XInputGetCapabilities(DWORD dwUserIndex, DWORD dwFlags, XINPUT_CAPABILITIES *pCapabilities) {
|
extern "C" DWORD XInputGetCapabilities(DWORD dwUserIndex, DWORD dwFlags, XINPUT_CAPABILITIES *pCapabilities) {
|
||||||
static auto fp = getFP("XInputGetCapabilities");
|
static auto fp = getFP("XInputGetCapabilities");
|
||||||
|
|
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",
|
"name": "Enable Geode-Themed Colors",
|
||||||
"description": "When enabled, the Geode menu has a <ca>Geode-themed color scheme</c>. <cy>This does not affect any other menus!</c>"
|
"description": "When enabled, the Geode menu has a <ca>Geode-themed color scheme</c>. <cy>This does not affect any other menus!</c>"
|
||||||
},
|
},
|
||||||
|
"infinite-local-mods-list": {
|
||||||
|
"type": "bool",
|
||||||
|
"default": false,
|
||||||
|
"name": "Expand Installed Mods List",
|
||||||
|
"description": "Make the installed mods list a single infinite scrollable list instead of having pages"
|
||||||
|
},
|
||||||
"copy-mods": {
|
"copy-mods": {
|
||||||
"type": "custom:copy-mods",
|
"type": "custom:copy-mods",
|
||||||
"name": ""
|
"name": ""
|
||||||
|
|
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();
|
void free();
|
||||||
|
|
||||||
char* getStorage();
|
char* getStorage();
|
||||||
void setStorage(const std::string_view);
|
void setStorage(std::string_view);
|
||||||
|
|
||||||
size_t getSize();
|
size_t getSize();
|
||||||
void setSize(size_t);
|
void setSize(size_t);
|
||||||
|
|
|
@ -100,11 +100,11 @@ namespace gd {
|
||||||
bool string::operator==(string const& other) const {
|
bool string::operator==(string const& other) const {
|
||||||
return std::string_view(*this) == std::string_view(other);
|
return std::string_view(*this) == std::string_view(other);
|
||||||
}
|
}
|
||||||
bool string::operator==(std::string_view const other) const {
|
bool string::operator==(std::string_view other) const {
|
||||||
return std::string_view(*this) == other;
|
return std::string_view(*this) == other;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::strong_ordering string::operator<=>(std::string_view const other) const {
|
std::strong_ordering string::operator<=>(std::string_view other) const {
|
||||||
return static_cast<std::strong_ordering>(std::string_view(*this).compare(other) <=> 0);
|
return static_cast<std::strong_ordering>(std::string_view(*this).compare(other) <=> 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1026,14 +1026,6 @@ std::optional<AxisAlignment> AxisLayoutOptions::getCrossAxisAlignment() const {
|
||||||
return m_impl->m_crossAxisAlignment;
|
return m_impl->m_crossAxisAlignment;
|
||||||
}
|
}
|
||||||
|
|
||||||
AxisLayoutOptions* AxisLayoutOptions::setMaxScale(float scale) {
|
|
||||||
m_impl->m_scaleLimits.second = scale;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
AxisLayoutOptions* AxisLayoutOptions::setMinScale(float scale) {
|
|
||||||
m_impl->m_scaleLimits.first = scale;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
AxisLayoutOptions* AxisLayoutOptions::setScaleLimits(std::optional<float> min, std::optional<float> max) {
|
AxisLayoutOptions* AxisLayoutOptions::setScaleLimits(std::optional<float> min, std::optional<float> max) {
|
||||||
m_impl->m_scaleLimits = { min, max };
|
m_impl->m_scaleLimits = { min, max };
|
||||||
return this;
|
return this;
|
||||||
|
|
|
@ -8,11 +8,15 @@ $execute {
|
||||||
// this is needed because the transitions in cocos uses dynamic cast to check
|
// this is needed because the transitions in cocos uses dynamic cast to check
|
||||||
// layers, which fail on user layers due to typeinfo not matching
|
// layers, which fail on user layers due to typeinfo not matching
|
||||||
|
|
||||||
|
#if defined(GEODE_IS_MAC) && GEODE_COMP_GD_VERSION != 22074
|
||||||
|
#error "Unsupported version for macOS dynamic cast fix, please update the addresses"
|
||||||
|
#endif
|
||||||
|
|
||||||
#if defined(GEODE_IS_INTEL_MAC)
|
#if defined(GEODE_IS_INTEL_MAC)
|
||||||
void* dynamicCastAddr = reinterpret_cast<void*>(base::get() + 0x7dd5e7);
|
void* dynamicCastAddr = reinterpret_cast<void*>(base::get() + 0x7ba1d8);
|
||||||
(void) Mod::get()->hook(dynamicCastAddr, &cast::typeinfoCastInternal, "__dynamic_cast");
|
(void) Mod::get()->hook(dynamicCastAddr, &cast::typeinfoCastInternal, "__dynamic_cast");
|
||||||
#elif defined(GEODE_IS_ARM_MAC)
|
#elif defined(GEODE_IS_ARM_MAC)
|
||||||
void* dynamicCastAddr = reinterpret_cast<void*>(base::get() + 0x6dfb10);
|
void* dynamicCastAddr = reinterpret_cast<void*>(base::get() + 0x6c8bcc);
|
||||||
(void)Mod::get()->hook(dynamicCastAddr, &cast::typeinfoCastInternal, "__dynamic_cast");
|
(void)Mod::get()->hook(dynamicCastAddr, &cast::typeinfoCastInternal, "__dynamic_cast");
|
||||||
#elif defined(GEODE_IS_ANDROID)
|
#elif defined(GEODE_IS_ANDROID)
|
||||||
void* handle = dlopen("libcocos2dcpp.so", RTLD_LAZY | RTLD_NOLOAD);
|
void* handle = dlopen("libcocos2dcpp.so", RTLD_LAZY | RTLD_NOLOAD);
|
||||||
|
|
|
@ -62,10 +62,6 @@ public:
|
||||||
return meta;
|
return meta;
|
||||||
}
|
}
|
||||||
|
|
||||||
FieldContainer* getFieldContainer() {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
FieldContainer* getFieldContainer(char const* forClass) {
|
FieldContainer* getFieldContainer(char const* forClass) {
|
||||||
if (!m_classFieldContainers.count(forClass)) {
|
if (!m_classFieldContainers.count(forClass)) {
|
||||||
m_classFieldContainers[forClass] = new FieldContainer();
|
m_classFieldContainers[forClass] = new FieldContainer();
|
||||||
|
@ -104,16 +100,11 @@ size_t modifier::getFieldIndexForClass(char const* name) {
|
||||||
return s_nextIndex[name]++;
|
return s_nextIndex[name]++;
|
||||||
}
|
}
|
||||||
|
|
||||||
// not const because might modify contents
|
|
||||||
FieldContainer* CCNode::getFieldContainer() {
|
|
||||||
return GeodeNodeMetadata::set(this)->getFieldContainer();
|
|
||||||
}
|
|
||||||
|
|
||||||
FieldContainer* CCNode::getFieldContainer(char const* forClass) {
|
FieldContainer* CCNode::getFieldContainer(char const* forClass) {
|
||||||
return GeodeNodeMetadata::set(this)->getFieldContainer(forClass);
|
return GeodeNodeMetadata::set(this)->getFieldContainer(forClass);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string CCNode::getID() {
|
const std::string& CCNode::getID() {
|
||||||
return GeodeNodeMetadata::set(this)->m_id;
|
return GeodeNodeMetadata::set(this)->m_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -121,7 +112,11 @@ void CCNode::setID(std::string const& id) {
|
||||||
GeodeNodeMetadata::set(this)->m_id = id;
|
GeodeNodeMetadata::set(this)->m_id = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
CCNode* CCNode::getChildByID(std::string const& id) {
|
void CCNode::setID(std::string&& id) {
|
||||||
|
GeodeNodeMetadata::set(this)->m_id = std::move(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
CCNode* CCNode::getChildByID(std::string_view id) {
|
||||||
for (auto child : CCArrayExt<CCNode*>(this->getChildren())) {
|
for (auto child : CCArrayExt<CCNode*>(this->getChildren())) {
|
||||||
if (child->getID() == id) {
|
if (child->getID() == id) {
|
||||||
return child;
|
return child;
|
||||||
|
@ -130,7 +125,7 @@ CCNode* CCNode::getChildByID(std::string const& id) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
CCNode* CCNode::getChildByIDRecursive(std::string const& id) {
|
CCNode* CCNode::getChildByIDRecursive(std::string_view id) {
|
||||||
if (auto child = this->getChildByID(id)) {
|
if (auto child = this->getChildByID(id)) {
|
||||||
return child;
|
return child;
|
||||||
}
|
}
|
||||||
|
@ -189,7 +184,7 @@ private:
|
||||||
std::unique_ptr<NodeQuery> m_next = nullptr;
|
std::unique_ptr<NodeQuery> m_next = nullptr;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static Result<std::unique_ptr<NodeQuery>> parse(std::string const& query) {
|
static Result<std::unique_ptr<NodeQuery>> parse(std::string_view query) {
|
||||||
if (query.empty()) {
|
if (query.empty()) {
|
||||||
return Err("Query may not be empty");
|
return Err("Query may not be empty");
|
||||||
}
|
}
|
||||||
|
@ -287,7 +282,7 @@ public:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
CCNode* CCNode::querySelector(std::string const& queryStr) {
|
CCNode* CCNode::querySelector(std::string_view queryStr) {
|
||||||
auto res = NodeQuery::parse(queryStr);
|
auto res = NodeQuery::parse(queryStr);
|
||||||
if (!res) {
|
if (!res) {
|
||||||
log::error("Invalid CCNode::querySelector query '{}': {}", queryStr, res.unwrapErr());
|
log::error("Invalid CCNode::querySelector query '{}': {}", queryStr, res.unwrapErr());
|
||||||
|
@ -298,7 +293,7 @@ CCNode* CCNode::querySelector(std::string const& queryStr) {
|
||||||
return query->match(this);
|
return query->match(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CCNode::removeChildByID(std::string const& id) {
|
void CCNode::removeChildByID(std::string_view id) {
|
||||||
if (auto child = this->getChildByID(id)) {
|
if (auto child = this->getChildByID(id)) {
|
||||||
this->removeChild(child);
|
this->removeChild(child);
|
||||||
}
|
}
|
||||||
|
@ -344,7 +339,7 @@ void CCNode::updateLayout(bool updateChildOrder) {
|
||||||
UserObjectSetEvent::UserObjectSetEvent(CCNode* node, std::string const& id, CCObject* value)
|
UserObjectSetEvent::UserObjectSetEvent(CCNode* node, std::string const& id, CCObject* value)
|
||||||
: node(node), id(id), value(value) {}
|
: node(node), id(id), value(value) {}
|
||||||
|
|
||||||
ListenerResult AttributeSetFilter::handle(MiniFunction<Callback> fn, UserObjectSetEvent* event) {
|
ListenerResult AttributeSetFilter::handle(std::function<Callback> fn, UserObjectSetEvent* event) {
|
||||||
if (event->id == m_targetID) {
|
if (event->id == m_targetID) {
|
||||||
fn(event);
|
fn(event);
|
||||||
}
|
}
|
||||||
|
@ -455,13 +450,4 @@ void CCNode::updateAnchoredPosition(Anchor anchor, CCPoint const& offset, CCPoin
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef GEODE_EXPORTING
|
|
||||||
|
|
||||||
void CCNode::setAttribute(std::string const& attr, matjson::Value const& value) {}
|
|
||||||
std::optional<matjson::Value> CCNode::getAttributeInternal(std::string const& attr) {
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#pragma warning(pop)
|
#pragma warning(pop)
|
||||||
|
|
|
@ -75,6 +75,7 @@ struct CustomMenuLayer : Modify<CustomMenuLayer, MenuLayer> {
|
||||||
|
|
||||||
this->fixSocialMenu();
|
this->fixSocialMenu();
|
||||||
|
|
||||||
|
//this code doesnt run have fun figuring out why idc enough
|
||||||
if (auto node = this->getChildByID("settings-gamepad-icon")) {
|
if (auto node = this->getChildByID("settings-gamepad-icon")) {
|
||||||
node->setPositionX(
|
node->setPositionX(
|
||||||
bottomMenu->getChildByID("settings-button")->getPositionX() + winSize.width / 2
|
bottomMenu->getChildByID("settings-button")->getPositionX() + winSize.width / 2
|
||||||
|
@ -83,13 +84,12 @@ struct CustomMenuLayer : Modify<CustomMenuLayer, MenuLayer> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// show if some mods failed to load
|
// show if some mods failed to load
|
||||||
if (Loader::get()->getProblems().size()) {
|
if (Loader::get()->getLoadProblems().size()) {
|
||||||
static bool shownProblemPopup = false;
|
static bool shownProblemPopup = false;
|
||||||
if (!shownProblemPopup) {
|
if (!shownProblemPopup) {
|
||||||
shownProblemPopup = true;
|
shownProblemPopup = true;
|
||||||
Notification::create("There were errors - see Geode page!", NotificationIcon::Error)->show();
|
Notification::create("There were errors - see Geode page!", NotificationIcon::Error)->show();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_fields->m_geodeButton) {
|
if (m_fields->m_geodeButton) {
|
||||||
m_fields->m_exclamation = CCSprite::createWithSpriteFrameName("exMark_001.png");
|
m_fields->m_exclamation = CCSprite::createWithSpriteFrameName("exMark_001.png");
|
||||||
m_fields->m_exclamation->setPosition(m_fields->m_geodeButton->getContentSize() - ccp(10, 10));
|
m_fields->m_exclamation->setPosition(m_fields->m_geodeButton->getContentSize() - ccp(10, 10));
|
||||||
|
|
|
@ -41,9 +41,9 @@ $execute {
|
||||||
// hook them to call our own handler
|
// hook them to call our own handler
|
||||||
if (LoaderImpl::get()->isForwardCompatMode()) return;
|
if (LoaderImpl::get()->isForwardCompatMode()) return;
|
||||||
|
|
||||||
#if GEODE_COMP_GD_VERSION == 22060
|
#if GEODE_COMP_GD_VERSION == 22074
|
||||||
const uintptr_t offset1 = 0x75d00; // member function in CCEGLView
|
const uintptr_t offset1 = 0x75D90; // member function in CCEGLView
|
||||||
const uintptr_t offset2 = 0x75d60; // static function
|
const uintptr_t offset2 = 0x75DF0; // static function
|
||||||
|
|
||||||
(void) Mod::get()->hook(
|
(void) Mod::get()->hook(
|
||||||
reinterpret_cast<void*>(geode::base::getCocos() + offset1),
|
reinterpret_cast<void*>(geode::base::getCocos() + offset1),
|
||||||
|
|
|
@ -37,15 +37,15 @@ static void patchCall(uintptr_t addr, uintptr_t newCall) {
|
||||||
$execute {
|
$execute {
|
||||||
if (LoaderImpl::get()->isForwardCompatMode()) return;
|
if (LoaderImpl::get()->isForwardCompatMode()) return;
|
||||||
|
|
||||||
|
#if GEODE_COMP_GD_VERSION == 22040
|
||||||
// BitmapDC::~BitmapDC
|
// BitmapDC::~BitmapDC
|
||||||
#if 0 // TODO: mat GEODE_COMP_GD_VERSION == 22040
|
|
||||||
patchCall(0xC9A56, (uintptr_t)&RemoveFontResourceWHook);
|
patchCall(0xC9A56, (uintptr_t)&RemoveFontResourceWHook);
|
||||||
|
|
||||||
// BitmapDC::setFont
|
// BitmapDC::setFont
|
||||||
patchCall(0xCB5BC, (uintptr_t)&RemoveFontResourceWHook);
|
patchCall(0xCB5BC, (uintptr_t)&RemoveFontResourceWHook);
|
||||||
patchCall(0xCB642, (uintptr_t)&AddFontResourceWHook);
|
patchCall(0xCB642, (uintptr_t)&AddFontResourceWHook);
|
||||||
#else
|
#else
|
||||||
// #pragma message("Unsupported GD version!")
|
#pragma message("Unsupported GD version!")
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -2,10 +2,15 @@
|
||||||
|
|
||||||
using namespace geode::prelude;
|
using namespace geode::prelude;
|
||||||
|
|
||||||
|
#ifdef GEODE_IS_WINDOWS
|
||||||
|
#include <Geode/modify/AppDelegate.hpp>
|
||||||
|
#else
|
||||||
|
#include <Geode/modify/AchievementNotifier.hpp>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace geode {
|
||||||
|
|
||||||
#ifdef GEODE_IS_WINDOWS
|
#ifdef GEODE_IS_WINDOWS
|
||||||
|
|
||||||
#include <Geode/modify/AppDelegate.hpp>
|
|
||||||
struct SceneSwitch : Modify<SceneSwitch, AppDelegate> {
|
struct SceneSwitch : Modify<SceneSwitch, AppDelegate> {
|
||||||
GEODE_FORWARD_COMPAT_DISABLE_HOOKS("persist disabled")
|
GEODE_FORWARD_COMPAT_DISABLE_HOOKS("persist disabled")
|
||||||
void willSwitchToScene(CCScene* scene) {
|
void willSwitchToScene(CCScene* scene) {
|
||||||
|
@ -15,8 +20,6 @@ struct SceneSwitch : Modify<SceneSwitch, AppDelegate> {
|
||||||
};
|
};
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
#include <Geode/modify/AchievementNotifier.hpp>
|
|
||||||
struct SceneSwitch : Modify<SceneSwitch, AchievementNotifier> {
|
struct SceneSwitch : Modify<SceneSwitch, AchievementNotifier> {
|
||||||
GEODE_FORWARD_COMPAT_DISABLE_HOOKS("persist disabled")
|
GEODE_FORWARD_COMPAT_DISABLE_HOOKS("persist disabled")
|
||||||
void willSwitchToScene(CCScene* scene) {
|
void willSwitchToScene(CCScene* scene) {
|
||||||
|
@ -26,3 +29,5 @@ struct SceneSwitch : Modify<SceneSwitch, AchievementNotifier> {
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
}
|
|
@ -8,7 +8,7 @@ using namespace geode::prelude;
|
||||||
namespace {
|
namespace {
|
||||||
void saveModData() {
|
void saveModData() {
|
||||||
log::info("Saving mod data...");
|
log::info("Saving mod data...");
|
||||||
log::pushNest();
|
log::NestScope nest;
|
||||||
|
|
||||||
auto begin = std::chrono::high_resolution_clock::now();
|
auto begin = std::chrono::high_resolution_clock::now();
|
||||||
|
|
||||||
|
@ -17,8 +17,6 @@ namespace {
|
||||||
auto end = std::chrono::high_resolution_clock::now();
|
auto end = std::chrono::high_resolution_clock::now();
|
||||||
auto time = std::chrono::duration_cast<std::chrono::milliseconds>(end - begin).count();
|
auto time = std::chrono::duration_cast<std::chrono::milliseconds>(end - begin).count();
|
||||||
log::info("Took {}s", static_cast<float>(time) / 1000.f);
|
log::info("Took {}s", static_cast<float>(time) / 1000.f);
|
||||||
|
|
||||||
log::popNest();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -46,6 +46,11 @@ $register_ids(MenuLayer) {
|
||||||
}
|
}
|
||||||
|
|
||||||
setIDSafe<CCLabelBMFont>(this, labelOffset++, "player-username");
|
setIDSafe<CCLabelBMFont>(this, labelOffset++, "player-username");
|
||||||
|
|
||||||
|
if(auto node = this->getChildByID("settings-gamepad-icon")) {
|
||||||
|
// hide it until someone figures out how to bind the positioning to the actual button
|
||||||
|
node->setVisible(false);
|
||||||
|
}
|
||||||
|
|
||||||
// main menu
|
// main menu
|
||||||
if (auto menu = this->getChildByType<CCMenu>(0)) {
|
if (auto menu = this->getChildByType<CCMenu>(0)) {
|
||||||
|
|
|
@ -2,15 +2,14 @@
|
||||||
|
|
||||||
#include <Geode/DefaultInclude.hpp>
|
#include <Geode/DefaultInclude.hpp>
|
||||||
//#include <Geode/utils/general.hpp>
|
//#include <Geode/utils/general.hpp>
|
||||||
#include <Geode/utils/MiniFunction.hpp>
|
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
class FileWatcher {
|
class FileWatcher {
|
||||||
public:
|
public:
|
||||||
using FileWatchCallback = geode::utils::MiniFunction<void(std::filesystem::path)>;
|
using FileWatchCallback = std::function<void(std::filesystem::path)>;
|
||||||
using ErrorCallback = geode::utils::MiniFunction<void(std::string)>;
|
using ErrorCallback = std::function<void(std::string)>;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
std::filesystem::path m_file;
|
std::filesystem::path m_file;
|
||||||
|
|
|
@ -20,7 +20,7 @@ protected:
|
||||||
std::string content;
|
std::string content;
|
||||||
std::string optionA;
|
std::string optionA;
|
||||||
std::string optionB;
|
std::string optionB;
|
||||||
MiniFunction<void(bool)> after;
|
std::function<void(bool)> after;
|
||||||
};
|
};
|
||||||
|
|
||||||
EventListener<server::ModDownloadFilter> m_download;
|
EventListener<server::ModDownloadFilter> m_download;
|
||||||
|
@ -79,7 +79,7 @@ protected:
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void start() {
|
void start() {
|
||||||
for (auto problem : Loader::get()->getProblems()) {
|
for (auto problem : Loader::get()->getAllProblems()) {
|
||||||
switch (problem.type) {
|
switch (problem.type) {
|
||||||
// Errors where the correct solution is to just delete the invalid .geode package
|
// Errors where the correct solution is to just delete the invalid .geode package
|
||||||
case LoadProblem::Type::InvalidFile:
|
case LoadProblem::Type::InvalidFile:
|
||||||
|
|
|
@ -24,7 +24,8 @@ void crashlog::printGeodeInfo(std::stringstream& stream) {
|
||||||
<< "Loader Commit: " << about::getLoaderCommitHash() << "\n"
|
<< "Loader Commit: " << about::getLoaderCommitHash() << "\n"
|
||||||
<< "Bindings Commit: " << about::getBindingsCommitHash() << "\n"
|
<< "Bindings Commit: " << about::getBindingsCommitHash() << "\n"
|
||||||
<< "Installed mods: " << Loader::get()->getAllMods().size() << "\n"
|
<< "Installed mods: " << Loader::get()->getAllMods().size() << "\n"
|
||||||
<< "Problems: " << Loader::get()->getProblems().size() << "\n";
|
<< "Outdated mods: " << Loader::get()->getOutdated().size() << "\n"
|
||||||
|
<< "Problems: " << Loader::get()->getLoadProblems().size() << "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
void crashlog::printMods(std::stringstream& stream) {
|
void crashlog::printMods(std::stringstream& stream) {
|
||||||
|
@ -44,7 +45,8 @@ void crashlog::printMods(std::stringstream& stream) {
|
||||||
stream << fmt::format("{} | [{}] {}\n",
|
stream << fmt::format("{} | [{}] {}\n",
|
||||||
mod->isCurrentlyLoading() ? "o"sv :
|
mod->isCurrentlyLoading() ? "o"sv :
|
||||||
mod->isEnabled() ? "x"sv :
|
mod->isEnabled() ? "x"sv :
|
||||||
mod->hasProblems() ? "!"sv : // thank you for this bug report
|
mod->hasLoadProblems() ? "!"sv : // thank you for this bug report
|
||||||
|
mod->targetsOutdatedVersion() ? "*"sv : // thank you very much for this bug report
|
||||||
mod->shouldLoad() ? "~"sv :
|
mod->shouldLoad() ? "~"sv :
|
||||||
" "sv,
|
" "sv,
|
||||||
mod->getVersion().toVString(), mod->getID()
|
mod->getVersion().toVString(), mod->getID()
|
||||||
|
|
|
@ -7,7 +7,6 @@
|
||||||
#include <Geode/loader/Loader.hpp>
|
#include <Geode/loader/Loader.hpp>
|
||||||
#include <Geode/loader/Log.hpp>
|
#include <Geode/loader/Log.hpp>
|
||||||
#include <Geode/loader/Mod.hpp>
|
#include <Geode/loader/Mod.hpp>
|
||||||
#include <Geode/loader/SettingEvent.hpp>
|
|
||||||
#include <Geode/utils/JsonValidation.hpp>
|
#include <Geode/utils/JsonValidation.hpp>
|
||||||
#include <loader/LogImpl.hpp>
|
#include <loader/LogImpl.hpp>
|
||||||
|
|
||||||
|
@ -30,8 +29,7 @@ $on_mod(Loaded) {
|
||||||
std::vector<matjson::Value> res;
|
std::vector<matjson::Value> res;
|
||||||
|
|
||||||
auto args = *event->messageData;
|
auto args = *event->messageData;
|
||||||
JsonChecker checker(args);
|
auto root = checkJson(args, "[ipc/list-mods]");
|
||||||
auto root = checker.root("[ipc/list-mods]").obj();
|
|
||||||
|
|
||||||
auto includeRunTimeInfo = root.has("include-runtime-info").get<bool>();
|
auto includeRunTimeInfo = root.has("include-runtime-info").get<bool>();
|
||||||
auto dontIncludeLoader = root.has("dont-include-loader").get<bool>();
|
auto dontIncludeLoader = root.has("dont-include-loader").get<bool>();
|
||||||
|
@ -69,13 +67,13 @@ void tryShowForwardCompat() {
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// TODO: change text later
|
// TODO: change text later
|
||||||
console::messageBox(
|
// console::messageBox(
|
||||||
"Forward Compatibility Warning",
|
// "Forward Compatibility Warning",
|
||||||
"Geode is running in a newer version of GD than Geode targets.\n"
|
// "Geode is running in a newer version of GD than Geode targets.\n"
|
||||||
"UI is going to be disabled, platform console is forced on and crashes can be more common.\n"
|
// "UI is going to be disabled, platform console is forced on and crashes can be more common.\n"
|
||||||
"However, if your game crashes, it is probably caused by an outdated mod and not Geode itself.",
|
// "However, if your game crashes, it is probably caused by an outdated mod and not Geode itself.",
|
||||||
Severity::Warning
|
// Severity::Warning
|
||||||
);
|
// );
|
||||||
|
|
||||||
Mod::get()->setSavedValue<std::string>(
|
Mod::get()->setSavedValue<std::string>(
|
||||||
"last-forward-compat-warn-popup-ver",
|
"last-forward-compat-warn-popup-ver",
|
||||||
|
@ -86,7 +84,7 @@ void tryShowForwardCompat() {
|
||||||
#ifdef GEODE_IS_WINDOWS
|
#ifdef GEODE_IS_WINDOWS
|
||||||
bool safeModeCheck() {
|
bool safeModeCheck() {
|
||||||
// yes this is quite funny
|
// yes this is quite funny
|
||||||
if (GetAsyncKeyState(VK_SHIFT) == 0) {
|
if (!(GetAsyncKeyState(VK_SHIFT) & (1 << 15))) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -140,17 +138,18 @@ int geodeEntry(void* platformData) {
|
||||||
|
|
||||||
// set up internal mod, settings and data
|
// set up internal mod, settings and data
|
||||||
log::info("Setting up internal mod");
|
log::info("Setting up internal mod");
|
||||||
log::pushNest();
|
{
|
||||||
auto internalSetupRes = LoaderImpl::get()->setupInternalMod();
|
log::NestScope nest;
|
||||||
log::popNest();
|
auto internalSetupRes = LoaderImpl::get()->setupInternalMod();
|
||||||
if (!internalSetupRes) {
|
if (!internalSetupRes) {
|
||||||
console::messageBox(
|
console::messageBox(
|
||||||
"Unable to Load Geode!",
|
"Unable to Load Geode!",
|
||||||
"There was a fatal error setting up "
|
"There was a fatal error setting up "
|
||||||
"the internal mod and Geode can not be loaded: " + internalSetupRes.unwrapErr()
|
"the internal mod and Geode can not be loaded: " + internalSetupRes.unwrapErr()
|
||||||
);
|
);
|
||||||
LoaderImpl::get()->forceReset();
|
LoaderImpl::get()->forceReset();
|
||||||
return 1;
|
return 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tryShowForwardCompat();
|
tryShowForwardCompat();
|
||||||
|
@ -163,26 +162,28 @@ int geodeEntry(void* platformData) {
|
||||||
|
|
||||||
// set up loader, load mods, etc.
|
// set up loader, load mods, etc.
|
||||||
log::info("Setting up loader");
|
log::info("Setting up loader");
|
||||||
log::pushNest();
|
{
|
||||||
auto setupRes = LoaderImpl::get()->setup();
|
log::NestScope nest;
|
||||||
log::popNest();
|
auto setupRes = LoaderImpl::get()->setup();
|
||||||
if (!setupRes) {
|
if (!setupRes) {
|
||||||
console::messageBox(
|
console::messageBox(
|
||||||
"Unable to Load Geode!",
|
"Unable to Load Geode!",
|
||||||
"There was an unknown fatal error setting up "
|
"There was an unknown fatal error setting up "
|
||||||
"the loader and Geode can not be loaded. "
|
"the loader and Geode can not be loaded. "
|
||||||
"(" + setupRes.unwrapErr() + ")"
|
"(" + setupRes.unwrapErr() + ")"
|
||||||
);
|
);
|
||||||
LoaderImpl::get()->forceReset();
|
LoaderImpl::get()->forceReset();
|
||||||
return 1;
|
return 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
crashlog::setupPlatformHandlerPost();
|
crashlog::setupPlatformHandlerPost();
|
||||||
|
|
||||||
log::debug("Setting up IPC");
|
log::debug("Setting up IPC");
|
||||||
log::pushNest();
|
{
|
||||||
ipc::setup();
|
log::NestScope nest;
|
||||||
log::popNest();
|
ipc::setup();
|
||||||
|
}
|
||||||
|
|
||||||
// download and install new loader update in the background
|
// download and install new loader update in the background
|
||||||
|
|
||||||
|
|
|
@ -24,21 +24,20 @@ void CopyButtonSettingNode::onCopy(CCObject*) {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
std::stringstream ss;
|
std::string modsList;
|
||||||
using namespace std::string_view_literals;
|
using namespace std::string_view_literals;
|
||||||
for (int i = 0; i < mods.size(); i++) {
|
for (int i = 0; i < mods.size(); i++) {
|
||||||
auto& mod = mods[i];
|
auto& mod = mods[i];
|
||||||
ss << fmt::format("{} | [{}] {}",
|
modsList += fmt::format("{} | [{}] {}{}",
|
||||||
mod->isEnabled() ? "x"sv :
|
mod->isEnabled() ? "x"sv :
|
||||||
mod->hasProblems() ? "!"sv :
|
mod->hasLoadProblems() ? "!"sv :
|
||||||
|
mod->targetsOutdatedVersion() ? "*"sv :
|
||||||
" "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();
|
Notification::create("Mods list copied to clipboard", NotificationIcon::Info, 0.5f)->show();
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,14 +5,14 @@ using namespace geode::prelude;
|
||||||
|
|
||||||
class CopyButtonSetting : public SettingV3 {
|
class CopyButtonSetting : public SettingV3 {
|
||||||
public:
|
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 res = std::make_shared<CopyButtonSetting>();
|
||||||
auto root = checkJson(json, "CopyButtonSetting");
|
auto root = checkJson(json, "CopyButtonSetting");
|
||||||
|
|
||||||
res->init(key, modID, root);
|
res->init(key, modID, root);
|
||||||
res->parseNameAndDescription(root);
|
res->parseNameAndDescription(root);
|
||||||
|
|
||||||
return root.ok(res);
|
return root.ok(std::static_pointer_cast<SettingV3>(res));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool load(matjson::Value const& json) override {
|
bool load(matjson::Value const& json) override {
|
||||||
|
|
|
@ -98,7 +98,7 @@ std::string_view Hook::Impl::getDisplayName() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
matjson::Value Hook::Impl::getRuntimeInfo() const {
|
matjson::Value Hook::Impl::getRuntimeInfo() const {
|
||||||
auto json = matjson::Object();
|
matjson::Value json;
|
||||||
json["address"] = std::to_string(reinterpret_cast<uintptr_t>(m_address));
|
json["address"] = std::to_string(reinterpret_cast<uintptr_t>(m_address));
|
||||||
json["detour"] = std::to_string(reinterpret_cast<uintptr_t>(m_detour));
|
json["detour"] = std::to_string(reinterpret_cast<uintptr_t>(m_detour));
|
||||||
json["name"] = m_displayName;
|
json["name"] = m_displayName;
|
||||||
|
|
|
@ -19,7 +19,7 @@ ipc::IPCEvent::IPCEvent(
|
||||||
|
|
||||||
ipc::IPCEvent::~IPCEvent() {}
|
ipc::IPCEvent::~IPCEvent() {}
|
||||||
|
|
||||||
ListenerResult ipc::IPCFilter::handle(utils::MiniFunction<Callback> fn, IPCEvent* event) {
|
ListenerResult ipc::IPCFilter::handle(std::function<Callback> fn, IPCEvent* event) {
|
||||||
if (event->targetModID == m_modID && event->messageID == m_messageID) {
|
if (event->targetModID == m_modID && event->messageID == m_messageID) {
|
||||||
event->replyData = fn(event);
|
event->replyData = fn(event);
|
||||||
return ListenerResult::Stop;
|
return ListenerResult::Stop;
|
||||||
|
@ -33,19 +33,18 @@ ipc::IPCFilter::IPCFilter(std::string const& modID, std::string const& messageID
|
||||||
matjson::Value ipc::processRaw(void* rawHandle, std::string const& buffer) {
|
matjson::Value ipc::processRaw(void* rawHandle, std::string const& buffer) {
|
||||||
matjson::Value reply;
|
matjson::Value reply;
|
||||||
|
|
||||||
std::string error;
|
auto res = matjson::Value::parse(buffer);
|
||||||
auto res = matjson::parse(buffer, error);
|
if (!res) {
|
||||||
if (error.size() > 0) {
|
log::warn("Received IPC message that isn't valid JSON: {}", res.unwrapErr());
|
||||||
log::warn("Received IPC message that isn't valid JSON: {}", error);
|
|
||||||
return reply;
|
return reply;
|
||||||
}
|
}
|
||||||
matjson::Value json = res.value();
|
matjson::Value json = res.unwrap();
|
||||||
|
|
||||||
if (!json.contains("mod") || !json["mod"].is_string()) {
|
if (!json.contains("mod") || !json["mod"].isString()) {
|
||||||
log::warn("Received IPC message without 'mod' field");
|
log::warn("Received IPC message without 'mod' field");
|
||||||
return reply;
|
return reply;
|
||||||
}
|
}
|
||||||
if (!json.contains("message") || !json["message"].is_string()) {
|
if (!json.contains("message") || !json["message"].isString()) {
|
||||||
log::warn("Received IPC message without 'message' field");
|
log::warn("Received IPC message without 'message' field");
|
||||||
return reply;
|
return reply;
|
||||||
}
|
}
|
||||||
|
@ -55,6 +54,6 @@ matjson::Value ipc::processRaw(void* rawHandle, std::string const& buffer) {
|
||||||
}
|
}
|
||||||
// log::debug("Posting IPC event");
|
// log::debug("Posting IPC event");
|
||||||
// ! warning: if the event system is ever made asynchronous this will break!
|
// ! warning: if the event system is ever made asynchronous this will break!
|
||||||
IPCEvent(rawHandle, json["mod"].as_string(), json["message"].as_string(), data, reply).post();
|
IPCEvent(rawHandle, json["mod"].asString().unwrap(), json["message"].asString().unwrap(), data, reply).post();
|
||||||
return reply;
|
return reply;
|
||||||
}
|
}
|
||||||
|
|
|
@ -68,13 +68,19 @@ std::vector<Mod*> Loader::getAllMods() {
|
||||||
std::vector<LoadProblem> Loader::getAllProblems() const {
|
std::vector<LoadProblem> Loader::getAllProblems() const {
|
||||||
return m_impl->getProblems();
|
return m_impl->getProblems();
|
||||||
}
|
}
|
||||||
std::vector<LoadProblem> Loader::getProblems() const {
|
std::vector<LoadProblem> Loader::getLoadProblems() const {
|
||||||
std::vector<LoadProblem> result;
|
std::vector<LoadProblem> result;
|
||||||
for (auto problem : this->getAllProblems()) {
|
for (auto problem : this->getAllProblems()) {
|
||||||
if (
|
if (problem.isProblem()) {
|
||||||
problem.type != LoadProblem::Type::Recommendation &&
|
result.push_back(problem);
|
||||||
problem.type != LoadProblem::Type::Suggestion
|
}
|
||||||
) {
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
std::vector<LoadProblem> Loader::getOutdated() const {
|
||||||
|
std::vector<LoadProblem> result;
|
||||||
|
for (auto problem : this->getAllProblems()) {
|
||||||
|
if (problem.isOutdated()) {
|
||||||
result.push_back(problem);
|
result.push_back(problem);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -83,10 +89,7 @@ std::vector<LoadProblem> Loader::getProblems() const {
|
||||||
std::vector<LoadProblem> Loader::getRecommendations() const {
|
std::vector<LoadProblem> Loader::getRecommendations() const {
|
||||||
std::vector<LoadProblem> result;
|
std::vector<LoadProblem> result;
|
||||||
for (auto problem : this->getAllProblems()) {
|
for (auto problem : this->getAllProblems()) {
|
||||||
if (
|
if (problem.isSuggestion()) {
|
||||||
problem.type == LoadProblem::Type::Recommendation ||
|
|
||||||
problem.type == LoadProblem::Type::Suggestion
|
|
||||||
) {
|
|
||||||
result.push_back(problem);
|
result.push_back(problem);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -109,14 +112,14 @@ std::vector<std::string> Loader::getLaunchArgumentNames() const {
|
||||||
return m_impl->getLaunchArgumentNames();
|
return m_impl->getLaunchArgumentNames();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Loader::hasLaunchArgument(std::string_view const name) const {
|
bool Loader::hasLaunchArgument(std::string_view name) const {
|
||||||
return m_impl->hasLaunchArgument(name);
|
return m_impl->hasLaunchArgument(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<std::string> Loader::getLaunchArgument(std::string_view const name) const {
|
std::optional<std::string> Loader::getLaunchArgument(std::string_view name) const {
|
||||||
return m_impl->getLaunchArgument(name);
|
return m_impl->getLaunchArgument(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Loader::getLaunchFlag(std::string_view const name) const {
|
bool Loader::getLaunchFlag(std::string_view name) const {
|
||||||
return m_impl->getLaunchFlag(name);
|
return m_impl->getLaunchFlag(name);
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,9 @@
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include <server/DownloadManager.hpp>
|
||||||
|
#include <Geode/ui/Popup.hpp>
|
||||||
|
|
||||||
using namespace geode::prelude;
|
using namespace geode::prelude;
|
||||||
|
|
||||||
Loader::Impl* LoaderImpl::get() {
|
Loader::Impl* LoaderImpl::get() {
|
||||||
|
@ -42,6 +45,11 @@ Loader::Impl::~Impl() = default;
|
||||||
// Initialization
|
// Initialization
|
||||||
|
|
||||||
bool Loader::Impl::isForwardCompatMode() {
|
bool Loader::Impl::isForwardCompatMode() {
|
||||||
|
#ifdef GEODE_IS_ANDROID
|
||||||
|
// forward compat mode doesn't really make sense on android
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
|
||||||
if (!m_forwardCompatMode.has_value()) {
|
if (!m_forwardCompatMode.has_value()) {
|
||||||
m_forwardCompatMode = !this->getGameVersion().empty() &&
|
m_forwardCompatMode = !this->getGameVersion().empty() &&
|
||||||
this->getGameVersion() != GEODE_STR(GEODE_GD_VERSION);
|
this->getGameVersion() != GEODE_STR(GEODE_GD_VERSION);
|
||||||
|
@ -73,35 +81,32 @@ Result<> Loader::Impl::setup() {
|
||||||
|
|
||||||
if (this->supportsLaunchArguments()) {
|
if (this->supportsLaunchArguments()) {
|
||||||
log::debug("Loading launch arguments");
|
log::debug("Loading launch arguments");
|
||||||
log::pushNest();
|
log::NestScope nest;
|
||||||
this->initLaunchArguments();
|
this->initLaunchArguments();
|
||||||
log::popNest();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// on some platforms, using the crash handler overrides more convenient native handlers
|
// on some platforms, using the crash handler overrides more convenient native handlers
|
||||||
if (!this->getLaunchFlag("disable-crash-handler")) {
|
if (!this->getLaunchFlag("disable-crash-handler")) {
|
||||||
log::debug("Setting up crash handler");
|
log::debug("Setting up crash handler");
|
||||||
log::pushNest();
|
log::NestScope nest;
|
||||||
if (!crashlog::setupPlatformHandler()) {
|
if (!crashlog::setupPlatformHandler()) {
|
||||||
log::debug("Failed to set up crash handler");
|
log::debug("Failed to set up crash handler");
|
||||||
}
|
}
|
||||||
log::popNest();
|
|
||||||
} else {
|
} else {
|
||||||
log::debug("Crash handler setup skipped");
|
log::debug("Crash handler setup skipped");
|
||||||
}
|
}
|
||||||
|
|
||||||
log::debug("Loading hooks");
|
log::debug("Loading hooks");
|
||||||
log::pushNest();
|
if (log::NestScope nest; !this->loadHooks()) {
|
||||||
if (!this->loadHooks()) {
|
|
||||||
return Err("There were errors loading some hooks, see console for details");
|
return Err("There were errors loading some hooks, see console for details");
|
||||||
}
|
}
|
||||||
log::popNest();
|
|
||||||
|
|
||||||
log::debug("Setting up directories");
|
log::debug("Setting up directories");
|
||||||
log::pushNest();
|
{
|
||||||
this->createDirectories();
|
log::NestScope nest;
|
||||||
this->addSearchPaths();
|
this->createDirectories();
|
||||||
log::popNest();
|
this->addSearchPaths();
|
||||||
|
}
|
||||||
|
|
||||||
// Trigger on_mod(Loaded) for the internal mod
|
// Trigger on_mod(Loaded) for the internal mod
|
||||||
// this function is already on the gd thread, so this should be fine
|
// this function is already on the gd thread, so this should be fine
|
||||||
|
@ -121,7 +126,7 @@ void Loader::Impl::addSearchPaths() {
|
||||||
|
|
||||||
void Loader::Impl::updateResources(bool forceReload) {
|
void Loader::Impl::updateResources(bool forceReload) {
|
||||||
log::debug("Adding resources");
|
log::debug("Adding resources");
|
||||||
log::pushNest();
|
log::NestScope nest;
|
||||||
for (auto const& [_, mod] : m_mods) {
|
for (auto const& [_, mod] : m_mods) {
|
||||||
if (!forceReload && ModImpl::getImpl(mod)->m_resourcesLoaded)
|
if (!forceReload && ModImpl::getImpl(mod)->m_resourcesLoaded)
|
||||||
continue;
|
continue;
|
||||||
|
@ -132,7 +137,6 @@ void Loader::Impl::updateResources(bool forceReload) {
|
||||||
// we have to call it in both places since setup is only called once ever, but updateResources is called
|
// we have to call it in both places since setup is only called once ever, but updateResources is called
|
||||||
// on every texture reload
|
// on every texture reload
|
||||||
CCFileUtils::get()->updatePaths();
|
CCFileUtils::get()->updatePaths();
|
||||||
log::popNest();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<Mod*> Loader::Impl::getAllMods() {
|
std::vector<Mod*> Loader::Impl::getAllMods() {
|
||||||
|
@ -170,24 +174,22 @@ bool Loader::Impl::isModVersionSupported(VersionInfo const& target) {
|
||||||
void Loader::Impl::saveData() {
|
void Loader::Impl::saveData() {
|
||||||
for (auto& [id, mod] : m_mods) {
|
for (auto& [id, mod] : m_mods) {
|
||||||
log::debug("{}", mod->getID());
|
log::debug("{}", mod->getID());
|
||||||
log::pushNest();
|
log::NestScope nest;
|
||||||
auto r = mod->saveData();
|
auto r = mod->saveData();
|
||||||
if (!r) {
|
if (!r) {
|
||||||
log::warn("Unable to save data for mod \"{}\": {}", mod->getID(), r.unwrapErr());
|
log::warn("Unable to save data for mod \"{}\": {}", mod->getID(), r.unwrapErr());
|
||||||
}
|
}
|
||||||
log::popNest();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Loader::Impl::loadData() {
|
void Loader::Impl::loadData() {
|
||||||
for (auto& [_, mod] : m_mods) {
|
for (auto& [_, mod] : m_mods) {
|
||||||
log::debug("{}", mod->getID());
|
log::debug("{}", mod->getID());
|
||||||
log::pushNest();
|
log::NestScope nest;
|
||||||
auto r = mod->loadData();
|
auto r = mod->loadData();
|
||||||
if (!r) {
|
if (!r) {
|
||||||
log::warn("Unable to load data for mod \"{}\": {}", mod->getID(), r.unwrapErr());
|
log::warn("Unable to load data for mod \"{}\": {}", mod->getID(), r.unwrapErr());
|
||||||
}
|
}
|
||||||
log::popNest();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -230,7 +232,7 @@ void Loader::Impl::updateModResources(Mod* mod) {
|
||||||
return;
|
return;
|
||||||
|
|
||||||
log::debug("{}", mod->getID());
|
log::debug("{}", mod->getID());
|
||||||
log::pushNest();
|
log::NestScope nest;
|
||||||
|
|
||||||
for (auto const& sheet : mod->getMetadata().getSpritesheets()) {
|
for (auto const& sheet : mod->getMetadata().getSpritesheets()) {
|
||||||
log::debug("Adding sheet {}", sheet);
|
log::debug("Adding sheet {}", sheet);
|
||||||
|
@ -250,8 +252,6 @@ void Loader::Impl::updateModResources(Mod* mod) {
|
||||||
CCSpriteFrameCache::get()->addSpriteFramesWithFile(plist.c_str());
|
CCSpriteFrameCache::get()->addSpriteFramesWithFile(plist.c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
log::popNest();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Loader::Impl::addProblem(LoadProblem const& problem) {
|
void Loader::Impl::addProblem(LoadProblem const& problem) {
|
||||||
|
@ -267,14 +267,14 @@ void Loader::Impl::addProblem(LoadProblem const& problem) {
|
||||||
void Loader::Impl::queueMods(std::vector<ModMetadata>& modQueue) {
|
void Loader::Impl::queueMods(std::vector<ModMetadata>& modQueue) {
|
||||||
for (auto const& dir : m_modSearchDirectories) {
|
for (auto const& dir : m_modSearchDirectories) {
|
||||||
log::debug("Searching {}", dir);
|
log::debug("Searching {}", dir);
|
||||||
log::pushNest();
|
log::NestScope nest;
|
||||||
for (auto const& entry : std::filesystem::directory_iterator(dir)) {
|
for (auto const& entry : std::filesystem::directory_iterator(dir)) {
|
||||||
if (!std::filesystem::is_regular_file(entry) ||
|
if (!std::filesystem::is_regular_file(entry) ||
|
||||||
entry.path().extension() != GEODE_MOD_EXTENSION)
|
entry.path().extension() != GEODE_MOD_EXTENSION)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
log::debug("Found {}", entry.path().filename());
|
log::debug("Found {}", entry.path().filename());
|
||||||
log::pushNest();
|
log::NestScope nest;
|
||||||
|
|
||||||
auto res = ModMetadata::createFromGeodeFile(entry.path());
|
auto res = ModMetadata::createFromGeodeFile(entry.path());
|
||||||
if (!res) {
|
if (!res) {
|
||||||
|
@ -284,7 +284,6 @@ void Loader::Impl::queueMods(std::vector<ModMetadata>& modQueue) {
|
||||||
res.unwrapErr()
|
res.unwrapErr()
|
||||||
});
|
});
|
||||||
log::error("Failed to queue: {}", res.unwrapErr());
|
log::error("Failed to queue: {}", res.unwrapErr());
|
||||||
log::popNest();
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
auto modMetadata = res.unwrap();
|
auto modMetadata = res.unwrap();
|
||||||
|
@ -302,14 +301,11 @@ void Loader::Impl::queueMods(std::vector<ModMetadata>& modQueue) {
|
||||||
"A mod with the same ID is already present."
|
"A mod with the same ID is already present."
|
||||||
});
|
});
|
||||||
log::error("Failed to queue: a mod with the same ID is already queued");
|
log::error("Failed to queue: a mod with the same ID is already queued");
|
||||||
log::popNest();
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
modQueue.push_back(modMetadata);
|
modQueue.push_back(modMetadata);
|
||||||
log::popNest();
|
|
||||||
}
|
}
|
||||||
log::popNest();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -327,7 +323,7 @@ void Loader::Impl::populateModList(std::vector<ModMetadata>& modQueue) {
|
||||||
|
|
||||||
for (auto const& metadata : modQueue) {
|
for (auto const& metadata : modQueue) {
|
||||||
log::debug("{} {}", metadata.getID(), metadata.getVersion());
|
log::debug("{} {}", metadata.getID(), metadata.getVersion());
|
||||||
log::pushNest();
|
log::NestScope nest;
|
||||||
|
|
||||||
auto mod = new Mod(metadata);
|
auto mod = new Mod(metadata);
|
||||||
|
|
||||||
|
@ -339,20 +335,17 @@ void Loader::Impl::populateModList(std::vector<ModMetadata>& modQueue) {
|
||||||
res.unwrapErr()
|
res.unwrapErr()
|
||||||
});
|
});
|
||||||
log::error("Failed to set up: {}", res.unwrapErr());
|
log::error("Failed to set up: {}", res.unwrapErr());
|
||||||
log::popNest();
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_mods.insert({metadata.getID(), mod});
|
m_mods.insert({metadata.getID(), mod});
|
||||||
|
|
||||||
log::popNest();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Loader::Impl::buildModGraph() {
|
void Loader::Impl::buildModGraph() {
|
||||||
for (auto const& [id, mod] : m_mods) {
|
for (auto const& [id, mod] : m_mods) {
|
||||||
log::debug("{}", mod->getID());
|
log::debug("{}", mod->getID());
|
||||||
log::pushNest();
|
log::NestScope nest;
|
||||||
for (auto& dependency : mod->m_impl->m_metadata.m_impl->m_dependencies) {
|
for (auto& dependency : mod->m_impl->m_metadata.m_impl->m_dependencies) {
|
||||||
log::debug("{}", dependency.id);
|
log::debug("{}", dependency.id);
|
||||||
if (!m_mods.contains(dependency.id)) {
|
if (!m_mods.contains(dependency.id)) {
|
||||||
|
@ -379,11 +372,38 @@ void Loader::Impl::buildModGraph() {
|
||||||
incompatibility.mod =
|
incompatibility.mod =
|
||||||
m_mods.contains(incompatibility.id) ? m_mods[incompatibility.id] : nullptr;
|
m_mods.contains(incompatibility.id) ? m_mods[incompatibility.id] : nullptr;
|
||||||
}
|
}
|
||||||
log::popNest();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Loader::Impl::loadModGraph(Mod* node, bool early) {
|
void Loader::Impl::loadModGraph(Mod* node, bool early) {
|
||||||
|
// Check version first, as it's not worth trying to load a mod with an
|
||||||
|
// invalid target version
|
||||||
|
// Also this makes it so that when GD updates, outdated mods get shown as
|
||||||
|
// "Outdated" in the UI instead of "Missing Dependencies"
|
||||||
|
auto res = node->getMetadata().checkGameVersion();
|
||||||
|
if (!res) {
|
||||||
|
this->addProblem({
|
||||||
|
LoadProblem::Type::UnsupportedVersion,
|
||||||
|
node,
|
||||||
|
res.unwrapErr()
|
||||||
|
});
|
||||||
|
log::error("{}", res.unwrapErr());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto geodeVerRes = node->getMetadata().checkGeodeVersion();
|
||||||
|
if (!geodeVerRes) {
|
||||||
|
this->addProblem({
|
||||||
|
node->getMetadata().getGeodeVersion() > this->getVersion() ?
|
||||||
|
LoadProblem::Type::NeedsNewerGeodeVersion :
|
||||||
|
LoadProblem::Type::UnsupportedGeodeVersion,
|
||||||
|
node,
|
||||||
|
geodeVerRes.unwrapErr()
|
||||||
|
});
|
||||||
|
log::error("{}", geodeVerRes.unwrapErr());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (node->hasUnresolvedDependencies()) {
|
if (node->hasUnresolvedDependencies()) {
|
||||||
log::debug("{} {} has unresolved dependencies", node->getID(), node->getVersion());
|
log::debug("{} {} has unresolved dependencies", node->getID(), node->getVersion());
|
||||||
return;
|
return;
|
||||||
|
@ -393,12 +413,10 @@ void Loader::Impl::loadModGraph(Mod* node, bool early) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
log::debug("{} {}", node->getID(), node->getVersion());
|
log::NestScope nest;
|
||||||
log::pushNest();
|
|
||||||
|
|
||||||
if (node->isEnabled()) {
|
if (node->isEnabled()) {
|
||||||
log::warn("Mod {} already loaded, this should never happen", node->getID());
|
log::warn("Mod {} already loaded, this should never happen", node->getID());
|
||||||
log::popNest();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -409,6 +427,7 @@ void Loader::Impl::loadModGraph(Mod* node, bool early) {
|
||||||
|
|
||||||
auto unzipFunction = [this, node]() {
|
auto unzipFunction = [this, node]() {
|
||||||
log::debug("Unzip");
|
log::debug("Unzip");
|
||||||
|
log::NestScope nest;
|
||||||
auto res = node->m_impl->unzipGeodeFile(node->getMetadata());
|
auto res = node->m_impl->unzipGeodeFile(node->getMetadata());
|
||||||
return res;
|
return res;
|
||||||
};
|
};
|
||||||
|
@ -416,6 +435,7 @@ void Loader::Impl::loadModGraph(Mod* node, bool early) {
|
||||||
auto loadFunction = [this, node, early]() {
|
auto loadFunction = [this, node, early]() {
|
||||||
if (node->shouldLoad()) {
|
if (node->shouldLoad()) {
|
||||||
log::debug("Load");
|
log::debug("Load");
|
||||||
|
log::NestScope nest;
|
||||||
auto res = node->m_impl->loadBinary();
|
auto res = node->m_impl->loadBinary();
|
||||||
if (!res) {
|
if (!res) {
|
||||||
this->addProblem({
|
this->addProblem({
|
||||||
|
@ -441,36 +461,6 @@ void Loader::Impl::loadModGraph(Mod* node, bool early) {
|
||||||
});
|
});
|
||||||
log::error("{}", reason.value());
|
log::error("{}", reason.value());
|
||||||
m_refreshingModCount -= 1;
|
m_refreshingModCount -= 1;
|
||||||
log::popNest();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto res = node->getMetadata().checkGameVersion();
|
|
||||||
if (!res) {
|
|
||||||
this->addProblem({
|
|
||||||
LoadProblem::Type::UnsupportedVersion,
|
|
||||||
node,
|
|
||||||
res.unwrapErr()
|
|
||||||
});
|
|
||||||
log::error("{}", res.unwrapErr());
|
|
||||||
m_refreshingModCount -= 1;
|
|
||||||
log::popNest();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this->isModVersionSupported(node->getMetadata().getGeodeVersion())) {
|
|
||||||
this->addProblem({
|
|
||||||
node->getMetadata().getGeodeVersion() > this->getVersion() ? LoadProblem::Type::NeedsNewerGeodeVersion : LoadProblem::Type::UnsupportedGeodeVersion,
|
|
||||||
node,
|
|
||||||
fmt::format(
|
|
||||||
"Geode version {}\nis required to run this mod\n(installed: {})",
|
|
||||||
node->getMetadata().getGeodeVersion().toVString(),
|
|
||||||
this->getVersion().toVString()
|
|
||||||
)
|
|
||||||
});
|
|
||||||
log::error("Unsupported Geode version: {}", node->getMetadata().getGeodeVersion());
|
|
||||||
m_refreshingModCount -= 1;
|
|
||||||
log::popNest();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -485,7 +475,6 @@ void Loader::Impl::loadModGraph(Mod* node, bool early) {
|
||||||
});
|
});
|
||||||
log::error("Failed to unzip: {}", res.unwrapErr());
|
log::error("Failed to unzip: {}", res.unwrapErr());
|
||||||
m_refreshingModCount -= 1;
|
m_refreshingModCount -= 1;
|
||||||
log::popNest();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
loadFunction();
|
loadFunction();
|
||||||
|
@ -496,7 +485,7 @@ void Loader::Impl::loadModGraph(Mod* node, bool early) {
|
||||||
thread::setName("Mod Unzip");
|
thread::setName("Mod Unzip");
|
||||||
log::loadNest(nest);
|
log::loadNest(nest);
|
||||||
auto res = unzipFunction();
|
auto res = unzipFunction();
|
||||||
this->queueInMainThread([=, this]() {
|
this->queueInMainThread([=, this, res = std::move(res)]() {
|
||||||
auto prevNest = log::saveNest();
|
auto prevNest = log::saveNest();
|
||||||
log::loadNest(nest);
|
log::loadNest(nest);
|
||||||
if (!res) {
|
if (!res) {
|
||||||
|
@ -515,7 +504,6 @@ void Loader::Impl::loadModGraph(Mod* node, bool early) {
|
||||||
});
|
});
|
||||||
}).detach();
|
}).detach();
|
||||||
}
|
}
|
||||||
log::popNest();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Loader::Impl::findProblems() {
|
void Loader::Impl::findProblems() {
|
||||||
|
@ -524,8 +512,12 @@ void Loader::Impl::findProblems() {
|
||||||
log::debug("{} is not enabled", id);
|
log::debug("{} is not enabled", id);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (mod->targetsOutdatedVersion()) {
|
||||||
|
log::debug("{} is outdated", id);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
log::debug("{}", id);
|
log::debug("{}", id);
|
||||||
log::pushNest();
|
log::NestScope nest;
|
||||||
|
|
||||||
for (auto const& dep : mod->getMetadata().getDependencies()) {
|
for (auto const& dep : mod->getMetadata().getDependencies()) {
|
||||||
if (dep.mod && dep.mod->isEnabled() && dep.version.compare(dep.mod->getVersion()))
|
if (dep.mod && dep.mod->isEnabled() && dep.version.compare(dep.mod->getVersion()))
|
||||||
|
@ -652,18 +644,15 @@ void Loader::Impl::findProblems() {
|
||||||
});
|
});
|
||||||
log::error("{} failed to load for an unknown reason", id);
|
log::error("{} failed to load for an unknown reason", id);
|
||||||
}
|
}
|
||||||
|
|
||||||
log::popNest();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Loader::Impl::refreshModGraph() {
|
void Loader::Impl::refreshModGraph() {
|
||||||
log::info("Refreshing mod graph");
|
log::info("Refreshing mod graph");
|
||||||
log::pushNest();
|
log::NestScope nest;
|
||||||
|
|
||||||
if (m_isSetup) {
|
if (m_isSetup) {
|
||||||
log::error("Cannot refresh mod graph after startup");
|
log::error("Cannot refresh mod graph after startup");
|
||||||
log::popNest();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -673,48 +662,51 @@ void Loader::Impl::refreshModGraph() {
|
||||||
|
|
||||||
m_loadingState = LoadingState::Queue;
|
m_loadingState = LoadingState::Queue;
|
||||||
log::debug("Queueing mods");
|
log::debug("Queueing mods");
|
||||||
log::pushNest();
|
|
||||||
std::vector<ModMetadata> modQueue;
|
std::vector<ModMetadata> modQueue;
|
||||||
this->queueMods(modQueue);
|
{
|
||||||
log::popNest();
|
log::NestScope nest;
|
||||||
|
this->queueMods(modQueue);
|
||||||
|
}
|
||||||
|
|
||||||
m_loadingState = LoadingState::List;
|
m_loadingState = LoadingState::List;
|
||||||
log::debug("Populating mod list");
|
log::debug("Populating mod list");
|
||||||
log::pushNest();
|
{
|
||||||
this->populateModList(modQueue);
|
log::NestScope nest;
|
||||||
modQueue.clear();
|
this->populateModList(modQueue);
|
||||||
log::popNest();
|
modQueue.clear();
|
||||||
|
}
|
||||||
|
|
||||||
m_loadingState = LoadingState::Graph;
|
m_loadingState = LoadingState::Graph;
|
||||||
log::debug("Building mod graph");
|
log::debug("Building mod graph");
|
||||||
log::pushNest();
|
{
|
||||||
this->buildModGraph();
|
log::NestScope nest;
|
||||||
log::popNest();
|
this->buildModGraph();
|
||||||
|
}
|
||||||
|
|
||||||
log::debug("Ordering mod stack");
|
log::debug("Ordering mod stack");
|
||||||
log::pushNest();
|
{
|
||||||
this->orderModStack();
|
log::NestScope nest;
|
||||||
log::popNest();
|
this->orderModStack();
|
||||||
|
}
|
||||||
|
|
||||||
m_loadingState = LoadingState::EarlyMods;
|
m_loadingState = LoadingState::EarlyMods;
|
||||||
log::debug("Loading early mods");
|
log::debug("Loading early mods");
|
||||||
log::pushNest();
|
{
|
||||||
while (!m_modsToLoad.empty() && m_modsToLoad.front()->needsEarlyLoad()) {
|
log::NestScope nest;
|
||||||
auto mod = m_modsToLoad.front();
|
while (!m_modsToLoad.empty() && m_modsToLoad.front()->needsEarlyLoad()) {
|
||||||
m_modsToLoad.pop_front();
|
auto mod = m_modsToLoad.front();
|
||||||
this->loadModGraph(mod, true);
|
m_modsToLoad.pop_front();
|
||||||
|
this->loadModGraph(mod, true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
log::popNest();
|
|
||||||
|
|
||||||
auto end = std::chrono::high_resolution_clock::now();
|
auto end = std::chrono::high_resolution_clock::now();
|
||||||
auto time = std::chrono::duration_cast<std::chrono::milliseconds>(end - begin).count();
|
auto time = std::chrono::duration_cast<std::chrono::milliseconds>(end - begin).count();
|
||||||
log::info("Took {}s. Continuing next frame...", static_cast<float>(time) / 1000.f);
|
log::info("Took {}s. Continuing next frame...", static_cast<float>(time) / 1000.f);
|
||||||
|
|
||||||
log::popNest();
|
|
||||||
|
|
||||||
m_loadingState = LoadingState::Mods;
|
m_loadingState = LoadingState::Mods;
|
||||||
|
|
||||||
queueInMainThread([&]() {
|
queueInMainThread([this]() {
|
||||||
this->continueRefreshModGraph();
|
this->continueRefreshModGraph();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -768,7 +760,7 @@ void Loader::Impl::orderModStack() {
|
||||||
|
|
||||||
void Loader::Impl::continueRefreshModGraph() {
|
void Loader::Impl::continueRefreshModGraph() {
|
||||||
if (m_refreshingModCount != 0) {
|
if (m_refreshingModCount != 0) {
|
||||||
queueInMainThread([&]() {
|
queueInMainThread([this]() {
|
||||||
this->continueRefreshModGraph();
|
this->continueRefreshModGraph();
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
|
@ -781,28 +773,27 @@ void Loader::Impl::continueRefreshModGraph() {
|
||||||
}
|
}
|
||||||
|
|
||||||
log::info("Continuing mod graph refresh...");
|
log::info("Continuing mod graph refresh...");
|
||||||
log::pushNest();
|
log::NestScope nest;
|
||||||
|
|
||||||
m_timerBegin = std::chrono::high_resolution_clock::now();
|
m_timerBegin = std::chrono::high_resolution_clock::now();
|
||||||
|
|
||||||
switch (m_loadingState) {
|
switch (m_loadingState) {
|
||||||
case LoadingState::Mods:
|
case LoadingState::Mods:
|
||||||
if (!m_modsToLoad.empty()) {
|
if (!m_modsToLoad.empty()) {
|
||||||
log::debug("Loading mods");
|
|
||||||
log::pushNest();
|
|
||||||
auto mod = m_modsToLoad.front();
|
auto mod = m_modsToLoad.front();
|
||||||
m_modsToLoad.pop_front();
|
m_modsToLoad.pop_front();
|
||||||
|
log::debug("Loading mod {} {}", mod->getID(), mod->getVersion());
|
||||||
this->loadModGraph(mod, false);
|
this->loadModGraph(mod, false);
|
||||||
log::popNest();
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
m_loadingState = LoadingState::Problems;
|
m_loadingState = LoadingState::Problems;
|
||||||
[[fallthrough]];
|
[[fallthrough]];
|
||||||
case LoadingState::Problems:
|
case LoadingState::Problems:
|
||||||
log::debug("Finding problems");
|
log::debug("Finding problems");
|
||||||
log::pushNest();
|
{
|
||||||
this->findProblems();
|
log::NestScope nest;
|
||||||
log::popNest();
|
this->findProblems();
|
||||||
|
}
|
||||||
m_loadingState = LoadingState::Done;
|
m_loadingState = LoadingState::Done;
|
||||||
{
|
{
|
||||||
auto end = std::chrono::high_resolution_clock::now();
|
auto end = std::chrono::high_resolution_clock::now();
|
||||||
|
@ -818,12 +809,10 @@ void Loader::Impl::continueRefreshModGraph() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_loadingState != LoadingState::Done) {
|
if (m_loadingState != LoadingState::Done) {
|
||||||
queueInMainThread([&]() {
|
queueInMainThread([this]() {
|
||||||
this->continueRefreshModGraph();
|
this->continueRefreshModGraph();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
log::popNest();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<LoadProblem> Loader::Impl::getProblems() const {
|
std::vector<LoadProblem> Loader::Impl::getProblems() const {
|
||||||
|
@ -927,11 +916,11 @@ std::vector<std::string> Loader::Impl::getLaunchArgumentNames() const {
|
||||||
return map::keys(m_launchArgs);
|
return map::keys(m_launchArgs);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Loader::Impl::hasLaunchArgument(std::string_view const name) const {
|
bool Loader::Impl::hasLaunchArgument(std::string_view name) const {
|
||||||
return m_launchArgs.find(std::string(name)) != m_launchArgs.end();
|
return m_launchArgs.find(std::string(name)) != m_launchArgs.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<std::string> Loader::Impl::getLaunchArgument(std::string_view const name) const {
|
std::optional<std::string> Loader::Impl::getLaunchArgument(std::string_view name) const {
|
||||||
auto value = m_launchArgs.find(std::string(name));
|
auto value = m_launchArgs.find(std::string(name));
|
||||||
if (value == m_launchArgs.end()) {
|
if (value == m_launchArgs.end()) {
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
|
@ -939,7 +928,7 @@ std::optional<std::string> Loader::Impl::getLaunchArgument(std::string_view cons
|
||||||
return std::optional(value->second);
|
return std::optional(value->second);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Loader::Impl::getLaunchFlag(std::string_view const name) const {
|
bool Loader::Impl::getLaunchFlag(std::string_view name) const {
|
||||||
auto arg = this->getLaunchArgument(name);
|
auto arg = this->getLaunchArgument(name);
|
||||||
return arg.has_value() && arg.value() == "true";
|
return arg.has_value() && arg.value() == "true";
|
||||||
}
|
}
|
||||||
|
@ -966,4 +955,171 @@ bool Loader::Impl::isSafeMode() const {
|
||||||
|
|
||||||
void Loader::Impl::forceSafeMode() {
|
void Loader::Impl::forceSafeMode() {
|
||||||
m_forceSafeMode = true;
|
m_forceSafeMode = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Loader::Impl::installModManuallyFromFile(std::filesystem::path const& path, std::function<void()> after) {
|
||||||
|
auto res = ModMetadata::createFromGeodeFile(path);
|
||||||
|
if (!res) {
|
||||||
|
FLAlertLayer::create(
|
||||||
|
"Invalid File",
|
||||||
|
fmt::format(
|
||||||
|
"The path <cy>'{}'</c> is not a valid Geode mod: {}",
|
||||||
|
path.string(),
|
||||||
|
res.unwrapErr()
|
||||||
|
),
|
||||||
|
"OK"
|
||||||
|
)->show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto meta = res.unwrap();
|
||||||
|
|
||||||
|
auto check = meta.checkTargetVersions();
|
||||||
|
if (!check) {
|
||||||
|
FLAlertLayer::create(
|
||||||
|
"Invalid Mod Version",
|
||||||
|
fmt::format(
|
||||||
|
"The mod <cy>{}</c> can not be installed: {}",
|
||||||
|
meta.getID(),
|
||||||
|
check.unwrapErr()
|
||||||
|
),
|
||||||
|
"OK"
|
||||||
|
)->show();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto doInstallModFromFile = [this, path, meta, after]() {
|
||||||
|
std::error_code ec;
|
||||||
|
|
||||||
|
static size_t MAX_ATTEMPTS = 10;
|
||||||
|
|
||||||
|
// Figure out a free path to install to
|
||||||
|
auto installTo = dirs::getModsDir() / fmt::format("{}.geode", meta.getID());
|
||||||
|
size_t counter = 0;
|
||||||
|
while (std::filesystem::exists(installTo, ec) && counter < MAX_ATTEMPTS) {
|
||||||
|
installTo = dirs::getModsDir() / fmt::format("{}-{}.geode", meta.getID(), counter);
|
||||||
|
counter += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is incredibly unlikely but theoretically possible
|
||||||
|
if (counter >= MAX_ATTEMPTS) {
|
||||||
|
FLAlertLayer::create(
|
||||||
|
"Unable to Install",
|
||||||
|
fmt::format(
|
||||||
|
"Unable to install mod <co>{}</c>: Can't find a free filename!",
|
||||||
|
meta.getID()
|
||||||
|
),
|
||||||
|
"OK"
|
||||||
|
)->show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Actually copy the file over to the install directory
|
||||||
|
std::filesystem::copy_file(path, installTo, ec);
|
||||||
|
if (ec) {
|
||||||
|
FLAlertLayer::create(
|
||||||
|
"Unable to Install",
|
||||||
|
fmt::format(
|
||||||
|
"Unable to install mod <co>{}</c>: {} (Error code <cr>{}</c>)",
|
||||||
|
meta.getID(), ec.message(), ec.value()
|
||||||
|
),
|
||||||
|
"OK"
|
||||||
|
)->show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mark an updated mod as updated or add to the mods list
|
||||||
|
if (m_mods.contains(meta.getID())) {
|
||||||
|
m_mods.at(meta.getID())->m_impl->m_requestedAction = ModRequestedAction::Update;
|
||||||
|
}
|
||||||
|
// Otherwise add a new Mod
|
||||||
|
// This should be safe as all of the scary stuff in setup() is only relevant
|
||||||
|
// for mods that are actually running
|
||||||
|
else {
|
||||||
|
auto mod = new Mod(meta);
|
||||||
|
auto res = mod->m_impl->setup();
|
||||||
|
if (!res) {
|
||||||
|
log::error("Unable to set up manually installed mod: {}", res.unwrapErr());
|
||||||
|
}
|
||||||
|
(void)mod->enable();
|
||||||
|
m_mods.insert({ meta.getID(), mod });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (after) after();
|
||||||
|
|
||||||
|
// No need for the user to go and manually clean up the file
|
||||||
|
createQuickPopup(
|
||||||
|
"Mod Installed",
|
||||||
|
fmt::format(
|
||||||
|
"Mod <co>{}</c> has been successfully installed from file! "
|
||||||
|
"<cy>Do you want to delete the original file?</c>",
|
||||||
|
meta.getName()
|
||||||
|
),
|
||||||
|
"OK", "Delete File",
|
||||||
|
[path](auto, bool btn2) {
|
||||||
|
if (btn2) {
|
||||||
|
std::error_code ec;
|
||||||
|
std::filesystem::remove(path, ec);
|
||||||
|
if (ec) {
|
||||||
|
FLAlertLayer::create(
|
||||||
|
"Unable to Delete",
|
||||||
|
fmt::format(
|
||||||
|
"Unable to delete <cy>{}</c>: {} (Error code <cr>{}</c>)",
|
||||||
|
path, ec.message(), ec.value()
|
||||||
|
),
|
||||||
|
"OK"
|
||||||
|
)->show();
|
||||||
|
}
|
||||||
|
// No need to show a confirmation popup if successful since that's
|
||||||
|
// to be assumed via pressing the button on the previous popup
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (auto existing = Loader::get()->getInstalledMod(meta.getID())) {
|
||||||
|
createQuickPopup(
|
||||||
|
"Already Installed",
|
||||||
|
fmt::format(
|
||||||
|
"The mod <cy>{}</c> <cj>{}</c> has already been installed "
|
||||||
|
"as version <cl>{}</c>. Do you want to <co>replace the "
|
||||||
|
"installed version with the file</c>?",
|
||||||
|
meta.getID(), meta.getVersion(),
|
||||||
|
existing->getVersion()
|
||||||
|
),
|
||||||
|
"Cancel", "Replace",
|
||||||
|
[doInstallModFromFile, path, existing, meta](auto, bool btn2) mutable {
|
||||||
|
std::error_code ec;
|
||||||
|
std::filesystem::remove(existing->getPackagePath(), ec);
|
||||||
|
if (ec) {
|
||||||
|
FLAlertLayer::create(
|
||||||
|
"Unable to Uninstall",
|
||||||
|
fmt::format(
|
||||||
|
"Unable to uninstall <cy>{}</c>: {} (Error code <cr>{}</c>)",
|
||||||
|
existing->getID(), ec.message(), ec.value()
|
||||||
|
),
|
||||||
|
"OK"
|
||||||
|
)->show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
doInstallModFromFile();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
doInstallModFromFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Loader::Impl::isRestartRequired() const {
|
||||||
|
for (auto mod : Loader::get()->getAllMods()) {
|
||||||
|
if (mod->getRequestedAction() != ModRequestedAction::None) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (ModSettingsManager::from(mod)->restartRequired()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (server::ModDownloadManager::get()->wantsRestart()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|