diff --git a/.clang-format b/.clang-format index 72ca691..2bf504d 100644 --- a/.clang-format +++ b/.clang-format @@ -1,4 +1,3 @@ ---- Language: Cpp BasedOnStyle: Google AccessModifierOffset: -1 @@ -26,21 +25,24 @@ DerivePointerAlignment: false FixNamespaceComments: false IncludeBlocks: Regroup IncludeCategories: - - Regex: '^' + - Regex: "^<[^.>]+>$" Priority: 2 - SortPriority: 0 + SortPriority: 3 CaseSensitive: false - - Regex: '^<.*\.h>' - Priority: 1 - SortPriority: 0 - CaseSensitive: false - - Regex: "^<.*" + - Regex: '^<.+\.(h|hpp)>$' Priority: 2 - SortPriority: 0 + SortPriority: 4 CaseSensitive: false + - Regex: '^"[A-Z].*\/.+\.hpp"$' + Priority: 5 + CaseSensitive: true + - Regex: "^.+/shared/.+" + Priority: 1 + SortPriority: 2 + CaseSensitive: true - Regex: ".*" - Priority: 3 - SortPriority: 0 + Priority: 1 + SortPriority: 1 CaseSensitive: false IndentExternBlock: Indent IndentRequiresClause: false @@ -51,7 +53,7 @@ LineEnding: LF NamespaceIndentation: All PackConstructorInitializers: CurrentLine PenaltyExcessCharacter: 100 -PenaltyReturnTypeOnItsOwnLine: 0 +PenaltyReturnTypeOnItsOwnLine: 50 QualifierAlignment: Right RequiresClausePosition: OwnLine RequiresExpressionIndentation: OuterScope diff --git a/.clangd b/.clangd index ba4cbb3..284e9cf 100644 --- a/.clangd +++ b/.clangd @@ -1,2 +1,7 @@ Diagnostics: UnusedIncludes: None +--- +If: + PathMatch: ./extern/.* +Index: + Background: Skip diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6e73bb5..eff5763 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -2,13 +2,19 @@ name: Build env: module_id: metacore - qmodName: MetaCore on: workflow_dispatch: push: branches-ignore: - - 'version*' + - "version*" + paths-ignore: + - "**.md" + - "**.yml" + - ".clangd" + - ".clang-format" + - ".gitignore" + - "!.github/workflows/build.yml" pull_request: jobs: @@ -16,69 +22,53 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v5 name: Checkout with: submodules: true lfs: true - - uses: seanmiddleditch/gha-setup-ninja@v3 - - - name: Create ndkpath.txt - run: | - echo "$ANDROID_NDK_LATEST_HOME" > ${GITHUB_WORKSPACE}/ndkpath.txt - cat ${GITHUB_WORKSPACE}/ndkpath.txt - - - name: QPM Action + - name: Setup QPM uses: Fernthedev/qpm-action@v1 with: workflow_token: ${{ secrets.GITHUB_TOKEN }} - restore: true resolve_ndk: true cache: true - - name: List Post Restore - run: | - echo includes: - ls -la ${GITHUB_WORKSPACE}/extern/includes - echo libs: - ls -la ${GITHUB_WORKSPACE}/extern/libs - - - name: Build & create qmod + - name: Build & Create QMOD run: | cd ${GITHUB_WORKSPACE} - qpm s qmod + qpm qmod zip - - name: Get Library Name - id: libname + - name: Get Output Names + id: names run: | - cd ./build/ + pattern="*.qmod" + files=( $pattern ) + echo QMOD="${files[0]}" >> $GITHUB_OUTPUT + cd build pattern="lib${module_id}*.so" files=( $pattern ) - echo NAME="${files[0]}" >> $GITHUB_OUTPUT - - - name: Rename debug - run: | - mv ./build/debug/${{ steps.libname.outputs.NAME }} ./build/debug/debug_${{ steps.libname.outputs.NAME }} + echo LIB="${files[0]}" >> $GITHUB_OUTPUT - - name: Upload non-debug artifact - uses: actions/upload-artifact@v4 + - name: Upload Artifact + uses: actions/upload-artifact@v6 with: - name: ${{ steps.libname.outputs.NAME }} - path: ./build/${{ steps.libname.outputs.NAME }} + name: ${{ steps.names.outputs.LIB }} + path: ./build/${{ steps.names.outputs.LIB }} if-no-files-found: error - - name: Upload debug artifact - uses: actions/upload-artifact@v4 + - name: Upload Debug Artifact + uses: actions/upload-artifact@v6 with: - name: debug_${{ steps.libname.outputs.NAME }} - path: ./build/debug/debug_${{ steps.libname.outputs.NAME }} + name: debug_${{ steps.names.outputs.LIB }} + path: ./build/debug/${{ steps.names.outputs.LIB }} if-no-files-found: error - - name: Upload qmod artifact - uses: actions/upload-artifact@v4 + - name: Upload QMOD Artifact + uses: actions/upload-artifact@v6 with: - name: ${{ env.qmodName }}.qmod - path: ./${{ env.qmodName }}.qmod + name: ${{ steps.names.outputs.QMOD }} + path: ./${{ steps.names.outputs.QMOD }} if-no-files-found: error diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 3588102..ccdd304 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -2,16 +2,16 @@ name: Publish env: module_id: metacore - qmodName: MetaCore + published_qmod: MetaCore on: workflow_dispatch: inputs: - version: - description: 'Version to release (v will be added to the tag)' + release_ver: + description: "Version to release (v will be added to the tag)" required: true release_msg: - description: 'Message for release' + description: "Message for release" required: true jobs: @@ -19,24 +19,16 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v5 name: Checkout with: submodules: true lfs: true - - uses: seanmiddleditch/gha-setup-ninja@v3 - - - name: Create ndkpath.txt - run: | - echo "$ANDROID_NDK_LATEST_HOME" > ${GITHUB_WORKSPACE}/ndkpath.txt - cat ${GITHUB_WORKSPACE}/ndkpath.txt - - - name: QPM Action + - name: Setup QPM uses: Fernthedev/qpm-action@v1 with: workflow_token: ${{ secrets.GITHUB_TOKEN }} - restore: true resolve_ndk: true cache: true @@ -49,41 +41,34 @@ jobs: qpm_release_bin: true qpm_debug_bin: false - qpm_qmod: ${{ env.qmodName }}.qmod + qpm_qmod: ${{ env.published_qmod }}.qmod - - name: List Post Restore - run: | - echo includes: - ls -la ${GITHUB_WORKSPACE}/extern/includes - echo libs: - ls -la ${GITHUB_WORKSPACE}/extern/libs - - - name: Build & create qmod + - name: Build & Create QMOD run: | cd ${GITHUB_WORKSPACE} - qpm s qmod + qpm qmod zip - - name: Get Library Name - id: libname + - name: Get Output Names + id: names run: | - cd ./build/ - pattern="lib${module_id}*.so" - files=( $pattern ) - echo NAME="${files[0]}" >> $GITHUB_OUTPUT + qmod_files=( "*.qmod" ) + echo "QMOD=${qmod_files[0]}" >> ${GITHUB_OUTPUT} - - name: Rename debug - run: | - mv ./build/debug/${{ steps.libname.outputs.NAME }} ./build/debug/debug_${{ steps.libname.outputs.NAME }} + cd build + + lib_files=( "lib${module_id}*.so" ) + echo "LIB=${lib_files[0]}" >> ${GITHUB_OUTPUT} + + cd debug + mv ${lib_files[0]} debug_${lib_files[0]} - - name: Upload to Release - id: upload_file_release - uses: softprops/action-gh-release@v0.1.12 + - name: Create Release + uses: softprops/action-gh-release@v2 with: + token: ${{ secrets.GITHUB_TOKEN }} name: ${{ github.event.inputs.release_msg }} - tag_name: "v${{ github.event.inputs.version }}" + tag_name: "v${{ github.event.inputs.release_ver }}" files: | - ./build/${{ steps.libname.outputs.NAME }} - ./build/debug/debug_${{ steps.libname.outputs.NAME }} - ./${{ env.qmodName }}.qmod - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + build/${{ steps.names.outputs.LIB }} + build/debug/debug_${{ steps.names.outputs.LIB }} + ${{ steps.names.outputs.QMOD }} diff --git a/.gitignore b/.gitignore index 8d3e02c..e5f22a1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,23 +1,26 @@ # VSCode .cache/ +.vscode/ -# Jetbrains +# Jetbrains IDEs .idea/ -# NDK -ndkpath.txt -*.zip -*.txt -*.log - -# QPM +# Files generated by QPM extern/ +ndkpath.txt qpm_defines.cmake extern.cmake +mod.json -# build -!CMakeLists.txt +# Build outputs and artifacts build/ -mod.json -mod.json.schema +*.zip *.qmod + +# Script outputs and artifacts +*.txt +*.log +*.html + +# Exceptions +!CMakeLists.txt diff --git a/CMakeLists.txt b/CMakeLists.txt index 5c1b8d4..51c75a0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,79 +1,67 @@ cmake_minimum_required(VERSION 3.21) -# include some defines automatically made by qpm +# Include definitions generated by QPM on restore include(qpm_defines.cmake) -set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE) - project(${COMPILE_ID}) -# c++ standard -set(CMAKE_CXX_STANDARD 20) -set(CMAKE_CXX_STANDARD_REQUIRED 20) +# C++ standard +set(CMAKE_CXX_STANDARD 23) +set(CMAKE_CXX_STANDARD_REQUIRED 23) -set(CMAKE_EXPORT_COMPILE_COMMANDS on) +# Provide compile commands so they can be used for intellisense +set(CMAKE_EXPORT_COMPILE_COMMANDS TRUE) -# define that stores the actual source directory -set(SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/src) -set(SHARED_DIR ${CMAKE_CURRENT_SOURCE_DIR}/shared) -set(INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/include) +# Set compiler options +set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE) +add_compile_options(-frtti -fexceptions -fvisibility=hidden -O3) -# compile options used -add_compile_options(-frtti -fexceptions) -add_compile_options(-O3 -fvisibility=hidden) +# Expose definitions from qpm_defines.cmake +add_compile_definitions(VERSION="${MOD_VERSION}") +add_compile_definitions(MOD_ID="${MOD_ID}") -# compile definitions used -add_compile_definitions(VERSION=\"${MOD_VERSION}\") -add_compile_definitions(MOD_ID=\"${MOD_ID}\") +# Add any extra definitions from the build script +get_cmake_property(variable_names VARIABLES) -string(LENGTH "${CMAKE_CURRENT_LIST_DIR}/" FOLDER_LENGTH) +foreach(variable_name ${variable_names}) + if(variable_name MATCHES "^_bs_build_def_.*$") + string(SUBSTRING ${variable_name} 14 -1 definition_name) + add_compile_definitions(${definition_name}=${${variable_name}}) + message("Added definition: " ${definition_name} "=" ${${variable_name}}) + endif() +endforeach() + +# Trim source folder in logcat messages +string(LENGTH "${CMAKE_CURRENT_SOURCE_DIR}/" FOLDER_LENGTH) add_compile_definitions("PAPER_ROOT_FOLDER_LENGTH=${FOLDER_LENGTH}") -# recursively get all src files +# Define the code directories +set(SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/src) +set(INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/include) +set(SHARED_DIR ${CMAKE_CURRENT_SOURCE_DIR}/shared) + +# Recursively get all source files recurse_files(cpp_file_list ${SOURCE_DIR}/*.cpp) recurse_files(c_file_list ${SOURCE_DIR}/*.c) -recurse_files(inline_hook_c ${EXTERN_DIR}/includes/beatsaber-hook/shared/inline-hook/*.c) -recurse_files(inline_hook_cpp ${EXTERN_DIR}/includes/beatsaber-hook/shared/inline-hook/*.cpp) - -# add all src files to compile -add_library( - ${COMPILE_ID} SHARED ${cpp_file_list} ${c_file_list} ${inline_hook_c} ${inline_hook_cpp} -) - -target_include_directories(${COMPILE_ID} PRIVATE ${SHARED_DIR}) +# Add all source files to compilation +add_library(${COMPILE_ID} SHARED ${cpp_file_list} ${c_file_list}) +# Add include directories to compilation target_include_directories(${COMPILE_ID} PRIVATE ${INCLUDE_DIR}) +target_include_directories(${COMPILE_ID} PRIVATE ${SHARED_DIR}) +# Link necessary Android libraries target_link_libraries(${COMPILE_ID} PRIVATE -llog) -# add extern stuff like libs and other includes +# Add autogenerated definitions and link libraries for dependencies include(extern.cmake) add_custom_command( TARGET ${COMPILE_ID} POST_BUILD - COMMAND ${CMAKE_STRIP} -d --strip-all "lib${COMPILE_ID}.so" -o "stripped_lib${COMPILE_ID}.so" - COMMENT "Strip debug symbols done on final binary." -) - -add_custom_command( - TARGET ${COMPILE_ID} - POST_BUILD - COMMAND ${CMAKE_COMMAND} -E make_directory debug - COMMENT "Make directory for debug symbols" -) - -add_custom_command( - TARGET ${COMPILE_ID} - POST_BUILD - COMMAND ${CMAKE_COMMAND} -E rename lib${COMPILE_ID}.so debug/lib${COMPILE_ID}.so - COMMENT "Rename the lib to debug_ since it has debug symbols" -) - -add_custom_command( - TARGET ${COMPILE_ID} - POST_BUILD - COMMAND ${CMAKE_COMMAND} -E rename stripped_lib${COMPILE_ID}.so lib${COMPILE_ID}.so - COMMENT "Rename the stripped lib to regular" + COMMAND ${CMAKE_COMMAND} -E make_directory "debug" + COMMAND ${CMAKE_COMMAND} -E rename "lib${COMPILE_ID}.so" "debug/lib${COMPILE_ID}.so" + COMMAND ${CMAKE_STRIP} -d --strip-all "debug/lib${COMPILE_ID}.so" -o "lib${COMPILE_ID}.so" + COMMENT "Create stripped and debug libraries" ) diff --git a/README.md b/README.md index caaaddb..9b2bdef 100644 --- a/README.md +++ b/README.md @@ -4,9 +4,11 @@ Provides way too many utilities for mod developers. The main idea for this came from creating Qounters++ and its API, and felt like many of the statistics and events it provided could be useful without needing any ties to Qounters++ itself. From there, I thought of every bit of reused code across mods I had, and inter-mod communications I could facilitate, and now we're here. +For easier updates, and the likely never to be realized idea of using this for other games, all Beat Saber specific utilities are now in [MetaCoreBS](https://github.com/Metalit/MetaCoreBS). + ## Documentation -This is just the general idea for each header file with the general contents to expect for each. Documentation for specific functions and variables can be found in the files themselves (except `stats.hpp`, which should be fairly self explanatory, and `internals.hpp`, which would be too annoying to document). Also, many of the functions (in particular everything in `events.hpp`) are not designed for multithreading, so when in doubt use the MainThreadScheduler from BSML. (All callbacks will be run on the main thread for you.) +This is just the general idea for each header file with the general contents to expect for each. Documentation for specific functions and variables can be found in the files themselves. There is also more in [MetaCoreBS](https://github.com/Metalit/MetaCoreBS). ### `assets.cmake` @@ -24,26 +26,10 @@ Provides definitions and utilities for assets included using the cmake script. Improves on BSML's delegate helpers to make even easier (less verbose) delegate creation, specifically around lambdas. -### `events.hpp` - -Provides the ability to register callbacks to and broadcast events, with some available by default and the option to add more from other mods. - -### `game.hpp` - -Provides utilities for various aspects of Beat Saber game state and singletons. - ### `il2cpp.hpp` Provides experimental utilities and wrappers for Il2Cpp types. -### `input.hpp` - -Provides utilities for managing controllers and their input. - -### `internals.hpp` - -Allows access and modification to the internal statistics tracked by the mod, in case another mod changes them in an unusual way. - ### `java.hpp` Allows easier and less verbose use of java and JNI functions. @@ -60,26 +46,6 @@ Defines two potentially useful std::map wrappers for different types of data acc Provides magic macros that make operators usable on cordl types. (Credit to [Qwasyx](https://github.com/Qwasyx).) -### `pp.hpp` - -Provides BeatLeader and ScoreSaber PP-related information retrieval and calculations. - -### `songs.hpp` - -Provides utilities related to songs and beatmaps. - -### `stats.hpp` - -Provides getters for many statistics about the currently playing level. - -### `ui.hpp` - -Provides utilities that I personally use to make creating and updating BSML (Lite) UI a little easier. - -### `uiimpl.hpp` - -Defines the functions in `ui.hpp`, in order to make BSML not a required dependency. - ### `unity.hpp` Provides various functions to enhance working with Unity Components, Transforms, Quaternions, and other objects. Namespace is named `Engine` instead of `Unity` to avoid collisions. diff --git a/include/hooks.hpp b/include/hooks.hpp index 1ad8bcb..8eb2fa4 100644 --- a/include/hooks.hpp +++ b/include/hooks.hpp @@ -1,7 +1,7 @@ #pragma once -#include "beatsaber-hook/shared/utils/hooking.hpp" #include "main.hpp" +#include "beatsaber-hook/shared/hooking.hpp" class Hooks { private: @@ -18,7 +18,7 @@ class Hooks { } }; -#define AUTO_INSTALL_FUNCTION(name_) \ +#define AUTO_HOOK_FUNCTION(name_) \ namespace { \ struct Auto_Install_##name_ { \ static void Install(); \ @@ -29,37 +29,31 @@ class Hooks { void Auto_Install_##name_::Install() #define AUTO_INSTALL_ORIG(name_) \ - AUTO_INSTALL_FUNCTION(Hook_##name_) { ::Hooking::InstallOrigHook(logger); } + AUTO_HOOK_FUNCTION(hook_##name_) { INSTALL_HOOK_ORIG(logger, name_); } #define AUTO_INSTALL(name_) \ - AUTO_INSTALL_FUNCTION(Hook_##name_) { ::Hooking::InstallHook(logger); } - -#define MAKE_AUTO_HOOK_MATCH(name_, mPtr, retval, ...) \ - struct Hook_##name_ { \ - using funcType = retval (*)(__VA_ARGS__); \ - static_assert(MATCH_HOOKABLE_ASSERT(mPtr)); \ - static_assert(std::is_same_v::funcType>, "Hook method signature does not match!"); \ - constexpr static const char* name() { return #name_; } \ - static const MethodInfo* getInfo() { return ::il2cpp_utils::il2cpp_type_check::MetadataGetter::methodInfo(); } \ - static funcType* trampoline() { return &name_; } \ - static inline retval (*name_)(__VA_ARGS__) = nullptr; \ - static funcType hook() { return &::Hooking::HookCatchWrapper<&hook_##name_, funcType>::wrapper; } \ - static retval hook_##name_(__VA_ARGS__); \ - }; \ - AUTO_INSTALL(name_) \ - retval Hook_##name_::hook_##name_(__VA_ARGS__) - -#define MAKE_AUTO_ORIG_HOOK_MATCH(name_, mPtr, retval, ...) \ - struct Hook_##name_ { \ - using funcType = retval (*)(__VA_ARGS__); \ - static_assert(MATCH_HOOKABLE_ASSERT(mPtr)); \ - static_assert(std::is_same_v::funcType>, "Hook method signature does not match!"); \ - constexpr static const char* name() { return #name_; } \ - static const MethodInfo* getInfo() { return ::il2cpp_utils::il2cpp_type_check::MetadataGetter::methodInfo(); } \ - static funcType* trampoline() { return &name_; } \ - static inline retval (*name_)(__VA_ARGS__) = nullptr; \ - static funcType hook() { return &::Hooking::HookCatchWrapper<&hook_##name_, funcType>::wrapper; } \ - static retval hook_##name_(__VA_ARGS__); \ - }; \ - AUTO_INSTALL_ORIG(name_) \ - retval Hook_##name_::hook_##name_(__VA_ARGS__) + AUTO_HOOK_FUNCTION(hook_##name_) { INSTALL_HOOK(logger, name_); } + +#define MAKE_AUTO_HOOK_MATCH(name_, method, ret_type, ...) \ + struct BS_HOOK_HIDDEN hook_##name_ { \ + static constexpr auto cast_test = []() { return requires { static_cast(method); }; }; \ + using func_t = ret_type (*)(__VA_ARGS__); \ + using cast_t = ::i2c::detail::method_check::type; \ + static_assert(cast_test.operator()(), "Hook method signature does not match!"); \ + static_assert(::i2c::detail::match_hookable(method)>, "Method cannot be hooked!"); \ + __INTERNAL_HOOK_STRUCT(name_, ::i2c::metadata_getter(method)>::method_info(), ret_type, __VA_ARGS__) \ + }; \ + AUTO_INSTALL(name_) \ + ret_type hook_##name_::hook_m_##name_(__VA_ARGS__) + +#define MAKE_AUTO_ORIG_HOOK_MATCH(name_, method, ret_type, ...) \ + struct BS_HOOK_HIDDEN hook_##name_ { \ + static constexpr auto cast_test = []() { return requires { static_cast(method); }; }; \ + using func_t = ret_type (*)(__VA_ARGS__); \ + using cast_t = ::i2c::detail::method_check::type; \ + static_assert(cast_test.operator()(), "Hook method signature does not match!"); \ + static_assert(::i2c::detail::match_hookable(method)>, "Method cannot be hooked!"); \ + __INTERNAL_HOOK_STRUCT(name_, ::i2c::metadata_getter(method)>::method_info(), ret_type, __VA_ARGS__) \ + }; \ + AUTO_INSTALL_ORIG(name_) \ + ret_type hook_##name_::hook_m_##name_(__VA_ARGS__) diff --git a/include/main.hpp b/include/main.hpp index a673174..5a10cb6 100644 --- a/include/main.hpp +++ b/include/main.hpp @@ -1,8 +1,5 @@ #pragma once -#include "beatsaber-hook/shared/utils/logging.hpp" +#include "paper2_scotland2/shared/logger.hpp" constexpr auto logger = Paper::ConstLoggerContext(MOD_ID); - -#define SLOW_UPDATES_PER_SEC 4 -#define BASE_GAME_ID "__vanilla_beat_games_not_a_mod_dont_use_thx" diff --git a/include/types.hpp b/include/types.hpp index 807d41d..ea69dec 100644 --- a/include/types.hpp +++ b/include/types.hpp @@ -1,13 +1,8 @@ #pragma once -#include - -#include "UnityEngine/EventSystems/IEventSystemHandler.hpp" -#include "UnityEngine/EventSystems/IPointerUpHandler.hpp" -#include "UnityEngine/MonoBehaviour.hpp" #include "custom-types/shared/macros.hpp" -#define UES UnityEngine::EventSystems +#include "UnityEngine/MonoBehaviour.hpp" DECLARE_CLASS_CODEGEN(MetaCore, ObjectSignal, UnityEngine::MonoBehaviour) { DECLARE_DEFAULT_CTOR(); @@ -22,23 +17,6 @@ DECLARE_CLASS_CODEGEN(MetaCore, ObjectSignal, UnityEngine::MonoBehaviour) { static std::unordered_map> onDestroys; }; -DECLARE_CLASS_CODEGEN_INTERFACES(MetaCore, EndDragHandler, UnityEngine::MonoBehaviour, UES::IEventSystemHandler*, UES::IPointerUpHandler*) { - DECLARE_DEFAULT_CTOR(); - - DECLARE_OVERRIDE_METHOD_MATCH(void, OnPointerUp, &UES::IPointerUpHandler::OnPointerUp, UES::PointerEventData* eventData); - - public: - std::function callback = nullptr; -}; - -DECLARE_CLASS_CODEGEN(MetaCore, KeyboardCloseHandler, UnityEngine::MonoBehaviour) { - DECLARE_DEFAULT_CTOR(); - - public: - std::function closeCallback = nullptr; - std::function okCallback = nullptr; -}; - DECLARE_CLASS_CODEGEN(MetaCore, MainThreadScheduler, UnityEngine::MonoBehaviour) { DECLARE_DEFAULT_CTOR(); @@ -54,5 +32,3 @@ DECLARE_CLASS_CODEGEN(MetaCore, MainThreadScheduler, UnityEngine::MonoBehaviour) Schedule([task]() { return task->IsCompleted; }, std::move(callback)); } }; - -#undef UES diff --git a/mod.template.json b/mod.template.json index 92925e4..01e756d 100644 --- a/mod.template.json +++ b/mod.template.json @@ -1,15 +1,15 @@ { - "$schema": "https://raw.githubusercontent.com/Lauriethefish/QuestPatcher.QMod/main/QuestPatcher.QMod/Resources/qmod.schema.json", "_QPVersion": "0.1.1", "name": "${mod_name}", "id": "${mod_id}", - "author": "Metalit", "version": "${version}", + "author": "Metalit", "packageId": "com.beatgames.beatsaber", - "packageVersion": "1.40.8_7379", - "description": "Provides way too many utilities for mod developers", + "packageVersion": "1.42.3_15380", + "description": "Generic utilities for mod developers", "dependencies": [], "modFiles": ["${binary}"], + "lateModFiles": [], "libraryFiles": [], "fileCopies": [] } diff --git a/qpm.json b/qpm.json index 4bf7d1a..49c964a 100644 --- a/qpm.json +++ b/qpm.json @@ -6,7 +6,7 @@ "info": { "name": "MetaCore", "id": "metacore", - "version": "1.6.3", + "version": "2.0.0", "url": "https://github.com/Metalit/MetaCore", "additionalData": { "overrideSoName": "libmetacore.so", @@ -16,18 +16,19 @@ "workspace": { "scripts": { "build": [ - "pwsh ./scripts/build.ps1 $0?" + "python ./extern/includes/mod-scripts/shared/build.py $0:?" ], "copy": [ - "pwsh ./scripts/copy.ps1 $0:?", - "pwsh ./scripts/restart-game.ps1" + "python ./extern/includes/mod-scripts/shared/cpy.py $0:?" + ], + "dbg": [ + "python ./extern/includes/mod-scripts/shared/dbg.py $0:?" ], "log": [ - "pwsh ./scripts/start-logging.ps1 $0:?" + "python ./extern/includes/mod-scripts/shared/log.py $0:?" ], - "qmod": [ - "pwsh ./scripts/build.ps1 $0?", - "pwsh ./scripts/createqmod.ps1" + "profile": [ + "python ./extern/includes/mod-scripts/shared/profile.py $0:?" ], "restart": [ "pwsh ./scripts/restart-game.ps1" @@ -39,59 +40,54 @@ "pwsh ./scripts/pull-tombstone.ps1 -analyze" ] }, - "ndk": "^27.3.13750724", + "ndk": "^29.0.14206865", "qmodIncludeDirs": [ "./build", - "./extern/libs" + "./extern/libs", + "." ], "qmodIncludeFiles": [], "qmodOutput": "./MetaCore.qmod" }, "dependencies": [ { - "id": "beatsaber-hook", - "versionRange": "^6.4.2", - "additionalData": {} + "id": "mod-scripts", + "versionRange": "^1.0.0", + "additionalData": { + "private": true + } }, { "id": "scotland2", - "versionRange": "^0.1.6", + "versionRange": "^0.1.7", "additionalData": { - "includeQmod": false + "includeQmod": false, + "private": true } }, - { - "id": "bs-cordl", - "versionRange": "^4008.0.0", - "additionalData": {} - }, { "id": "paper2_scotland2", - "versionRange": "^4.6.4", + "versionRange": "^4.7.0", "additionalData": {} }, { - "id": "rapidjson-macros", - "versionRange": "^2.1.0", + "id": "flamingo", + "versionRange": "^1.2.1", "additionalData": {} }, { - "id": "custom-types", - "versionRange": "^0.18.3", - "additionalData": { - "private": true - } + "id": "beatsaber-hook", + "versionRange": "^8.0.0", + "additionalData": {} }, { - "id": "song-details", - "versionRange": "^1.0.4", - "additionalData": { - "private": true - } + "id": "bs-cordl", + "versionRange": "^4401.1.0", + "additionalData": {} }, { - "id": "web-utils", - "versionRange": "^0.6.8", + "id": "custom-types", + "versionRange": "^0.20.0", "additionalData": { "private": true } diff --git a/qpm.shared.json b/qpm.shared.json index a9edd66..9bf6c7d 100644 --- a/qpm.shared.json +++ b/qpm.shared.json @@ -7,7 +7,7 @@ "info": { "name": "MetaCore", "id": "metacore", - "version": "1.6.3", + "version": "2.0.0", "url": "https://github.com/Metalit/MetaCore", "additionalData": { "overrideSoName": "libmetacore.so", @@ -17,18 +17,19 @@ "workspace": { "scripts": { "build": [ - "pwsh ./scripts/build.ps1 $0?" + "python ./extern/includes/mod-scripts/shared/build.py $0:?" ], "copy": [ - "pwsh ./scripts/copy.ps1 $0:?", - "pwsh ./scripts/restart-game.ps1" + "python ./extern/includes/mod-scripts/shared/cpy.py $0:?" + ], + "dbg": [ + "python ./extern/includes/mod-scripts/shared/dbg.py $0:?" ], "log": [ - "pwsh ./scripts/start-logging.ps1 $0:?" + "python ./extern/includes/mod-scripts/shared/log.py $0:?" ], - "qmod": [ - "pwsh ./scripts/build.ps1 $0?", - "pwsh ./scripts/createqmod.ps1" + "profile": [ + "python ./extern/includes/mod-scripts/shared/profile.py $0:?" ], "restart": [ "pwsh ./scripts/restart-game.ps1" @@ -40,59 +41,54 @@ "pwsh ./scripts/pull-tombstone.ps1 -analyze" ] }, - "ndk": "^27.3.13750724", + "ndk": "^29.0.14206865", "qmodIncludeDirs": [ "./build", - "./extern/libs" + "./extern/libs", + "." ], "qmodIncludeFiles": [], "qmodOutput": "./MetaCore.qmod" }, "dependencies": [ { - "id": "beatsaber-hook", - "versionRange": "^6.4.2", - "additionalData": {} + "id": "mod-scripts", + "versionRange": "^1.0.0", + "additionalData": { + "private": true + } }, { "id": "scotland2", - "versionRange": "^0.1.6", + "versionRange": "^0.1.7", "additionalData": { - "includeQmod": false + "includeQmod": false, + "private": true } }, - { - "id": "bs-cordl", - "versionRange": "^4008.0.0", - "additionalData": {} - }, { "id": "paper2_scotland2", - "versionRange": "^4.6.4", + "versionRange": "^4.7.0", "additionalData": {} }, { - "id": "rapidjson-macros", - "versionRange": "^2.1.0", + "id": "flamingo", + "versionRange": "^1.2.1", "additionalData": {} }, { - "id": "custom-types", - "versionRange": "^0.18.3", - "additionalData": { - "private": true - } + "id": "beatsaber-hook", + "versionRange": "^8.0.0", + "additionalData": {} }, { - "id": "song-details", - "versionRange": "^1.0.4", - "additionalData": { - "private": true - } + "id": "bs-cordl", + "versionRange": "^4401.1.0", + "additionalData": {} }, { - "id": "web-utils", - "versionRange": "^0.6.8", + "id": "custom-types", + "versionRange": "^0.20.0", "additionalData": { "private": true } @@ -103,13 +99,13 @@ { "dependency": { "id": "custom-types", - "versionRange": "=0.18.3", + "versionRange": "=0.20.0", "additionalData": { - "soLink": "https://github.com/QuestPackageManager/Il2CppQuestTypePatching/releases/download/v0.18.3/libcustom-types.so", - "debugSoLink": "https://github.com/QuestPackageManager/Il2CppQuestTypePatching/releases/download/v0.18.3/debug_libcustom-types.so", + "soLink": "https://github.com/QuestPackageManager/Il2CppQuestTypePatching/releases/download/v0.20.0/libcustom-types.so", + "debugSoLink": "https://github.com/QuestPackageManager/Il2CppQuestTypePatching/releases/download/v0.20.0/debug_libcustom-types.so", "overrideSoName": "libcustom-types.so", - "modLink": "https://github.com/QuestPackageManager/Il2CppQuestTypePatching/releases/download/v0.18.3/CustomTypes.qmod", - "branchName": "version/v0_18_3", + "modLink": "https://github.com/QuestPackageManager/Il2CppQuestTypePatching/releases/download/v0.20.0/CustomTypes.qmod", + "branchName": "version/v0_20_0", "compileOptions": { "cppFlags": [ "-Wno-invalid-offsetof" @@ -118,62 +114,61 @@ "cmake": true } }, - "version": "0.18.3" + "version": "0.20.0" }, { "dependency": { - "id": "rapidjson-macros", - "versionRange": "=2.1.0", + "id": "mod-scripts", + "versionRange": "=1.0.1", "additionalData": { "headersOnly": true, - "branchName": "version/v2_1_0", + "branchName": "version/v1_0_1", + "cmake": false + } + }, + "version": "1.0.1" + }, + { + "dependency": { + "id": "paper2_scotland2", + "versionRange": "=4.7.0", + "additionalData": { + "soLink": "https://github.com/Fernthedev/paperlog/releases/download/v4.7.0/libpaper2_scotland2.so", + "overrideSoName": "libpaper2_scotland2.so", + "modLink": "https://github.com/Fernthedev/paperlog/releases/download/v4.7.0/paper2_scotland2.qmod", + "branchName": "version/v4_7_0", + "compileOptions": { + "systemIncludes": [ + "shared/utfcpp/source" + ] + }, "cmake": false } }, - "version": "2.1.0" + "version": "4.7.0" }, { "dependency": { "id": "bs-cordl", - "versionRange": "=4008.0.0", + "versionRange": "=4401.1.0", "additionalData": { "headersOnly": true, - "branchName": "version/v4008_0_0", + "branchName": "version/v4401_1_0", "compileOptions": { "includePaths": [ "include" ], "cppFeatures": [], "cppFlags": [ - "-DNEED_UNSAFE_CSHARP", "-fdeclspec", - "-DUNITY_2021", + "-DUNITY_6", "-DHAS_CODEGEN", "-Wno-invalid-offsetof" ] } } }, - "version": "4008.0.0" - }, - { - "dependency": { - "id": "paper2_scotland2", - "versionRange": "=4.6.4", - "additionalData": { - "soLink": "https://github.com/Fernthedev/paperlog/releases/download/v4.6.4/libpaper2_scotland2.so", - "overrideSoName": "libpaper2_scotland2.so", - "modLink": "https://github.com/Fernthedev/paperlog/releases/download/v4.6.4/paper2_scotland2.qmod", - "branchName": "version/v4_6_4", - "compileOptions": { - "systemIncludes": [ - "shared/utfcpp/source" - ] - }, - "cmake": false - } - }, - "version": "4.6.4" + "version": "4401.1.0" }, { "dependency": { @@ -197,13 +192,13 @@ { "dependency": { "id": "beatsaber-hook", - "versionRange": "=6.4.2", + "versionRange": "=8.0.0", "additionalData": { - "soLink": "https://github.com/QuestPackageManager/beatsaber-hook/releases/download/v6.4.2/libbeatsaber-hook.so", - "debugSoLink": "https://github.com/QuestPackageManager/beatsaber-hook/releases/download/v6.4.2/debug_libbeatsaber-hook.so", + "soLink": "https://github.com/QuestPackageManager/beatsaber-hook/releases/download/v8.0.0/libbeatsaber-hook.so", + "debugSoLink": "https://github.com/QuestPackageManager/beatsaber-hook/releases/download/v8.0.0/debug_libbeatsaber-hook.so", "overrideSoName": "libbeatsaber-hook.so", - "modLink": "https://github.com/QuestPackageManager/beatsaber-hook/releases/download/v6.4.2/beatsaber-hook.qmod", - "branchName": "version/v6_4_2", + "modLink": "https://github.com/QuestPackageManager/beatsaber-hook/releases/download/v8.0.0/beatsaber-hook.qmod", + "branchName": "version/v8_0_0", "compileOptions": { "cppFlags": [ "-Wno-extra-qualification" @@ -212,55 +207,39 @@ "cmake": true } }, - "version": "6.4.2" - }, - { - "dependency": { - "id": "web-utils", - "versionRange": "=0.6.8", - "additionalData": { - "soLink": "https://github.com/bsq-ports/WebUtils/releases/download/v0.6.8/libweb-utils.so", - "debugSoLink": "https://github.com/bsq-ports/WebUtils/releases/download/v0.6.8/debug_libweb-utils.so", - "overrideSoName": "libweb-utils.so", - "modLink": "https://github.com/bsq-ports/WebUtils/releases/download/v0.6.8/WebUtils.qmod", - "branchName": "version/v0_6_8", - "cmake": false - } - }, - "version": "0.6.8" + "version": "8.0.0" }, { "dependency": { "id": "scotland2", - "versionRange": "=0.1.6", + "versionRange": "=0.1.7", "additionalData": { - "soLink": "https://github.com/sc2ad/scotland2/releases/download/v0.1.6/libsl2.so", - "debugSoLink": "https://github.com/sc2ad/scotland2/releases/download/v0.1.6/debug_libsl2.so", + "soLink": "https://github.com/sc2ad/scotland2/releases/download/v0.1.7/libsl2.so", + "debugSoLink": "https://github.com/sc2ad/scotland2/releases/download/v0.1.7/debug_libsl2.so", "overrideSoName": "libsl2.so", - "branchName": "version/v0_1_6" + "branchName": "version/v0_1_7" } }, - "version": "0.1.6" + "version": "0.1.7" }, { "dependency": { - "id": "song-details", - "versionRange": "=1.0.4", + "id": "flamingo", + "versionRange": "=1.2.1", "additionalData": { - "soLink": "https://github.com/bsq-ports/SongDetails/releases/download/v1.0.4/libsongdetails.so", - "debugSoLink": "https://github.com/bsq-ports/SongDetails/releases/download/v1.0.4/debug_libsongdetails.so", - "overrideSoName": "libsongdetails.so", - "modLink": "https://github.com/bsq-ports/SongDetails/releases/download/v1.0.4/SongDetails.qmod", - "branchName": "version/v1_0_4", - "cmake": false + "soLink": "https://github.com/QuestPackageManager/Flamingo/releases/download/v1.2.1/libflamingo.so", + "debugSoLink": "https://github.com/QuestPackageManager/Flamingo/releases/download/v1.2.1/debug_libflamingo.so", + "overrideSoName": "libflamingo.so", + "modLink": "https://github.com/QuestPackageManager/Flamingo/releases/download/v1.2.1/flamingo.qmod", + "branchName": "version/v1_2_1" } }, - "version": "1.0.4" + "version": "1.2.1" }, { "dependency": { "id": "libil2cpp", - "versionRange": "=0.4.0", + "versionRange": "=0.5.0", "additionalData": { "headersOnly": true, "compileOptions": { @@ -271,7 +250,7 @@ } } }, - "version": "0.4.0" + "version": "0.5.0" } ] } \ No newline at end of file diff --git a/scripts/build.ps1 b/scripts/build.ps1 deleted file mode 100644 index 1884bc7..0000000 --- a/scripts/build.ps1 +++ /dev/null @@ -1,28 +0,0 @@ -Param( - [Parameter(Mandatory=$false)] - [Switch] $clean, - - [Parameter(Mandatory=$false)] - [Switch] $help -) - -if ($help -eq $true) { - Write-Output "`"Build`" - Copiles your mod into a `".so`" or a `".a`" library" - Write-Output "`n-- Arguments --`n" - - Write-Output "-Clean `t`t Deletes the `"build`" folder, so that the entire library is rebuilt" - - exit -} - -# if user specified clean, remove all build files -if ($clean.IsPresent -and (Test-Path -Path "build")) { - Remove-Item build -R -} - -if (-not (Test-Path -Path "build")) { - New-Item -Path build -ItemType Directory -} - -& cmake -G "Ninja" -DCMAKE_BUILD_TYPE="RelWithDebInfo" -B build -& cmake --build ./build diff --git a/scripts/copy.ps1 b/scripts/copy.ps1 deleted file mode 100644 index 609af32..0000000 --- a/scripts/copy.ps1 +++ /dev/null @@ -1,73 +0,0 @@ -Param( - [Parameter(Mandatory=$false)] - [Switch] $clean, - - [Parameter(Mandatory=$false)] - [Switch] $log, - - [Parameter(Mandatory=$false)] - [Switch] $useDebug, - - [Parameter(Mandatory=$false)] - [Switch] $self, - - [Parameter(Mandatory=$false)] - [Switch] $all, - - [Parameter(Mandatory=$false)] - [String] $custom="", - - [Parameter(Mandatory=$false)] - [String] $file="", - - [Parameter(Mandatory=$false)] - [Switch] $help -) - -if ($help -eq $true) { - Write-Output "`"Copy`" - Builds and copies your mod to your quest, and also starts Beat Saber with optional logging" - Write-Output "`n-- Arguments --`n" - - Write-Output "-Clean `t`t Performs a clean build (equvilant to running `"build -clean`")" - Write-Output "-UseDebug `t Copies the debug version of the mod to your quest" - Write-Output "-Log `t`t Logs Beat Saber using the `"Start-Logging`" command" - - Write-Output "`n-- Logging Arguments --`n" - - & $PSScriptRoot/start-logging.ps1 -help -excludeHeader - - exit -} - -& $PSScriptRoot/build.ps1 -clean:$clean - -if ($LASTEXITCODE -ne 0) { - Write-Output "Failed to build, exiting..." - exit $LASTEXITCODE -} - -& $PSScriptRoot/validate-modjson.ps1 -if ($LASTEXITCODE -ne 0) { - exit $LASTEXITCODE -} -$modJson = Get-Content "./mod.json" -Raw | ConvertFrom-Json - -foreach ($fileName in $modJson.modFiles) { - if ($useDebug -eq $true) { - & adb push build/debug/$fileName /sdcard/ModData/com.beatgames.beatsaber/Modloader/early_mods/$fileName - } else { - & adb push build/$fileName /sdcard/ModData/com.beatgames.beatsaber/Modloader/early_mods/$fileName - } -} - -foreach ($fileName in $modJson.lateModFiles) { - if ($useDebug -eq $true) { - & adb push build/debug/$fileName /sdcard/ModData/com.beatgames.beatsaber/Modloader/mods/$fileName - } else { - & adb push build/$fileName /sdcard/ModData/com.beatgames.beatsaber/Modloader/mods/$fileName - } -} - -if ($log -eq $true) { - & $PSScriptRoot/start-logging.ps1 -self:$self -all:$all -custom:$custom -file:$file -} diff --git a/scripts/createqmod.ps1 b/scripts/createqmod.ps1 deleted file mode 100644 index 9bd5551..0000000 --- a/scripts/createqmod.ps1 +++ /dev/null @@ -1,78 +0,0 @@ -Param( - [Parameter(Mandatory=$false)] - [String] $qmodName="", - - [Parameter(Mandatory=$false)] - [Switch] $help -) - -if ($help -eq $true) { - Write-Output "`"createqmod`" - Creates a .qmod file with your compiled libraries and mod.json." - Write-Output "`n-- Arguments --`n" - - Write-Output "-QmodName `t The file name of your qmod" - - exit -} - -$mod = "./mod.json" - -& $PSScriptRoot/validate-modjson.ps1 -if ($LASTEXITCODE -ne 0) { - exit $LASTEXITCODE -} -$modJson = Get-Content $mod -Raw | ConvertFrom-Json - -if ($qmodName -eq "") { - $qmodName = $modJson.name -} - -$filelist = @($mod) - -$cover = "./" + $modJson.coverImage -if ((-not ($cover -eq "./")) -and (Test-Path $cover)) { - $filelist += ,$cover -} - -foreach ($mod in $modJson.modFiles) { - $path = "./build/" + $mod - if (-not (Test-Path $path)) { - $path = "./extern/libs/" + $mod - } - if (-not (Test-Path $path)) { - Write-Output "Error: could not find dependency: $path" - exit 1 - } - $filelist += $path -} - -foreach ($mod in $modJson.lateModFiles) { - $path = "./build/" + $mod - if (-not (Test-Path $path)) { - $path = "./extern/libs/" + $mod - } - if (-not (Test-Path $path)) { - Write-Output "Error: could not find dependency: $path" - exit 1 - } - $filelist += $path -} - -foreach ($lib in $modJson.libraryFiles) { - $path = "./build/" + $lib - if (-not (Test-Path $path)) { - $path = "./extern/libs/" + $lib - } - if (-not (Test-Path $path)) { - Write-Output "Error: could not find dependency: $path" - exit 1 - } - $filelist += $path -} - -$zip = $qmodName + ".zip" -$qmod = $qmodName + ".qmod" - -Compress-Archive -Path $filelist -DestinationPath $zip -Update -Start-Sleep 1 -Move-Item $zip $qmod -Force diff --git a/scripts/ndk-stack.ps1 b/scripts/ndk-stack.ps1 deleted file mode 100644 index b247dbc..0000000 --- a/scripts/ndk-stack.ps1 +++ /dev/null @@ -1,29 +0,0 @@ -Param( - [Parameter(Mandatory=$false)] - [String] $logName = "log.log", - - [Parameter(Mandatory=$false)] - [Switch] $help -) - -if ($help -eq $true) { - Write-Output "`"NDK-Stack`" - Processes a tombstone using the debug .so to find file locations" - Write-Output "`n-- Arguments --`n" - - Write-Output "LogName `t`t The file name of the tombstone to process" - - exit -} - -if (Test-Path "./ndkpath.txt") { - $NDKPath = Get-Content ./ndkpath.txt -} else { - $NDKPath = $ENV:ANDROID_NDK_HOME -} - -$stackScript = "$NDKPath/ndk-stack" -if (-not ($PSVersionTable.PSEdition -eq "Core")) { - $stackScript += ".cmd" -} - -Get-Content $logName | & $stackScript -sym ./build/debug/ > "$($logName)_processed.log" diff --git a/scripts/pull-tombstone.ps1 b/scripts/pull-tombstone.ps1 deleted file mode 100644 index 17d3f83..0000000 --- a/scripts/pull-tombstone.ps1 +++ /dev/null @@ -1,52 +0,0 @@ -Param( - [Parameter(Mandatory=$false)] - [String] $fileName = "RecentCrash.log", - - [Parameter(Mandatory=$false)] - [Switch] $analyze, - - [Parameter(Mandatory=$false)] - [Switch] $help -) - -if ($help -eq $true) { - Write-Output "`"Pull-Tombstone`" - Finds and pulls the most recent tombstone from your quest, optionally analyzing it with ndk-stack" - Write-Output "`n-- Arguments --`n" - - Write-Output "-FileName `t The name for the output file, defaulting to RecentCrash.log" - Write-Output "-Analyze `t Runs ndk-stack on the file after pulling" - - exit -} - -$global:currentDate = get-date -$global:recentDate = $Null -$global:recentTombstone = $Null - -for ($i = 0; $i -lt 3; $i++) { - $stats = & adb shell stat /sdcard/Android/data/com.beatgames.beatsaber/files/tombstone_0$i - $date = (Select-String -Input $stats -Pattern "(?<=Modify: )\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}(?=.\d{9})").Matches.Value - if([string]::IsNullOrEmpty($date)) { - Write-Output "Failed to pull tombstone, exiting..." - exit 1; - } - $dateObj = [datetime]::ParseExact($date, "yyyy-MM-dd HH:mm:ss", $Null) - $difference = [math]::Round(($currentDate - $dateObj).TotalMinutes) - if ($difference -eq 1) { - Write-Output "Found tombstone_0$i $difference minute ago" - } else { - Write-Output "Found tombstone_0$i $difference minutes ago" - } - if (-not $recentDate -or $recentDate -lt $dateObj) { - $recentDate = $dateObj - $recentTombstone = $i - } -} - -Write-Output "Latest tombstone was tombstone_0$recentTombstone" - -& adb pull /sdcard/Android/data/com.beatgames.beatsaber/files/tombstone_0$recentTombstone $fileName - -if ($analyze) { - & $PSScriptRoot/ndk-stack.ps1 -logName:$fileName -} diff --git a/scripts/restart-game.ps1 b/scripts/restart-game.ps1 deleted file mode 100644 index fd0196a..0000000 --- a/scripts/restart-game.ps1 +++ /dev/null @@ -1,2 +0,0 @@ -adb shell am force-stop com.beatgames.beatsaber -adb shell am start com.beatgames.beatsaber/com.unity3d.player.UnityPlayerActivity diff --git a/scripts/start-logging.ps1 b/scripts/start-logging.ps1 deleted file mode 100644 index 1f8a074..0000000 --- a/scripts/start-logging.ps1 +++ /dev/null @@ -1,85 +0,0 @@ -Param( - [Parameter(Mandatory = $false)] - [Switch] $self, - - [Parameter(Mandatory = $false)] - [Switch] $all, - - [Parameter(Mandatory = $false)] - [String] $custom = "", - - [Parameter(Mandatory = $false)] - [String] $file = "", - - [Parameter(Mandatory = $false)] - [Switch] $help, - - [Parameter(Mandatory = $false)] - [Switch] $trim, - - [Parameter(Mandatory = $false)] - [Switch] $excludeHeader -) - -if ($help -eq $true) { - if ($excludeHeader -eq $false) { - Write-Output "`"Start-Logging`" - Logs Beat Saber using `"adb logcat`"" - Write-Output "`n-- Arguments --`n" - } - - Write-Output "-Self `t`t Only logs from your mod and crashes" - Write-Output "-All `t`t Logs everything, including from non Beat Saber processes" - Write-Output "-Custom `t Specify a specific logging pattern, e.g `"custom-types|questui`"" - Write-Output "`t`t NOTE: The paterent `"AndriodRuntime|CRASH`" is always appended to a custom pattern" - Write-Output "-Trim `t Removes time, level, and mod from the start of lines`"" - Write-Output "-File `t`t Saves the output of the log to the file name given" - - exit -} - -$bspid = adb shell pidof com.beatgames.beatsaber -$command = "adb logcat " - -if ($all -eq $false) { - $loops = 0 - while ([string]::IsNullOrEmpty($bspid) -and $loops -lt 3) { - Start-Sleep -Milliseconds 100 - $bspid = adb shell pidof com.beatgames.beatsaber - $loops += 1 - } - - if ([string]::IsNullOrEmpty($bspid)) { - Write-Output "Could not connect to adb, exiting..." - exit 1 - } - - $command += "--pid $bspid" -} - -if ($all -eq $false) { - $pattern = "(" - if ($self -eq $true) { - $modID = (Get-Content "./mod.json" -Raw | ConvertFrom-Json).id - $pattern += "$modID|" - } - if (![string]::IsNullOrEmpty($custom)) { - $pattern += "$custom|" - } - if ($pattern -eq "(") { - $pattern = "(QuestHook|modloader|" - } - $pattern += "AndroidRuntime|CRASH)" - $command += " | Select-String -pattern `"$pattern`"" -} - -if ($trim -eq $true) { - $command += " | % {`$_ -replace `"^(?(?=.*\]:).*?\]: |.*?: )`", `"`"}" -} - -if (![string]::IsNullOrEmpty($file)) { - $command += " | Out-File -FilePath $PSScriptRoot\$file" -} - -Write-Output "Logging using Command `"$command`"" -& adb logcat -c -Invoke-Expression $command diff --git a/scripts/validate-modjson.ps1 b/scripts/validate-modjson.ps1 deleted file mode 100644 index 41f0e5d..0000000 --- a/scripts/validate-modjson.ps1 +++ /dev/null @@ -1,46 +0,0 @@ -$mod = "./mod.json" -$modTemplate = "./mod.template.json" -$qpmShared = "./qpm.shared.json" - -if (Test-Path -Path $modTemplate) { - $update = -not (Test-Path -Path $mod) - if (-not $update) { - $update = (Get-Item $modTemplate).LastWriteTime -gt (Get-Item $mod).LastWriteTime - } - if (-not $update -and (Test-Path -Path $qpmShared)) { - $update = (Get-Item $qpmShared).LastWriteTime -gt (Get-Item $mod).LastWriteTime - } - - if ($update) { - & qpm qmod manifest - if ($LASTEXITCODE -ne 0) { - exit $LASTEXITCODE - } - } -} -elseif (-not (Test-Path -Path $mod)) { - Write-Output "Error: mod.json and mod.template.json were not present" - exit 1 -} - -$psVersion = $PSVersionTable.PSVersion.Major -if ($psVersion -ge 6) { - $schemaUrl = "https://raw.githubusercontent.com/Lauriethefish/QuestPatcher.QMod/main/QuestPatcher.QMod/Resources/qmod.schema.json" - Invoke-WebRequest $schemaUrl -OutFile ./mod.schema.json - - $schema = "./mod.schema.json" - $modJsonRaw = Get-Content $mod -Raw - $modSchemaRaw = Get-Content $schema -Raw - - Remove-Item $schema - - Write-Output "Validating mod.json..." - if (-not ($modJsonRaw | Test-Json -Schema $modSchemaRaw)) { - Write-Output "Error: mod.json is not valid" - exit 1 - } -} -else { - Write-Output "Could not validate mod.json with schema: powershell version was too low (< 6)" -} -exit diff --git a/shared/assets.cmake b/shared/assets.cmake index eea9a08..8bd3731 100644 --- a/shared/assets.cmake +++ b/shared/assets.cmake @@ -43,6 +43,7 @@ if(EXISTS ${ASSETS_DIRECTORY}) # Create matching subdirectories in the prepended assets folder. get_filename_component(REL_DIR ${REL_FILE} DIRECTORY) + if(NOT REL_DIR STREQUAL "") file(MAKE_DIRECTORY "${PREPENDED_ASSETS_DIR}/${REL_DIR}") endif() @@ -50,8 +51,7 @@ if(EXISTS ${ASSETS_DIRECTORY}) # Create a prepended copy of the asset (with 32 extra bytes at the beginning). add_custom_command( OUTPUT "${PREPENDED_ASSETS_DIR}/${REL_FILE}" - COMMAND ${CMAKE_COMMAND} -E echo_append " " > - "${PREPENDED_ASSETS_DIR}/${REL_FILE}" + COMMAND ${CMAKE_COMMAND} -E echo_append " " > "${PREPENDED_ASSETS_DIR}/${REL_FILE}" COMMAND ${CMAKE_COMMAND} -E cat "${SRC_FILE}" >> "${PREPENDED_ASSETS_DIR}/${REL_FILE}" COMMAND ${CMAKE_COMMAND} -E echo_append " " >> "${PREPENDED_ASSETS_DIR}/${REL_FILE}" DEPENDS "${SRC_FILE}" @@ -69,7 +69,7 @@ if(EXISTS ${ASSETS_DIRECTORY}) add_custom_command( OUTPUT "${OUTPUT_OBJ}" COMMAND ${CMAKE_OBJCOPY} "${REL_FILE}" "${OUTPUT_OBJ}" --input-target binary - --output-target elf64-aarch64 --set-section-flags binary=strings + --output-target elf64-aarch64 --set-section-flags binary=strings DEPENDS "${PREPENDED_ASSETS_DIR}/${REL_FILE}" WORKING_DIRECTORY "${PREPENDED_ASSETS_DIR}" ) @@ -102,6 +102,7 @@ if(EXISTS ${ASSETS_DIRECTORY}) foreach(DIR IN LISTS DIR_LIST) string(REGEX REPLACE "[^a-zA-Z0-9]" "_" NAMESPACE_PART ${DIR}) + if(NAMESPACE STREQUAL "") set(NAMESPACE ${NAMESPACE_PART}) else() @@ -155,6 +156,7 @@ namespace IncludedAssets { " ) list(LENGTH BINARY_ASSET_FILES COUNT) + if(${COUNT} GREATER 0) # Check if the output file already exists. if(EXISTS ${ASSET_HEADER_PATH}) diff --git a/shared/assets.hpp b/shared/assets.hpp index cf60346..e479712 100644 --- a/shared/assets.hpp +++ b/shared/assets.hpp @@ -1,6 +1,7 @@ #pragma once -#include "beatsaber-hook/shared/utils/typedefs.h" +#include "beatsaber-hook/shared/arrayw.hpp" +#include "beatsaber-hook/shared/types.hpp" /// @brief A struct that holds references to an asset and provides accessors to its data struct IncludedAsset { @@ -25,7 +26,7 @@ struct IncludedAsset { private: void init() const { if (!array->klass) - array->klass = classof(Array*); + array->klass = i2c::class_of*>(); } Array* const array; }; diff --git a/shared/delegates.hpp b/shared/delegates.hpp index 0f14175..68dc093 100644 --- a/shared/delegates.hpp +++ b/shared/delegates.hpp @@ -1,8 +1,16 @@ #pragma once +#include "custom-types/shared/delegate.hpp" + #include "System/Action.hpp" #include "System/Action_1.hpp" +#include "System/Action_10.hpp" #include "System/Action_11.hpp" +#include "System/Action_12.hpp" +#include "System/Action_13.hpp" +#include "System/Action_14.hpp" +#include "System/Action_15.hpp" +#include "System/Action_16.hpp" #include "System/Action_2.hpp" #include "System/Action_3.hpp" #include "System/Action_4.hpp" @@ -10,12 +18,12 @@ #include "System/Action_6.hpp" #include "System/Action_7.hpp" #include "System/Action_8.hpp" +#include "System/Action_9.hpp" #include "UnityEngine/Events/UnityAction.hpp" #include "UnityEngine/Events/UnityAction_1.hpp" #include "UnityEngine/Events/UnityAction_2.hpp" #include "UnityEngine/Events/UnityAction_3.hpp" #include "UnityEngine/Events/UnityAction_4.hpp" -#include "custom-types/shared/delegate.hpp" namespace MetaCore::Delegates { /// @brief Creates a System::Action from a std::function @@ -43,12 +51,24 @@ namespace MetaCore::Delegates { return custom_types::MakeDelegate*>(fun); else if constexpr (argc == 8) return custom_types::MakeDelegate*>(fun); + else if constexpr (argc == 9) + return custom_types::MakeDelegate*>(fun); + else if constexpr (argc == 10) + return custom_types::MakeDelegate*>(fun); else if constexpr (argc == 11) return custom_types::MakeDelegate*>(fun); + else if constexpr (argc == 12) + return custom_types::MakeDelegate*>(fun); + else if constexpr (argc == 13) + return custom_types::MakeDelegate*>(fun); + else if constexpr (argc == 14) + return custom_types::MakeDelegate*>(fun); + else if constexpr (argc == 15) + return custom_types::MakeDelegate*>(fun); + else if constexpr (argc == 16) + return custom_types::MakeDelegate*>(fun); - static_assert(argc != 9, "System::Action_9 does not exist"); - static_assert(argc != 10, "System::Action_10 does not exist"); - static_assert(argc < 12, "System::Action_12 and higher do not exist"); + static_assert(argc < 17, "System::Action_17 and higher do not exist"); } /// @brief Creates a System::Action from a lambda diff --git a/shared/events.hpp b/shared/events.hpp deleted file mode 100644 index fa82fb5..0000000 --- a/shared/events.hpp +++ /dev/null @@ -1,115 +0,0 @@ -#pragma once - -#include -#include - -#include "export.h" - -namespace MetaCore::Events { - /// @brief The global ids of all built-in events - enum Events { - // After any change to the current or maximum score - ScoreChanged, - // After any note is cut, good or bad, and the computation for it is finished - NoteCut, - // After any basic or chain note is missed - NoteMissed, - // After a bomb is cut - BombCut, - // After any wall is entered, even if the player was already inside one - WallHit, - // After any change to the combo - ComboChanged, - // After any change to the player health / energy - HealthChanged, - // Once per frame during a map, after the song time updates - Update, - // 4 times per second, unless the framerate is somehow lower - SlowUpdate, - // After a new map or difficulty is selected in the menu - MapSelected, - // After the current map is deselected without a new one being selected - MapDeselected, - // After a new playlist is selected in the menu - PlaylistSelected, - // After the current playlist is deselected without a new one being selected - PlaylistDeselected, - // After a new map is fully started - MapStarted, - // After a map is paused - MapPaused, - // After a map is unpaused - MapUnpaused, - // After a map is restarted - MapRestarted, - // Immediately when a map ends - MapEnded, - // After the scene has finished transitioning to gameplay and stats are available - GameplaySceneStarted, - // After a map ends or restarts and the scene has transitioned out of gameplay - GameplaySceneEnded, - // After the score submission status changes - ScoreSubmission, - // Immediately when the game does a soft or internal restart - SoftRestart, - EventMax = SoftRestart, - }; - - /// @brief Registers a custom event for future broadcasts - /// @param mod The unique id of the mod registering the event - /// @param modEvent The per-mod id of the event being registered - /// @return The unique global id of the registered event (> EventMax), or -1 on failure - METACORE_EXPORT int RegisterEvent(std::string mod, int modEvent); - - /// @brief Finds the unique global id of a custom event - /// @param mod The unique id of the mod that registered the event - /// @param modEvent The per-mod id of the registered event - /// @return The unique global id of the event, or -1 if it does not exist - METACORE_EXPORT int FindEvent(std::string mod, int modEvent); - - /// @brief Registers a callback to an event - /// @param event The global id of the event - /// @param callback The function to be called when the event is broadcast - /// @param once If the callback should only be called once and then removed - /// @return The id for removal if the event was successfully registered to (>= 0), or -1 on failure - METACORE_EXPORT int AddCallback(int event, std::function callback, bool once = false); - /// @brief Registers a callback to an event - /// @param mod The unique id of the mod that registered the event - /// @param modEvent The per-mod id of the event - /// @param callback The function to be called when the event is broadcast - /// @param once If the callback should only be called once and then removed - /// @return The id for removal if the event was successfully registered to (>= 0), or -1 on failure - METACORE_EXPORT int AddCallback(std::string mod, int modEvent, std::function callback, bool once = false); - /// @brief Registers a callback to all events that will be called before event-specific callbacks - /// @param callback The function to be called with the global id of any broadcast event - /// @param once If the callback should only be called once and then removed - /// @return The id for removal (>= 0) - METACORE_EXPORT int AddCallback(std::function callback, bool once = false); - - /// @brief Removes a previously registered callback - /// @param id The id returned by a call to AddCallback - METACORE_EXPORT void RemoveCallback(int id); - - /// @brief Globally broadcasts an event - /// @param event The global id of the event - /// @return If the event was successfully broadcast - METACORE_EXPORT bool Broadcast(int event); - /// @brief Globally broadcasts an event - /// @param mod The unique id of the mod that registered the event - /// @param modEvent The per-mod id of the event - /// @return If the event was successfully broadcast - METACORE_EXPORT bool Broadcast(std::string mod, int modEvent); -} - -#define CONCAT_WRAPPED(x, y) x##y -#define CONCAT(x, y) CONCAT_WRAPPED(x, y) - -#define AUTO_FUNCTION \ -static __attribute__((constructor)) void CONCAT(auto_function_dlopen_, __LINE__)() - -#define ON_EVENT(...) \ -static void CONCAT(auto_function_event_, __LINE__)(); \ -AUTO_FUNCTION { \ - ::MetaCore::Events::AddCallback(__VA_ARGS__, CONCAT(auto_function_event_, __LINE__)); \ -} \ -static void CONCAT(auto_function_event_, __LINE__)() diff --git a/shared/export.h b/shared/export.h index e97a141..303d832 100644 --- a/shared/export.h +++ b/shared/export.h @@ -1,3 +1,5 @@ #pragma once +#ifndef METACORE_EXPORT #define METACORE_EXPORT __attribute__((visibility("default"))) +#endif diff --git a/shared/game.hpp b/shared/game.hpp deleted file mode 100644 index ca67351..0000000 --- a/shared/game.hpp +++ /dev/null @@ -1,77 +0,0 @@ -#pragma once - -#include "GlobalNamespace/BeatmapCharacteristicCollection.hpp" -#include "GlobalNamespace/EnvironmentsListModel.hpp" -#include "GlobalNamespace/MainFlowCoordinator.hpp" -#include "GlobalNamespace/MainSystemInit.hpp" -#include "GlobalNamespace/MenuTransitionsHelper.hpp" -#include "GlobalNamespace/PlayerDataModel.hpp" -#include "GlobalNamespace/SongPreviewPlayer.hpp" -#include "UnityEngine/AudioClip.hpp" -#include "UnityEngine/Material.hpp" -#include "Zenject/DiContainer.hpp" -#include "export.h" - -namespace MetaCore::Game { - /// @brief Enables or disables score submission for a mod. Score submission will only be enabled if no mods have it disabled - /// @param mod The unique id of the mod modifying score submission - /// @param enable If score submission should be enabled for this mod - METACORE_EXPORT void SetScoreSubmission(std::string mod, bool enable); - /// @brief Disables score submission for a mod for the current song only, reenabling on the next GameplaySceneEnded event - /// @param mod The unique id of the mod modifying score submission - METACORE_EXPORT void DisableScoreSubmissionOnce(std::string mod); - /// @brief Gets if score submission is currently disabled - /// @return If score submission is currently disabled - METACORE_EXPORT bool IsScoreSubmissionDisabled(); - /// @brief Gets all the mods currently disabling score submission - /// @return All the mods currently disabling score submission - METACORE_EXPORT std::set const& GetScoreSubmissionDisablers(); - - /// @brief Fades the main camera in or out for a mod. If any mod is requesting it, the camera will be faded out (to a black screen) - /// @param mod The unique id of the mod modifying the camera - /// @param fadeOut If this mod wants the camera to be faded out - /// @param fadeDuration The duration for the fade in or out if it changes - METACORE_EXPORT void SetCameraFadeOut(std::string mod, bool fadeOut, float fadeDuration = -1); - /// @brief Gets if the main camera is currently faded out (including if the base game does it) - /// @return If the main camera is currently faded out - METACORE_EXPORT bool IsCameraFadedOut(); - - /// @brief Plays an audio clip with the song preview player, fading out other menu music or song previews - /// @param audio The audio clip to play (song preview or otherwise) - /// @param volume An optional volume modifier - /// @param start The time to start in the audio clip - /// @param duration The length to play the audio clip for, or the full clip if < 0 - METACORE_EXPORT void PlaySongPreview(UnityEngine::AudioClip* audio, float volume = 0, float start = 0, float duration = -1); - - /// @brief Gets the collection of all beatmap characteristics from the main flow coordinator - /// @return The BeatmapCharacteristicCollection instance - METACORE_EXPORT GlobalNamespace::BeatmapCharacteristicCollection* GetCharacteristics(); - - /// @brief Gets the collection of all map envirnoments from the main flow coordinator - /// @return The EnvironmentsListModel instance - METACORE_EXPORT GlobalNamespace::EnvironmentsListModel* GetEnvironments(); - - /// @brief Gets the player data model from the main flow coordinator - /// @return The PlayerDataModel instance - METACORE_EXPORT GlobalNamespace::PlayerDataModel* GetPlayerData(); - - /// @brief Gets the material used for curved corners on sprites (for example the playlist images) - /// @return The Material instance - METACORE_EXPORT UnityEngine::Material* GetCurvedCornersMaterial(); - - /// @brief Gets the menu transitions helper from the main flow coordinator - /// @return The MenuTransitionsHelper instance - METACORE_EXPORT GlobalNamespace::MenuTransitionsHelper* GetMenuTransitionsHelper(); - - /// @brief Gets the main flow coordinator - /// @return The MainFlowCoordinator instance - METACORE_EXPORT GlobalNamespace::MainFlowCoordinator* GetMainFlowCoordinator(); - - /// @brief Gets the main system init - /// @return The MainFlowCoordinator instance - METACORE_EXPORT GlobalNamespace::MainSystemInit* GetMainSystemInit(); - - /// @brief Gets the zenject DiContainer from AppInit - /// @return The DiContainer instance - METACORE_EXPORT Zenject::DiContainer* GetAppDiContainer(); -} diff --git a/shared/il2cpp.hpp b/shared/il2cpp.hpp index 9c2a389..30e1b5c 100644 --- a/shared/il2cpp.hpp +++ b/shared/il2cpp.hpp @@ -1,10 +1,12 @@ #pragma once +#include "beatsaber-hook/shared/arrayw.hpp" +#include "beatsaber-hook/shared/types.hpp" + #include #include "System/Collections/Generic/Dictionary_2.hpp" #include "System/Collections/Generic/InsertionBehavior.hpp" -#include "beatsaber-hook/shared/utils/typedefs.h" // experimental @@ -19,7 +21,7 @@ struct ConstArray { void init() const noexcept { if (!this->klass) - const_cast*>(this)->klass = classof(Array*); + const_cast*>(this)->klass = i2c::class_of*>(); } constexpr Array* to_array() { @@ -219,19 +221,11 @@ struct DictionaryW { }; MARK_GEN_REF_T(DictionaryW); -static_assert(il2cpp_utils::has_il2cpp_conversion>); +static_assert(i2c::type_check::wrapper_ref_type>); template -struct BS_HOOKS_HIDDEN ::il2cpp_utils::il2cpp_type_check::need_box> { - constexpr static bool value = false; -}; - -template -struct BS_HOOKS_HIDDEN ::il2cpp_utils::il2cpp_type_check::il2cpp_no_arg_class> { - static inline Il2CppClass* get() { - auto klass = ::il2cpp_utils::il2cpp_type_check::il2cpp_no_arg_class::Wrapped*>::get(); - return klass; - } +struct BS_HOOK_HIDDEN i2c::type_check::no_arg_class> { + static inline Il2CppClass* get() { return i2c::class_of::Wrapped*>(); } }; template diff --git a/shared/input.hpp b/shared/input.hpp deleted file mode 100644 index 19bd463..0000000 --- a/shared/input.hpp +++ /dev/null @@ -1,91 +0,0 @@ -#pragma once - -#include "UnityEngine/Pose.hpp" -#include "UnityEngine/Vector2.hpp" -#include "VRUIControls/VRInputModule.hpp" -#include "export.h" - -namespace MetaCore::Input { - enum Buttons { - // The lower buttons on the controller's face, A or X - AX, - // The upper buttons on the controller's face, B or Y - BY, - // Specficially pressing on the thumbstick - Thumbstick, - // The trigger on the grip of the controller - Grip, - // The trigger on the front of the controller - Trigger, - // The button normally used to pause a map, only on the left controller - Start, - ButtonsMax = Start, - }; - - enum Axes { - // The vertical component of the thumbstick position - ThumbstickVertical, - // The horizontal component of the thumbstick position - ThumbstickHorizontal, - // The amount the trigger on the grip of the controller is pressed - GripStrength, - // The amount the trigger on the front of the controller is pressed - TriggerStrength, - }; - - enum Controllers { - // The left hand controller - Left, - // The right hand controller - Right, - // The controller with the most recently pressed button - Active, - // Either controller - Either, - }; - - /// @brief Gets if a button is currently pressed for a given controller - /// @param controller The controller to check the button state for - /// @param button The button to check - /// @return If the button is pressed on the controller - METACORE_EXPORT bool GetPressed(Controllers controller, Buttons button); - /// @brief Gets a 1-dimensional axis or strength of an input - /// @param controller The controller to check the axis state for. If Either, the maximum will be returned - /// @param axis The axis input to check - /// @return The axis amount from 0 to 1, or -1 to 1 if a thumbstick axis - METACORE_EXPORT float GetAxis(Controllers controller, Axes axis); - /// @brief Gets the current thumbstick position of a controller - /// @param controller The controller to check the thumbstick state for. If Either, the maximum will be returned - /// @return The 2-dimensional thumbstick position with axes from -1 to 1 - METACORE_EXPORT UnityEngine::Vector2 GetThumbstick(Controllers controller); - - /// @brief The string identifier that maps the Buttons enum to events run when they are pressed, when used in the "mod" parameter - std::string const PressEvents = "MetaCoreButtonPresses"; - /// @brief The string identifier that maps the Buttons enum to events run when they are released, when used in the "mod" parameter - std::string const ReleaseEvents = "MetaCoreButtonReleases"; - /// @brief The string identifier that maps the Buttons enum to events run every frame they are held, when used in the "mod" parameter - std::string const HoldEvents = "MetaCoreButtonHolds"; - - /// @brief Gets the current position and rotation of a controller - /// @param left If the left controller should be returned, otherwise the right controller - /// @return The tracked pose of the controller - METACORE_EXPORT UnityEngine::Pose GetHandPose(bool left); - /// @brief Gets the current position and rotation of the headset - /// @return The tracked pose of the headset - METACORE_EXPORT UnityEngine::Pose GetHeadPose(); - - /// @brief Finds the currently active VRInputModule if any - /// @return The currently active VRInputModule, or nullptr if none can be found - METACORE_EXPORT VRUIControls::VRInputModule* GetCurrentInputModule(); - - /// @brief Enables or disables haptic feedback for a mod. Haptics will only be enabled if no mods have it disabled - /// @param mod The unique id of the mod modifying haptic feedback - /// @param enable If haptic feedback should be enabled for this mod - METACORE_EXPORT void SetHaptics(std::string mod, bool enable); - /// @brief Gets if haptic feedback is currently disabled - /// @return If haptic feedback is currently disabled - METACORE_EXPORT bool IsHapticsDisabled(); - /// @brief Gets all the mods currently disabling haptic feedback - /// @return All the mods currently disabling haptic feedback - METACORE_EXPORT std::set const& GetHapticsDisablers(); -} diff --git a/shared/internals.hpp b/shared/internals.hpp deleted file mode 100644 index 64e2a8b..0000000 --- a/shared/internals.hpp +++ /dev/null @@ -1,121 +0,0 @@ -#pragma once - -#include "GlobalNamespace/BeatmapCallbacksController.hpp" -#include "GlobalNamespace/BeatmapData.hpp" -#include "GlobalNamespace/BeatmapKey.hpp" -#include "GlobalNamespace/BeatmapLevel.hpp" -#include "GlobalNamespace/BeatmapLevelPack.hpp" -#include "GlobalNamespace/BeatmapObjectManager.hpp" -#include "GlobalNamespace/ColorScheme.hpp" -#include "GlobalNamespace/ComboController.hpp" -#include "GlobalNamespace/EnvironmentInfoSO.hpp" -#include "GlobalNamespace/GameEnergyCounter.hpp" -#include "GlobalNamespace/GameplayModifiers.hpp" -#include "GlobalNamespace/SaberManager.hpp" -#include "GlobalNamespace/ScoreController.hpp" -#include "UnityEngine/Camera.hpp" -#include "UnityEngine/Quaternion.hpp" -#include "export.h" - -// no per-variable documentation here, sorry - -namespace MetaCore::Internals { - METACORE_EXPORT extern int leftScore; - METACORE_EXPORT extern int rightScore; - METACORE_EXPORT extern int leftMaxScore; - METACORE_EXPORT extern int rightMaxScore; - METACORE_EXPORT extern int songMaxScore; - METACORE_EXPORT extern int leftCombo; - METACORE_EXPORT extern int rightCombo; - METACORE_EXPORT extern int combo; - METACORE_EXPORT extern int highestLeftCombo; - METACORE_EXPORT extern int highestRightCombo; - METACORE_EXPORT extern int highestCombo; - METACORE_EXPORT extern int multiplier; - METACORE_EXPORT extern int multiplierProgress; - METACORE_EXPORT extern float health; - METACORE_EXPORT extern float songTime; - METACORE_EXPORT extern float songLength; - METACORE_EXPORT extern float songSpeed; - METACORE_EXPORT extern int notesLeftCut; - METACORE_EXPORT extern int notesRightCut; - METACORE_EXPORT extern int notesLeftBadCut; - METACORE_EXPORT extern int notesRightBadCut; - METACORE_EXPORT extern int notesLeftMissed; - METACORE_EXPORT extern int notesRightMissed; - METACORE_EXPORT extern int bombsLeftHit; - METACORE_EXPORT extern int bombsRightHit; - METACORE_EXPORT extern int wallsHit; - METACORE_EXPORT extern int uncountedNotesLeftCut; - METACORE_EXPORT extern int uncountedNotesRightCut; - METACORE_EXPORT extern int remainingNotesLeft; - METACORE_EXPORT extern int remainingNotesRight; - METACORE_EXPORT extern int songNotesLeft; - METACORE_EXPORT extern int songNotesRight; - METACORE_EXPORT extern int leftPreSwing; - METACORE_EXPORT extern int rightPreSwing; - METACORE_EXPORT extern int leftPostSwing; - METACORE_EXPORT extern int rightPostSwing; - METACORE_EXPORT extern int leftAccuracy; - METACORE_EXPORT extern int rightAccuracy; - METACORE_EXPORT extern float leftTimeDependence; - METACORE_EXPORT extern float rightTimeDependence; - METACORE_EXPORT extern std::vector leftSpeeds; - METACORE_EXPORT extern std::vector rightSpeeds; - METACORE_EXPORT extern std::vector leftAngles; - METACORE_EXPORT extern std::vector rightAngles; - METACORE_EXPORT extern bool noFail; - METACORE_EXPORT extern float positiveMods; - METACORE_EXPORT extern float negativeMods; - METACORE_EXPORT extern int personalBest; - METACORE_EXPORT extern int fails; - METACORE_EXPORT extern int restarts; - METACORE_EXPORT extern int leftMissedMaxScore; - METACORE_EXPORT extern int rightMissedMaxScore; - METACORE_EXPORT extern int leftMissedFixedScore; - METACORE_EXPORT extern int rightMissedFixedScore; - METACORE_EXPORT extern UnityEngine::Quaternion prevRotLeft; - METACORE_EXPORT extern UnityEngine::Quaternion prevRotRight; - - // references - METACORE_EXPORT extern GlobalNamespace::GameplayModifiers* modifiers; - METACORE_EXPORT extern GlobalNamespace::ColorScheme* colors; - METACORE_EXPORT extern GlobalNamespace::BeatmapLevel* beatmapLevel; - METACORE_EXPORT extern GlobalNamespace::BeatmapKey beatmapKey; - METACORE_EXPORT extern GlobalNamespace::BeatmapData* beatmapData; - METACORE_EXPORT extern GlobalNamespace::EnvironmentInfoSO* environment; - METACORE_EXPORT extern GlobalNamespace::AudioTimeSyncController* audioTimeSyncController; - METACORE_EXPORT extern GlobalNamespace::BeatmapCallbacksController* beatmapCallbacksController; - METACORE_EXPORT extern GlobalNamespace::BeatmapObjectManager* beatmapObjectManager; - METACORE_EXPORT extern GlobalNamespace::ComboController* comboController; - METACORE_EXPORT extern GlobalNamespace::GameEnergyCounter* gameEnergyCounter; - METACORE_EXPORT extern GlobalNamespace::ScoreController* scoreController; - METACORE_EXPORT extern GlobalNamespace::SaberManager* saberManager; - METACORE_EXPORT extern UnityEngine::Camera* mainCamera; - - METACORE_EXPORT extern bool stateValid; - METACORE_EXPORT extern bool referencesValid; - METACORE_EXPORT extern bool mapWasQuit; - METACORE_EXPORT extern bool mapWasRestarted; - - METACORE_EXPORT void Initialize(); - METACORE_EXPORT void DoSlowUpdate(); - METACORE_EXPORT void Finish(bool quit, bool restart); - - METACORE_EXPORT extern GlobalNamespace::BeatmapKey selectedKey; - METACORE_EXPORT extern GlobalNamespace::BeatmapLevel* selectedLevel; - METACORE_EXPORT extern GlobalNamespace::BeatmapLevelPack* selectedPlaylist; - METACORE_EXPORT extern bool isLevelSelected; - METACORE_EXPORT extern bool isPlaylistSelected; - - METACORE_EXPORT void SetLevel(GlobalNamespace::BeatmapKey key, GlobalNamespace::BeatmapLevel* level); - METACORE_EXPORT void ClearLevel(); - METACORE_EXPORT void SetPlaylist(GlobalNamespace::BeatmapLevelPack* playlist); - METACORE_EXPORT void ClearPlaylist(); - - METACORE_EXPORT void SetEndDragUI(UnityEngine::Component* component, std::function callback); - METACORE_EXPORT std::function - SetKeyboardCloseUI(UnityEngine::Component* component, std::function onClosed, std::function onOk); - - METACORE_EXPORT bool IsAprilFirst(); -} diff --git a/shared/jutils.hpp b/shared/jutils.hpp index e329940..e84da29 100644 --- a/shared/jutils.hpp +++ b/shared/jutils.hpp @@ -1,10 +1,9 @@ #pragma once -#include +#include "export.h" #include - -#include "export.h" +#include /* JNI field and method signatures - Field signatures are simply their type in JNI form. @@ -71,6 +70,9 @@ namespace MetaCore::Java { /// @brief Constructor with an already known java field id /// @param field The already known java field id to use FindFieldID(jfieldID field) : field(field) {} + /// @brief Constructor with the name and signature of a java field + /// @param name The name of a java field + /// @param signature The signature of a java field FindFieldID(std::string name, std::string signature) : name(name), signature(signature) {} }; diff --git a/shared/pp.hpp b/shared/pp.hpp deleted file mode 100644 index 789a3fe..0000000 --- a/shared/pp.hpp +++ /dev/null @@ -1,89 +0,0 @@ -#pragma once - -#include "GlobalNamespace/BeatmapKey.hpp" -#include "GlobalNamespace/GameplayModifiers.hpp" -#include "export.h" -#include "rapidjson-macros/shared/macros.hpp" - -namespace MetaCore::PP { - /// @brief The BeatLeader ratings for speed-changing modifiers specifically - DECLARE_JSON_STRUCT(BLSpeedModifiers) { - VALUE(float, ssPassRating); - VALUE(float, ssAccRating); - VALUE(float, ssTechRating); - VALUE(float, fsPassRating); - VALUE(float, fsAccRating); - VALUE(float, fsTechRating); - VALUE(float, sfPassRating); - VALUE(float, sfAccRating); - VALUE(float, sfTechRating); - }; - - /// @brief BeatLeader ranking information for a specific characteristic/difficulty of a map - DECLARE_JSON_STRUCT(BLSongDiff) { - NAMED_VALUE(std::string, Difficulty, "difficultyName"); - NAMED_VALUE(std::string, Characteristic, "modeName"); - NAMED_VALUE_DEFAULT(int, RankedStatus, 0, "status"); - NAMED_VALUE_DEFAULT(float, Stars, 0, "stars"); - NAMED_VALUE_DEFAULT(float, Predicted, 0, "predictedAcc"); - NAMED_VALUE_DEFAULT(float, Pass, 0, "passRating"); - NAMED_VALUE_DEFAULT(float, Acc, 0, "accRating"); - NAMED_VALUE_DEFAULT(float, Tech, 0, "techRating"); - NAMED_MAP_DEFAULT(float, ModifierValues, {}, "modifierValues"); - NAMED_VALUE_OPTIONAL(BLSpeedModifiers, ModifierRatings, "modifiersRating"); - }; - - /// @brief BeatLeader ranking information for a map - DECLARE_JSON_STRUCT(BLSong) { - NAMED_VECTOR(BLSongDiff, Difficulties, "difficulties"); - }; - - /// @brief ScoreSaber ranking information for a specific characteristic/difficulty of a map (just a star rating) - using SSSongDiff = float; - - /// @brief The acc to PP calculation cure for BeatLeader - METACORE_EXPORT extern std::vector> const BeatLeaderCurve; - /// @brief The acc to PP calculation cure for ScoreSaber - METACORE_EXPORT extern std::vector> const ScoreSaberCurve; - - /// @brief Maps gameplay modifiers to their BeatLeader two-letter string representations - /// @param modifiers The gameplay modifiers class instance - /// @param speeds If speed-changing modifiers should be included - /// @param failed If No Fail should be included, if enabled at all - /// @return A consistently ordered vector of the lowercase representations of the modifiers - METACORE_EXPORT std::vector GetModStringsBL(GlobalNamespace::GameplayModifiers* modifiers, bool speeds, bool failed); - /// @brief Calculates the curve value for a given accuracy - /// @param accuracy The accuracy value from 0 to 1 - /// @param curve BeatLeaderCurve or ScoreSaberCurve - /// @return The value of the curve at the given accuracy, interpolated if there is not an exact point in the curve - METACORE_EXPORT float AccCurve(float accuracy, std::vector> const& curve); - - /// @brief Checks if a BeatLeader map characteristic/difficulty is ranked - /// @param map The BeatLeader ranking information - /// @return If the map characteristic/difficulty is ranked - METACORE_EXPORT bool IsRanked(BLSongDiff const& map); - /// @brief Checks if a ScoreSaber map characteristic/difficulty is ranked - /// @param map The ScoreSaber ranking information - /// @return If the map characteristic/difficulty is ranked - METACORE_EXPORT bool IsRanked(SSSongDiff const& map); - - /// @brief Calculates the BeatLeader PP value for a given accuracy, map, and modifiers - /// @param map The BeatLeader ranking information - /// @param accuracy The accuracy value from 0 to 1 - /// @param modifiers The current gameplay modifiers - /// @param failed If the player's health has reached 0, with or without No Fail - /// @return The exact PP value - METACORE_EXPORT float Calculate(BLSongDiff const& map, float accuracy, GlobalNamespace::GameplayModifiers* modifiers, bool failed); - /// @brief Calculates the ScoreSaber PP value for a given accuracy, map, and modifiers - /// @param map The ScoreSaber ranking information - /// @param accuracy The accuracy value from 0 to 1 - /// @param modifiers The current gameplay modifiers - /// @param failed If the player's health has reached 0, with or without No Fail - /// @return The exact PP value - METACORE_EXPORT float Calculate(SSSongDiff const& map, float accuracy, GlobalNamespace::GameplayModifiers* modifiers, bool failed); - - /// @brief Finds the BeatLeader and ScoreSaber ranking information for a given map characteristic/difficulty - /// @param map The map characteristic/difficulty to query - /// @param callback A callback called with the available ranking info once found - METACORE_EXPORT void GetMapInfo(GlobalNamespace::BeatmapKey map, std::function, std::optional)> callback); -} diff --git a/shared/songs.hpp b/shared/songs.hpp deleted file mode 100644 index 24cd3d9..0000000 --- a/shared/songs.hpp +++ /dev/null @@ -1,68 +0,0 @@ -#pragma once - -#include "GlobalNamespace/BeatmapKey.hpp" -#include "GlobalNamespace/BeatmapLevel.hpp" -#include "GlobalNamespace/BeatmapLevelPack.hpp" -#include "GlobalNamespace/IReadonlyBeatmapData.hpp" -#include "UnityEngine/Sprite.hpp" -#include "export.h" - -namespace MetaCore::Songs { - /// @brief Finds the hash in a level id - /// @param levelId The level id - /// @return The hash of the level if found, otherwise an empty string - METACORE_EXPORT std::string GetHash(std::string levelId); - /// @brief Finds the hash in a beatmap key - /// @param beatmap The beatmap key - /// @return The hash of the beatmap key if found, otherwise an empty string - METACORE_EXPORT std::string GetHash(GlobalNamespace::BeatmapKey beatmap); - /// @brief Finds the hash in a beatmap level - /// @param beatmap The beatmap level - /// @return The hash of the beatmap level if found, otherwise an empty string - METACORE_EXPORT std::string GetHash(GlobalNamespace::BeatmapLevel* beatmap); - - /// @brief Asynchronously retrieves the BeatmapData of a beatmap, will only run one task per beatmap at a time - /// @param beatmap The beatmap key - /// @param callback The callback with the data once it has been retrieved, or nullptr if it fails - METACORE_EXPORT void GetBeatmapData(GlobalNamespace::BeatmapKey beatmap, std::function callback); - - /// @brief Asynchronously retrieves the cover sprite of a beatmap - /// @param beatmap The beatmap level - /// @param callback The callback with the sprite once it has been retrieved, or nullptr if it fails - METACORE_EXPORT void GetSongCover(GlobalNamespace::BeatmapLevel* beatmap, std::function callback); - - /// @brief Finds the BeatmapLevel for a level id - /// @param levelId The level id - /// @return The beatmap level, or nullptr if not found - METACORE_EXPORT GlobalNamespace::BeatmapLevel* FindLevel(std::string levelId); - /// @brief Finds the BeatmapLevel for a beatmap key - /// @param beatmap The beatmap key - /// @return The beatmap level, or nullptr if not found - METACORE_EXPORT GlobalNamespace::BeatmapLevel* FindLevel(GlobalNamespace::BeatmapKey beatmap); - - /// @brief Gets the currently selected beatmap key - /// @param last If the last selected beatmap should be returned even if the detail view has been closed - /// @return The currently selected beatmap key, or default if none - METACORE_EXPORT GlobalNamespace::BeatmapKey GetSelectedKey(bool last = true); - /// @brief Gets the currently selected beatmap level - /// @param last If the last selected beatmap should be returned even if the detail view has been closed - /// @return The currently selected beatmap level, or nullptr if none - METACORE_EXPORT GlobalNamespace::BeatmapLevel* GetSelectedLevel(bool last = true); - /// @brief Gets the currently selected playlist, either OST or custom - /// @param last If the last selected playlist should be returned even if none is currently selected - /// @return The currently selected playlist, or nullptr if none - METACORE_EXPORT GlobalNamespace::BeatmapLevelPack* GetSelectedPlaylist(bool last = true); - - /// @brief Navigates to and selects a given beatmap level in the single player level selection, optionally in a playlist - /// @param level The level to select, will not select anything if nullptr or not in the playlist - /// @param playlist The playlist to select, will open the "All Songs" menu if nullptr - METACORE_EXPORT void SelectLevel(GlobalNamespace::BeatmapLevel* level, GlobalNamespace::BeatmapLevelPack* playlist = nullptr); - /// @brief Navigates to and selects a given beatmap level and difficulty in the single player level selection, optionally in a playlist - /// @param key The level, characteristic, and difficulty to select, will not select anything if nullptr or not in the playlist - /// @param playlist The playlist to select, will open the "All Songs" menu if nullptr - METACORE_EXPORT void SelectLevel(GlobalNamespace::BeatmapKey key, GlobalNamespace::BeatmapLevelPack* playlist = nullptr); - - /// @brief Plays the preview audio of a level - /// @param beatmap The level to play the preview of - METACORE_EXPORT void PlayLevelPreview(GlobalNamespace::BeatmapLevel* beatmap); -} diff --git a/shared/stats.hpp b/shared/stats.hpp deleted file mode 100644 index 45ad6f0..0000000 --- a/shared/stats.hpp +++ /dev/null @@ -1,59 +0,0 @@ -#pragma once - -#include "GlobalNamespace/NoteData.hpp" -#include "export.h" - -namespace MetaCore::Stats { - /// @brief Checks if a note is a fake note, such as from noodle extensions - /// @param data The note data - /// @return If the note is fake - METACORE_EXPORT bool IsFakeNote(GlobalNamespace::NoteData* data); - /// @brief Checks if a note should be counted in calculations involving swing parts or note count - /// @param data The note data - /// @return If the note should be counted - METACORE_EXPORT bool ShouldCountNote(GlobalNamespace::NoteData* data); - - constexpr int LeftSaber = 0; - constexpr int RightSaber = 1; - constexpr int BothSabers = 2; - - // no per-function documentation here, sorry - - METACORE_EXPORT int GetScore(int saber); - METACORE_EXPORT int GetMaxScore(int saber); - METACORE_EXPORT int GetSongMaxScore(); - METACORE_EXPORT int GetCombo(int saber); - METACORE_EXPORT int GetHighestCombo(int saber); - METACORE_EXPORT bool GetFullCombo(int saber); - METACORE_EXPORT int GetMultiplier(); - METACORE_EXPORT float GetMultiplierProgress(bool allLevels); - METACORE_EXPORT int GetMultiplierProgressInt(bool allLevels); - METACORE_EXPORT int GetMaxMultiplier(); - METACORE_EXPORT float GetMaxMultiplierProgress(bool allLevels); - METACORE_EXPORT int GetMaxMultiplierProgressInt(bool allLevels); - METACORE_EXPORT float GetHealth(); - METACORE_EXPORT float GetSongTime(); - METACORE_EXPORT float GetSongLength(); - METACORE_EXPORT float GetSongSpeed(); - METACORE_EXPORT int GetTotalNotes(int saber); - METACORE_EXPORT int GetNotesCut(int saber, bool includeUncounted = false); - METACORE_EXPORT int GetNotesMissed(int saber); - METACORE_EXPORT int GetNotesBadCut(int saber); - METACORE_EXPORT int GetBombsHit(int saber); - METACORE_EXPORT int GetWallsHit(); - METACORE_EXPORT int GetSongNotes(int saber); - METACORE_EXPORT int GetNotesRemaining(int saber); - METACORE_EXPORT float GetPreSwing(int saber); - METACORE_EXPORT float GetPostSwing(int saber); - METACORE_EXPORT float GetAccuracy(int saber); - METACORE_EXPORT float GetTimeDependence(int saber); - METACORE_EXPORT float GetAverageSpeed(int saber); - METACORE_EXPORT float GetBestSpeed5Secs(int saber); - METACORE_EXPORT float GetLastSecAngle(int saber); - METACORE_EXPORT float GetHighestSecAngle(int saber); - METACORE_EXPORT float GetModifierMultiplier(bool positive, bool negative); - METACORE_EXPORT int GetBestScore(); - METACORE_EXPORT int GetFails(); - METACORE_EXPORT int GetRestarts(); - METACORE_EXPORT int GetFCScore(int saber); -} diff --git a/shared/strings.hpp b/shared/strings.hpp index f19d91f..6764903 100644 --- a/shared/strings.hpp +++ b/shared/strings.hpp @@ -1,10 +1,9 @@ #pragma once -#include +#include "export.h" #include - -#include "export.h" +#include namespace MetaCore::Strings { /// @brief Santizes a path to remove all invalid characters with an aggressive whitelist diff --git a/shared/ui.hpp b/shared/ui.hpp deleted file mode 100644 index 43d93af..0000000 --- a/shared/ui.hpp +++ /dev/null @@ -1,164 +0,0 @@ -#pragma once - -#if __has_include("bsml/shared/BSML-Lite.hpp") - -#include "HMUI/IconSegmentedControl.hpp" -#include "HMUI/InputFieldView.hpp" -#include "HMUI/ModalView.hpp" -#include "HMUI/TextSegmentedControl.hpp" -#include "bsml/shared/BSML-Lite/TransformWrapper.hpp" -#include "bsml/shared/BSML/Components/Settings/DropdownListSetting.hpp" -#include "bsml/shared/BSML/Components/Settings/IncrementSetting.hpp" -#include "bsml/shared/BSML/Components/Settings/SliderSetting.hpp" -#include "bsml/shared/BSML/Components/Settings/ToggleSetting.hpp" - -namespace MetaCore::UI { - /// @brief Sets the preferred size parameters of the LayoutElement on a given object, adding one if not present - /// @param object The object with a LayoutElement or to have one added - /// @param width The preferred width for the LayoutElement (-1 is unset) - /// @param height The preferred height for the LayoutElement (-1 is unset) - /// @param flexWidth The flexible width for the LayoutElement (-1 is unset) - /// @param flexHeight The flexible height for the LayoutElement (-1 is unset) - inline void SetLayoutSize(BSML::Lite::TransformWrapper object, float width, float height, float flexWidth = -1, float flexHeight = -1); - /// @brief Sets the preferred width of all LayoutElements in the immediate children of a given object - /// @param parent The parent object - /// @param width The preferred width for parent's children - inline void SetChildrenWidth(BSML::Lite::TransformWrapper parent, float width); - /// @brief Sets the sorting order of a Canvas, enabling overrideSorting and temporarily toggling it active if necessary - /// @param canvas The object with a Canvas - /// @param value The new sorting order - inline void SetCanvasSorting(BSML::Lite::TransformWrapper canvas, int value); - - /// @brief Sets the value of an toggle setting without playing the animation, does not trigger its change callback - /// @param toggle The toggle setting to modify - /// @param value The new value - inline void InstantSetToggle(BSML::ToggleSetting* toggle, bool value); - /// @brief Sets the value of an increment setting, does not trigger its change callback - /// @param increment The increment setting to modify - /// @param value The new value - inline void SetIncrementValue(BSML::IncrementSetting* increment, float value); - /// @brief Sets the selected value for a dropdown setting, does not trigger its selection callback - /// @param dropdown The dropdown setting to modify - /// @param value The new value to have selected - /// @return If the value was in the possible options and was selected - inline bool SetDropdownValue(BSML::DropdownListSetting* dropdown, std::string value); - /// @brief Sets the options and selected value for a dropdown setting, does not trigger its selection callback - /// @param dropdown The dropdown setting to modify - /// @param values The new value options for the dropdown - /// @param selected The option that should be selected - /// @return If the option that should be selected was in the list of values, if not the first value will be selected - inline bool SetDropdownValues(BSML::DropdownListSetting* dropdown, std::vector values, std::string selected); - /// @brief Sets a cell of an IconSegmentedControl to be interactable or not - /// @param cell The cell to change - /// @param interactable If the cell should be interactable - inline void SetIconSegmentInteractable(HMUI::IconSegmentedControl* iconControl, int cell, bool interactable); - /// @brief Sets the sprite of an icon button (not a clickable image) - /// @param button The button to modify, previously created with CreateIconButton - /// @param sprite The new sprite for the button - inline void SetIconButtonSprite(UnityEngine::UI::Button* button, UnityEngine::Sprite* sprite); - /// @brief Resizes and adds an image to a BSML-created button - /// @param button The button to modify - /// @param sprite The sprite for the button - inline void ConvertToIconButton(UnityEngine::UI::Button* button, UnityEngine::Sprite* sprite); - - /// @brief Creates extra increment buttons around an increment setting, shifting the increment to the left to compensate - /// @param setting The increment setting to modify - /// @param increment The amount to increase or decrease the setting by when the new buttons are pressed - inline void AddIncrementIncrement(BSML::IncrementSetting* setting, float increment); - - /// @brief Adds or sets a callback for when a specific slider is released (both by dragging or a single press) - /// @param slider The slider to monitor - /// @param onEndDrag The callback to run when released - inline void AddSliderEndDrag(BSML::SliderSetting* slider, std::function onEndDrag); - - /// @brief Adds or sets a callback for when a specific string setting's keyboard closes, either by clicking away, pressing the OK button - /// @param input The string setting to monitor - /// @param onClosed The callback to run when the keyboard closes or the clear button is pressed - inline void AddStringSettingOnClose(HMUI::InputFieldView* input, std::function onClosed); - /// @brief Adds or sets callbacks for when a specific string setting's keyboard is closed, and for when its OK button is pressed - /// @param input The string setting to monitor - /// @param onClosed The callback to run when the keyboard is closed by clicking away, or the clear button is pressed if onOk is null - /// @param onOk The callback to run when the OK button is pressed, or the clear button is pressed - inline void AddStringSettingOnClose(HMUI::InputFieldView* input, std::function onClosed, std::function onOk); - - /// @brief Animates a modal to fade in or out so that modals on top of it have clear boundaries - /// @param modal The modal to animate - /// @param out If the modal should fade out and become slightly transparent - inline void AnimateModal(HMUI::ModalView* modal, bool out); - /// @brief Adds animations to a dropdown such that the modal behind it fades out when the dropdown modal is opened - /// @param dropdown The dropdown to trigger the animations - /// @param behindModal The modal to fade out behind the dropdown modal - inline void AddModalAnimations(HMUI::SimpleTextDropdown* dropdown, HMUI::ModalView* behindModal); - - /// @brief Moves a slider created with BSML to have a different parent, and removes its label text - /// @param slider The slider to move - /// @param parent The new parent for the slider - /// @param width The new width for the slider area - /// @return The new slider component - inline BSML::SliderSetting* ReparentSlider(BSML::SliderSetting* slider, BSML::Lite::TransformWrapper parent, float width); - - /// @brief Creates a square button with an image inside instead of text - /// @param parent The parent for the button - /// @param sprite The sprite to display inside the button - /// @param onClick The callback when the button is clicked - /// @return The created button - inline UnityEngine::UI::Button* - CreateIconButton(BSML::Lite::TransformWrapper parent, UnityEngine::Sprite* sprite, std::function onClick = nullptr); - /// @brief Creates a square button with an image inside instead of text, with a non-default button template - /// @param parent The parent for the button - /// @param sprite The sprite to display inside the button - /// @param buttonTemplate The instantiated template to use for the button - /// @param onClick The callback when the button is clicked - /// @return The created button - inline UnityEngine::UI::Button* CreateIconButton( - BSML::Lite::TransformWrapper parent, UnityEngine::Sprite* sprite, std::string buttonTemplate, std::function onClick = nullptr - ); - - /// @brief Creates a dropdown setting for strings, with a fixed height - /// @param parent The parent for the dropdown - /// @param name The setting name to display next to the dropdown - /// @param value The starting value of the dropdown - /// @param values The list of possible values for the dropdown - /// @param onChange The callback when a dropdown option is selected (by displayed name) - /// @return The created dropdown setting - inline BSML::DropdownListSetting* CreateDropdown( - BSML::Lite::TransformWrapper parent, - std::string name, - std::string value, - std::vector values, - std::function onChange - ); - /// @brief Creates a dropdown setting for an enum, with a fixed height - /// @param parent The parent for the dropdown - /// @param name The setting name to display next to the dropdown - /// @param value The starting value of the dropdown - /// @param values The list of possible values for the dropdown - /// @param onChange The callback when a dropdown option is selected (by index) - /// @return The created dropdown setting - inline BSML::DropdownListSetting* CreateDropdownEnum( - BSML::Lite::TransformWrapper parent, std::string name, int value, std::vector values, std::function onChange - ); - /// @brief Creates a TextSegmentedControl like in the help menu - /// @param parent The parent for the TextSegmentedControl - /// @param texts The texts to display in each segment - /// @param onSelected The callback when a segment is selected (by index) - /// @return The created TextSegmentedControl - inline HMUI::TextSegmentedControl* - CreateTextSegmentedControl(BSML::Lite::TransformWrapper parent, std::vector texts, std::function onSelected); - /// @brief Creates an IconSegmentedControl like the Custom Levels/Favorites/etc selector - /// @param parent The parent for the IconSegmentedControl - /// @param icons The icons to display in each segment - /// @param onSelected The callback when a segment is selected (by index) - /// @param hints The hover hints to display for each segment - /// @return The created IconSegmentedControl - inline HMUI::IconSegmentedControl* CreateIconSegmentedControl( - BSML::Lite::TransformWrapper parent, - std::vector icons, - std::function onSelected, - std::vector hints = {} - ); -} - -#include "uiimpl.hpp" - -#endif diff --git a/shared/uiimpl.hpp b/shared/uiimpl.hpp deleted file mode 100644 index 77ad7ed..0000000 --- a/shared/uiimpl.hpp +++ /dev/null @@ -1,325 +0,0 @@ -#pragma once - -#include "HMUI/AnimatedSwitchView.hpp" -#include "HMUI/ButtonBinder.hpp" -#include "HMUI/IconSegmentedControlCell.hpp" -#include "HMUI/ModalView.hpp" -#include "UnityEngine/Canvas.hpp" -#include "UnityEngine/CanvasGroup.hpp" -#include "UnityEngine/Resources.hpp" -#include "UnityEngine/UI/LayoutElement.hpp" -#include "bsml/shared/BSML-Lite.hpp" -#include "delegates.hpp" -#include "internals.hpp" -#include "ui.hpp" -#include "unity.hpp" - -#define UUI UnityEngine::UI - -inline void MetaCore::UI::SetLayoutSize(BSML::Lite::TransformWrapper object, float width, float height, float flexWidth, float flexHeight) { - auto layout = Engine::GetOrAddComponent(object); - layout->preferredWidth = width; - layout->preferredHeight = height; - layout->flexibleWidth = flexWidth; - layout->flexibleHeight = flexHeight; -} - -inline void MetaCore::UI::SetChildrenWidth(BSML::Lite::TransformWrapper parent, float width) { - for (int i = 0; i < parent->GetChildCount(); i++) { - bool first = true; - for (auto layout : parent->GetChild(i)->GetComponents()) { - if (first) - layout->preferredWidth = width; - else - UnityEngine::Object::Destroy(layout); - first = false; - } - } -} - -inline void MetaCore::UI::SetCanvasSorting(BSML::Lite::TransformWrapper canvas, int value) { - auto object = canvas->gameObject; - bool wasActive = object->activeSelf; - object->active = true; - auto component = object->GetComponent(); - component->overrideSorting = true; - component->sortingOrder = value; - object->active = wasActive; -} - -inline void MetaCore::UI::InstantSetToggle(BSML::ToggleSetting* toggle, bool value) { - if (toggle->toggle->m_IsOn == value) - return; - toggle->toggle->m_IsOn = value; - auto animatedSwitch = toggle->toggle->GetComponent(); - animatedSwitch->HandleOnValueChanged(value); - animatedSwitch->_switchAmount = value; - animatedSwitch->LerpPosition(value); - animatedSwitch->LerpColors(value, animatedSwitch->_highlightAmount, animatedSwitch->_disabledAmount); -} - -inline void MetaCore::UI::SetIncrementValue(BSML::IncrementSetting* increment, float value) { - increment->currentValue = value; - increment->UpdateState(); -} - -inline bool MetaCore::UI::SetDropdownValue(BSML::DropdownListSetting* dropdown, std::string value) { - auto values = ListW(dropdown->values); - for (int i = 0; i < values.size(); i++) { - if (values[i] == value) { - dropdown->set_Value(dropdown->values[i]); - return true; - } - } - return false; -} - -inline bool MetaCore::UI::SetDropdownValues(BSML::DropdownListSetting* dropdown, std::vector values, std::string selected) { - auto texts = ListW::New(values.size()); - int idx = -1; - bool present = true; - for (int i = 0; i < values.size(); i++) { - texts->Add((System::Object*) StringW(values[i]).convert()); - if (values[i] == selected) - idx = i; - } - if (idx == -1) { - idx = 0; - present = false; - } - dropdown->values = texts; - dropdown->UpdateChoices(); - if (!texts.empty()) - dropdown->set_Value(texts[idx]); - return present; -} - -inline void MetaCore::UI::SetIconSegmentInteractable(HMUI::IconSegmentedControl* iconControl, int cell, bool interactable) { - iconControl->_dataItems[cell]->interactable = false; - auto cellInstance = iconControl->_cells->get_Item(cell).cast(); - cellInstance->hideBackgroundImage = !interactable; - cellInstance->enabled = interactable; - cellInstance->SetHighlight(false, HMUI::SelectableCell::TransitionType::Instant, true); -} - -inline void MetaCore::UI::SetIconButtonSprite(UUI::Button* button, UnityEngine::Sprite* sprite) { - if (auto icon = button->transform->Find("MetaCoreButtonImage")) - icon->GetComponent()->sprite = sprite; -} - -inline void MetaCore::UI::ConvertToIconButton(UUI::Button* button, UnityEngine::Sprite* sprite) { - auto icon = BSML::Lite::CreateImage(button, sprite); - icon->name = "MetaCoreButtonImage"; - icon->preserveAspect = true; - icon->transform->localScale = {0.8, 0.8, 0.8}; - MetaCore::UI::SetLayoutSize(button, 8, 8); -} - -inline void MetaCore::UI::AddIncrementIncrement(BSML::IncrementSetting* setting, float increment) { - auto transform = setting->transform->Find("ValuePicker").cast(); - auto currentPos = transform->anchoredPosition; - transform->anchoredPosition = {currentPos.x - 6, currentPos.y}; - - auto leftButton = BSML::Lite::CreateUIButton(transform, "", "DecButton", {-7, 0}, {7, 8}, [setting, increment]() { - setting->currentValue -= increment; - setting->EitherPressed(); - }); - auto rightButton = BSML::Lite::CreateUIButton(transform, "", "IncButton", {7, 0}, {8, 8}, [setting, increment]() { - setting->currentValue += increment; - setting->EitherPressed(); - }); -} - -inline void MetaCore::UI::AddSliderEndDrag(BSML::SliderSetting* slider, std::function onEndDrag) { - std::function boundCallback = [slider = slider->slider, onEndDrag]() { - onEndDrag(slider->value); - }; - Internals::SetEndDragUI(slider->slider, boundCallback); - if (slider->showButtons && slider->incButton && slider->decButton) { - slider->incButton->onClick->AddListener(Delegates::MakeUnityAction(boundCallback)); - slider->decButton->onClick->AddListener(Delegates::MakeUnityAction(boundCallback)); - } -} - -inline void MetaCore::UI::AddStringSettingOnClose(HMUI::InputFieldView* input, std::function onClosed) { - AddStringSettingOnClose(input, onClosed, onClosed); -} - -inline void MetaCore::UI::AddStringSettingOnClose(HMUI::InputFieldView* input, std::function onClosed, std::function onOk) { - auto clearCallback = Internals::SetKeyboardCloseUI(input, std::move(onClosed), std::move(onOk)); - input->_buttonBinder->AddBinding(input->_clearSearchButton, Delegates::MakeSystemAction(clearCallback)); -} - -inline void MetaCore::UI::AnimateModal(HMUI::ModalView* modal, bool out) { - auto bg = modal->transform->Find("BG")->GetComponent(); - auto canvas = modal->GetComponent(); - - if (out) { - bg->color = {0.2, 0.2, 0.2, 1}; - canvas->alpha = 0.9; - } else { - bg->color = {1, 1, 1, 1}; - canvas->alpha = 1; - } -} - -inline void MetaCore::UI::AddModalAnimations(HMUI::SimpleTextDropdown* dropdown, HMUI::ModalView* behindModal) { - dropdown->_button->onClick->AddListener(Delegates::MakeUnityAction([behindModal]() { AnimateModal(behindModal, true); })); - dropdown->add_didSelectCellWithIdxEvent(Delegates::MakeSystemAction([behindModal](UnityW, int) { - AnimateModal(behindModal, false); - })); - dropdown->_modalView->add_blockerClickedEvent(Delegates::MakeSystemAction([behindModal]() { AnimateModal(behindModal, false); })); - if (auto modal = il2cpp_utils::try_cast(dropdown->_modalView.unsafePtr()).value_or(nullptr)) - modal->_animateParentCanvas = false; -} - -inline BSML::SliderSetting* MetaCore::UI::ReparentSlider(BSML::SliderSetting* slider, BSML::Lite::TransformWrapper parent, float width) { - auto newSlider = slider->slider->gameObject->AddComponent(); - newSlider->slider = slider->slider; - newSlider->onChange = std::move(slider->onChange); - newSlider->formatter = std::move(slider->formatter); - newSlider->isInt = slider->isInt; - newSlider->increments = slider->increments; - newSlider->slider->minValue = slider->slider->minValue; - newSlider->slider->maxValue = slider->slider->maxValue; - auto transform = newSlider->GetComponent(); - transform->sizeDelta = {width, 0}; - transform->SetParent(parent->transform, false); - newSlider->slider->valueSize = newSlider->slider->_containerRect->rect.width / 2; - UnityEngine::Object::DestroyImmediate(slider->gameObject); - // due to the weird way bsml does string formatting for sliders, - // this needs to be called after destroying the old slider - newSlider->Setup(); - return newSlider; -} - -inline UUI::Button* MetaCore::UI::CreateIconButton(BSML::Lite::TransformWrapper parent, UnityEngine::Sprite* sprite, std::function onClick) { - auto button = BSML::Lite::CreateUIButton(parent, "", onClick); - ConvertToIconButton(button, sprite); - return button; -} - -inline UUI::Button* MetaCore::UI::CreateIconButton( - BSML::Lite::TransformWrapper parent, UnityEngine::Sprite* sprite, std::string buttonTemplate, std::function onClick -) { - auto button = BSML::Lite::CreateUIButton(parent, "", buttonTemplate, onClick); - ConvertToIconButton(button, sprite); - return button; -} - -inline BSML::DropdownListSetting* MetaCore::UI::CreateDropdown( - BSML::Lite::TransformWrapper parent, - std::string name, - std::string value, - std::vector values, - std::function onChange -) { - auto object = BSML::Lite::CreateDropdown(parent, name, value, values, [onChange](StringW value) { onChange(value); }); - object->transform->parent->GetComponent()->preferredHeight = 7; - return object; -} - -inline BSML::DropdownListSetting* MetaCore::UI::CreateDropdownEnum( - BSML::Lite::TransformWrapper parent, std::string name, int value, std::vector values, std::function onChange -) { - auto object = BSML::Lite::CreateDropdown(parent, name, values[value], values, [onChange, values](StringW value) { - for (int i = 0; i < values.size(); i++) { - if (value == values[i]) { - onChange(i); - break; - } - } - }); - object->transform->parent->GetComponent()->preferredHeight = 7; - if (auto behindModal = parent->gameObject->GetComponentInParent(true)) - AddModalAnimations(object->dropdown, behindModal); - return object; -} - -inline HMUI::TextSegmentedControl* MetaCore::UI::CreateTextSegmentedControl( - BSML::Lite::TransformWrapper parent, std::vector texts, std::function onSelected -) { - static UnityW textSegmentedControlTemplate; - if (!textSegmentedControlTemplate) { - textSegmentedControlTemplate = UnityEngine::Resources::FindObjectsOfTypeAll()->FirstOrDefault([](auto x) { - UnityEngine::Transform* parent = x->transform->parent; - if (!parent) - return false; - if (parent->name != "PlayerStatisticsViewController") - return false; - return x->_container != nullptr; - }); - } - - auto textSegmentedControl = UnityEngine::Object::Instantiate(textSegmentedControlTemplate.ptr(), parent->transform, false); - textSegmentedControl->_dataSource = nullptr; - - UnityEngine::GameObject* gameObject = textSegmentedControl->gameObject; - gameObject->name = "MetaCoreTextSegmentedControl"; - textSegmentedControl->_container = textSegmentedControlTemplate->_container; - - auto transform = gameObject->GetComponent(); - transform->anchoredPosition = {0, 0}; - int childCount = transform->childCount; - for (int i = 1; i <= childCount; i++) - UnityEngine::Object::DestroyImmediate(transform->GetChild(childCount - i)->gameObject); - - auto textList = ListW::New(texts.size()); - for (auto text : texts) - textList->Add(text); - textSegmentedControl->SetTexts(textList->i___System__Collections__Generic__IReadOnlyList_1_T_(), nullptr); - - if (onSelected) - textSegmentedControl->add_didSelectCellEvent(Delegates::MakeSystemAction([onSelected](UnityW, int selectedIndex) { - onSelected(selectedIndex); - })); - - gameObject->active = true; - return textSegmentedControl; -} - -inline HMUI::IconSegmentedControl* MetaCore::UI::CreateIconSegmentedControl( - BSML::Lite::TransformWrapper parent, std::vector icons, std::function onSelected, std::vector hints -) { - static UnityW iconSegmentedControlTemplate; - if (!iconSegmentedControlTemplate) { - iconSegmentedControlTemplate = UnityEngine::Resources::FindObjectsOfTypeAll()->FirstOrDefault([](auto x) { - if (std::string(x->name) != "BeatmapCharacteristicSegmentedControl") - return false; - return x->_container != nullptr; - }); - } - - auto iconSegmentedControl = UnityEngine::Object::Instantiate(iconSegmentedControlTemplate.ptr(), parent->transform, false); - iconSegmentedControl->_isInitialized = false; - iconSegmentedControl->_dataSource = nullptr; - iconSegmentedControl->_hideCellBackground = false; - iconSegmentedControl->_overrideCellSize = true; - iconSegmentedControl->_iconSize = 4; - iconSegmentedControl->_padding = 1; - - UnityEngine::GameObject* gameObject = iconSegmentedControl->gameObject; - gameObject->name = "MetaCoreIconSegmentedControl"; - iconSegmentedControl->_container = iconSegmentedControlTemplate->_container; - - auto transform = gameObject->GetComponent(); - transform->anchoredPosition = {0, 0}; - int childCount = transform->childCount; - for (int i = 1; i <= childCount; i++) - UnityEngine::Object::DestroyImmediate(transform->GetChild(childCount - i)->gameObject); - - auto dataList = ListW::New(icons.size()); - for (int i = 0; i < icons.size(); i++) - dataList->Add(HMUI::IconSegmentedControl::DataItem::New_ctor(icons[i], i < hints.size() ? hints[i] : "", true)); - iconSegmentedControl->SetData(dataList.to_array()); - - if (onSelected) - iconSegmentedControl->add_didSelectCellEvent(Delegates::MakeSystemAction([onSelected](UnityW, int selectedIndex) { - onSelected(selectedIndex); - })); - - gameObject->active = true; - return iconSegmentedControl; -} - -#undef UUI diff --git a/shared/unity.hpp b/shared/unity.hpp index 2804022..8129184 100644 --- a/shared/unity.hpp +++ b/shared/unity.hpp @@ -1,16 +1,19 @@ #pragma once +#include "export.h" + +#include + +#if __has_include("BSML/shared/Helpers/utilities.hpp") +#include "BSML/shared/Helpers/utilities.hpp" +#endif + #include "UnityEngine/GameObject.hpp" #include "UnityEngine/Quaternion.hpp" #include "UnityEngine/Sprite.hpp" #include "UnityEngine/Texture2D.hpp" #include "UnityEngine/Transform.hpp" #include "UnityEngine/Vector3.hpp" -#include "export.h" - -#if __has_include("BSML/shared/Helpers/utilities.hpp") -#include "BSML/shared/Helpers/utilities.hpp" -#endif namespace MetaCore::Engine { template @@ -134,6 +137,12 @@ namespace MetaCore::Engine { /// @param wait A function that returns true once the callback should be run /// @param callback The function to be run once the condition is true METACORE_EXPORT void ScheduleMainThread(std::function wait, std::function callback); + /// @brief Schedules a function to be run on the main thread once a task is completed + /// @param task A task with an IsCompleted field or property that is set once it finishes + /// @param callback The function to be run once the task is finished + inline void AwaitMainThread(auto task, std::function callback) { + ScheduleMainThread([task]() { return task->IsCompleted; }, std::move(callback)); + } /// @brief Sets a function to be run when a given object is enabled (via Unity's OnEnable callback) /// @param object The object to attach the callback to diff --git a/src/events.cpp b/src/events.cpp deleted file mode 100644 index f9150a0..0000000 --- a/src/events.cpp +++ /dev/null @@ -1,134 +0,0 @@ -#include "events.hpp" - -#include "input.hpp" -#include "main.hpp" -#include "maps.hpp" - -static std::map> customEvents = {}; -static std::map>> callbacks = {}; -static MetaCore::IndexMap> globalCallbacks = {}; - -static MetaCore::IndexMap> registrations = {}; - -static int maxEvent = (int) MetaCore::Events::EventMax; - -struct EventGuard { - bool Guard(int newEvent) { - if (events.contains(newEvent)) - return false; - events.emplace(newEvent); - event = newEvent; - return true; - } - ~EventGuard() { - if (event >= 0) - events.erase(event); - } - - private: - int event = -1; - static inline std::set events = {}; -}; - -int MetaCore::Events::RegisterEvent(std::string mod, int modEvent) { - if (!customEvents.contains(mod)) - customEvents[mod] = {}; - else if (customEvents[mod].contains(modEvent)) - return -1; - customEvents[mod][modEvent] = ++maxEvent; - return maxEvent; -} - -int MetaCore::Events::FindEvent(std::string mod, int modEvent) { - if (!customEvents.contains(mod)) - return -1; - if (!customEvents[mod].contains(modEvent)) - return -1; - return customEvents[mod][modEvent]; -} - -template -static int AddCallbackImpl(MetaCore::IndexMap>& list, std::function callback, bool once, int event) { - if (!once) - return registrations.push({Global, event, list.push(std::move(callback))}); - - int idx = list.push(nullptr); - int registered = registrations.push({Global, event, idx}); - // list should be a safe reference to keep around - list[idx] = [&list, idx, registered, callback = std::move(callback)](Ts... params) { - callback(params...); - // guaranteed to still have this function at idx, since it's being called - list.erase(idx); - registrations.erase(registered); - }; - return registered; -} - -template -static int AddCallbackImpl(std::function callback, bool once, int event) { - if constexpr (Global) - return AddCallbackImpl(globalCallbacks, callback, once, event); - else - return AddCallbackImpl(callbacks[event], callback, once, event); -} - -int MetaCore::Events::AddCallback(int event, std::function callback, bool once) { - if (event < 0 || event > maxEvent) - return -1; - if (!callbacks.contains(event)) - callbacks[event] = {}; - return AddCallbackImpl(std::move(callback), once, event); -} - -int MetaCore::Events::AddCallback(std::string mod, int modEvent, std::function callback, bool once) { - return AddCallback(FindEvent(mod, modEvent), std::move(callback), once); -} - -int MetaCore::Events::AddCallback(std::function callback, bool once) { - return AddCallbackImpl(std::move(callback), once, -1); -} - -void MetaCore::Events::RemoveCallback(int id) { - if (!registrations.contains(id)) - return; - auto const& [global, event, idx] = registrations[id]; - if (global) - globalCallbacks.erase(idx); - else if (callbacks.contains(event)) - callbacks[event].erase(idx); -} - -template -static inline void SafeCallCallbacks(MetaCore::IndexMap> const& callbacks, Ts... params) { - std::vector> copies; - copies.reserve(callbacks.size()); - for (auto const& [_, callback] : callbacks) - copies.emplace_back(callback); - for (auto const& callback : copies) - callback(params...); -} - -bool MetaCore::Events::Broadcast(int event) { - if (event < 0 || event > maxEvent) - return false; - - EventGuard guard; - if (!guard.Guard(event)) { - logger.error("Event {} was broadcast even though it was already being run!", event); - return false; - } - - SafeCallCallbacks(globalCallbacks, event); - if (callbacks.contains(event)) - SafeCallCallbacks(callbacks[event]); - - return true; -} - -bool MetaCore::Events::Broadcast(std::string mod, int modEvent) { - if (!customEvents.contains(mod)) - return false; - if (!customEvents[mod].contains(modEvent)) - return false; - return Broadcast(customEvents[mod][modEvent]); -} diff --git a/src/game.cpp b/src/game.cpp deleted file mode 100644 index efe5306..0000000 --- a/src/game.cpp +++ /dev/null @@ -1,167 +0,0 @@ -#include "game.hpp" - -#include "GlobalNamespace/FadeInOutController.hpp" -#include "GlobalNamespace/LevelCollectionNavigationController.hpp" -#include "GlobalNamespace/LevelCollectionViewController.hpp" -#include "GlobalNamespace/LevelSelectionFlowCoordinator.hpp" -#include "GlobalNamespace/LevelSelectionNavigationController.hpp" -#include "GlobalNamespace/ObservableVariableSO_1.hpp" -#include "GlobalNamespace/PlayerDataFileModel.hpp" -#include "GlobalNamespace/PlayerDataModel.hpp" -#include "UnityEngine/Resources.hpp" -#include "Zenject/SceneContext.hpp" -#include "Zenject/SceneContextRegistry.hpp" -#include "events.hpp" -#include "main.hpp" -#include "types.hpp" -#include "unity.hpp" - -using namespace GlobalNamespace; - -static std::set scoreDisablers; - -static std::set cameraDisablers; - -void MetaCore::Game::SetScoreSubmission(std::string mod, bool enable) { - bool wasEmpty = scoreDisablers.empty(); - if (enable) - scoreDisablers.erase(mod); - else - scoreDisablers.emplace(std::move(mod)); - if (scoreDisablers.empty() != wasEmpty) - Events::Broadcast(Events::ScoreSubmission); -} - -void MetaCore::Game::DisableScoreSubmissionOnce(std::string mod) { - Events::AddCallback(Events::GameplaySceneEnded, [mod]() { SetScoreSubmission(std::move(mod), true); }, true); - SetScoreSubmission(std::move(mod), false); -} - -bool MetaCore::Game::IsScoreSubmissionDisabled() { - return !scoreDisablers.empty(); -} - -std::set const& MetaCore::Game::GetScoreSubmissionDisablers() { - return scoreDisablers; -} - -static FadeInOutController* GetFadeInOut() { - static UnityW fadeInOutController; - if (!fadeInOutController) { - fadeInOutController = UnityEngine::Resources::FindObjectsOfTypeAll()->FirstOrDefault(); - if (fadeInOutController) - MetaCore::Engine::SetOnDestroy(fadeInOutController, []() { fadeInOutController = nullptr; }); - } - if (!fadeInOutController) - logger.warn("GetFadeInOut returning null"); - return fadeInOutController.unsafePtr(); -} - -static void UpdateFadeInOut(float duration) { - auto controller = GetFadeInOut(); - bool fadedOut = controller->_easeValue->value == 0; - bool toFadeOut = MetaCore::Game::IsCameraFadedOut(); - if (fadedOut == toFadeOut) - return; - controller->StopAllCoroutines(); - if (duration < 0) - duration = toFadeOut ? controller->_defaultFadeOutDuration : controller->_defaultFadeInDuration; - if (duration == 0) - controller->_easeValue->value = toFadeOut ? 0 : 1; - else if (toFadeOut) - controller->StartCoroutine(controller->Fade(controller->_easeValue->value, 0, duration, 0, controller->_fadeOutCurve, nullptr)); - else - controller->StartCoroutine(controller->Fade(0, 1, duration, controller->_fadeInStartDelay, controller->_fadeInCurve, nullptr)); -} - -void MetaCore::Game::SetCameraFadeOut(std::string mod, bool fadeOut, float fadeDuration) { - bool wasEmpty = cameraDisablers.empty(); - if (fadeOut) - cameraDisablers.emplace(std::move(mod)); - else - cameraDisablers.erase(mod); - if (wasEmpty == fadeOut) - UpdateFadeInOut(fadeDuration); -} - -bool MetaCore::Game::IsCameraFadedOut() { - return !cameraDisablers.empty(); -} - -void MetaCore::Game::PlaySongPreview(UnityEngine::AudioClip* audio, float volume, float start, float duration) { - GetMainFlowCoordinator() - ->_soloFreePlayFlowCoordinator->levelSelectionNavigationController->_levelCollectionNavigationController->_levelCollectionViewController - ->_songPreviewPlayer->CrossfadeTo(audio, volume, start, duration, nullptr); -} - -BeatmapCharacteristicCollection* MetaCore::Game::GetCharacteristics() { - return GetPlayerData()->_playerDataFileModel->_beatmapCharacteristicCollection; -} - -EnvironmentsListModel* MetaCore::Game::GetEnvironments() { - return GetPlayerData()->_playerDataFileModel->_environmentsListModel; -} - -PlayerDataModel* MetaCore::Game::GetPlayerData() { - return GetMainFlowCoordinator()->_playerDataModel; -} - -UnityEngine::Material* MetaCore::Game::GetCurvedCornersMaterial() { - static UnityW material; - if (!material) - material = UnityEngine::Resources::FindObjectsOfTypeAll()->First([](auto mat) { - return mat->name == std::string("UINoGlowRoundEdge"); - }); - return material; -} - -MenuTransitionsHelper* MetaCore::Game::GetMenuTransitionsHelper() { - static UnityW menuTransitionsHelper; - if (!menuTransitionsHelper) { - menuTransitionsHelper = UnityEngine::Resources::FindObjectsOfTypeAll()->FirstOrDefault(); - if (menuTransitionsHelper) - Engine::SetOnDestroy(menuTransitionsHelper, []() { menuTransitionsHelper = nullptr; }); - } - if (!menuTransitionsHelper) - logger.warn("GetMenuTransitionsHelper returning null"); - return menuTransitionsHelper.unsafePtr(); -} - -MainFlowCoordinator* MetaCore::Game::GetMainFlowCoordinator() { - static UnityW mainFlowCoordinator; - if (!mainFlowCoordinator) { - mainFlowCoordinator = UnityEngine::Resources::FindObjectsOfTypeAll()->FirstOrDefault(); - if (mainFlowCoordinator) - Engine::SetOnDestroy(mainFlowCoordinator, []() { mainFlowCoordinator = nullptr; }); - } - if (!mainFlowCoordinator) - logger.warn("GetMainFlowCoordinator returning null"); - return mainFlowCoordinator.unsafePtr(); -} - -MainSystemInit* MetaCore::Game::GetMainSystemInit() { - static UnityW mainSystemInit; - if (!mainSystemInit) { - mainSystemInit = UnityEngine::Resources::FindObjectsOfTypeAll()->FirstOrDefault(); - if (mainSystemInit) - Engine::SetOnDestroy(mainSystemInit, []() { mainSystemInit = nullptr; }); - } - if (!mainSystemInit) - logger.warn("GetMainSystemInit returning null"); - return mainSystemInit.unsafePtr(); -} - -Zenject::DiContainer* MetaCore::Game::GetAppDiContainer() { - // notable alternative is - // Zenject::ProjectContext::Instance()->_container->Resolve()->GetContextForScene("QuestInit")->_container - static UnityW appSceneContext; - if (!appSceneContext) { - appSceneContext = UnityEngine::GameObject::Find("AppCoreSceneContext"); - if (appSceneContext) - Engine::SetOnDestroy(appSceneContext, []() { appSceneContext = nullptr; }); - } - auto container = appSceneContext ? appSceneContext->GetComponent()->_container : nullptr; - if (!container) - logger.warn("GetAppDiContainer returning null"); - return container; -} diff --git a/src/hooks.cpp b/src/hooks.cpp index 223e5e3..f061c25 100644 --- a/src/hooks.cpp +++ b/src/hooks.cpp @@ -1,728 +1,43 @@ #include "hooks.hpp" -#include "GlobalNamespace/AnnotatedBeatmapLevelCollectionsViewController.hpp" -#include "GlobalNamespace/AudioTimeSyncController.hpp" -#include "GlobalNamespace/BeatmapObjectExecutionRatingsRecorder.hpp" -#include "GlobalNamespace/BeatmapObjectManager.hpp" -#include "GlobalNamespace/CutScoreBuffer.hpp" -#include "GlobalNamespace/FadeInOutController.hpp" -#include "GlobalNamespace/GameEnergyCounter.hpp" -#include "GlobalNamespace/GameScenesManager.hpp" -#include "GlobalNamespace/LevelCompletionResults.hpp" -#include "GlobalNamespace/LevelCompletionResultsHelper.hpp" -#include "GlobalNamespace/MenuTransitionsHelper.hpp" -#include "GlobalNamespace/MissionCompletionResults.hpp" -#include "GlobalNamespace/MissionDataSO.hpp" -#include "GlobalNamespace/MissionLevelDetailViewController.hpp" -#include "GlobalNamespace/MissionNode.hpp" -#include "GlobalNamespace/MultiplayerLocalActivePlayerInGameMenuController.hpp" -#include "GlobalNamespace/NoteController.hpp" -#include "GlobalNamespace/NoteCutInfo.hpp" -#include "GlobalNamespace/OVRInput.hpp" -#include "GlobalNamespace/OculusAdvancedHapticFeedbackPlayer.hpp" -#include "GlobalNamespace/OculusVRHelper.hpp" -#include "GlobalNamespace/PartyFreePlayFlowCoordinator.hpp" -#include "GlobalNamespace/PauseMenuManager.hpp" -#include "GlobalNamespace/ScoreController.hpp" -#include "GlobalNamespace/ScoreModel.hpp" -#include "GlobalNamespace/ScoreMultiplierCounter.hpp" -#include "GlobalNamespace/ScoringElement.hpp" -#include "GlobalNamespace/StandardLevelDetailView.hpp" -#include "GlobalNamespace/UIKeyboardManager.hpp" -#include "HMUI/IconSegmentedControl.hpp" -#include "HMUI/IconSegmentedControlCell.hpp" -#include "HMUI/InputFieldView.hpp" -#include "System/Action.hpp" -#include "UnityEngine/GameObject.hpp" -#include "UnityEngine/Time.hpp" -#include "UnityEngine/WaitForSeconds.hpp" -#include "VRUIControls/VRGraphicRaycaster.hpp" -#include "beatsaber-hook/shared/utils/hooking.hpp" -#include "custom-types/shared/coroutine.hpp" -#include "events.hpp" -#include "game.hpp" -#include "input.hpp" -#include "internals.hpp" #include "main.hpp" -#include "songs.hpp" -#include "stats.hpp" #include "types.hpp" -#include "unity.hpp" +#include "beatsaber-hook/shared/binary.hpp" -using namespace GlobalNamespace; using namespace MetaCore; -static bool inGameplayScene = false; - -static std::set pressedButtons; - -static bool IsGameplayScene(UnityW scene) { - return scene && scene.try_cast(); -} - -static void CheckInitialize(UnityW scene) { - if (!IsGameplayScene(scene)) - return; - logger.debug("gameplay scene start"); - Internals::Initialize(); - Events::Broadcast(Events::GameplaySceneStarted); - inGameplayScene = true; -} - -static void CheckEarlyFinish(LevelCompletionResults::LevelEndAction action) { - if (!inGameplayScene) - return; - Internals::Finish(action == LevelCompletionResults::LevelEndAction::Quit, action == LevelCompletionResults::LevelEndAction::Restart); - if (Internals::mapWasRestarted) - Events::Broadcast(Events::MapRestarted); - else - Events::Broadcast(Events::MapEnded); -} - -static void CheckSceneFinish() { - if (!inGameplayScene) - return; - logger.debug("gameplay scene finish"); - Events::Broadcast(Events::GameplaySceneEnded); - inGameplayScene = false; -} - -// update score and max score -MAKE_AUTO_HOOK_MATCH( - ScoreController_DespawnScoringElement, &ScoreController::DespawnScoringElement, void, ScoreController* self, ScoringElement* scoringElement -) { - ScoreController_DespawnScoringElement(self, scoringElement); - - int cutScore = scoringElement->cutScore * scoringElement->multiplier; - int maxCutScore = scoringElement->maxPossibleCutScore * scoringElement->maxMultiplier; - - bool badCut = scoringElement->multiplierEventType == ScoreMultiplierCounter::MultiplierEventType::Negative && - scoringElement->wouldBeCorrectCutBestPossibleMultiplierEventType == ScoreMultiplierCounter::MultiplierEventType::Positive && - cutScore == 0 && maxCutScore > 0; - - // NoteScoreDefinition fixedCutScore, for now only this case - bool isGoodScoreFixed = scoringElement->noteData->gameplayType == NoteData::GameplayType::BurstSliderElement; - - if (scoringElement->noteData->colorType == ColorType::ColorA) { - Internals::leftScore += cutScore; - Internals::leftMaxScore += maxCutScore; - if (badCut) { - if (isGoodScoreFixed) - Internals::leftMissedFixedScore += maxCutScore; - else - Internals::leftMissedMaxScore += maxCutScore; - } else - Internals::leftMissedFixedScore += (scoringElement->cutScore * scoringElement->maxMultiplier) - cutScore; +static void raise_exc_lol() { + auto parse = i2c::find_method({"System", "Int32"}, {"Parse", {}, {i2c::type_of()}}); + auto str = StringW("notanint"); + auto cvt = str.convert(); + Il2CppException* exc; + i2c::functions::runtime_invoke(parse, nullptr, &cvt, &exc); + if (exc) { + logger.debug("raise"); + i2c::functions::raise_exception(exc); } else { - Internals::rightScore += cutScore; - Internals::rightMaxScore += maxCutScore; - if (badCut) { - if (isGoodScoreFixed) - Internals::rightMissedFixedScore += maxCutScore; - else - Internals::rightMissedMaxScore += maxCutScore; - } else - Internals::rightMissedFixedScore += (scoringElement->cutScore * scoringElement->maxMultiplier) - cutScore; - } - Events::Broadcast(Events::ScoreChanged); -} - -// update combo and good/bad cuts -MAKE_AUTO_HOOK_MATCH( - BeatmapObjectManager_HandleNoteControllerNoteWasCut, - &BeatmapObjectManager::HandleNoteControllerNoteWasCut, - void, - BeatmapObjectManager* self, - NoteController* noteController, - ByRef info -) { - BeatmapObjectManager_HandleNoteControllerNoteWasCut(self, noteController, info); - - bool left = info->saberType == SaberType::SaberA; - bool bomb = noteController->noteData->gameplayType == NoteData::GameplayType::Bomb; - if (!bomb && Stats::IsFakeNote(noteController->noteData)) - return; - - if (!bomb && Stats::ShouldCountNote(noteController->noteData)) { - if (left) - Internals::remainingNotesLeft--; - else - Internals::remainingNotesRight--; - } - - if (info->allIsOK) { - if (++Internals::combo > Internals::highestCombo) - Internals::highestCombo = Internals::combo; - if (left) { - if (++Internals::leftCombo > Internals::highestLeftCombo) - Internals::highestLeftCombo = Internals::leftCombo; - } else { - if (++Internals::rightCombo > Internals::highestRightCombo) - Internals::highestRightCombo = Internals::rightCombo; - } - } else { - Internals::combo = 0; - if (left) { - if (bomb) - Internals::bombsLeftHit++; - else - Internals::notesLeftBadCut++; - Internals::leftCombo = 0; - } else { - if (bomb) - Internals::bombsRightHit++; - else - Internals::notesRightBadCut++; - Internals::rightCombo = 0; - } - if (bomb) - Events::Broadcast(Events::BombCut); - else - Events::Broadcast(Events::NoteCut); - } - Events::Broadcast(Events::ComboChanged); -} - -// update combo and misses -MAKE_AUTO_HOOK_MATCH( - BeatmapObjectManager_HandleNoteControllerNoteWasMissed, - &BeatmapObjectManager::HandleNoteControllerNoteWasMissed, - void, - BeatmapObjectManager* self, - NoteController* noteController -) { - BeatmapObjectManager_HandleNoteControllerNoteWasMissed(self, noteController); - - if (noteController->noteData->gameplayType == NoteData::GameplayType::Bomb || Stats::IsFakeNote(noteController->noteData)) - return; - - Internals::combo = 0; - if (noteController->noteData->colorType == ColorType::ColorA) { - Internals::leftCombo = 0; - Internals::notesLeftMissed++; - if (Stats::ShouldCountNote(noteController->noteData)) - Internals::remainingNotesLeft--; - } else { - Internals::rightCombo = 0; - Internals::notesRightMissed++; - if (Stats::ShouldCountNote(noteController->noteData)) - Internals::remainingNotesRight--; - } - Events::Broadcast(Events::NoteMissed); - Events::Broadcast(Events::ComboChanged); -} - -// update swing statistics -static void HandleCutFinish(CutScoreBuffer* buffer) { - if (!buffer->noteCutInfo.allIsOK) - return; - if (Stats::ShouldCountNote(buffer->noteCutInfo.noteData)) { - int after = buffer->afterCutScore; - if (buffer->noteScoreDefinition->maxAfterCutScore == 0) // TODO: selectively exclude from averages? - after = 30; - if (buffer->noteCutInfo.saberType == SaberType::SaberA) { - Internals::notesLeftCut++; - Internals::leftPreSwing += buffer->beforeCutScore; - Internals::leftPostSwing += after; - Internals::leftAccuracy += buffer->centerDistanceCutScore; - Internals::leftTimeDependence += std::abs(buffer->noteCutInfo.cutNormal.z); - } else { - Internals::notesRightCut++; - Internals::rightPreSwing += buffer->beforeCutScore; - Internals::rightPostSwing += after; - Internals::rightAccuracy += buffer->centerDistanceCutScore; - Internals::rightTimeDependence += std::abs(buffer->noteCutInfo.cutNormal.z); - } - Events::Broadcast(Events::NoteCut); - } else if (!Stats::IsFakeNote(buffer->noteCutInfo.noteData)) { - if (buffer->noteCutInfo.saberType == SaberType::SaberA) - Internals::uncountedNotesLeftCut++; - else - Internals::uncountedNotesRightCut++; - Events::Broadcast(Events::NoteCut); - } -} - -MAKE_AUTO_HOOK_MATCH( - CutScoreBuffer_HandleSaberSwingRatingCounterDidFinish, - &CutScoreBuffer::HandleSaberSwingRatingCounterDidFinish, - void, - CutScoreBuffer* self, - ISaberSwingRatingCounter* swingRatingCounter -) { - CutScoreBuffer_HandleSaberSwingRatingCounterDidFinish(self, swingRatingCounter); - HandleCutFinish(self); -} - -MAKE_AUTO_HOOK_MATCH(CutScoreBuffer_Init, &CutScoreBuffer::Init, bool, CutScoreBuffer* self, ByRef noteCutInfo) { - bool notYetFinished = CutScoreBuffer_Init(self, noteCutInfo); - if (!notYetFinished) - HandleCutFinish(self); - return notYetFinished; -} - -// update combo and walls hit -MAKE_AUTO_HOOK_MATCH( - BeatmapObjectExecutionRatingsRecorder_HandlePlayerHeadDidEnterObstacle, - &BeatmapObjectExecutionRatingsRecorder::HandlePlayerHeadDidEnterObstacle, - void, - BeatmapObjectExecutionRatingsRecorder* self, - ObstacleController* obstacleController -) { - BeatmapObjectExecutionRatingsRecorder_HandlePlayerHeadDidEnterObstacle(self, obstacleController); - - Internals::wallsHit++; - Internals::combo = 0; - Events::Broadcast(Events::WallHit); - Events::Broadcast(Events::ComboChanged); -} - -// update health and no fail -MAKE_AUTO_HOOK_MATCH( - GameEnergyCounter_ProcessEnergyChange, &GameEnergyCounter::ProcessEnergyChange, void, GameEnergyCounter* self, float energyChange -) { - bool wasAbove0 = !self->_didReach0Energy; - - GameEnergyCounter_ProcessEnergyChange(self, energyChange); - - if (Internals::noFail && wasAbove0 && self->_didReach0Energy) { - Internals::negativeMods -= 0.5; - Events::Broadcast(Events::ScoreChanged); - } - Internals::health = self->energy; - Events::Broadcast(Events::HealthChanged); -} - -// initialize as soon as the scene is loaded -MAKE_AUTO_HOOK_MATCH( - GameScenesManager_PushScenes_Delegate, - &GameScenesManager::__c__DisplayClass40_0::_PushScenes_b__1, - void, - GameScenesManager::__c__DisplayClass40_0* self, - Zenject::DiContainer* container -) { - CheckInitialize(self->scenesTransitionSetupData); - - GameScenesManager_PushScenes_Delegate(self, container); -} - -// initialize on level restarts as well -MAKE_AUTO_HOOK_MATCH( - GameScenesManager_ReplaceScenes_Delegate_AfterLoad, - &GameScenesManager::__c__DisplayClass42_0::_ReplaceScenes_b__2, - void, - GameScenesManager::__c__DisplayClass42_0* self, - Zenject::DiContainer* container -) { - CheckInitialize(self->scenesTransitionSetupData); - - GameScenesManager_ReplaceScenes_Delegate_AfterLoad(self, container); -} - -// broadcast level start -MAKE_AUTO_HOOK_MATCH( - AudioTimeSyncController_StartSong, &AudioTimeSyncController::StartSong, void, AudioTimeSyncController* self, float startTimeOffset -) { - logger.info("level start"); - Events::Broadcast(Events::MapStarted); - - AudioTimeSyncController_StartSong(self, startTimeOffset); -} - -// update song time and run update events -MAKE_AUTO_HOOK_MATCH(AudioTimeSyncController_Update, &AudioTimeSyncController::Update, void, AudioTimeSyncController* self) { - - AudioTimeSyncController_Update(self); - - if (!Internals::stateValid) - return; - - Internals::DoSlowUpdate(); - Internals::songTime = self->songTime; - Events::Broadcast(Events::Update); -} - -// run pause event -MAKE_AUTO_HOOK_MATCH(PauseMenuManager_ShowMenu, &PauseMenuManager::ShowMenu, void, PauseMenuManager* self) { - - PauseMenuManager_ShowMenu(self); - Events::Broadcast(Events::MapPaused); -} - -// run unpause event -MAKE_AUTO_HOOK_MATCH( - PauseMenuManager_HandleResumeFromPauseAnimationDidFinish, &PauseMenuManager::HandleResumeFromPauseAnimationDidFinish, void, PauseMenuManager* self -) { - PauseMenuManager_HandleResumeFromPauseAnimationDidFinish(self); - Events::Broadcast(Events::MapUnpaused); -} - -// run pause event in multiplayer -MAKE_AUTO_HOOK_MATCH( - MultiplayerLocalActivePlayerInGameMenuController_ShowInGameMenu, - &MultiplayerLocalActivePlayerInGameMenuController::ShowInGameMenu, - void, - MultiplayerLocalActivePlayerInGameMenuController* self -) { - MultiplayerLocalActivePlayerInGameMenuController_ShowInGameMenu(self); - Events::Broadcast(Events::MapPaused); -} - -// run unpause event in multiplayer -MAKE_AUTO_HOOK_MATCH( - MultiplayerLocalActivePlayerInGameMenuController_HideInGameMenu, - &MultiplayerLocalActivePlayerInGameMenuController::HideInGameMenu, - void, - MultiplayerLocalActivePlayerInGameMenuController* self -) { - MultiplayerLocalActivePlayerInGameMenuController_HideInGameMenu(self); - Events::Broadcast(Events::MapUnpaused); -} - -// handle level end and restart -MAKE_AUTO_HOOK_MATCH( - MenuTransitionsHelper_HandleMainGameSceneDidFinish, - &MenuTransitionsHelper::HandleMainGameSceneDidFinish, - void, - MenuTransitionsHelper* self, - StandardLevelScenesTransitionSetupDataSO* standardLevelScenesTransitionSetupData, - LevelCompletionResults* levelCompletionResults -) { - logger.info("standard level end {}", (int) levelCompletionResults->levelEndAction); - CheckEarlyFinish(levelCompletionResults->levelEndAction); - - MenuTransitionsHelper_HandleMainGameSceneDidFinish(self, standardLevelScenesTransitionSetupData, levelCompletionResults); -} - -// handle campaign level end and restart -MAKE_AUTO_HOOK_MATCH( - MenuTransitionsHelper_HandleMissionLevelSceneDidFinish, - &MenuTransitionsHelper::HandleMissionLevelSceneDidFinish, - void, - MenuTransitionsHelper* self, - MissionLevelScenesTransitionSetupDataSO* missionLevelScenesTransitionSetupData, - MissionCompletionResults* missionCompletionResults -) { - logger.info("campaign level end {}", (int) missionCompletionResults->levelCompletionResults->levelEndAction); - CheckEarlyFinish(missionCompletionResults->levelCompletionResults->levelEndAction); - - MenuTransitionsHelper_HandleMissionLevelSceneDidFinish(self, missionLevelScenesTransitionSetupData, missionCompletionResults); -} - -// handle multiplayer level end -MAKE_AUTO_HOOK_MATCH( - MenuTransitionsHelper_HandleMultiplayerLevelDidFinish, - &MenuTransitionsHelper::HandleMultiplayerLevelDidFinish, - void, - MenuTransitionsHelper* self, - MultiplayerLevelScenesTransitionSetupDataSO* multiplayerLevelScenesTransitionSetupData, - MultiplayerResultsData* multiplayerResultsData -) { - logger.info("multiplayer level end"); - CheckEarlyFinish(LevelCompletionResults::LevelEndAction::None); - - MenuTransitionsHelper_HandleMultiplayerLevelDidFinish(self, multiplayerLevelScenesTransitionSetupData, multiplayerResultsData); -} - -// handle multiplayer level end by disconnect -MAKE_AUTO_HOOK_MATCH( - MenuTransitionsHelper_HandleMultiplayerLevelDidDisconnect, - &MenuTransitionsHelper::HandleMultiplayerLevelDidDisconnect, - void, - MenuTransitionsHelper* self, - MultiplayerLevelScenesTransitionSetupDataSO* multiplayerLevelScenesTransitionSetupData, - DisconnectedReason disconnectedReason -) { - logger.info("multiplayer level disconnect"); - CheckEarlyFinish(LevelCompletionResults::LevelEndAction::Quit); - - MenuTransitionsHelper_HandleMultiplayerLevelDidDisconnect(self, multiplayerLevelScenesTransitionSetupData, disconnectedReason); -} - -// track when gameplay scenes are removed -MAKE_AUTO_HOOK_MATCH( - GameScenesManager_PopScenes_Delegate, - &GameScenesManager::__c__DisplayClass41_0::_PopScenes_b__0, - void, - GameScenesManager::__c__DisplayClass41_0* self, - Zenject::DiContainer* container -) { - CheckSceneFinish(); - - GameScenesManager_PopScenes_Delegate(self, container); -} - -// track when gameplay scenes are replaced for level restarts -MAKE_AUTO_HOOK_MATCH( - GameScenesManager_ReplaceScenes_Delegate_AfterUnload, - &GameScenesManager::__c__DisplayClass42_0::_ReplaceScenes_b__0, - void, - GameScenesManager::__c__DisplayClass42_0* self, - Zenject::DiContainer* container -) { - CheckSceneFinish(); - - GameScenesManager_ReplaceScenes_Delegate_AfterUnload(self, container); -} - -// handle soft restart -MAKE_AUTO_HOOK_MATCH( - MenuTransitionsHelper_RestartGame, - &MenuTransitionsHelper::RestartGame, - void, - MenuTransitionsHelper* self, - System::Action_1* finishCallback -) { - logger.info("soft restart"); - Events::Broadcast(Events::SoftRestart); - - MenuTransitionsHelper_RestartGame(self, finishCallback); -} - -// prevent score submission in solo and multiplayer mode -MAKE_AUTO_HOOK_MATCH( - LevelCompletionResultsHelper_ProcessScore, - &LevelCompletionResultsHelper::ProcessScore, - void, - ByRef beatmapKey, - PlayerData* playerData, - PlayerLevelStatsData* playerLevelStats, - LevelCompletionResults* levelCompletionResults, - IReadonlyBeatmapData* transformedBeatmapData, - PlatformLeaderboardsModel* platformLeaderboardsModel -) { - if (!Game::IsScoreSubmissionDisabled()) - LevelCompletionResultsHelper_ProcessScore( - beatmapKey, playerData, playerLevelStats, levelCompletionResults, transformedBeatmapData, platformLeaderboardsModel - ); - else - logger.info("disabling submission of score"); -} - -// prevent score submission in party mode -MAKE_AUTO_HOOK_MATCH( - PartyFreePlayFlowCoordinator_WillScoreGoToLeaderboard, - &PartyFreePlayFlowCoordinator::WillScoreGoToLeaderboard, - bool, - PartyFreePlayFlowCoordinator* self, - LevelCompletionResults* levelCompletionResults, - StringW leaderboardId, - bool practice -) { - bool ret = PartyFreePlayFlowCoordinator_WillScoreGoToLeaderboard(self, levelCompletionResults, leaderboardId, practice); - if (Game::IsScoreSubmissionDisabled()) { - logger.info("disabling submission of score"); - return false; - } - return ret; -} - -static void AddSignalUpdates(UnityEngine::Component* self, std::function disable, std::function enable) { - auto signal = Engine::GetOrAddComponent(self); - signal->onDisable = disable; - // although having onEnable will make for duplicate updates most of the time, - // it's needed if a submenu such as replay, playlist manager, etc is opened (for when it goes back to the level selection) - signal->onEnable = enable; - signal->onEnable(); -} - -// track level selection -MAKE_AUTO_HOOK_MATCH( - StandardLevelDetailView_SetContentForBeatmapData, &StandardLevelDetailView::SetContentForBeatmapData, void, StandardLevelDetailView* self -) { - logger.debug("StandardLevelDetailView SetContentForBeatmapData"); - AddSignalUpdates(self, Internals::ClearLevel, [self]() { Internals::SetLevel(self->beatmapKey, self->_beatmapLevel); }); - - StandardLevelDetailView_SetContentForBeatmapData(self); -} - -// track level selection in campaign -MAKE_AUTO_HOOK_MATCH( - MissionLevelDetailViewController_RefreshContent, &MissionLevelDetailViewController::RefreshContent, void, MissionLevelDetailViewController* self -) { - logger.debug("MissionLevelDetailViewController RefreshContent"); - AddSignalUpdates(self, Internals::ClearLevel, [key = self->missionNode->missionData->beatmapKey]() { - Internals::SetLevel(key, Songs::FindLevel(key)); - }); - - MissionLevelDetailViewController_RefreshContent(self); -} - -// track playlist selection -MAKE_AUTO_HOOK_MATCH( - AnnotatedBeatmapLevelCollectionsViewController_HandleDidSelectAnnotatedBeatmapLevelCollection, - &AnnotatedBeatmapLevelCollectionsViewController::HandleDidSelectAnnotatedBeatmapLevelCollection, - void, - AnnotatedBeatmapLevelCollectionsViewController* self, - BeatmapLevelPack* pack -) { - AnnotatedBeatmapLevelCollectionsViewController_HandleDidSelectAnnotatedBeatmapLevelCollection(self, pack); - - AddSignalUpdates(self, Internals::ClearPlaylist, [pack]() { Internals::SetPlaylist(pack); }); -} - -// track initial playlist selection -MAKE_AUTO_HOOK_MATCH( - AnnotatedBeatmapLevelCollectionsViewController_SetData, - &AnnotatedBeatmapLevelCollectionsViewController::SetData, - void, - AnnotatedBeatmapLevelCollectionsViewController* self, - System::Collections::Generic::IReadOnlyList_1* packs, - int selectedItemIndex, - bool hideIfOneOrNoPacks -) { - AnnotatedBeatmapLevelCollectionsViewController_SetData(self, packs, selectedItemIndex, hideIfOneOrNoPacks); - - AddSignalUpdates(self, Internals::ClearPlaylist, [pack = packs->get_Item(selectedItemIndex)]() { Internals::SetPlaylist(pack); }); -} - -// run input button events -MAKE_AUTO_HOOK_MATCH(OVRInput_Update, &OVRInput::Update, void) { - OVRInput_Update(); - - for (int i = 0; i <= Input::ButtonsMax; i++) { - bool pressed = Input::GetPressed(Input::Either, (Input::Buttons) i); - bool wasPressed = pressedButtons.contains(i); - if (pressed && !wasPressed) { - Events::Broadcast(Input::PressEvents, i); - pressedButtons.emplace(i); - } - if (pressed) - Events::Broadcast(Input::HoldEvents, i); - if (!pressed && wasPressed) { - Events::Broadcast(Input::ReleaseEvents, i); - pressedButtons.erase(i); - } + logger.debug("no exc??"); } } -// run keyboard closed callbacks -MAKE_AUTO_HOOK_MATCH( - InputFieldView_DeactivateKeyboard, &HMUI::InputFieldView::DeactivateKeyboard, void, HMUI::InputFieldView* self, HMUI::UIKeyboard* keyboard -) { - InputFieldView_DeactivateKeyboard(self, keyboard); - - auto handler = self->GetComponent(); - if (handler && handler->closeCallback) - handler->closeCallback(); -} - -// run keyboard ok button callbacks -MAKE_AUTO_HOOK_MATCH(UIKeyboardManager_HandleKeyboardOkButton, &UIKeyboardManager::HandleKeyboardOkButton, void, UIKeyboardManager* self) { - - auto handler = self->_selectedInput->GetComponent(); - if (handler && handler->okCallback) - handler->okCallback(); - - UIKeyboardManager_HandleKeyboardOkButton(self); -} - -// fix non interactable IconSegmentedControl cells -MAKE_AUTO_HOOK_MATCH( - IconSegmentedControl_CellForCellNumber, - &HMUI::IconSegmentedControl::CellForCellNumber, - UnityW, - HMUI::IconSegmentedControl* self, - int cellNumber -) { - auto cell = IconSegmentedControl_CellForCellNumber(self, cellNumber); - - if (!cell->interactable) { - cell->enabled = false; - if (auto cast = cell.try_cast().value_or(nullptr)) - cast->hideBackgroundImage = true; - } else - cell->enabled = true; - - return cell; -} - -static custom_types::Helpers::Coroutine DelayCallback(System::Action* callback, float duration) { - co_yield (System::Collections::IEnumerator*) UnityEngine::WaitForSeconds::New_ctor(duration); - callback->Invoke(); - co_return; -} - -// redirect fade in through our system -MAKE_AUTO_ORIG_HOOK_MATCH( - FadeInOutController_FadeIn, - static_cast(&FadeInOutController::FadeIn), - void, - FadeInOutController* self, - float duration, - System::Action* finishedCallback -) { - Game::SetCameraFadeOut(BASE_GAME_ID, false, duration); - if (!finishedCallback) - return; - if (duration == 0) - finishedCallback->Invoke(); - else - self->StartCoroutine(custom_types::Helpers::CoroutineHelper::New(DelayCallback(finishedCallback, duration + self->_fadeInStartDelay))); -} - -// redirect fade out through our system -MAKE_AUTO_ORIG_HOOK_MATCH( - FadeInOutController_FadeOut, - static_cast(&FadeInOutController::FadeOut), - void, - FadeInOutController* self, - float duration, - System::Action* finishedCallback -) { - Game::SetCameraFadeOut(BASE_GAME_ID, true, duration); - if (!finishedCallback) - return; - if (duration == 0) - finishedCallback->Invoke(); - else - self->StartCoroutine(custom_types::Helpers::CoroutineHelper::New(DelayCallback(finishedCallback, duration))); -} - -// disable haptics if requested -MAKE_AUTO_HOOK_MATCH( - OculusVRHelper_TriggerHapticPulse, - &OculusVRHelper::TriggerHapticPulse, - void, - OculusVRHelper* self, - UnityEngine::XR::XRNode node, - float duration, - float strength, - float frequency -) { - if (!Input::IsHapticsDisabled()) - OculusVRHelper_TriggerHapticPulse(self, node, duration, strength, frequency); -} - -MAKE_AUTO_HOOK_MATCH( - OculusAdvancedHapticFeedbackPlayer_PlayHapticFeedback, - &OculusAdvancedHapticFeedbackPlayer::PlayHapticFeedback, - void, - OculusAdvancedHapticFeedbackPlayer* self, - UnityEngine::XR::XRNode node, - Libraries::HM::HMLib::VR::HapticPresetSO* hapticPreset -) { - if (!Input::IsHapticsDisabled()) - OculusAdvancedHapticFeedbackPlayer_PlayHapticFeedback(self, node, hapticPreset); -} - // hook abort and provide backtraces -MAKE_HOOK(Abort, nullptr, void) { +MAKE_HOOK(Abort, (nullptr), void) { auto logger = Paper::ConstLoggerContext("abort_hook"); logger.info("abort called"); logger.Backtrace(40); + // raise_exc_lol(); Abort(); } -AUTO_INSTALL_FUNCTION(Abort) { +AUTO_HOOK_FUNCTION(Abort) { auto libc = dlopen("libc.so", RTLD_NOW); auto abort_address = dlsym(libc, "abort"); - INSTALL_HOOK_DIRECT(logger, Abort, abort_address); + INSTALL_HOOK(logger, Abort, abort_address); } -// hook abort and provide backtraces -MAKE_HOOK(delete_object_internal_step1, nullptr, void, char* object) { +// hook unity object destruction (OnDestroy and dtors need to be debugged in CT) +MAKE_HOOK(delete_object_internal_step1, (nullptr), void, char* object) { int instanceId = *(int*) (object + 8); auto destroy = ObjectSignal::onDestroys.find(instanceId); if (destroy != ObjectSignal::onDestroys.end() && destroy->second) { @@ -732,7 +47,8 @@ MAKE_HOOK(delete_object_internal_step1, nullptr, void, char* object) { delete_object_internal_step1(object); } -AUTO_INSTALL_FUNCTION(delete_object_internal_step1) { - uintptr_t addr = baseAddr("libunity.so") + 0x8d2898; - INSTALL_HOOK_DIRECT(logger, delete_object_internal_step1, (void*) addr); +AUTO_HOOK_FUNCTION(delete_object_internal_step1) { + // TODO: fix addr (or just remove hook) + uintptr_t addr = i2c::binary::base_addr("libunity.so") + 0x8d2898; + INSTALL_HOOK(logger, delete_object_internal_step1, (void*) addr); } diff --git a/src/input.cpp b/src/input.cpp deleted file mode 100644 index 1474541..0000000 --- a/src/input.cpp +++ /dev/null @@ -1,103 +0,0 @@ -#include "input.hpp" - -#include "GlobalNamespace/OVRInput.hpp" -#include "UnityEngine/EventSystems/EventSystem.hpp" -#include "UnityEngine/Resources.hpp" -#include "UnityEngine/SpatialTracking/PoseDataSource.hpp" -#include "UnityEngine/XR/XRNode.hpp" -#include "unity.hpp" - -using namespace GlobalNamespace; - -static std::vector const ButtonsMap = { - OVRInput::Button::One, - OVRInput::Button::Two, - OVRInput::Button::PrimaryThumbstick, - OVRInput::Button::PrimaryHandTrigger, - OVRInput::Button::PrimaryIndexTrigger, - OVRInput::Button::Start, -}; - -static std::vector const AxesMap = { - OVRInput::Axis1D::None, - OVRInput::Axis1D::None, - OVRInput::Axis1D::PrimaryHandTrigger, - OVRInput::Axis1D::PrimaryIndexTrigger, -}; - -static std::vector const ControllersMap = { - OVRInput::Controller::LTouch, OVRInput::Controller::RTouch, OVRInput::Controller::Active, OVRInput::Controller::Touch -}; - -static std::set hapticsDisablers; - -bool MetaCore::Input::GetPressed(Controllers controller, Buttons button) { - if (button < 0 || button >= ButtonsMap.size() || controller < 0 || controller >= ControllersMap.size()) - return false; - return OVRInput::Get(ButtonsMap[button], ControllersMap[controller]); -} - -float MetaCore::Input::GetAxis(Controllers controller, Axes axis) { - if (axis < 0 || axis >= AxesMap.size() || controller < 0 || controller >= ControllersMap.size()) - return 0; - if (AxesMap[axis] != OVRInput::Axis1D::None) - return OVRInput::Get(AxesMap[axis], ControllersMap[controller]); - auto vector = GetThumbstick(controller); - return axis == Axes::ThumbstickHorizontal ? vector.x : vector.y; -} - -UnityEngine::Vector2 MetaCore::Input::GetThumbstick(Controllers controller) { - return OVRInput::Get(OVRInput::Axis2D::PrimaryThumbstick, ControllersMap[controller]); -} - -static UnityEngine::Pose GetNodePose(UnityEngine::XR::XRNode node) { - auto pose = UnityEngine::Pose::get_identity(); - UnityEngine::SpatialTracking::PoseDataSource::GetNodePoseData(node, byref(pose)); - return pose; -} - -UnityEngine::Pose MetaCore::Input::GetHandPose(bool left) { - return GetNodePose(left ? UnityEngine::XR::XRNode::LeftHand : UnityEngine::XR::XRNode::RightHand); -} - -UnityEngine::Pose MetaCore::Input::GetHeadPose() { - return GetNodePose(UnityEngine::XR::XRNode::CenterEye); -} - -static VRUIControls::VRInputModule* FindCurrentInputModule() { - auto eventSystem = UnityEngine::EventSystems::EventSystem::get_current(); - if (!eventSystem) { - auto eventSystems = UnityEngine::Resources::FindObjectsOfTypeAll(); - eventSystem = eventSystems->FirstOrDefault([](auto e) { return e->isActiveAndEnabled; }); - if (!eventSystem) - eventSystem = eventSystems->FirstOrDefault(); - } - if (!eventSystem) - return nullptr; - return eventSystem->GetComponent(); -} - -VRUIControls::VRInputModule* MetaCore::Input::GetCurrentInputModule() { - static VRUIControls::VRInputModule* input; - if (!input) { - input = FindCurrentInputModule(); - if (input) - Engine::SetOnDisable(input, []() { input = nullptr; }, true); - } - return input; -} - -void MetaCore::Input::SetHaptics(std::string mod, bool enable) { - if (enable) - hapticsDisablers.erase(mod); - else - hapticsDisablers.emplace(std::move(mod)); -} - -bool MetaCore::Input::IsHapticsDisabled() { - return !hapticsDisablers.empty(); -} - -std::set const& MetaCore::Input::GetHapticsDisablers() { - return hapticsDisablers; -} diff --git a/src/internals.cpp b/src/internals.cpp deleted file mode 100644 index 270efca..0000000 --- a/src/internals.cpp +++ /dev/null @@ -1,449 +0,0 @@ -#include "internals.hpp" - -#include "GlobalNamespace/AudioTimeSyncController.hpp" -#include "GlobalNamespace/BeatmapCallbacksUpdater.hpp" -#include "GlobalNamespace/BeatmapDataSortedListForTypeAndIds_1.hpp" -#include "GlobalNamespace/GameplayCoreInstaller.hpp" -#include "GlobalNamespace/GameplayCoreSceneSetupData.hpp" -#include "GlobalNamespace/GameplayModifierParamsSO.hpp" -#include "GlobalNamespace/GameplayModifiersModelSO.hpp" -#include "GlobalNamespace/IGameEnergyCounter.hpp" -#include "GlobalNamespace/ISortedList_1.hpp" -#include "GlobalNamespace/PlayerAllOverallStatsData.hpp" -#include "GlobalNamespace/PlayerData.hpp" -#include "GlobalNamespace/PlayerDataModel.hpp" -#include "GlobalNamespace/PlayerLevelStatsData.hpp" -#include "GlobalNamespace/Saber.hpp" -#include "GlobalNamespace/ScoreModel.hpp" -#include "System/Collections/Generic/Dictionary_2.hpp" -#include "System/Collections/Generic/LinkedList_1.hpp" -#include "System/Collections/IEnumerator.hpp" -#include "UnityEngine/AudioClip.hpp" -#include "UnityEngine/Resources.hpp" -#include "UnityEngine/Time.hpp" -#include "UnityEngine/Transform.hpp" -#include "delegates.hpp" -#include "events.hpp" -#include "main.hpp" -#include "stats.hpp" -#include "types.hpp" -#include "unity.hpp" - -using namespace GlobalNamespace; -using namespace MetaCore; -using namespace UnityEngine; - -static std::string lastBeatmap; -static float timeSinceSlowUpdate; - -static std::pair GetNoteCount(BeatmapCallbacksUpdater* updater, bool left) { - using LinkedList = System::Collections::Generic::LinkedList_1; - - if (!updater) - return {0, 0}; - - int noteCount = 0; - int totalCount = 0; - - auto bcc = updater->_beatmapCallbacksController; - auto songTime = bcc->_startFilterTime; - - auto data = il2cpp_utils::try_cast(bcc->_beatmapData).value_or(nullptr); - if (!data) { - logger.warn("IReadonlyBeatmapData was {} not BeatmapData", il2cpp_functions::class_get_name(((Il2CppObject*) bcc->_beatmapData)->klass)); - return {0, 0}; - } - - auto noteDataItemsList = (LinkedList*) data->_beatmapDataItemsPerTypeAndId->GetList(csTypeOf(NoteData*), 0)->items; - auto enumerator = noteDataItemsList->GetEnumerator(); - while (enumerator.MoveNext()) { - auto noteData = (NoteData*) enumerator.Current; - if (Stats::ShouldCountNote(noteData) && (noteData->colorType == ColorType::ColorA) == left) { - totalCount++; - if (noteData->time >= songTime) - noteCount++; - } - } - return {noteCount, totalCount}; -} - -static int GetMaxScore(BeatmapCallbacksUpdater* updater) { - if (!updater) - return 0; - return ScoreModel::ComputeMaxMultipliedScoreForBeatmap(updater->_beatmapCallbacksController->_beatmapData); -} - -static float GetSongLength(ScoreController* controller) { - if (!controller) - return 0; - return controller->_audioTimeSyncController->_initData->audioClip->length; -} - -static float GetSongSpeed(ScoreController* controller) { - if (!controller) - return 1; - return controller->_audioTimeSyncController->_initData->timeScale; -} - -static int GetFailCount(PlayerDataModel* data) { - if (!data) - return 0; - return data->playerData->playerAllOverallStatsData->get_allOverallStatsData()->failedLevelsCount; -} - -static float GetPositiveMods(ScoreController* controller) { - if (!controller || !controller->_gameplayModifierParams) - return 0; - float ret = 0; - auto mods = ListW(controller->_gameplayModifierParams); - for (auto& mod : mods) { - float mult = mod->multiplier; - if (mult > 0) - ret += mult; - } - return ret; -} - -static float GetNegativeMods(ScoreController* controller) { - if (!controller || !controller->_gameplayModifierParams) - return 0; - float ret = 0; - auto mods = ListW(controller->_gameplayModifierParams); - for (auto& mod : mods) { - if (mod == controller->_gameplayModifiersModel->_noFailOn0Energy.ptr()) { - Internals::noFail = true; - continue; - } - float mult = mod->multiplier; - if (mult < 0) - ret += mult; - } - return ret; -} - -static int GetHighScore(PlayerDataModel* data, GameplayCoreSceneSetupData* setupData) { - if (!data || !setupData) - return -1; - auto beatmap = setupData->beatmapKey; - if (!data->playerData->levelsStatsData->ContainsKey(beatmap)) - return -1; - return data->playerData->levelsStatsData->get_Item(beatmap)->highScore; -} - -static float GetHealth(ScoreController* controller) { - if (!controller) - return 1; - return controller->_gameEnergyCounter->energy; -} - -static GameplayCoreSceneSetupData* FindSceneSetupData() { - auto gameplayCoreInstallers = Resources::FindObjectsOfTypeAll(); - for (auto& installer : gameplayCoreInstallers) { - if (installer->isActiveAndEnabled && installer->_sceneSetupData != nullptr) - return installer->_sceneSetupData; - } - if (auto installer = gameplayCoreInstallers->FirstOrDefault()) - return installer->_sceneSetupData; - return nullptr; -} - -int Internals::leftScore; -int Internals::rightScore; -int Internals::leftMaxScore; -int Internals::rightMaxScore; -int Internals::songMaxScore; -int Internals::leftCombo; -int Internals::rightCombo; -int Internals::combo; -int Internals::highestLeftCombo; -int Internals::highestRightCombo; -int Internals::highestCombo; -int Internals::multiplier; -int Internals::multiplierProgress; -float Internals::health; -float Internals::songTime; -float Internals::songLength; -float Internals::songSpeed; -int Internals::notesLeftCut; -int Internals::notesRightCut; -int Internals::notesLeftBadCut; -int Internals::notesRightBadCut; -int Internals::notesLeftMissed; -int Internals::notesRightMissed; -int Internals::bombsLeftHit; -int Internals::bombsRightHit; -int Internals::wallsHit; -int Internals::uncountedNotesLeftCut; -int Internals::uncountedNotesRightCut; -int Internals::remainingNotesLeft; -int Internals::remainingNotesRight; -int Internals::songNotesLeft; -int Internals::songNotesRight; -int Internals::leftPreSwing; -int Internals::rightPreSwing; -int Internals::leftPostSwing; -int Internals::rightPostSwing; -int Internals::leftAccuracy; -int Internals::rightAccuracy; -float Internals::leftTimeDependence; -float Internals::rightTimeDependence; -std::vector Internals::leftSpeeds; -std::vector Internals::rightSpeeds; -std::vector Internals::leftAngles; -std::vector Internals::rightAngles; -bool Internals::noFail; -float Internals::positiveMods; -float Internals::negativeMods; -int Internals::personalBest; -int Internals::fails; -int Internals::restarts; -int Internals::leftMissedMaxScore; -int Internals::rightMissedMaxScore; -int Internals::leftMissedFixedScore; -int Internals::rightMissedFixedScore; -Quaternion Internals::prevRotLeft; -Quaternion Internals::prevRotRight; - -GameplayModifiers* Internals::modifiers; -ColorScheme* Internals::colors; -BeatmapLevel* Internals::beatmapLevel; -BeatmapKey Internals::beatmapKey; -BeatmapData* Internals::beatmapData; -EnvironmentInfoSO* Internals::environment; -AudioTimeSyncController* Internals::audioTimeSyncController; -BeatmapCallbacksController* Internals::beatmapCallbacksController; -BeatmapObjectManager* Internals::beatmapObjectManager; -ComboController* Internals::comboController; -GameEnergyCounter* Internals::gameEnergyCounter; -ScoreController* Internals::scoreController; -SaberManager* Internals::saberManager; -Camera* Internals::mainCamera; - -bool Internals::stateValid = false; -bool Internals::referencesValid = false; -bool Internals::mapWasQuit = false; -bool Internals::mapWasRestarted = false; - -void Internals::Initialize() { - auto beatmapCallbacksUpdater = Object::FindObjectOfType(true); - auto playerDataModel = Object::FindObjectOfType(true); - - auto setupData = FindSceneSetupData(); - - // out of order since we use it - scoreController = Object::FindObjectOfType(true); - - modifiers = scoreController ? scoreController->_gameplayModifiers : nullptr; - - colors = setupData ? setupData->colorScheme : nullptr; - beatmapLevel = setupData ? setupData->beatmapLevel : nullptr; - beatmapKey = setupData ? setupData->beatmapKey : BeatmapKey(); - beatmapData = beatmapCallbacksUpdater ? (BeatmapData*) beatmapCallbacksUpdater->_beatmapCallbacksController->_beatmapData : nullptr; - environment = setupData ? setupData->targetEnvironmentInfo : nullptr; - - audioTimeSyncController = scoreController ? scoreController->_audioTimeSyncController : nullptr; - beatmapCallbacksController = beatmapCallbacksUpdater ? beatmapCallbacksUpdater->_beatmapCallbacksController : nullptr; - beatmapObjectManager = scoreController ? scoreController->_beatmapObjectManager : nullptr; - comboController = Object::FindObjectOfType(true); - gameEnergyCounter = scoreController ? il2cpp_utils::try_cast(scoreController->_gameEnergyCounter).value_or(nullptr) : nullptr; - saberManager = Object::FindObjectOfType(true); - mainCamera = Camera::get_main(); - - referencesValid = setupData && scoreController && beatmapCallbacksUpdater && comboController && gameEnergyCounter && saberManager && mainCamera; - - if (!referencesValid) - logger.critical( - "Not all expected references were found! Got: {} {} {} {} {} {} {}", - (bool) setupData, - (bool) scoreController, - (bool) beatmapCallbacksUpdater, - (bool) comboController, - (bool) gameEnergyCounter, - (bool) saberManager, - (bool) mainCamera - ); - - if (scoreController) - scoreController->add_multiplierDidChangeEvent(Delegates::MakeSystemAction([](int mult, float normalizedProgress) { - if (!stateValid) - return; - multiplier = mult; - multiplierProgress = normalizedProgress * mult * 2; - })); - - leftScore = 0; - rightScore = 0; - leftMaxScore = 0; - rightMaxScore = 0; - songMaxScore = GetMaxScore(beatmapCallbacksUpdater); - leftCombo = 0; - rightCombo = 0; - combo = 0; - highestLeftCombo = 0; - highestRightCombo = 0; - highestCombo = 0; - multiplier = 1; - multiplierProgress = 0; - health = GetHealth(scoreController); - songTime = 0; - songLength = GetSongLength(scoreController); - songSpeed = GetSongSpeed(scoreController); - notesLeftCut = 0; - notesRightCut = 0; - notesLeftBadCut = 0; - notesRightBadCut = 0; - notesLeftMissed = 0; - notesRightMissed = 0; - bombsLeftHit = 0; - bombsRightHit = 0; - wallsHit = 0; - uncountedNotesLeftCut = 0; - uncountedNotesRightCut = 0; - auto pair = GetNoteCount(beatmapCallbacksUpdater, true); - remainingNotesLeft = pair.first; - songNotesLeft = pair.second; - pair = GetNoteCount(beatmapCallbacksUpdater, false); - remainingNotesRight = pair.first; - songNotesRight = pair.second; - leftPreSwing = 0; - rightPreSwing = 0; - leftPostSwing = 0; - rightPostSwing = 0; - leftAccuracy = 0; - rightAccuracy = 0; - leftTimeDependence = 0; - rightTimeDependence = 0; - leftSpeeds = {}; - rightSpeeds = {}; - leftAngles = {}; - rightAngles = {}; - noFail = false; - // GetNegativeMods sets noFail - positiveMods = GetPositiveMods(scoreController); - negativeMods = GetNegativeMods(scoreController); - personalBest = GetHighScore(playerDataModel, setupData); - fails = GetFailCount(playerDataModel); - - logger.debug("modifiers {} -{}", positiveMods, negativeMods); - - std::string beatmap = "Unknown"; - if (setupData) - beatmap = (std::string) setupData->beatmapKey.SerializedName(); - if (beatmap != lastBeatmap) - restarts = 0; - else - restarts++; - lastBeatmap = beatmap; - timeSinceSlowUpdate = 0; - - leftMissedMaxScore = 0; - rightMissedMaxScore = 0; - leftMissedFixedScore = 0; - rightMissedFixedScore = 0; - - prevRotLeft = Quaternion::get_identity(); - prevRotRight = Quaternion::get_identity(); - - stateValid = true; -} - -void Internals::DoSlowUpdate() { - if (!referencesValid) - return; - - timeSinceSlowUpdate += Time::get_deltaTime(); - if (timeSinceSlowUpdate > 1 / (float) SLOW_UPDATES_PER_SEC) { - if (saberManager && saberManager->leftSaber && saberManager->rightSaber) { - leftSpeeds.emplace_back(saberManager->leftSaber->bladeSpeed); - rightSpeeds.emplace_back(saberManager->rightSaber->bladeSpeed); - - auto rotLeft = saberManager->leftSaber->transform->rotation; - auto rotRight = saberManager->rightSaber->transform->rotation; - // use speeds array as tracker for if prevRots have accurate values - if (leftSpeeds.size() > 1) { - leftAngles.emplace_back(Quaternion::Angle(rotLeft, prevRotLeft)); - rightAngles.emplace_back(Quaternion::Angle(rotRight, prevRotRight)); - } - prevRotLeft = rotLeft; - prevRotRight = rotRight; - } - timeSinceSlowUpdate -= 1 / (float) SLOW_UPDATES_PER_SEC; - Events::Broadcast(Events::SlowUpdate); - } -} - -void Internals::Finish(bool quit, bool restart) { - stateValid = false; - referencesValid = false; - mapWasQuit = quit; - mapWasRestarted = restart; -} - -BeatmapKey Internals::selectedKey = {}; -BeatmapLevel* Internals::selectedLevel = nullptr; -BeatmapLevelPack* Internals::selectedPlaylist = nullptr; -bool Internals::isLevelSelected = false; -bool Internals::isPlaylistSelected = false; - -void Internals::SetLevel(BeatmapKey key, BeatmapLevel* level) { - logger.debug("set level {}", level ? level->songName : "none"); - if (!level || !key.IsValid()) { - ClearLevel(); - return; - } - if (isLevelSelected && level == selectedLevel && key.Equals(selectedKey)) - return; - selectedKey = key; - selectedLevel = level; - isLevelSelected = true; - Events::Broadcast(Events::MapSelected); -} - -void Internals::ClearLevel() { - logger.debug("clear level"); - isLevelSelected = false; - Events::Broadcast(Events::MapDeselected); -} - -void Internals::SetPlaylist(BeatmapLevelPack* playlist) { - logger.debug("set playlist {}", playlist ? playlist->packName : "none"); - if (!playlist) { - ClearLevel(); - return; - } - if (isPlaylistSelected && playlist == selectedPlaylist) - return; - selectedPlaylist = playlist; - isPlaylistSelected = true; - Events::Broadcast(Events::PlaylistSelected); -} - -void Internals::ClearPlaylist() { - logger.debug("clear playlist"); - isPlaylistSelected = false; - Events::Broadcast(Events::PlaylistDeselected); -} - -void Internals::SetEndDragUI(Component* component, std::function callback) { - Engine::GetOrAddComponent(component)->callback = std::move(callback); -} - -std::function Internals::SetKeyboardCloseUI(Component* component, std::function onClosed, std::function onOk) { - auto handler = Engine::GetOrAddComponent(component); - handler->closeCallback = std::move(onClosed); - handler->okCallback = std::move(onOk); - return [handler]() { - if (handler->okCallback) - handler->okCallback(); - else if (handler->closeCallback) - handler->closeCallback(); - }; -} - -bool Internals::IsAprilFirst() { - auto time = std::time(nullptr); - auto info = std::localtime(&time); - if (!info) - return false; - return info->tm_mon == 3 && info->tm_mday == 1; -} diff --git a/src/java.cpp b/src/java.cpp index 3f3e1f2..d3a2714 100644 --- a/src/java.cpp +++ b/src/java.cpp @@ -2,28 +2,23 @@ #include "jtypes.hpp" #include "main.hpp" - -struct cleanup { - cleanup(std::function fn) : fn(fn) {} - ~cleanup() { fn(); } - - std::function fn; -}; +#include "beatsaber-hook/shared/exceptions.hpp" +#include "beatsaber-hook/shared/utils.hpp" #define EXCEPTION_CLEANUP \ - cleanup exceptionCleanup([env]() { \ + i2c::on_scope_exit exceptionCleanup([env]() { \ if (env->ExceptionCheck()) { \ jthrowable exc = env->ExceptionOccurred(); \ env->ExceptionClear(); \ logger.warn("JNI error: {}", DescribeError(env, exc)); \ - throw std::runtime_error(ToString(env, exc)); \ + throw i2c::trace_exception(ToString(env, exc)); \ } \ }) #define GET_VA(arg) \ va_list va; \ va_start(va, arg); \ - cleanup vaCleanup([&va]() { va_end(va); }) + i2c::on_scope_exit vaCleanup([&va]() { va_end(va); }) JNIEnv* MetaCore::Java::GetEnv() { JNIEnv* env; @@ -147,9 +142,8 @@ jclass MetaCore::Java::LoadClass(JNIEnv* env, std::string name, std::string_view auto dexBuffer = env->NewDirectByteBuffer((void*) dexBytes.data(), dexBytes.length() - 1); - // not sure if necessary to run this on the UnityPlayer class auto baseClassLoader = RunMethod( - env, {GetClass(env, {"java/lang/Class"}), GetClass(env, {"com/unity3d/player/UnityPlayer"})}, {"getClassLoader", "()Ljava/lang/ClassLoader;"} + env, {GetClass(env, {"java/lang/Class"}), GetClass(env, {"java/lang/Class"})}, {"getClassLoader", "()Ljava/lang/ClassLoader;"} ); auto classLoader = diff --git a/src/main.cpp b/src/main.cpp index 14befd5..4835b9e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,31 +1,21 @@ #include "main.hpp" -#include "UnityEngine/GameObject.hpp" -#include "events.hpp" +#include "export.h" #include "hooks.hpp" -#include "input.hpp" -#include "scotland2/shared/modloader.h" #include "types.hpp" +#include "scotland2/shared/modloader.h" -static modloader::ModInfo modInfo = {MOD_ID, VERSION, 0}; +#include "UnityEngine/GameObject.hpp" -static void RegisterButtonEvents() { - for (int i = 0; i <= MetaCore::Input::ButtonsMax; i++) { - MetaCore::Events::RegisterEvent(MetaCore::Input::PressEvents, i); - MetaCore::Events::RegisterEvent(MetaCore::Input::ReleaseEvents, i); - MetaCore::Events::RegisterEvent(MetaCore::Input::HoldEvents, i); - } -} +static modloader::ModInfo modInfo = {MOD_ID, VERSION, 0}; extern "C" METACORE_EXPORT void setup(CModInfo* info) { *info = modInfo.to_c(); Paper::Logger::RegisterFileContextId(MOD_ID); - RegisterButtonEvents(); logger.info("Completed setup!"); } extern "C" METACORE_EXPORT void late_load() { - il2cpp_functions::Init(); custom_types::Register::AutoRegister(); Hooks::Install(); diff --git a/src/pp.cpp b/src/pp.cpp deleted file mode 100644 index b91c418..0000000 --- a/src/pp.cpp +++ /dev/null @@ -1,357 +0,0 @@ -#include "pp.hpp" - -#include "GlobalNamespace/BeatmapCharacteristicSO.hpp" -#include "GlobalNamespace/BeatmapDifficulty.hpp" -#include "GlobalNamespace/BeatmapDifficultySerializedMethods.hpp" -#include "System/Action_1.hpp" -#include "custom-types/shared/delegate.hpp" -#include "events.hpp" -#include "main.hpp" -#include "maps.hpp" -#include "song-details/shared/SongDetails.hpp" -#include "songs.hpp" -#include "types.hpp" -#include "web-utils/shared/WebUtils.hpp" - -using namespace GlobalNamespace; -using namespace MetaCore; - -static CacheMap, std::optional>, 32> songCache; - -struct Request { - using callback = std::function, std::optional)>; - - std::string const key; - - std::optional blSong = std::nullopt; - bool hasBl = false; - std::optional ssSong = std::nullopt; - bool hasSs = false; - - std::vector callbacks{}; - - bool CheckDone() { - if (!hasBl || !hasSs) - return false; - for (auto& callback : callbacks) - callback(blSong, ssSong); - songCache.push(key, std::make_pair(std::move(blSong), std::move(ssSong))); - return true; - } - - bool AddBl(std::optional song) { - blSong = song; - hasBl = true; - return CheckDone(); - } - - bool AddSs(std::optional song) { - ssSong = song; - hasSs = true; - return CheckDone(); - } - - Request() = default; - Request(std::string key) : key(std::move(key)) {} -}; - -static std::map requests; - -static constexpr double ScoresaberMult = 42.117208413; - -std::vector> const PP::BeatLeaderCurve = { - {1.0, 7.424}, {0.999, 6.241}, {0.9975, 5.158}, {0.995, 4.010}, {0.9925, 3.241}, {0.99, 2.700}, {0.9875, 2.303}, {0.985, 2.007}, - {0.9825, 1.786}, {0.98, 1.618}, {0.9775, 1.490}, {0.975, 1.392}, {0.9725, 1.315}, {0.97, 1.256}, {0.965, 1.167}, {0.96, 1.094}, - {0.955, 1.039}, {0.95, 1.000}, {0.94, 0.931}, {0.93, 0.867}, {0.92, 0.813}, {0.91, 0.768}, {0.9, 0.729}, {0.875, 0.650}, - {0.85, 0.581}, {0.825, 0.522}, {0.8, 0.473}, {0.75, 0.404}, {0.7, 0.345}, {0.65, 0.296}, {0.6, 0.256}, {0.0, 0.000}, -}; - -std::vector> const PP::ScoreSaberCurve = { - {1.0, 5.367394282890631}, - {0.9995, 5.019543595874787}, - {0.999, 4.715470646416203}, - {0.99825, 4.325027383589547}, - {0.9975, 3.996793606763322}, - {0.99625, 3.5526145337555373}, - {0.995, 3.2022017597337955}, - {0.99375, 2.9190155639254955}, - {0.9925, 2.685667856592722}, - {0.99125, 2.4902905794106913}, - {0.99, 2.324506282149922}, - {0.9875, 2.058947159052738}, - {0.985, 1.8563887693647105}, - {0.9825, 1.697536248647543}, - {0.98, 1.5702410055532239}, - {0.9775, 1.4664726399289512}, - {0.975, 1.3807102743105126}, - {0.9725, 1.3090333065057616}, - {0.97, 1.2485807759957321}, - {0.965, 1.1552120359501035}, - {0.96, 1.0871883573850478}, - {0.955, 1.0388633331418984}, - {0.95, 1.0}, - {0.94, 0.9417362980580238}, - {0.93, 0.9039994071865736}, - {0.92, 0.8728710341448851}, - {0.91, 0.8488375988124467}, - {0.9, 0.825756123560842}, - {0.875, 0.7816934560296046}, - {0.85, 0.7462290664143185}, - {0.825, 0.7150465663454271}, - {0.8, 0.6872268862950283}, - {0.75, 0.6451808210101443}, - {0.7, 0.6125565959114954}, - {0.65, 0.5866010012767576}, - {0.6, 0.18223233667439062}, - {0.0, 0.}, -}; - -std::vector PP::GetModStringsBL(GameplayModifiers* modifiers, bool speeds, bool failed) { - std::vector ret; - if (modifiers->_disappearingArrows) - ret.emplace_back("da"); - if (speeds && modifiers->_songSpeed == GameplayModifiers::SongSpeed::Faster) - ret.emplace_back("fs"); - if (speeds && modifiers->_songSpeed == GameplayModifiers::SongSpeed::Slower) - ret.emplace_back("ss"); - if (speeds && modifiers->_songSpeed == GameplayModifiers::SongSpeed::SuperFast) - ret.emplace_back("sf"); - if (modifiers->_ghostNotes) - ret.emplace_back("gn"); - if (modifiers->_noArrows) - ret.emplace_back("na"); - if (modifiers->_noBombs) - ret.emplace_back("nb"); - if (failed && modifiers->_noFailOn0Energy) - ret.emplace_back("nf"); - if (modifiers->_enabledObstacleType == GameplayModifiers::EnabledObstacleType::NoObstacles) - ret.emplace_back("no"); - if (modifiers->_proMode) - ret.emplace_back("pm"); - if (modifiers->_smallCubes) - ret.emplace_back("sc"); - if (modifiers->_instaFail) - ret.emplace_back("if"); - if (modifiers->_energyType == GameplayModifiers::EnergyType::Battery) - ret.emplace_back("be"); - if (modifiers->_strictAngles) - ret.emplace_back("sa"); - if (modifiers->_zenMode) - ret.emplace_back("zm"); - return ret; -} - -float PP::AccCurve(float acc, std::vector> const& curve) { - int i = 1; - for (; i < curve.size(); i++) { - if (curve[i].first <= acc) - break; - } - - double const middle_dis = (acc - curve[i - 1].first) / (curve[i].first - curve[i - 1].first); - return curve[i - 1].second + middle_dis * (curve[i].second - curve[i - 1].second); -} - -static std::tuple CalculatePP(float accuracy, float accRating, float passRating, float techRating) { - float passPP = passRating > 0 ? 15.2 * std::exp(std::pow(passRating, 1 / 2.62)) - 30 : 0; - if (passPP < 0) - passPP = 0; - float const accPP = PP::AccCurve(accuracy, PP::BeatLeaderCurve) * accRating * 34; - float const techPP = std::exp(1.9 * accuracy) * 1.08 * techRating; - - return {passPP, accPP, techPP}; -} - -static inline float Inflate(float pp) { - return 650 * std::pow(pp, 1.3) / std::pow(650, 1.3); -} - -bool PP::IsRanked(PP::BLSongDiff const& map) { - // https://github.com/BeatLeader/beatleader-qmod/blob/b5b7dc811f6b39f52451d2dad9ebb70f3ad4ad57/src/UI/LevelInfoUI.cpp#L78 - return map.Stars > 0 && map.RankedStatus == 3; -} - -bool PP::IsRanked(PP::SSSongDiff const& map) { - return map > 0; -} - -float PP::Calculate(PP::BLSongDiff const& map, float percentage, GameplayModifiers* modifiers, bool failed) { - if (failed) - return 0; - float passRating = map.Pass; - float accRating = map.Acc; - float techRating = map.Tech; - - bool const precalculatedSpeeds = map.ModifierRatings.has_value(); - if (precalculatedSpeeds) { - switch (modifiers->_songSpeed) { - case GameplayModifiers::SongSpeed::Slower: - passRating = map.ModifierRatings->ssPassRating; - accRating = map.ModifierRatings->ssAccRating; - techRating = map.ModifierRatings->ssTechRating; - break; - case GameplayModifiers::SongSpeed::Faster: - passRating = map.ModifierRatings->fsPassRating; - accRating = map.ModifierRatings->fsAccRating; - techRating = map.ModifierRatings->fsTechRating; - break; - case GameplayModifiers::SongSpeed::SuperFast: - passRating = map.ModifierRatings->sfPassRating; - accRating = map.ModifierRatings->sfAccRating; - techRating = map.ModifierRatings->sfTechRating; - break; - default: - break; - } - } - - float multiplier = 1; - auto const mods = GetModStringsBL(modifiers, !precalculatedSpeeds, failed); - for (auto& mod : mods) { - auto value = map.ModifierValues.find(mod); - if (value != map.ModifierValues.end()) - multiplier += value->second; - } - passRating *= multiplier; - accRating *= multiplier; - techRating *= multiplier; - - logger.debug("calculating pp with ratings {} {} {}", passRating, accRating, techRating); - - auto const [passPP, accPP, techPP] = CalculatePP(percentage, accRating, passRating, techRating); - auto const rawPP = Inflate(passPP + accPP + techPP); - return rawPP; -} - -float PP::Calculate(PP::SSSongDiff const& map, float percentage, GameplayModifiers* modifiers, bool failed) { - if (failed) - percentage /= 2; - float const multiplier = AccCurve(percentage, ScoreSaberCurve); - return multiplier * map * ScoresaberMult; -} - -static void ProcessResponseBL(PP::BLSong song, BeatmapKey map) { - logger.debug("processing bl respose"); - std::string const characteristic = map.beatmapCharacteristic->serializedName; - std::string const difficulty = BeatmapDifficultySerializedMethods::SerializedName(map.difficulty); - std::string const name = map.SerializedName(); - - for (auto const& diff : song.Difficulties) { - if (diff.Characteristic == characteristic && diff.Difficulty == difficulty) { - logger.debug("found correct difficulty, {:.2f} stars", diff.Stars); - if (requests[name].AddBl(std::move(diff))) - requests.erase(name); - return; - } - } - logger.debug("failed to find characteristic {} and difficulty {} in response", characteristic, difficulty); - logger.debug("{}", WriteToString(song)); - if (requests[name].AddBl(std::nullopt)) - requests.erase(name); -} - -static void GetMapInfoBL(BeatmapKey map, std::string hash) { - static auto const setNone = [](BeatmapKey map) mutable { - std::string name = map.SerializedName(); - if (requests[name].AddBl(std::nullopt)) - requests.erase(name); - }; - - std::string const url = "https://api.beatleader.com/map/hash/" + hash; - - WebUtils::GetAsync({url, std::string(MOD_ID " " VERSION)}, [map](WebUtils::StringResponse response) { - if (!response.IsSuccessful() || !response.responseData) { - logger.error("bl pp request failed {} {}", response.httpCode, response.curlStatus); - setNone(map); - return; - } - logger.debug("got bl respose"); - PP::BLSong song; - try { - ReadFromString(*response.responseData, song); - MainThreadScheduler::Schedule([song = std::move(song), map]() { ProcessResponseBL(std::move(song), map); }); - } catch (std::exception const& e) { - logger.error("failed to parse beatleader response: {}", e.what()); - logger.debug("{}", *response.responseData); - setNone(map); - } - }); -} - -static SongDetailsCache::SongDetails* songDetailsInstance = nullptr; - -static void GetSongDetails(auto&& callback) { - if (songDetailsInstance) { - callback(songDetailsInstance); - return; - } - - logger.debug("initializing song details"); - - std::thread([callback]() mutable { - songDetailsInstance = SongDetailsCache::SongDetails::Init().get(); - callback(songDetailsInstance); - }).detach(); -} - -static void GetMapInfoSS(BeatmapKey map, std::string hash) { - std::string const characteristic = map.beatmapCharacteristic->serializedName; - int const difficulty = (int) map.difficulty; - std::string const name = map.SerializedName(); - - GetSongDetails([name, hash, characteristic, difficulty](auto details) { - auto const setNone = [&name]() { - if (requests[name].AddSs(std::nullopt)) - requests.erase(name); - }; - - logger.debug("got song details"); - auto const& song = details->songs.FindByHash(hash); - if (song == SongDetailsCache::Song::none) { - setNone(); - return; - } - logger.debug("processing song details"); - auto const& diff = song.GetDifficulty((SongDetailsCache::MapDifficulty) difficulty, characteristic); - if (diff == SongDetailsCache::SongDifficulty::none) { - setNone(); - return; - } - logger.debug("found correct difficulty, {:.2f} stars", diff.starsSS); - - MainThreadScheduler::Schedule([name, stars = diff.starsSS]() { - if (requests[name].AddSs(stars)) - requests.erase(name); - }); - }); -} - -void PP::GetMapInfo(BeatmapKey map, std::function, std::optional)> callback) { - if (!callback) - return; - - std::string const name = map.SerializedName(); - if (songCache.contains(name)) { - auto const& [bl, ss] = songCache[name]; - callback(bl, ss); - return; - } - if (requests.contains(name)) { - requests[name].callbacks.emplace_back(std::move(callback)); - return; - } - - std::string const id = map.levelId; - std::string const hash = Songs::GetHash(id); - if (hash.empty() || id.ends_with(" WIP")) { - callback(std::nullopt, std::nullopt); - return; - } - - logger.info("requesting PP info for {}", hash); - - requests.emplace(name, name).first->second.callbacks.emplace_back(std::move(callback)); - - GetMapInfoBL(map, hash); - GetMapInfoSS(map, hash); -} diff --git a/src/songs.cpp b/src/songs.cpp deleted file mode 100644 index 874f7ed..0000000 --- a/src/songs.cpp +++ /dev/null @@ -1,152 +0,0 @@ -#include "songs.hpp" - -#include "GlobalNamespace/BeatmapCharacteristicSO.hpp" -#include "GlobalNamespace/BeatmapDataLoader.hpp" -#include "GlobalNamespace/BeatmapLevelsModel.hpp" -#include "GlobalNamespace/IPreviewMediaData.hpp" -#include "GlobalNamespace/LevelCollectionNavigationController.hpp" -#include "GlobalNamespace/LevelCollectionViewController.hpp" -#include "GlobalNamespace/LevelSelectionFlowCoordinator.hpp" -#include "GlobalNamespace/LevelSelectionNavigationController.hpp" -#include "GlobalNamespace/MenuTransitionsHelper.hpp" -#include "GlobalNamespace/PlayerData.hpp" -#include "System/Threading/Tasks/Task.hpp" -#include "System/Threading/Tasks/Task_1.hpp" -#include "game.hpp" -#include "internals.hpp" -#include "main.hpp" -#include "types.hpp" - -using namespace GlobalNamespace; - -std::string MetaCore::Songs::GetHash(std::string levelId) { - auto prefixIndex = levelId.find("custom_level_"); - if (prefixIndex == std::string::npos) - return ""; - // remove prefix - levelId = levelId.substr(prefixIndex + 13); - auto wipIndex = levelId.find(" WIP"); - if (wipIndex != std::string::npos) - levelId = levelId.substr(0, wipIndex); - // make lowercase? - return levelId; -} - -std::string MetaCore::Songs::GetHash(BeatmapKey beatmap) { - return GetHash(beatmap.levelId); -} - -std::string MetaCore::Songs::GetHash(BeatmapLevel* beatmap) { - return GetHash(beatmap->levelID); -} - -static std::map>> dataRequests; - -void MetaCore::Songs::GetBeatmapData(BeatmapKey beatmap, std::function callback) { - logger.debug("loading beatmap data for {} {} {}", beatmap.levelId, beatmap.beatmapCharacteristic->_serializedName, (int) beatmap.difficulty); - - std::string name = beatmap.SerializedName(); - if (dataRequests.contains(name)) { - dataRequests[name].emplace_back(std::move(callback)); - return; - } - dataRequests.emplace(name, std::vector({std::move(callback)})); - - // I have no idea what BeatmapLevelDataVersion is for - auto levelDataTask = - Game::GetMenuTransitionsHelper()->_beatmapLevelsModel->LoadBeatmapLevelDataAsync(beatmap.levelId, BeatmapLevelDataVersion::Original, nullptr); - - MainThreadScheduler::Await(levelDataTask, [beatmap, name, levelDataTask]() { - if (levelDataTask->ResultOnSuccess.isError) { - logger.warn("failed to load beatmap data"); - for (auto& callback : dataRequests[name]) - callback(nullptr); - dataRequests.erase(name); - } else { - logger.debug("got beatmap level data"); - auto beatmapDataTask = Game::GetMenuTransitionsHelper()->_beatmapDataLoader->LoadBeatmapDataAsync( - levelDataTask->ResultOnSuccess.beatmapLevelData, - beatmap, - FindLevel(beatmap)->beatsPerMinute, - false, - nullptr, - nullptr, - BeatmapLevelDataVersion::Original, - nullptr, - nullptr, - true - ); - MainThreadScheduler::Await(beatmapDataTask, [beatmapDataTask, name]() { - for (auto& callback : dataRequests[name]) - callback(beatmapDataTask->ResultOnSuccess); - dataRequests.erase(name); - }); - } - }); -} - -void MetaCore::Songs::GetSongCover(BeatmapLevel* beatmap, std::function callback) { - auto task = beatmap->previewMediaData->GetCoverSpriteAsync(); - MainThreadScheduler::Await(task, [task, callback = std::move(callback)]() { callback(task->ResultOnSuccess); }); -} - -// avoid back and forth string -> StringW conversions -static BeatmapLevel* FindLevelInternal(StringW levelId) { - return MetaCore::Game::GetAppDiContainer()->Resolve()->GetBeatmapLevel(levelId); -} - -BeatmapLevel* MetaCore::Songs::FindLevel(std::string levelId) { - return FindLevelInternal(levelId); -} - -BeatmapLevel* MetaCore::Songs::FindLevel(BeatmapKey beatmap) { - return FindLevelInternal(beatmap.levelId); -} - -BeatmapKey MetaCore::Songs::GetSelectedKey(bool last) { - if (!last && !Internals::isLevelSelected) - return {}; - return Internals::selectedKey; -} - -BeatmapLevel* MetaCore::Songs::GetSelectedLevel(bool last) { - if (!last && !Internals::isLevelSelected) - return nullptr; - return Internals::selectedLevel; -} - -BeatmapLevelPack* MetaCore::Songs::GetSelectedPlaylist(bool last) { - if (!last && !Internals::isPlaylistSelected) - return nullptr; - return Internals::selectedPlaylist; -} - -void MetaCore::Songs::SelectLevel(BeatmapLevel* level, BeatmapLevelPack* playlist) { - auto main = Game::GetMainFlowCoordinator(); - main->DismissChildFlowCoordinatorsRecursively(true); - - auto state = LevelSelectionFlowCoordinator::State::New_ctor(playlist, level); - state->levelCategory.hasValue = true; - if (playlist) - state->levelCategory.value = playlist->packID.starts_with("custom_levelPack_") ? SelectLevelCategoryViewController::LevelCategory::CustomSongs - : SelectLevelCategoryViewController::LevelCategory::MusicPacks; - else - state->levelCategory.value = SelectLevelCategoryViewController::LevelCategory::All; - - main->_soloFreePlayFlowCoordinator->Setup(state); - main->PresentFlowCoordinator(main->_soloFreePlayFlowCoordinator, nullptr, HMUI::ViewController::AnimationDirection::Horizontal, true, false); -} - -void MetaCore::Songs::SelectLevel(BeatmapKey level, BeatmapLevelPack* playlist) { - auto main = Game::GetMainFlowCoordinator(); - main->_playerDataModel->playerData->SetLastSelectedBeatmapCharacteristic(level.beatmapCharacteristic); - main->_playerDataModel->playerData->SetLastSelectedBeatmapDifficulty(level.difficulty); - SelectLevel(FindLevel(level), playlist); -} - -void MetaCore::Songs::PlayLevelPreview(BeatmapLevel* beatmap) { - // not sure if this is ok lol, might be better to cache a resources call - Game::GetMainFlowCoordinator() - ->_soloFreePlayFlowCoordinator->levelSelectionNavigationController->_levelCollectionNavigationController->_levelCollectionViewController - ->SongPlayerCrossfadeToLevelAsync(beatmap, nullptr); -} diff --git a/src/stats.cpp b/src/stats.cpp deleted file mode 100644 index af6bf8a..0000000 --- a/src/stats.cpp +++ /dev/null @@ -1,365 +0,0 @@ -#include "stats.hpp" - -#include "internals.hpp" -#include "main.hpp" - -static inline bool IsLeft(int saber) { - return saber == MetaCore::Stats::LeftSaber || saber == MetaCore::Stats::BothSabers; -} -static inline bool IsRight(int saber) { - return saber == MetaCore::Stats::RightSaber || saber == MetaCore::Stats::BothSabers; -} - -bool MetaCore::Stats::IsFakeNote(GlobalNamespace::NoteData* data) { - return data->scoringType == GlobalNamespace::NoteData::ScoringType::NoScore || - data->scoringType == GlobalNamespace::NoteData::ScoringType::Ignore; -} - -bool MetaCore::Stats::ShouldCountNote(GlobalNamespace::NoteData* data) { - if (IsFakeNote(data)) - return false; - return data->gameplayType == GlobalNamespace::NoteData::GameplayType::Normal || - data->gameplayType == GlobalNamespace::NoteData::GameplayType::BurstSliderHead; -} - -int MetaCore::Stats::GetScore(int saber) { - int ret = 0; - if (IsLeft(saber)) - ret += Internals::leftScore; - if (IsRight(saber)) - ret += Internals::rightScore; - return ret; -} - -int MetaCore::Stats::GetMaxScore(int saber) { - int ret = 0; - if (IsLeft(saber)) - ret += Internals::leftMaxScore; - if (IsRight(saber)) - ret += Internals::rightMaxScore; - return ret; -} - -int MetaCore::Stats::GetSongMaxScore() { - return Internals::songMaxScore; -} - -int MetaCore::Stats::GetCombo(int saber) { - if (saber == LeftSaber) - return Internals::leftCombo; - else if (saber == RightSaber) - return Internals::rightCombo; - return Internals::combo; -} - -int MetaCore::Stats::GetHighestCombo(int saber) { - if (saber == LeftSaber) - return Internals::highestLeftCombo; - else if (saber == RightSaber) - return Internals::highestRightCombo; - return Internals::highestCombo; -} - -bool MetaCore::Stats::GetFullCombo(int saber) { - // ignore walls for individual sabers to match their individual combo trackers - if (saber == BothSabers && Internals::wallsHit > 0) - return false; - if (IsLeft(saber) && Internals::bombsLeftHit + Internals::notesLeftBadCut + Internals::notesLeftMissed > 0) - return false; - if (IsRight(saber) && Internals::bombsRightHit + Internals::notesRightBadCut + Internals::notesRightMissed > 0) - return false; - return true; -} - -int MetaCore::Stats::GetMultiplier() { - return Internals::multiplier; -} - -float MetaCore::Stats::GetMultiplierProgress(bool allLevels) { - float divisor = allLevels ? 2 + 4 + 8 : GetMultiplier() * 2; - return GetMultiplierProgressInt(allLevels) / divisor; -} - -int MetaCore::Stats::GetMultiplierProgressInt(bool allLevels) { - if (!allLevels) - return Internals::multiplierProgress; - int previous = 0; - if (Internals::multiplier == 8) - previous = 2 + 4 + 8; - else if (Internals::multiplier == 4) - previous = 2 + 4; - else if (Internals::multiplier == 2) - previous = 2; - return previous + Internals::multiplierProgress; -} - -int MetaCore::Stats::GetMaxMultiplier() { - int notes = GetTotalNotes(BothSabers); - if (notes < 2) - return 1; - if (notes < 2 + 4) - return 2; - if (notes < 2 + 4 + 8) - return 4; - return 8; -} - -float MetaCore::Stats::GetMaxMultiplierProgress(bool allLevels) { - float divisor = allLevels ? 2 + 4 + 8 : GetMaxMultiplier() * 2; - return GetMaxMultiplierProgressInt(allLevels) / divisor; -} - -int MetaCore::Stats::GetMaxMultiplierProgressInt(bool allLevels) { - int notes = GetTotalNotes(BothSabers); - if (allLevels) - return std::min(notes, 14); - if (notes >= 2 + 4 + 8) - return 0; - if (notes >= 2 + 4) - return notes - 2 - 4; - if (notes >= 2) - return notes - 2; - return notes; -} - -float MetaCore::Stats::GetHealth() { - return Internals::health; -} - -float MetaCore::Stats::GetSongTime() { - return Internals::songTime; -} - -float MetaCore::Stats::GetSongLength() { - return Internals::songLength; -} - -float MetaCore::Stats::GetSongSpeed() { - return Internals::songSpeed; -} - -int MetaCore::Stats::GetTotalNotes(int saber) { - int ret = 0; - if (IsLeft(saber)) - ret += Internals::notesLeftCut + Internals::notesLeftBadCut + Internals::notesLeftMissed + Internals::uncountedNotesLeftCut; - if (IsRight(saber)) - ret += Internals::notesRightCut + Internals::notesRightBadCut + Internals::notesRightMissed + Internals::uncountedNotesRightCut; - return ret; -} - -int MetaCore::Stats::GetNotesCut(int saber, bool includeUncounted) { - int ret = 0; - if (IsLeft(saber)) { - ret += Internals::notesLeftCut; - if (includeUncounted) - ret += Internals::uncountedNotesLeftCut; - } - if (IsRight(saber)) { - ret += Internals::notesRightCut; - if (includeUncounted) - ret += Internals::uncountedNotesRightCut; - } - return ret; -} - -int MetaCore::Stats::GetNotesMissed(int saber) { - int ret = 0; - if (IsLeft(saber)) - ret += Internals::notesLeftMissed; - if (IsRight(saber)) - ret += Internals::notesRightMissed; - return ret; -} - -int MetaCore::Stats::GetNotesBadCut(int saber) { - int ret = 0; - if (IsLeft(saber)) - ret += Internals::notesLeftBadCut; - if (IsRight(saber)) - ret += Internals::notesRightBadCut; - return ret; -} - -int MetaCore::Stats::GetBombsHit(int saber) { - int ret = 0; - if (IsLeft(saber)) - ret += Internals::bombsLeftHit; - if (IsRight(saber)) - ret += Internals::bombsRightHit; - return ret; -} - -int MetaCore::Stats::GetWallsHit() { - return Internals::wallsHit; -} - -int MetaCore::Stats::GetSongNotes(int saber) { - int ret = 0; - if (IsLeft(saber)) - ret += Internals::songNotesLeft; - if (IsRight(saber)) - ret += Internals::songNotesRight; - return ret; -} - -int MetaCore::Stats::GetNotesRemaining(int saber) { - int ret = 0; - if (IsLeft(saber)) - ret += Internals::remainingNotesLeft; - if (IsRight(saber)) - ret += Internals::remainingNotesRight; - return ret; -} - -float MetaCore::Stats::GetPreSwing(int saber) { - int notes = GetNotesCut(saber); - if (notes == 0) - return 0; - int ret = 0; - if (IsLeft(saber)) - ret += Internals::leftPreSwing; - if (IsRight(saber)) - ret += Internals::rightPreSwing; - return ret / (float) notes; -} - -float MetaCore::Stats::GetPostSwing(int saber) { - int notes = GetNotesCut(saber); - if (notes == 0) - return 0; - int ret = 0; - if (IsLeft(saber)) - ret += Internals::leftPostSwing; - if (IsRight(saber)) - ret += Internals::rightPostSwing; - return ret / (float) notes; -} - -float MetaCore::Stats::GetAccuracy(int saber) { - int notes = GetNotesCut(saber); - if (notes == 0) - return 0; - int ret = 0; - if (IsLeft(saber)) - ret += Internals::leftAccuracy; - if (IsRight(saber)) - ret += Internals::rightAccuracy; - return ret / (float) notes; -} - -float MetaCore::Stats::GetTimeDependence(int saber) { - int notes = GetNotesCut(saber); - if (notes == 0) - return 0; - float ret = 0; - if (IsLeft(saber)) - ret += Internals::leftTimeDependence; - if (IsRight(saber)) - ret += Internals::rightTimeDependence; - return ret / notes; -} - -float MetaCore::Stats::GetAverageSpeed(int saber) { - float ret = 0; - int div = 0; - if (IsLeft(saber)) { - for (auto& val : Internals::leftSpeeds) - ret += val; - div += Internals::leftSpeeds.size(); - } - if (IsRight(saber)) { - for (auto& val : Internals::rightSpeeds) - ret += val; - div += Internals::rightSpeeds.size(); - } - if (div == 0) - return 0; - return ret / div; -} - -float MetaCore::Stats::GetBestSpeed5Secs(int saber) { - float ret = 0; - int back = SLOW_UPDATES_PER_SEC * 5; - if (IsLeft(saber)) { - int size = Internals::leftSpeeds.size(); - int start = std::max(0, size - back); - for (int i = start; i < size; i++) - ret = std::max(ret, Internals::leftSpeeds[i]); - } - if (IsRight(saber)) { - int size = Internals::rightSpeeds.size(); - int start = std::max(0, size - back); - for (int i = start; i < size; i++) - ret = std::max(ret, Internals::rightSpeeds[i]); - } - return ret; -} - -float MetaCore::Stats::GetLastSecAngle(int saber) { - float ret = 0; - int back = SLOW_UPDATES_PER_SEC; - if (IsLeft(saber)) { - int size = Internals::leftAngles.size(); - int start = std::max(0, size - back); - for (int i = start; i < size; i++) - ret += Internals::leftAngles[i]; - } - if (IsRight(saber)) { - int size = Internals::rightAngles.size(); - int start = std::max(0, size - back); - for (int i = start; i < size; i++) - ret += Internals::rightAngles[i]; - } - if (saber == (int) BothSabers) - ret /= 2; - return ret; -} - -float MetaCore::Stats::GetHighestSecAngle(int saber) { - float ret = 0; - if (IsLeft(saber)) { - for (auto& val : Internals::leftAngles) - ret = std::max(ret, val); - } - if (IsRight(saber)) { - for (auto& val : Internals::rightAngles) - ret = std::max(ret, val); - } - return ret * SLOW_UPDATES_PER_SEC; -} - -float MetaCore::Stats::GetModifierMultiplier(bool positive, bool negative) { - float ret = 1; - if (positive) - ret += Internals::positiveMods; - if (negative) - ret += Internals::negativeMods; - return ret; -} - -int MetaCore::Stats::GetBestScore() { - return Internals::personalBest; -} - -int MetaCore::Stats::GetFails() { - return Internals::fails; -} - -int MetaCore::Stats::GetRestarts() { - return Internals::restarts; -} - -int MetaCore::Stats::GetFCScore(int saber) { - float swingRatio = (GetPreSwing(saber) + GetPostSwing(saber) + GetAccuracy(saber)) / 115; - int missed = 0; - int fixed = 0; - if (IsLeft(saber)) { - missed += Internals::leftMissedMaxScore; - fixed += Internals::leftMissedFixedScore; - } - if (IsRight(saber)) { - missed += Internals::rightMissedMaxScore; - fixed += Internals::rightMissedFixedScore; - } - return (missed * swingRatio) + fixed + GetScore(saber); -} diff --git a/src/types.cpp b/src/types.cpp index 8aeff12..2c9e76e 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -1,8 +1,8 @@ #include "types.hpp" +#include + DEFINE_TYPE(MetaCore, ObjectSignal); -DEFINE_TYPE(MetaCore, EndDragHandler); -DEFINE_TYPE(MetaCore, KeyboardCloseHandler); DEFINE_TYPE(MetaCore, MainThreadScheduler); std::unordered_map> MetaCore::ObjectSignal::onDestroys; @@ -17,11 +17,6 @@ void MetaCore::ObjectSignal::OnDisable() { onDisable(); } -void MetaCore::EndDragHandler::OnPointerUp(UnityEngine::EventSystems::PointerEventData* eventData) { - if (callback) - callback(); -} - static std::mutex callbacksMutex; static std::queue> callbacks; static std::mutex waitersMutex; diff --git a/src/unity.cpp b/src/unity.cpp index c6011ac..dbb9a84 100644 --- a/src/unity.cpp +++ b/src/unity.cpp @@ -1,5 +1,9 @@ #include "unity.hpp" +#include "main.hpp" +#include "operators.hpp" +#include "types.hpp" + #include "UnityEngine/Color.hpp" #include "UnityEngine/Graphics.hpp" #include "UnityEngine/ImageConversion.hpp" @@ -8,10 +12,6 @@ #include "UnityEngine/RenderTextureFormat.hpp" #include "UnityEngine/RenderTextureReadWrite.hpp" #include "UnityEngine/TextureFormat.hpp" -#include "beatsaber-hook/shared/utils/il2cpp-utils.hpp" -#include "main.hpp" -#include "operators.hpp" -#include "types.hpp" using namespace UnityEngine; @@ -26,7 +26,7 @@ Vector3 MetaCore::Engine::GetClampedEuler(Quaternion rotation) { static void DisableAllButImpl(Transform* original, Transform* source, std::set enabled, std::set disabled) { for (int i = 0; i < source->GetChildCount(); i++) { - auto child = source->GetChild(i).unsafePtr(); + auto child = source->GetChild(i).unsafe_ptr(); std::string name = child->name; if (enabled.contains(name)) { auto loopback = child; @@ -150,7 +150,7 @@ Texture2D* MetaCore::Engine::ScaleTexture(Texture2D* texture, int width, int hei void MetaCore::Engine::WriteTexture(Texture2D* texture, std::string file, Rect bounds) { auto bytes = ImageConversion::EncodeToPNG(GetReadable(texture, bounds)); - writefile(file, std::string((char*) bytes.begin(), bytes->get_Length())); + writefile(file, std::string((char*) bytes.begin(), bytes.size())); } void MetaCore::Engine::WriteSprite(Sprite* sprite, std::string file) {