Compare commits

...

133 commits

Author SHA1 Message Date
Justin
e32f38929e
this is driving me nuts 2024-11-18 20:09:44 -05:00
Justin
7e760b192b
this thing 2024-11-18 20:06:35 -05:00
Justin
00fa3b6236
how's this 2024-11-18 20:02:16 -05:00
Justin
1f3b2145e1
i love abi breaks 2024-11-18 19:56:40 -05:00
Justin
418b971da4
targetsOutdatedVersion 2024-11-18 19:31:03 -05:00
Justin
00ab705154
Merge branch 'main' into copy-mods 2024-11-18 19:28:08 -05:00
HJfod
1ff24f09c6 finish grid view 2024-11-19 00:18:23 +02:00
HJfod
6e86b38990 fix vv version 2024-11-18 21:31:17 +02:00
HJfod
7bcf50da57 grid view in mod list; some minor stuff left to do
Some checks failed
Build Binaries / Build Windows (push) Has been cancelled
Build Binaries / Build macOS (push) Has been cancelled
Build Binaries / Build Android (64-bit) (push) Has been cancelled
Build Binaries / Build Android (32-bit) (push) Has been cancelled
Build Binaries / Publish (push) Has been cancelled
2024-11-17 23:35:39 +02:00
matcool
38f3385c90 add safe mode tip text to windows crashlog window 2024-11-17 15:14:34 -03:00
dankmeme01
4d5e465ade fix utils::string::replaceIP hanging if filter is empty 2024-11-17 18:13:04 +01:00
HJfod
6e11d0a6b0 Merge branch 'main' of https://github.com/geode-sdk/geode into main
Some checks are pending
Build Binaries / Build Windows (push) Waiting to run
Build Binaries / Build macOS (push) Waiting to run
Build Binaries / Build Android (64-bit) (push) Waiting to run
Build Binaries / Build Android (32-bit) (push) Waiting to run
Build Binaries / Publish (push) Blocked by required conditions
2024-11-16 18:37:32 +02:00
HJfod
c94a533d1c fix updating causing the ui to lag out like hell 2024-11-16 18:36:45 +02:00
HJfod
b9fb2f6778 fix CCCallFuncExt 2024-11-16 18:36:34 +02:00
acaruso-xx
3fa91241aa
Add missing CCHttpRequest members and member functions (#1161) 2024-11-16 16:37:49 +01:00
HJfod
302eea1f47 disable enable button on outdated mods 2024-11-16 16:22:03 +02:00
Rod
06eb32310c
Changed PortuguesePT to Portuguese (#1160)
* Added Portuguese Portuguese

* sry i forgot this

* idk

* fixed bug

* fix

* fixed portuguese

* removed portuguese extra

---------

Co-authored-by: OmgRod <rlimafonseca2010@gmail.com>
Co-authored-by: Cvolton <cvolton@cvolton.eu>
2024-11-16 14:14:12 +01:00
Rod
a7f15bcc01
Added pt-pt (#1158)
Some checks are pending
Build Binaries / Build Windows (push) Waiting to run
Build Binaries / Build macOS (push) Waiting to run
Build Binaries / Build Android (64-bit) (push) Waiting to run
Build Binaries / Build Android (32-bit) (push) Waiting to run
Build Binaries / Publish (push) Blocked by required conditions
* Added Portuguese Portuguese

* sry i forgot this

---------

Co-authored-by: OmgRod <rlimafonseca2010@gmail.com>
2024-11-16 13:21:57 +01:00
altalk23
5919c7e09a i love tab! 2024-11-16 15:04:25 +03:00
altalk23
17bf7722c9 fix before/after priority stuff 2024-11-16 14:53:21 +03:00
altalk23
22210956bb fix nest issues on errors 2024-11-16 13:01:22 +03:00
Cvolton
d6f0c597f1
add GEODE_DESKTOP GEODE_MOBILE macros
Some checks are pending
Build Binaries / Build Windows (push) Waiting to run
Build Binaries / Build macOS (push) Waiting to run
Build Binaries / Build Android (64-bit) (push) Waiting to run
Build Binaries / Build Android (32-bit) (push) Waiting to run
Build Binaries / Publish (push) Blocked by required conditions
2024-11-16 00:19:08 +01:00
HJfod
f6c23220d8 fix VersionInfo toJSON 2024-11-16 01:10:53 +02:00
matcool
9fe3d133e9 beta.1 perhaps
Some checks failed
Build Binaries / Build Windows (push) Waiting to run
Build Binaries / Build macOS (push) Waiting to run
Build Binaries / Build Android (64-bit) (push) Waiting to run
Build Binaries / Build Android (32-bit) (push) Waiting to run
Build Binaries / Publish (push) Blocked by required conditions
Check CHANGELOG.md / Check CHANGELOG.md (push) Has been cancelled
2024-11-15 17:15:17 -03:00
matcool
3081164600 only send delegate update on closing ColorPickPopup 2024-11-15 16:45:06 -03:00
matcool
bebc7b4074 use fmtlib instead of sstream in numToString and intToHex 2024-11-15 16:43:36 -03:00
matcool
5645399a9f allow building loader in debug mode on windows 2024-11-15 15:38:14 -03:00
matcool
1a201e1d65 pimpl ColorPickPopup 2024-11-15 15:27:22 -03:00
matcool
5592ef68c3 disable forward compat msgbox as it confuses users 2024-11-15 14:55:34 -03:00
matcool
da92090108 change ModSettingsManager::save return type 2024-11-15 14:55:34 -03:00
B1rtek
64d9a289a3
Web support for multiple headers with same name (#1150)
Used for Set-Cookie, for example
2024-11-15 12:40:11 -03:00
matcool
fb504cbf83 theres no need for gdstring.lib since 2.206 2024-11-15 11:58:33 -03:00
matcool
9834cb2a22 fix dangling string crash on modpopup 2024-11-15 11:56:46 -03:00
slideglide
ecec11fa93
Fix every "succes" spelling mistakes I could find (#1151)
* Fix spelling mistakes

* Fix spelling mistake

* Fix spelling mistakes

* Fix spelling mistake

* Fix spelling mistake
2024-11-15 11:53:48 +01:00
matcool
0e8d4c60bc fix nest issue
Some checks are pending
Build Binaries / Build Windows (push) Waiting to run
Build Binaries / Build macOS (push) Waiting to run
Build Binaries / Build Android (64-bit) (push) Waiting to run
Build Binaries / Build Android (32-bit) (push) Waiting to run
Build Binaries / Publish (push) Blocked by required conditions
Check CHANGELOG.md / Check CHANGELOG.md (push) Waiting to run
2024-11-14 20:37:40 -03:00
matcool
81cd8d1bca add back warning for outdated fix 2024-11-14 20:37:40 -03:00
Fleeym
48cad613c7
Add changes from v3.9.2 (#1153)
* fix: fix mod downloads not checking version (#1149)

* bump version to 3.9.1

* fix(index): don't remove gd and geode params (#1152)

* chore: version bump

---------

Co-authored-by: Cvolton <clubpenguinmisa@gmail.com>
2024-11-15 01:22:51 +02:00
mat
f9fed578aa
update matjson to 3.1.0 2024-11-14 14:49:08 -03:00
HJfod
13b30e591a pre-emptively update changelog
Some checks are pending
Build Binaries / Build Windows (push) Waiting to run
Build Binaries / Build macOS (push) Waiting to run
Build Binaries / Build Android (64-bit) (push) Waiting to run
Build Binaries / Build Android (32-bit) (push) Waiting to run
Build Binaries / Publish (push) Blocked by required conditions
Check CHANGELOG.md / Check CHANGELOG.md (push) Waiting to run
2024-11-14 16:42:43 +02:00
HJfod
893b03e313 use tag display names from server 2024-11-14 16:37:07 +02:00
HJfod
7c4e06d20c Merge branch 'v4' into main 2024-11-14 15:58:26 +02:00
HJfod
e881dc5ef2 manually installing mods from files button 2024-11-14 15:57:11 +02:00
Fleeym
280b6efa94
fix: fix mod downloads not checking version (#1148)
Some checks are pending
Build Binaries / Build Windows (push) Waiting to run
Build Binaries / Build macOS (push) Waiting to run
Build Binaries / Build Android (64-bit) (push) Waiting to run
Build Binaries / Build Android (32-bit) (push) Waiting to run
Build Binaries / Publish (push) Blocked by required conditions
2024-11-14 02:26:40 +02:00
Fleeym
01807fedc9 fix(ModSource): fix bad unwrap in fetchValidTags
Some checks are pending
Build Binaries / Build Windows (push) Waiting to run
Build Binaries / Build macOS (push) Waiting to run
Build Binaries / Build Android (64-bit) (push) Waiting to run
Build Binaries / Build Android (32-bit) (push) Waiting to run
Build Binaries / Publish (push) Blocked by required conditions
Check CHANGELOG.md / Check CHANGELOG.md (push) Waiting to run
2024-11-13 23:19:07 +02:00
matcool
22a11b96e2 fix wrong type in Task::chain impl 2024-11-13 16:20:50 -03:00
Fleeym
6679a690a2 fix(ModItem): hide outdated label when updating 2024-11-13 19:48:47 +02:00
mat
ebd4c920f5
add placeholder link for new hook priority system
lets not forget to write docs for this..
2024-11-13 13:38:10 -03:00
dankmeme01
71f56ef49e add XInputSetState export in proxy loader, fixing certain steam emus
Some checks are pending
Build Binaries / Build Windows (push) Waiting to run
Build Binaries / Build macOS (push) Waiting to run
Build Binaries / Build Android (64-bit) (push) Waiting to run
Build Binaries / Build Android (32-bit) (push) Waiting to run
Build Binaries / Publish (push) Blocked by required conditions
Check CHANGELOG.md / Check CHANGELOG.md (push) Waiting to run
2024-11-13 14:59:12 +01:00
mat
1af10eea7f
add check for gd version in DynamicCastFix.cpp
prevents silently breaking macos
2024-11-13 10:41:13 -03:00
mat
37fb2a3ddf
missing commit hash on changelog 2024-11-13 10:06:00 -03:00
Chloe
290e0e0a34
update dynamic cast addresses 2024-11-13 02:38:00 -07:00
Chloe
a4f8295784
detect 2.207 hotfix versions on macos
Some checks are pending
Build Binaries / Build Windows (push) Waiting to run
Build Binaries / Build macOS (push) Waiting to run
Build Binaries / Build Android (64-bit) (push) Waiting to run
Build Binaries / Build Android (32-bit) (push) Waiting to run
Build Binaries / Publish (push) Blocked by required conditions
Check CHANGELOG.md / Check CHANGELOG.md (push) Waiting to run
2024-11-12 23:36:38 -07:00
matcool
9bebb9819c add link to task docs 2024-11-13 01:53:30 -03:00
matcool
6765bbbc75 add wip changelog 2024-11-13 01:26:48 -03:00
matcool
6dbe289a7a Revert "disable macos build for now"
This reverts commit 708e6dc7f2.
2024-11-12 23:47:25 -03:00
mat
47f0b555f3
Merge pull request #1144 from geode-sdk/v4
Geode 4.0.0
2024-11-12 21:13:07 -03:00
mat
708e6dc7f2
disable macos build for now
Some checks failed
Build Binaries / Build Windows (push) Has been cancelled
Build Binaries / Build macOS (push) Has been cancelled
Build Binaries / Build Android (64-bit) (push) Has been cancelled
Build Binaries / Build Android (32-bit) (push) Has been cancelled
Build Binaries / Publish (push) Has been cancelled
2024-11-12 20:17:07 -03:00
Fleeym
f96ea5e727 fix: write it a bit better
Some checks are pending
Build Binaries / Build Windows (push) Waiting to run
Build Binaries / Build macOS (push) Waiting to run
Build Binaries / Build Android (64-bit) (push) Waiting to run
Build Binaries / Build Android (32-bit) (push) Waiting to run
Build Binaries / Publish (push) Blocked by required conditions
2024-11-13 00:24:01 +02:00
Fleeym
0b2fc66a89 fix: don't show outdated if wantsRestart 2024-11-13 00:20:49 +02:00
Fleeym
df2528c8a5 fix: fix ModItem only displaying outdated GD 2024-11-13 00:11:01 +02:00
Fleeym
03a4387c80 bump version to v4.0.0-alpha.1 2024-11-12 23:47:56 +02:00
Fleeym
24294c7361
Merge pull request #1145 from STGamer24YT/main
Some checks are pending
Build Binaries / Build Windows (push) Waiting to run
Build Binaries / Build macOS (push) Waiting to run
Build Binaries / Build Android (64-bit) (push) Waiting to run
Build Binaries / Build Android (32-bit) (push) Waiting to run
Build Binaries / Publish (push) Blocked by required conditions
Translate english text in SpanishInternationalExtra.nsh
2024-11-12 23:41:20 +02:00
STGamer24YT
74a70112a4
Translate english text in SpanishInternationalExtra.nsh
Some text is in english, and I decided to translate it. Hopefully I did it right and this text can be used in the installer.
2024-11-12 17:34:27 -04:00
HJfod
5c85b3c48d Merge branch 'v4' of https://github.com/geode-sdk/geode into v4 2024-11-12 23:05:10 +02:00
HJfod
123b3abff3 somes settings UI fixes 2024-11-12 23:05:03 +02:00
Cvolton
6d13f7837d
fix outdated geode error swap 2024-11-12 21:12:05 +01:00
Cvolton
9b95301472
also show the loading circle on the former progress bar condition 2024-11-12 20:37:47 +01:00
Cvolton
02845d967e
disable the modlist slider 2024-11-12 20:35:01 +01:00
Cvolton
2d66279243
limit geode mod list error to the frame 2024-11-12 20:17:01 +01:00
Cvolton
b662aac29d
update messageboxfix to 2.2074 2024-11-12 19:56:15 +01:00
matcool
c197e2913c update libraries 2024-11-12 14:39:08 -03:00
matcool
ab196b9adf allow sending progress from Task coroutines by using co_yield 2024-11-12 12:23:50 -03:00
Oleksandr Nemesh
0ee9aebdee
add 2.2074 timestamp (#1143)
Some checks are pending
Build Binaries / Build Windows (push) Waiting to run
Build Binaries / Build macOS (push) Waiting to run
Build Binaries / Build Android (64-bit) (push) Waiting to run
Build Binaries / Build Android (32-bit) (push) Waiting to run
Build Binaries / Publish (push) Blocked by required conditions
2024-11-12 12:53:15 +01:00
matcool
00cc510d5b fix typename 2024-11-12 02:41:35 -03:00
matcool
e61b2c0595 co_await support for geode Task
see comment at the bottom of the header for more information
2024-11-12 02:32:05 -03:00
HJfod
acad3d2a8d Merge branch 'v4' of https://github.com/geode-sdk/geode into v4
Some checks are pending
Build Binaries / Build Windows (push) Waiting to run
Build Binaries / Build macOS (push) Waiting to run
Build Binaries / Build Android (64-bit) (push) Waiting to run
Build Binaries / Build Android (32-bit) (push) Waiting to run
Build Binaries / Publish (push) Blocked by required conditions
2024-11-12 01:03:37 +02:00
HJfod
09fa872781 show outdated mods in the UI + make outdated Geode ver count for that too 2024-11-12 01:03:19 +02:00
Chloe
d415c949d0
add android gameversion to mappings 2024-11-11 15:53:26 -07:00
Chloe
c9e97af18a
disable forward compat mode on android 2024-11-11 15:53:02 -07:00
Chloe
9d6b2954e5
ok there 2024-11-11 15:21:34 -07:00
Chloe
bcb856a302
update android binaries 2024-11-11 15:14:32 -07:00
Cvolton
f5f336532f
fix the stupid misaligned controller button 2024-11-11 21:36:39 +01:00
matcool
324883140a add Task::chain 2024-11-11 16:43:38 -03:00
altalk23
b1ab3eb373 thank you mat 2024-11-11 22:43:04 +03:00
altalk23
6db3084062 priorities production ready (not tested) 2024-11-11 22:40:30 +03:00
altalk23
673317d3cb hook priority changes with pre/post and Priority class for static priority values 2024-11-11 22:38:03 +03:00
altalk23
9c1f48ee64 update tuliphook take 2 2024-11-11 21:37:25 +03:00
altalk23
2940de38bc update tuliphook 2024-11-11 21:33:34 +03:00
altalk23
0c469b98fe update json and result 2024-11-11 21:25:06 +03:00
matcool
4ba8751c68 update json to 3.0.1
Some checks are pending
Build Binaries / Build Windows (push) Waiting to run
Build Binaries / Build macOS (push) Waiting to run
Build Binaries / Build Android (64-bit) (push) Waiting to run
Build Binaries / Build Android (32-bit) (push) Waiting to run
Build Binaries / Publish (push) Blocked by required conditions
2024-11-10 19:28:19 -03:00
HJfod
a3beed16f5 silly alk revert 2024-11-11 00:25:16 +02:00
Cvolton
038788bf57
fix safe mode dialog randomly appearing 2024-11-10 20:17:44 +01:00
dankmeme01
76db1268bf change some const string refs to string views and change getID to return a ref 2024-11-10 17:47:09 +01:00
dankmeme01
084fea220e code cleanup: remove unnecessary const in function parameters
Some checks are pending
Build Binaries / Build Windows (push) Waiting to run
Build Binaries / Build macOS (push) Waiting to run
Build Binaries / Build Android (64-bit) (push) Waiting to run
Build Binaries / Build Android (32-bit) (push) Waiting to run
Build Binaries / Publish (push) Blocked by required conditions
2024-11-10 16:05:20 +01:00
matcool
deab3d2517 remove GEODE_STATIC_PTR and GEODE_STATIC_VAR 2024-11-10 10:38:59 -03:00
matcool
7b171c56c9 avoid collision with result's geode_concat macro 2024-11-10 10:14:46 -03:00
HJfod
12e8bbb6a2 split problems into load problems and outdated GD version 2024-11-10 12:55:56 +02:00
HJfod
c9afa75367 move mod list arrows a bit to not overlap 2024-11-10 12:20:55 +02:00
HJfod
cad670fb31 hide search toggle 2024-11-10 12:20:44 +02:00
HJfod
efb1fbf729 add option for single-page local mods list 2024-11-10 12:17:14 +02:00
HJfod
a3b306a4e0 turns out that path was case-insensitive now... 2024-11-10 11:59:14 +02:00
HJfod
9d4e6ba0e4 header fixes (i think this might make compiling without PCH possible?) 2024-11-10 11:51:49 +02:00
matcool
be9ba27ba4 only set gd version to 2.207 on desktop
Some checks are pending
Build Binaries / Build Windows (push) Waiting to run
Build Binaries / Build macOS (push) Waiting to run
Build Binaries / Build Android (64-bit) (push) Waiting to run
Build Binaries / Build Android (32-bit) (push) Waiting to run
Build Binaries / Publish (push) Blocked by required conditions
2024-11-10 01:28:57 -03:00
matcool
2b0970d341 bump version 2024-11-10 01:26:25 -03:00
matcool
4f837a0f8b Merge remote-tracking branch 'origin/2207' into v4 2024-11-10 00:43:28 -03:00
matcool
5b10a91d8c do a few v4 todos 2024-11-10 00:43:06 -03:00
matcool
31d7c9d099 fix mod descriptions (nasty bug)
Some checks are pending
Build Binaries / Build Windows (push) Waiting to run
Build Binaries / Build macOS (push) Waiting to run
Build Binaries / Build Android (64-bit) (push) Waiting to run
Build Binaries / Build Android (32-bit) (push) Waiting to run
Build Binaries / Publish (push) Blocked by required conditions
this is a very bad issue with the result library :( alk pls fix
2024-11-09 18:34:12 -03:00
Chloe
70be7c6061
how much will this break 2024-11-09 14:33:03 -07:00
Chloe
5eda75311d
fix macos build 2024-11-09 14:27:24 -07:00
matcool
13b7cfc488 actually now 2024-11-09 16:04:38 -03:00
Justin
39dc184b88
2.207 patch updates (#1140)
Some checks failed
Build Binaries / Build Windows (push) Has been cancelled
Build Binaries / Build macOS (push) Has been cancelled
Build Binaries / Build Android (64-bit) (push) Has been cancelled
Build Binaries / Build Android (32-bit) (push) Has been cancelled
Build Binaries / Publish (push) Has been cancelled
* Update gdTimestampMap.hpp

* Update CMakeLists.txt

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

File diff suppressed because it is too large Load diff

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -2,7 +2,7 @@
#include "../DefaultInclude.hpp" #include "../DefaultInclude.hpp"
#include "../cocos/support/zip_support/ZipUtils.h" #include "../cocos/support/zip_support/ZipUtils.h"
#include "../utils/Result.hpp" #include <Geode/Result.hpp>
#include "../utils/VersionInfo.hpp" #include "../utils/VersionInfo.hpp"
#include "../utils/general.hpp" #include "../utils/general.hpp"
@ -10,7 +10,6 @@
#include "Hook.hpp" #include "Hook.hpp"
#include "ModMetadata.hpp" #include "ModMetadata.hpp"
#include "Setting.hpp" #include "Setting.hpp"
#include "SettingV3.hpp"
#include "Types.hpp" #include "Types.hpp"
#include "Loader.hpp" #include "Loader.hpp"
@ -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;

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

Binary file not shown.

View file

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

View file

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 16 KiB

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -29,6 +29,9 @@
#include <string_view> #include <string_view>
#include <vector> #include <vector>
#include <server/DownloadManager.hpp>
#include <Geode/ui/Popup.hpp>
using namespace geode::prelude; using namespace geode::prelude;
Loader::Impl* LoaderImpl::get() { Loader::Impl* LoaderImpl::get() {
@ -42,6 +45,11 @@ Loader::Impl::~Impl() = default;
// Initialization // Initialization
bool Loader::Impl::isForwardCompatMode() { bool Loader::Impl::isForwardCompatMode() {
#ifdef GEODE_IS_ANDROID
// forward compat mode doesn't really make sense on android
return false;
#endif
if (!m_forwardCompatMode.has_value()) { if (!m_forwardCompatMode.has_value()) {
m_forwardCompatMode = !this->getGameVersion().empty() && m_forwardCompatMode = !this->getGameVersion().empty() &&
this->getGameVersion() != GEODE_STR(GEODE_GD_VERSION); this->getGameVersion() != GEODE_STR(GEODE_GD_VERSION);
@ -73,35 +81,32 @@ Result<> Loader::Impl::setup() {
if (this->supportsLaunchArguments()) { if (this->supportsLaunchArguments()) {
log::debug("Loading launch arguments"); log::debug("Loading launch arguments");
log::pushNest(); log::NestScope nest;
this->initLaunchArguments(); this->initLaunchArguments();
log::popNest();
} }
// on some platforms, using the crash handler overrides more convenient native handlers // on some platforms, using the crash handler overrides more convenient native handlers
if (!this->getLaunchFlag("disable-crash-handler")) { if (!this->getLaunchFlag("disable-crash-handler")) {
log::debug("Setting up crash handler"); log::debug("Setting up crash handler");
log::pushNest(); log::NestScope nest;
if (!crashlog::setupPlatformHandler()) { if (!crashlog::setupPlatformHandler()) {
log::debug("Failed to set up crash handler"); log::debug("Failed to set up crash handler");
} }
log::popNest();
} else { } else {
log::debug("Crash handler setup skipped"); log::debug("Crash handler setup skipped");
} }
log::debug("Loading hooks"); log::debug("Loading hooks");
log::pushNest(); if (log::NestScope nest; !this->loadHooks()) {
if (!this->loadHooks()) {
return Err("There were errors loading some hooks, see console for details"); return Err("There were errors loading some hooks, see console for details");
} }
log::popNest();
log::debug("Setting up directories"); log::debug("Setting up directories");
log::pushNest(); {
this->createDirectories(); log::NestScope nest;
this->addSearchPaths(); this->createDirectories();
log::popNest(); this->addSearchPaths();
}
// Trigger on_mod(Loaded) for the internal mod // Trigger on_mod(Loaded) for the internal mod
// this function is already on the gd thread, so this should be fine // this function is already on the gd thread, so this should be fine
@ -121,7 +126,7 @@ void Loader::Impl::addSearchPaths() {
void Loader::Impl::updateResources(bool forceReload) { void Loader::Impl::updateResources(bool forceReload) {
log::debug("Adding resources"); log::debug("Adding resources");
log::pushNest(); log::NestScope nest;
for (auto const& [_, mod] : m_mods) { for (auto const& [_, mod] : m_mods) {
if (!forceReload && ModImpl::getImpl(mod)->m_resourcesLoaded) if (!forceReload && ModImpl::getImpl(mod)->m_resourcesLoaded)
continue; continue;
@ -132,7 +137,6 @@ void Loader::Impl::updateResources(bool forceReload) {
// we have to call it in both places since setup is only called once ever, but updateResources is called // we have to call it in both places since setup is only called once ever, but updateResources is called
// on every texture reload // on every texture reload
CCFileUtils::get()->updatePaths(); CCFileUtils::get()->updatePaths();
log::popNest();
} }
std::vector<Mod*> Loader::Impl::getAllMods() { std::vector<Mod*> Loader::Impl::getAllMods() {
@ -170,24 +174,22 @@ bool Loader::Impl::isModVersionSupported(VersionInfo const& target) {
void Loader::Impl::saveData() { void Loader::Impl::saveData() {
for (auto& [id, mod] : m_mods) { for (auto& [id, mod] : m_mods) {
log::debug("{}", mod->getID()); log::debug("{}", mod->getID());
log::pushNest(); log::NestScope nest;
auto r = mod->saveData(); auto r = mod->saveData();
if (!r) { if (!r) {
log::warn("Unable to save data for mod \"{}\": {}", mod->getID(), r.unwrapErr()); log::warn("Unable to save data for mod \"{}\": {}", mod->getID(), r.unwrapErr());
} }
log::popNest();
} }
} }
void Loader::Impl::loadData() { void Loader::Impl::loadData() {
for (auto& [_, mod] : m_mods) { for (auto& [_, mod] : m_mods) {
log::debug("{}", mod->getID()); log::debug("{}", mod->getID());
log::pushNest(); log::NestScope nest;
auto r = mod->loadData(); auto r = mod->loadData();
if (!r) { if (!r) {
log::warn("Unable to load data for mod \"{}\": {}", mod->getID(), r.unwrapErr()); log::warn("Unable to load data for mod \"{}\": {}", mod->getID(), r.unwrapErr());
} }
log::popNest();
} }
} }
@ -230,7 +232,7 @@ void Loader::Impl::updateModResources(Mod* mod) {
return; return;
log::debug("{}", mod->getID()); log::debug("{}", mod->getID());
log::pushNest(); log::NestScope nest;
for (auto const& sheet : mod->getMetadata().getSpritesheets()) { for (auto const& sheet : mod->getMetadata().getSpritesheets()) {
log::debug("Adding sheet {}", sheet); log::debug("Adding sheet {}", sheet);
@ -250,8 +252,6 @@ void Loader::Impl::updateModResources(Mod* mod) {
CCSpriteFrameCache::get()->addSpriteFramesWithFile(plist.c_str()); CCSpriteFrameCache::get()->addSpriteFramesWithFile(plist.c_str());
} }
} }
log::popNest();
} }
void Loader::Impl::addProblem(LoadProblem const& problem) { void Loader::Impl::addProblem(LoadProblem const& problem) {
@ -267,14 +267,14 @@ void Loader::Impl::addProblem(LoadProblem const& problem) {
void Loader::Impl::queueMods(std::vector<ModMetadata>& modQueue) { void Loader::Impl::queueMods(std::vector<ModMetadata>& modQueue) {
for (auto const& dir : m_modSearchDirectories) { for (auto const& dir : m_modSearchDirectories) {
log::debug("Searching {}", dir); log::debug("Searching {}", dir);
log::pushNest(); log::NestScope nest;
for (auto const& entry : std::filesystem::directory_iterator(dir)) { for (auto const& entry : std::filesystem::directory_iterator(dir)) {
if (!std::filesystem::is_regular_file(entry) || if (!std::filesystem::is_regular_file(entry) ||
entry.path().extension() != GEODE_MOD_EXTENSION) entry.path().extension() != GEODE_MOD_EXTENSION)
continue; continue;
log::debug("Found {}", entry.path().filename()); log::debug("Found {}", entry.path().filename());
log::pushNest(); log::NestScope nest;
auto res = ModMetadata::createFromGeodeFile(entry.path()); auto res = ModMetadata::createFromGeodeFile(entry.path());
if (!res) { if (!res) {
@ -284,7 +284,6 @@ void Loader::Impl::queueMods(std::vector<ModMetadata>& modQueue) {
res.unwrapErr() res.unwrapErr()
}); });
log::error("Failed to queue: {}", res.unwrapErr()); log::error("Failed to queue: {}", res.unwrapErr());
log::popNest();
continue; continue;
} }
auto modMetadata = res.unwrap(); auto modMetadata = res.unwrap();
@ -302,14 +301,11 @@ void Loader::Impl::queueMods(std::vector<ModMetadata>& modQueue) {
"A mod with the same ID is already present." "A mod with the same ID is already present."
}); });
log::error("Failed to queue: a mod with the same ID is already queued"); log::error("Failed to queue: a mod with the same ID is already queued");
log::popNest();
continue; continue;
} }
modQueue.push_back(modMetadata); modQueue.push_back(modMetadata);
log::popNest();
} }
log::popNest();
} }
} }
@ -327,7 +323,7 @@ void Loader::Impl::populateModList(std::vector<ModMetadata>& modQueue) {
for (auto const& metadata : modQueue) { for (auto const& metadata : modQueue) {
log::debug("{} {}", metadata.getID(), metadata.getVersion()); log::debug("{} {}", metadata.getID(), metadata.getVersion());
log::pushNest(); log::NestScope nest;
auto mod = new Mod(metadata); auto mod = new Mod(metadata);
@ -339,20 +335,17 @@ void Loader::Impl::populateModList(std::vector<ModMetadata>& modQueue) {
res.unwrapErr() res.unwrapErr()
}); });
log::error("Failed to set up: {}", res.unwrapErr()); log::error("Failed to set up: {}", res.unwrapErr());
log::popNest();
continue; continue;
} }
m_mods.insert({metadata.getID(), mod}); m_mods.insert({metadata.getID(), mod});
log::popNest();
} }
} }
void Loader::Impl::buildModGraph() { void Loader::Impl::buildModGraph() {
for (auto const& [id, mod] : m_mods) { for (auto const& [id, mod] : m_mods) {
log::debug("{}", mod->getID()); log::debug("{}", mod->getID());
log::pushNest(); log::NestScope nest;
for (auto& dependency : mod->m_impl->m_metadata.m_impl->m_dependencies) { for (auto& dependency : mod->m_impl->m_metadata.m_impl->m_dependencies) {
log::debug("{}", dependency.id); log::debug("{}", dependency.id);
if (!m_mods.contains(dependency.id)) { if (!m_mods.contains(dependency.id)) {
@ -379,11 +372,38 @@ void Loader::Impl::buildModGraph() {
incompatibility.mod = incompatibility.mod =
m_mods.contains(incompatibility.id) ? m_mods[incompatibility.id] : nullptr; m_mods.contains(incompatibility.id) ? m_mods[incompatibility.id] : nullptr;
} }
log::popNest();
} }
} }
void Loader::Impl::loadModGraph(Mod* node, bool early) { void Loader::Impl::loadModGraph(Mod* node, bool early) {
// Check version first, as it's not worth trying to load a mod with an
// invalid target version
// Also this makes it so that when GD updates, outdated mods get shown as
// "Outdated" in the UI instead of "Missing Dependencies"
auto res = node->getMetadata().checkGameVersion();
if (!res) {
this->addProblem({
LoadProblem::Type::UnsupportedVersion,
node,
res.unwrapErr()
});
log::error("{}", res.unwrapErr());
return;
}
auto geodeVerRes = node->getMetadata().checkGeodeVersion();
if (!geodeVerRes) {
this->addProblem({
node->getMetadata().getGeodeVersion() > this->getVersion() ?
LoadProblem::Type::NeedsNewerGeodeVersion :
LoadProblem::Type::UnsupportedGeodeVersion,
node,
geodeVerRes.unwrapErr()
});
log::error("{}", geodeVerRes.unwrapErr());
return;
}
if (node->hasUnresolvedDependencies()) { if (node->hasUnresolvedDependencies()) {
log::debug("{} {} has unresolved dependencies", node->getID(), node->getVersion()); log::debug("{} {} has unresolved dependencies", node->getID(), node->getVersion());
return; return;
@ -393,12 +413,10 @@ void Loader::Impl::loadModGraph(Mod* node, bool early) {
return; return;
} }
log::debug("{} {}", node->getID(), node->getVersion()); log::NestScope nest;
log::pushNest();
if (node->isEnabled()) { if (node->isEnabled()) {
log::warn("Mod {} already loaded, this should never happen", node->getID()); log::warn("Mod {} already loaded, this should never happen", node->getID());
log::popNest();
return; return;
} }
@ -409,6 +427,7 @@ void Loader::Impl::loadModGraph(Mod* node, bool early) {
auto unzipFunction = [this, node]() { auto unzipFunction = [this, node]() {
log::debug("Unzip"); log::debug("Unzip");
log::NestScope nest;
auto res = node->m_impl->unzipGeodeFile(node->getMetadata()); auto res = node->m_impl->unzipGeodeFile(node->getMetadata());
return res; return res;
}; };
@ -416,6 +435,7 @@ void Loader::Impl::loadModGraph(Mod* node, bool early) {
auto loadFunction = [this, node, early]() { auto loadFunction = [this, node, early]() {
if (node->shouldLoad()) { if (node->shouldLoad()) {
log::debug("Load"); log::debug("Load");
log::NestScope nest;
auto res = node->m_impl->loadBinary(); auto res = node->m_impl->loadBinary();
if (!res) { if (!res) {
this->addProblem({ this->addProblem({
@ -441,36 +461,6 @@ void Loader::Impl::loadModGraph(Mod* node, bool early) {
}); });
log::error("{}", reason.value()); log::error("{}", reason.value());
m_refreshingModCount -= 1; m_refreshingModCount -= 1;
log::popNest();
return;
}
auto res = node->getMetadata().checkGameVersion();
if (!res) {
this->addProblem({
LoadProblem::Type::UnsupportedVersion,
node,
res.unwrapErr()
});
log::error("{}", res.unwrapErr());
m_refreshingModCount -= 1;
log::popNest();
return;
}
if (!this->isModVersionSupported(node->getMetadata().getGeodeVersion())) {
this->addProblem({
node->getMetadata().getGeodeVersion() > this->getVersion() ? LoadProblem::Type::NeedsNewerGeodeVersion : LoadProblem::Type::UnsupportedGeodeVersion,
node,
fmt::format(
"Geode version {}\nis required to run this mod\n(installed: {})",
node->getMetadata().getGeodeVersion().toVString(),
this->getVersion().toVString()
)
});
log::error("Unsupported Geode version: {}", node->getMetadata().getGeodeVersion());
m_refreshingModCount -= 1;
log::popNest();
return; return;
} }
} }
@ -485,7 +475,6 @@ void Loader::Impl::loadModGraph(Mod* node, bool early) {
}); });
log::error("Failed to unzip: {}", res.unwrapErr()); log::error("Failed to unzip: {}", res.unwrapErr());
m_refreshingModCount -= 1; m_refreshingModCount -= 1;
log::popNest();
return; return;
} }
loadFunction(); loadFunction();
@ -496,7 +485,7 @@ void Loader::Impl::loadModGraph(Mod* node, bool early) {
thread::setName("Mod Unzip"); thread::setName("Mod Unzip");
log::loadNest(nest); log::loadNest(nest);
auto res = unzipFunction(); auto res = unzipFunction();
this->queueInMainThread([=, this]() { this->queueInMainThread([=, this, res = std::move(res)]() {
auto prevNest = log::saveNest(); auto prevNest = log::saveNest();
log::loadNest(nest); log::loadNest(nest);
if (!res) { if (!res) {
@ -515,7 +504,6 @@ void Loader::Impl::loadModGraph(Mod* node, bool early) {
}); });
}).detach(); }).detach();
} }
log::popNest();
} }
void Loader::Impl::findProblems() { void Loader::Impl::findProblems() {
@ -524,8 +512,12 @@ void Loader::Impl::findProblems() {
log::debug("{} is not enabled", id); log::debug("{} is not enabled", id);
continue; continue;
} }
if (mod->targetsOutdatedVersion()) {
log::debug("{} is outdated", id);
continue;
}
log::debug("{}", id); log::debug("{}", id);
log::pushNest(); log::NestScope nest;
for (auto const& dep : mod->getMetadata().getDependencies()) { for (auto const& dep : mod->getMetadata().getDependencies()) {
if (dep.mod && dep.mod->isEnabled() && dep.version.compare(dep.mod->getVersion())) if (dep.mod && dep.mod->isEnabled() && dep.version.compare(dep.mod->getVersion()))
@ -652,18 +644,15 @@ void Loader::Impl::findProblems() {
}); });
log::error("{} failed to load for an unknown reason", id); log::error("{} failed to load for an unknown reason", id);
} }
log::popNest();
} }
} }
void Loader::Impl::refreshModGraph() { void Loader::Impl::refreshModGraph() {
log::info("Refreshing mod graph"); log::info("Refreshing mod graph");
log::pushNest(); log::NestScope nest;
if (m_isSetup) { if (m_isSetup) {
log::error("Cannot refresh mod graph after startup"); log::error("Cannot refresh mod graph after startup");
log::popNest();
return; return;
} }
@ -673,48 +662,51 @@ void Loader::Impl::refreshModGraph() {
m_loadingState = LoadingState::Queue; m_loadingState = LoadingState::Queue;
log::debug("Queueing mods"); log::debug("Queueing mods");
log::pushNest();
std::vector<ModMetadata> modQueue; std::vector<ModMetadata> modQueue;
this->queueMods(modQueue); {
log::popNest(); log::NestScope nest;
this->queueMods(modQueue);
}
m_loadingState = LoadingState::List; m_loadingState = LoadingState::List;
log::debug("Populating mod list"); log::debug("Populating mod list");
log::pushNest(); {
this->populateModList(modQueue); log::NestScope nest;
modQueue.clear(); this->populateModList(modQueue);
log::popNest(); modQueue.clear();
}
m_loadingState = LoadingState::Graph; m_loadingState = LoadingState::Graph;
log::debug("Building mod graph"); log::debug("Building mod graph");
log::pushNest(); {
this->buildModGraph(); log::NestScope nest;
log::popNest(); this->buildModGraph();
}
log::debug("Ordering mod stack"); log::debug("Ordering mod stack");
log::pushNest(); {
this->orderModStack(); log::NestScope nest;
log::popNest(); this->orderModStack();
}
m_loadingState = LoadingState::EarlyMods; m_loadingState = LoadingState::EarlyMods;
log::debug("Loading early mods"); log::debug("Loading early mods");
log::pushNest(); {
while (!m_modsToLoad.empty() && m_modsToLoad.front()->needsEarlyLoad()) { log::NestScope nest;
auto mod = m_modsToLoad.front(); while (!m_modsToLoad.empty() && m_modsToLoad.front()->needsEarlyLoad()) {
m_modsToLoad.pop_front(); auto mod = m_modsToLoad.front();
this->loadModGraph(mod, true); m_modsToLoad.pop_front();
this->loadModGraph(mod, true);
}
} }
log::popNest();
auto end = std::chrono::high_resolution_clock::now(); auto end = std::chrono::high_resolution_clock::now();
auto time = std::chrono::duration_cast<std::chrono::milliseconds>(end - begin).count(); auto time = std::chrono::duration_cast<std::chrono::milliseconds>(end - begin).count();
log::info("Took {}s. Continuing next frame...", static_cast<float>(time) / 1000.f); log::info("Took {}s. Continuing next frame...", static_cast<float>(time) / 1000.f);
log::popNest();
m_loadingState = LoadingState::Mods; m_loadingState = LoadingState::Mods;
queueInMainThread([&]() { queueInMainThread([this]() {
this->continueRefreshModGraph(); this->continueRefreshModGraph();
}); });
} }
@ -768,7 +760,7 @@ void Loader::Impl::orderModStack() {
void Loader::Impl::continueRefreshModGraph() { void Loader::Impl::continueRefreshModGraph() {
if (m_refreshingModCount != 0) { if (m_refreshingModCount != 0) {
queueInMainThread([&]() { queueInMainThread([this]() {
this->continueRefreshModGraph(); this->continueRefreshModGraph();
}); });
return; return;
@ -781,28 +773,27 @@ void Loader::Impl::continueRefreshModGraph() {
} }
log::info("Continuing mod graph refresh..."); log::info("Continuing mod graph refresh...");
log::pushNest(); log::NestScope nest;
m_timerBegin = std::chrono::high_resolution_clock::now(); m_timerBegin = std::chrono::high_resolution_clock::now();
switch (m_loadingState) { switch (m_loadingState) {
case LoadingState::Mods: case LoadingState::Mods:
if (!m_modsToLoad.empty()) { if (!m_modsToLoad.empty()) {
log::debug("Loading mods");
log::pushNest();
auto mod = m_modsToLoad.front(); auto mod = m_modsToLoad.front();
m_modsToLoad.pop_front(); m_modsToLoad.pop_front();
log::debug("Loading mod {} {}", mod->getID(), mod->getVersion());
this->loadModGraph(mod, false); this->loadModGraph(mod, false);
log::popNest();
break; break;
} }
m_loadingState = LoadingState::Problems; m_loadingState = LoadingState::Problems;
[[fallthrough]]; [[fallthrough]];
case LoadingState::Problems: case LoadingState::Problems:
log::debug("Finding problems"); log::debug("Finding problems");
log::pushNest(); {
this->findProblems(); log::NestScope nest;
log::popNest(); this->findProblems();
}
m_loadingState = LoadingState::Done; m_loadingState = LoadingState::Done;
{ {
auto end = std::chrono::high_resolution_clock::now(); auto end = std::chrono::high_resolution_clock::now();
@ -818,12 +809,10 @@ void Loader::Impl::continueRefreshModGraph() {
} }
if (m_loadingState != LoadingState::Done) { if (m_loadingState != LoadingState::Done) {
queueInMainThread([&]() { queueInMainThread([this]() {
this->continueRefreshModGraph(); this->continueRefreshModGraph();
}); });
} }
log::popNest();
} }
std::vector<LoadProblem> Loader::Impl::getProblems() const { std::vector<LoadProblem> Loader::Impl::getProblems() const {
@ -927,11 +916,11 @@ std::vector<std::string> Loader::Impl::getLaunchArgumentNames() const {
return map::keys(m_launchArgs); return map::keys(m_launchArgs);
} }
bool Loader::Impl::hasLaunchArgument(std::string_view const name) const { bool Loader::Impl::hasLaunchArgument(std::string_view name) const {
return m_launchArgs.find(std::string(name)) != m_launchArgs.end(); return m_launchArgs.find(std::string(name)) != m_launchArgs.end();
} }
std::optional<std::string> Loader::Impl::getLaunchArgument(std::string_view const name) const { std::optional<std::string> Loader::Impl::getLaunchArgument(std::string_view name) const {
auto value = m_launchArgs.find(std::string(name)); auto value = m_launchArgs.find(std::string(name));
if (value == m_launchArgs.end()) { if (value == m_launchArgs.end()) {
return std::nullopt; return std::nullopt;
@ -939,7 +928,7 @@ std::optional<std::string> Loader::Impl::getLaunchArgument(std::string_view cons
return std::optional(value->second); return std::optional(value->second);
} }
bool Loader::Impl::getLaunchFlag(std::string_view const name) const { bool Loader::Impl::getLaunchFlag(std::string_view name) const {
auto arg = this->getLaunchArgument(name); auto arg = this->getLaunchArgument(name);
return arg.has_value() && arg.value() == "true"; return arg.has_value() && arg.value() == "true";
} }
@ -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;
}

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