diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..1ff0c42 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,63 @@ +############################################################################### +# Set default behavior to automatically normalize line endings. +############################################################################### +* text=auto + +############################################################################### +# Set default behavior for command prompt diff. +# +# This is need for earlier builds of msysgit that does not have it on by +# default for csharp files. +# Note: This is only used by command line +############################################################################### +#*.cs diff=csharp + +############################################################################### +# Set the merge driver for project and solution files +# +# Merging from the command prompt will add diff markers to the files if there +# are conflicts (Merging from VS is not affected by the settings below, in VS +# the diff markers are never inserted). Diff markers may cause the following +# file extensions to fail to load in VS. An alternative would be to treat +# these files as binary and thus will always conflict and require user +# intervention with every merge. To do so, just uncomment the entries below +############################################################################### +#*.sln merge=binary +#*.csproj merge=binary +#*.vbproj merge=binary +#*.vcxproj merge=binary +#*.vcproj merge=binary +#*.dbproj merge=binary +#*.fsproj merge=binary +#*.lsproj merge=binary +#*.wixproj merge=binary +#*.modelproj merge=binary +#*.sqlproj merge=binary +#*.wwaproj merge=binary + +############################################################################### +# behavior for image files +# +# image files are treated as binary by default. +############################################################################### +#*.jpg binary +#*.png binary +#*.gif binary + +############################################################################### +# diff behavior for common document formats +# +# Convert binary document formats to text before diffing them. This feature +# is only available from the command line. Turn it on by uncommenting the +# entries below. +############################################################################### +#*.doc diff=astextplain +#*.DOC diff=astextplain +#*.docx diff=astextplain +#*.DOCX diff=astextplain +#*.dot diff=astextplain +#*.DOT diff=astextplain +#*.pdf diff=astextplain +#*.PDF diff=astextplain +#*.rtf diff=astextplain +#*.RTF diff=astextplain diff --git a/.github/workflows/ndk-build.yml b/.github/workflows/ndk-build.yml index 064f1d6..d9ca253 100644 --- a/.github/workflows/ndk-build.yml +++ b/.github/workflows/ndk-build.yml @@ -39,10 +39,9 @@ on: - '!.gitignore' env: - module_id: multiquestensions - version: 0.1.1-Dev-${{ github.run_number }} - bs_hook: 2_3_2 - BS_Version: 1.17.1 + module_id: MultiQuestensions + version: 0.2.0-Dev-${{ github.run_number }} + BS_Version: 1.21.0 #ndkname: android-ndk-r23 jobs: diff --git a/.gitignore b/.gitignore index 7685831..c02ccb5 100644 --- a/.gitignore +++ b/.gitignore @@ -347,3 +347,5 @@ ARM64/ ndkbuild.props /MultiQuestensions/log_timestamp *.qmod +*.cmake +/build/ diff --git a/.vscode/launch.json b/.vscode/launch.json index 7a9dfa0..f18feed 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -4,12 +4,76 @@ // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ + { - "type": "pwa-chrome", + "name": "Launch Beat Saber", + "type": "fb-lldb", "request": "launch", - "name": "Launch Chrome against localhost", - "url": "http://localhost:8080", - "webRoot": "${workspaceFolder}" + "android": { + "application": { + "package": "com.beatgames.beatsaber", + "activity": "com.unity3d.player.UnityPlayerActivity" + }, + "lldbConfig": { + "sourceMaps": [ + "\\home\\runner\\work\\MultiplayerCore.Quest\\MultiplayerCore.Quest\\ ${workspaceFolder}/../", + "/home/runner/work/MultiplayerCore.Quest/MultiplayerCore.Quest/ ${workspaceFolder}/../", + "\\home\\runner\\work\\MultiQuestensions\\MultiQuestensions\\ ${workspaceFolder}/MultiQuestensions/", + "/home/runner/work/MultiQuestensions/ ${workspaceFolder}/MultiQuestensions/", + "/home/runner/work/SongDownloader/ ${workspaceFolder}/../", + "/home/runner/work/SongDownloader/SongDownloader/shared ${workspaceFolder}/extern/includes/SongDownloader/shared", + "/home/runner/work/SongLoader/ ${workspaceFolder}/../", + "/home/runner/work/SongLoader/SongLoader/shared ${workspaceFolder}/extern/includes/SongLoader/shared", + "/home/runner/work/questui/ ${workspaceFolder}/../", + "/home/runner/work/questui/questui/shared ${workspaceFolder}/extern/includes/questui/shared", + "/home/runner/work/beatsaber-hook/ ${workspaceFolder}/../", + "/home/runner/work/beatsaber-hook/beatsaber-hook/ ${workspaceFolder}/extern/includes/beatsaber-hook/", + "/home/runner/work/custom-types/ ${workspaceFolder}/../", + "/home/runner/work/Il2CppQuestTypePatching/Il2CppQuestTypePatching/shared ${workspaceFolder}/extern/includes/custom-types/shared" + ], + "librarySearchPaths": [ + "${workspaceFolder}/build/debug/", + "${workspaceFolder}/extern/libs/", + "${workspaceFolder}/../Symbols/", + "${workspaceFolder}/../MultiplayerCore.Quest/build/debug/", + ] + } + } + }, + { + "name": "Attach to running Beat Saber Instance", + "type": "fb-lldb", + "request": "attach", + "android": { + "application": { + "package": "com.beatgames.beatsaber", + "activity": "com.unity3d.player.UnityPlayerActivity" + }, + "lldbConfig": { + "sourceMaps": [ + "\\home\\runner\\work\\MultiplayerCore.Quest\\MultiplayerCore.Quest\\ ${workspaceFolder}/../", + "/home/runner/work/MultiplayerCore.Quest/MultiplayerCore.Quest/ ${workspaceFolder}/../", + "\\home\\runner\\work\\MultiQuestensions\\MultiQuestensions\\ ${workspaceFolder}/MultiQuestensions/", + "/home/runner/work/MultiQuestensions/ ${workspaceFolder}/MultiQuestensions/", + "/home/runner/work/SongDownloader/ ${workspaceFolder}/../", + "/home/runner/work/SongDownloader/SongDownloader/shared ${workspaceFolder}/extern/includes/SongDownloader/shared", + "/home/runner/work/SongLoader/ ${workspaceFolder}/../", + "/home/runner/work/SongLoader/SongLoader/shared ${workspaceFolder}/extern/includes/SongLoader/shared", + "/home/runner/work/questui/ ${workspaceFolder}/../", + "/home/runner/work/questui/questui/shared ${workspaceFolder}/extern/includes/questui/shared", + "/home/runner/work/beatsaber-hook/ ${workspaceFolder}/../", + "/home/runner/work/beatsaber-hook/beatsaber-hook/ ${workspaceFolder}/extern/includes/beatsaber-hook/", + "/home/runner/work/custom-types/ ${workspaceFolder}/../", + "/home/runner/work/Il2CppQuestTypePatching/Il2CppQuestTypePatching/shared ${workspaceFolder}/extern/includes/custom-types/shared" + ], + "librarySearchPaths": [ + "${workspaceFolder}/build/debug/", + "${workspaceFolder}/extern/libs/", + "${workspaceFolder}/../Symbols/", + "${workspaceFolder}/../MultiplayerCore.Quest/build/debug/", + ] + } + } } ] } \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..bd82047 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,4 @@ +{ + "cmake.sourceDirectory": "${workspaceFolder}/MultiQuestensions", + "cmake.configureOnOpen": false +} \ No newline at end of file diff --git a/MultiQuestensions.code-workspace b/MultiQuestensions.code-workspace new file mode 100644 index 0000000..876a149 --- /dev/null +++ b/MultiQuestensions.code-workspace @@ -0,0 +1,8 @@ +{ + "folders": [ + { + "path": "." + } + ], + "settings": {} +} \ No newline at end of file diff --git a/MultiQuestensions/.gitignore b/MultiQuestensions/.gitignore index cde7ff9..ed541cf 100644 --- a/MultiQuestensions/.gitignore +++ b/MultiQuestensions/.gitignore @@ -48,6 +48,7 @@ out/ [Ll]ibs/ [Oo]bj/ [Oo]bjs/ +[Bb]uild/ *.zip *.txt extern/ diff --git a/MultiQuestensions/Android.mk b/MultiQuestensions/Android.mk index fa98188..98a9790 100644 --- a/MultiQuestensions/Android.mk +++ b/MultiQuestensions/Android.mk @@ -18,14 +18,13 @@ TARGET_ARCH_ABI := $(APP_ABI) rwildcard=$(wildcard $1$2) $(foreach d,$(wildcard $1*),$(call rwildcard,$d/,$2)) -# Creating prebuilt for dependency: beatsaber-hook - version: 2.3.2 +# Creating prebuilt for dependency: beatsaber-hook - version: 3.6.1 include $(CLEAR_VARS) -LOCAL_MODULE := beatsaber-hook_2_3_2 +LOCAL_MODULE := beatsaber-hook_3_6_1 LOCAL_EXPORT_C_INCLUDES := extern/beatsaber-hook -LOCAL_SRC_FILES := extern/libbeatsaber-hook_2_3_2.so -LOCAL_CPP_FEATURES += exceptions +LOCAL_SRC_FILES := extern/libbeatsaber-hook_3_6_1.so include $(PREBUILT_SHARED_LIBRARY) -# Creating prebuilt for dependency: custom-types - version: 0.12.7 +# Creating prebuilt for dependency: custom-types - version: 0.15.2 include $(CLEAR_VARS) LOCAL_MODULE := custom-types LOCAL_EXPORT_C_INCLUDES := extern/custom-types @@ -37,13 +36,13 @@ LOCAL_MODULE := modloader LOCAL_EXPORT_C_INCLUDES := extern/modloader LOCAL_SRC_FILES := extern/libmodloader.so include $(PREBUILT_SHARED_LIBRARY) -# Creating prebuilt for dependency: songloader - version: 0.7.2 +# Creating prebuilt for dependency: songloader - version: 0.8.0 include $(CLEAR_VARS) LOCAL_MODULE := songloader LOCAL_EXPORT_C_INCLUDES := extern/songloader LOCAL_SRC_FILES := extern/libsongloader.so include $(PREBUILT_SHARED_LIBRARY) -# Creating prebuilt for dependency: questui - version: 0.11.1 +# Creating prebuilt for dependency: questui - version: 0.12.3 include $(CLEAR_VARS) LOCAL_MODULE := questui LOCAL_EXPORT_C_INCLUDES := extern/questui @@ -55,7 +54,7 @@ LOCAL_MODULE := songdownloader LOCAL_EXPORT_C_INCLUDES := extern/songdownloader LOCAL_SRC_FILES := extern/libsongdownloader.so include $(PREBUILT_SHARED_LIBRARY) -# Creating prebuilt for dependency: codegen - version: 0.14.0 +# Creating prebuilt for dependency: codegen - version: 0.19.0 include $(CLEAR_VARS) LOCAL_MODULE := codegen LOCAL_EXPORT_C_INCLUDES := extern/codegen @@ -68,7 +67,7 @@ LOCAL_SRC_FILES += $(call rwildcard,src/,*.cpp) LOCAL_SRC_FILES += $(call rwildcard,extern/beatsaber-hook/src/inline-hook,*.cpp) LOCAL_SRC_FILES += $(call rwildcard,extern/beatsaber-hook/src/inline-hook,*.c) LOCAL_SHARED_LIBRARIES += modloader -LOCAL_SHARED_LIBRARIES += beatsaber-hook_2_3_2 +LOCAL_SHARED_LIBRARIES += beatsaber-hook_3_6_1 LOCAL_SHARED_LIBRARIES += custom-types LOCAL_SHARED_LIBRARIES += songloader LOCAL_SHARED_LIBRARIES += questui @@ -78,4 +77,5 @@ LOCAL_LDLIBS += -llog LOCAL_CFLAGS += -I'extern/libil2cpp/il2cpp/libil2cpp' -DID='"multiquestensions"' -DVERSION='"$(VERSION)"' -DMPEX_PROTOCOL='"$(MPEX_PROTOCOL)"' -I'./shared' -I'./extern' -isystem'extern/codegen/include' -Ofast LOCAL_CPPFLAGS += -std=c++2a LOCAL_C_INCLUDES += ./include ./src +LOCAL_CPP_FEATURES += rtti exceptions include $(BUILD_SHARED_LIBRARY) diff --git a/MultiQuestensions/MultiQuestensions.vcxproj b/MultiQuestensions/MultiQuestensions.vcxproj index 1eee77b..e8ec6ee 100644 --- a/MultiQuestensions/MultiQuestensions.vcxproj +++ b/MultiQuestensions/MultiQuestensions.vcxproj @@ -81,6 +81,7 @@ + @@ -114,12 +115,12 @@ - src;include;extern;extern\libil2cpp\il2cpp\libil2cpp;extern\codegen\include;extern\custom-types\shared + src;include;extern\includes;extern\includes\libil2cpp\il2cpp\libil2cpp;extern\includes\codegen\include;extern\includes\custom-types\shared ID='"Mod ID"';VERSION='"Mod Version"';MPEX_PROTOCOL='"0.6.1"' "$(NdkPath)\build\ndk-build.cmd" NDK_PROJECT_PATH=$(ProjectDir) APP_BUILD_SCRIPT=$(ProjectDir)Android.mk NDK_APPLICATION_MK=$(ProjectDir)Application.mk "$(NdkPath)\build\ndk-build.cmd" -B "$(NdkPath)\build\ndk-build.cmd" clean - src;include;extern;extern\libil2cpp\il2cpp\libil2cpp;extern\codegen\include;extern\custom-types\shared;..\android-ndk-r23\toolchains\llvm\prebuilt\windows-x86_64\sysroot\usr\include\**;..\android-ndk-r23\sources\cxx-stl\system\include;..\android-ndk-r23\sources\cxx-stl\llvm-libc++\include;..\android-ndk-r23\sources\cxx-stl\llvm-libc++abi\include + src;include;extern\includes;extern\includes\libil2cpp\il2cpp\libil2cpp;extern\includes\codegen\include;extern\includes\custom-types\shared;..\android-ndk-r23\toolchains\llvm\prebuilt\windows-x86_64\sysroot\usr\include\**;..\android-ndk-r23\sources\cxx-stl\system\include;..\android-ndk-r23\sources\cxx-stl\llvm-libc++\include;..\android-ndk-r23\sources\cxx-stl\llvm-libc++abi\include src;include;extern;extern\libil2cpp\il2cpp\libil2cpp;extern\codegen\include;extern\custom-types\shared diff --git a/MultiQuestensions/MultiQuestensions.vcxproj.filters b/MultiQuestensions/MultiQuestensions.vcxproj.filters index ba30a51..8d605c3 100644 --- a/MultiQuestensions/MultiQuestensions.vcxproj.filters +++ b/MultiQuestensions/MultiQuestensions.vcxproj.filters @@ -229,5 +229,6 @@ + \ No newline at end of file diff --git a/MultiQuestensions/build.ps1 b/MultiQuestensions/build.ps1 index be252ad..31ba03b 100644 --- a/MultiQuestensions/build.ps1 +++ b/MultiQuestensions/build.ps1 @@ -1,41 +1,56 @@ Param ( +[Parameter(Mandatory=$false)][Switch]$clean, [Parameter(HelpMessage="The version the mod should be compiled with")][string]$Version, [Parameter(HelpMessage="The MpEx protocol version the mod should be compiled with")][Alias("MpEx-Protocol")][string]$MpEx_Protocol ) Write-Host $Version -$NDKPath = Get-Content $PSScriptRoot/ndkpath.txt -First 1 if ($env:VERSION) { $Version = $env:VERSION } if (!($Version)) { -$Version = "0.1.1" +$Version = "1.0.0" } if ($env:MPEX_PROTOCOL) { $MpEx_Protocol = $env:MPEX_PROTOCOL } if (!($MpEx_Protocol)) { -$MpEx_Protocol = "0.7.1" +$MpEx_Protocol = "1.0.0" } -if ((Test-Path "./extern/beatsaber-hook/src/inline-hook/And64InlineHook.cpp", "./extern/beatsaber-hook/src/inline-hook/inlineHook.c", "./extern/beatsaber-hook/src/inline-hook/relocate.c") -contains $false) { +if ((Test-Path "./extern/includes/beatsaber-hook/src/inline-hook/And64InlineHook.cpp", "./extern/includes/beatsaber-hook/src/inline-hook/inlineHook.c", "./extern/includes/beatsaber-hook/src/inline-hook/relocate.c") -contains $false) { Write-Host "Critical: Missing inline-hook" - if (!(Test-Path "./extern/beatsaber-hook/src/inline-hook/And64InlineHook.cpp")) { - Write-Host "./extern/beatsaber-hook/src/inline-hook/And64InlineHook.cpp" + if (!(Test-Path "./extern/includes/beatsaber-hook/src/inline-hook/And64InlineHook.cpp")) { + Write-Host "./extern/includes/beatsaber-hook/src/inline-hook/And64InlineHook.cpp" } - if (!(Test-Path "./extern/beatsaber-hook/src/inline-hook/inlineHook.c")) { - Write-Host "./extern/beatsaber-hook/src/inline-hook/inlineHook.c" + if (!(Test-Path "./extern/includes/beatsaber-hook/src/inline-hook/inlineHook.c")) { + Write-Host "./extern/includes/beatsaber-hook/src/inline-hook/inlineHook.c" } - if (!(Test-Path "./extern/beatsaber-hook/inline-hook/src/relocate.c")) { - Write-Host "./extern/beatsaber-hook/src/inline-hook/relocate.c" + if (!(Test-Path "./extern/includes/beatsaber-hook/inline-hook/src/relocate.c")) { + Write-Host "./extern/includes/beatsaber-hook/src/inline-hook/relocate.c" } Write-Host "Task Failed" exit 1; } -echo "Building MultiQuestensions Version: $Version with MpEx Protocol Version: $MpEx_Protocol" +Write-Output "Building MultiQuestensions Version: $Version with MpEx Protocol Version: $MpEx_Protocol" -$buildScript = "$NDKPath/build/ndk-build" -if (-not ($PSVersionTable.PSEdition -eq "Core")) { - $buildScript += ".cmd" +if ($clean.IsPresent) +{ + if (Test-Path -Path "build") + { + remove-item build -R + } +} + +if (($clean.IsPresent) -or (-not (Test-Path -Path "build"))) +{ + (new-item -Path build -ItemType Directory) | Out-Null } -& $buildScript NDK_PROJECT_PATH=. APP_BUILD_SCRIPT=./Android.mk NDK_APPLICATION_MK=./Application.mk VERSION=$Version MPEX_PROTOCOL=$MpEx_Protocol \ No newline at end of file +& qpm-rust package edit --version $Version + +Set-Location build +& cmake -G "Ninja" -DCMAKE_BUILD_TYPE="RelWithDebInfo" -DMPEX_PROTOCOL="$MpEx_Protocol" ../ +& cmake --build . -j 6 +$ExitCode = $LastExitCode +Set-Location .. +exit $ExitCode \ No newline at end of file diff --git a/MultiQuestensions/buildQMOD.ps1 b/MultiQuestensions/buildQMOD.ps1 index 67b8620..8c22e7a 100644 --- a/MultiQuestensions/buildQMOD.ps1 +++ b/MultiQuestensions/buildQMOD.ps1 @@ -1,66 +1,104 @@ -# Builds a .qmod file for loading with QP -if ($args.Count -eq 0) { -$ModID = "multiquestensions" -$BSHook = "2_3_2" -$VERSION = "0.1.0" -$BS_Version = "1.17.1" -$new_VERSION = Read-Host -Prompt "Input desired mod version (leave empty for default: '$VERSION')" -$bs_hook_version = $BSHook.Replace("_", ".") -$new_BSHook = Read-Host -Prompt "Input current bs-hook version (leave empty for default: '$bs_hook_version')" -if ($new_VERSION -ne "") { - $VERSION = $new_VERSION +Param( + [Parameter(Mandatory=$false, HelpMessage="The name the output qmod file should have")][String] $qmodname="MultiQuestensions", + + [Parameter(Mandatory=$false, HelpMessage="The version of the mod")][String] $version, + + [Parameter(Mandatory=$false, HelpMessage="Switch to create a clean compilation")] + [Alias("rebuild")] + [Switch] $clean, + + [Parameter(Mandatory=$false, HelpMessage="Prints the help instructions")] + [Switch] $help, + + [Parameter(Mandatory=$false, HelpMessage="Tells the script to not compile and only package the existing files")] + [Alias("actions", "pack")] + [Switch] $package +) + +# Builds a .qmod file for loading with QP or BMBF + + +if ($help -eq $true) { + Write-Output "`"BuildQmod `" - Copiles your mod into a `".so`" or a `".a`" library" + Write-Output "`n-- Parameters --`n" + Write-Output "qmodName `t The file name of your qmod" + + Write-Output "`n-- Arguments --`n" + + Write-Output "-clean `t`t Performs a clean build on both your library and the qmod" + Write-Output "-help `t`t Prints this" + Write-Output "-package `t Only packages existing files, without recompiling`n" + + exit } -if ($new_BSHook -ne "") { - $BSHook = $new_BSHook.Replace(".", "_") + +if ($qmodName -eq "") +{ + Write-Output "Give a proper qmod name and try again" + exit } -Write-Host "Compiling Mod" -& $PSScriptRoot/build.ps1 -Version $VERSION -& Copy-Item "./obj/local/arm64-v8a/lib$ModID.so" -Destination "./debug-builds/libmultiquestensions_$VERSION.so" + +if ($package -eq $true) { + $qmodName = "$($env:module_id)_$($env:version)" + Write-Output "Actions: Packaging QMod $qmodName" } +if (($args.Count -eq 0) -And $package -eq $false) { + Write-Output "Creating QMod $qmodName" + & $PSScriptRoot/build.ps1 -clean:$clean -version:$version + + if ($LASTEXITCODE -ne 0) { + Write-Output "Failed to build, exiting..." + exit $LASTEXITCODE + } -# TODO: Get the below working with Github Actions variables. -if ($args[0] -eq "--package") { - $ModID = $env:module_id - $BSHook = $env:bs_hook - $VERSION = $env:version - $BS_Version = $env:BSVersion - Write-Host "Github Actions Package started" + qpm-rust qmod build } -# Checks if the build was successful -if ($?) { - # Checks if any needed files are missing - if ((Test-Path "./libs/arm64-v8a/libbeatsaber-hook_$BSHook.so", "./libs/arm64-v8a/lib$ModID.so", "./mod.json") -contains $false) { - Write-Host "The following files are missing" - if (!(Test-Path "./libs/arm64-v8a/libbeatsaber-hook_$BSHook.so")) { - Write-Host "./libs/arm64-v8a/libbeatsaber-hook_$BSHook.so" - } - if (!(Test-Path "./libs/arm64-v8a/lib$ModID.so")) { - Write-Host "./libs/arm64-v8a/lib$ModID.so" - } - if (!(Test-Path ".\mod.json")) { - Write-Host ".\mod.json" - } - Write-Host "Task Failed" - exit 1; + +Write-Output "Creating qmod from mod.json" + +$mod = "./mod.json" +$modJson = Get-Content $mod -Raw | ConvertFrom-Json + +$filelist = @($mod) + +$cover = "./" + $modJson.coverImage +if ((-not ($cover -eq "./")) -and (Test-Path $cover)) +{ + $filelist += ,$cover +} else { + Write-Output "No cover Image found" +} + +foreach ($mod in $modJson.modFiles) +{ + $path = "./build/" + $mod + if (-not (Test-Path $path)) + { + $path = "./extern/libs/" + $mod } - else { - # If we have all files needed, go ahead and packe into qmod - if ($args.Count -eq 0 -or $args[0] -eq "--package") { - Write-Host "Upating mod.json" - $json = Get-Content $PSScriptRoot/mod.json -raw | ConvertFrom-Json - $json.packageVersion = "$BS_Version" - $json.version="$VERSION" - $json.libraryFiles=@("libbeatsaber-hook_$BSHook.so") - $json | ConvertTo-Json -depth 32| Set-Content $PSScriptRoot/mod.json - - Write-Host "Packaging QMod with ModID: $ModID Version: $VERSION and BS-Hook version: $BSHook" - $packagename = $ModID + "_v" + $VERSION - Compress-Archive -Path "./libs/arm64-v8a/lib$ModID.so", "./libs/arm64-v8a/libbeatsaber-hook_$BSHook.so", ".\mod.json" -DestinationPath "./Temp$packagename.zip" -Update - Move-Item "./Temp$packagename.zip" "./$packagename.qmod" -Force - } - Write-Host "Task Completed" + $filelist += $path +} + +foreach ($lib in $modJson.libraryFiles) +{ + $path = "./extern/libs/" + $lib + if (-not (Test-Path $path)) + { + $path = "./build/" + $lib } + $filelist += $path +} + +$zip = $qmodName + ".zip" +$qmod = $qmodName + ".qmod" + +if ((-not ($clean.IsPresent)) -and (Test-Path $qmod)) +{ + Write-Output "Making Clean Qmod" + Move-Item $qmod $zip -Force } -else { - Write-Host "Build failed, see output" -} \ No newline at end of file + +Compress-Archive -Path $filelist -DestinationPath $zip -Update +Move-Item $zip $qmod -Force + +Write-Output "Task Completed" \ No newline at end of file diff --git a/MultiQuestensions/include/Assets/Sprites.hpp b/MultiQuestensions/include/Assets/Sprites.hpp index 23337aa..8a2f60b 100644 --- a/MultiQuestensions/include/Assets/Sprites.hpp +++ b/MultiQuestensions/include/Assets/Sprites.hpp @@ -5,9 +5,11 @@ namespace MultiQuestensions { class Sprites { static std::string Oculus64; static std::string Steam64; + static std::string Meta64; public: static UnityEngine::Sprite* IconOculus64(); static UnityEngine::Sprite* IconSteam64(); + static UnityEngine::Sprite* IconMeta64(); }; } \ No newline at end of file diff --git a/MultiQuestensions/include/Beatmaps/PreviewBeatmapPacket.hpp b/MultiQuestensions/include/Beatmaps/PreviewBeatmapPacket.hpp deleted file mode 100644 index b8953a6..0000000 --- a/MultiQuestensions/include/Beatmaps/PreviewBeatmapPacket.hpp +++ /dev/null @@ -1,40 +0,0 @@ -#pragma once -#include "main.hpp" -#include "custom-types/shared/types.hpp" -#include "custom-types/shared/macros.hpp" - -#include "LiteNetLib/Utils/NetDataReader.hpp" -#include "LiteNetLib/Utils/NetDataWriter.hpp" -#include "LiteNetLib/Utils/INetSerializable.hpp" -#include "GlobalNamespace/ThreadStaticPacketPool_1.hpp" -#include "GlobalNamespace/PacketPool_1.hpp" -#include "GlobalNamespace/IPoolablePacket.hpp" - -#define PBPInterfaces { classof(LiteNetLib::Utils::INetSerializable*), classof(GlobalNamespace::IPoolablePacket*) } - -___DECLARE_TYPE_WRAPPER_INHERITANCE(MultiQuestensions::Beatmaps, PreviewBeatmapPacket, Il2CppTypeEnum::IL2CPP_TYPE_CLASS, - Il2CppObject, "MultiQuestensions::Beatmaps", - PBPInterfaces, 0, nullptr, - DECLARE_CTOR(New); - - // Basic Song Info/Metadata - DECLARE_INSTANCE_FIELD(Il2CppString*, levelId); - DECLARE_INSTANCE_FIELD(Il2CppString*, levelHash); - DECLARE_INSTANCE_FIELD(Il2CppString*, songName); - DECLARE_INSTANCE_FIELD(Il2CppString*, songSubName); - DECLARE_INSTANCE_FIELD(Il2CppString*, songAuthorName); - DECLARE_INSTANCE_FIELD(Il2CppString*, levelAuthorName); - DECLARE_INSTANCE_FIELD(float, beatsPerMinute); - DECLARE_INSTANCE_FIELD(float, songDuration); - - // Selection Info - DECLARE_INSTANCE_FIELD(Il2CppString*, characteristic); - DECLARE_INSTANCE_FIELD(uint, difficulty); - - DECLARE_OVERRIDE_METHOD(void, Serialize, il2cpp_utils::FindMethodUnsafe(classof(LiteNetLib::Utils::INetSerializable*), "Serialize", 1), LiteNetLib::Utils::NetDataWriter* writer); - DECLARE_OVERRIDE_METHOD(void, Deserialize, il2cpp_utils::FindMethodUnsafe(classof(LiteNetLib::Utils::INetSerializable*), "Deserialize", 1), LiteNetLib::Utils::NetDataReader* reader); - DECLARE_OVERRIDE_METHOD(void, Release, il2cpp_utils::FindMethodUnsafe(classof(GlobalNamespace::IPoolablePacket*), "Release", 0)); - public: - LiteNetLib::Utils::INetSerializable* ToINetSerializable() noexcept { return reinterpret_cast(this); } -) -#undef PBPInterfaces \ No newline at end of file diff --git a/MultiQuestensions/include/Beatmaps/PreviewBeatmapStub.hpp b/MultiQuestensions/include/Beatmaps/PreviewBeatmapStub.hpp deleted file mode 100644 index 539f947..0000000 --- a/MultiQuestensions/include/Beatmaps/PreviewBeatmapStub.hpp +++ /dev/null @@ -1,81 +0,0 @@ -#pragma once -#include "main.hpp" -#include "custom-types/shared/types.hpp" -#include "custom-types/shared/macros.hpp" - -#include "PreviewBeatmapPacket.hpp" -#include "System/Threading/CancellationToken.hpp" -#include "System/Threading/Tasks/Task_1.hpp" -#include "System/Func_2.hpp" -#include "UnityEngine/Sprite.hpp" -#include "UnityEngine/AudioClip.hpp" -#include "UnityEngine/Texture2D.hpp" -#include "UnityEngine/Rect.hpp" -#include "UnityEngine/Vector2.hpp" -#include "UnityEngine/Vector4.hpp" -#include "UnityEngine/SpriteMeshType.hpp" - -#include "GlobalNamespace/EnvironmentInfoSO.hpp" -#include "GlobalNamespace/PreviewDifficultyBeatmapSet.hpp" - -namespace MultiQuestensions::Beatmaps { - enum class DownloadableState { True, False, Unchecked }; -} - -DECLARE_CLASS_INTERFACES(MultiQuestensions::Beatmaps, PreviewBeatmapStub, "System", "Object", sizeof(Il2CppObject), - { classof(GlobalNamespace::IPreviewBeatmapLevel*) }, - - DECLARE_CTOR(FromPreviewPacket, Il2CppString* levelhash, GlobalNamespace::IPreviewBeatmapLevel* level, PreviewBeatmapPacket* lvl = nullptr); - //DECLARE_CTOR(FromPreview, Il2CppString* levelHash, GlobalNamespace::IPreviewBeatmapLevel*); - //DECLARE_CTOR(FromPacket, MultiQuestensions::Beatmaps::PreviewBeatmapPacket*); - - DECLARE_INSTANCE_METHOD(MultiQuestensions::Beatmaps::PreviewBeatmapPacket*, GetPacket, Il2CppString*, GlobalNamespace::BeatmapDifficulty); - - DECLARE_INSTANCE_FIELD(GlobalNamespace::IPreviewBeatmapLevel*, _preview); - DECLARE_INSTANCE_FIELD(UnityEngine::Sprite*, coverImage); - DECLARE_INSTANCE_FIELD(bool, isDownloaded); - DECLARE_INSTANCE_FIELD(bool, isDownloadable); - - - DECLARE_INSTANCE_FIELD(Il2CppString*, levelID); - DECLARE_INSTANCE_FIELD(Il2CppString*, levelHash); - - DECLARE_INSTANCE_FIELD(Il2CppString*, songName); - DECLARE_INSTANCE_FIELD(Il2CppString*, songSubName); - DECLARE_INSTANCE_FIELD(Il2CppString*, songAuthorName); - DECLARE_INSTANCE_FIELD(Il2CppString*, levelAuthorName); - DECLARE_INSTANCE_FIELD(float, beatsPerMinute); - DECLARE_INSTANCE_FIELD(float, songDuration); - - DECLARE_INSTANCE_FIELD(float, songTimeOffset); - DECLARE_INSTANCE_FIELD(float, previewDuration); - DECLARE_INSTANCE_FIELD(float, previewStartTime); - DECLARE_INSTANCE_FIELD(float, shuffle); - DECLARE_INSTANCE_FIELD(float, shufflePeriod); - DECLARE_INSTANCE_FIELD(GlobalNamespace::EnvironmentInfoSO*, allDirectionsEnvironmentInfo); - DECLARE_INSTANCE_FIELD(GlobalNamespace::EnvironmentInfoSO*, environmentInfo); - DECLARE_INSTANCE_FIELD(Array*, previewDifficultyBeatmapSets); - - DECLARE_OVERRIDE_METHOD(Il2CppString*, get_levelID, il2cpp_utils::FindMethodUnsafe(classof(GlobalNamespace::IPreviewBeatmapLevel*), "get_levelID", 0)); - DECLARE_OVERRIDE_METHOD(Il2CppString*, get_songName, il2cpp_utils::FindMethodUnsafe(classof(GlobalNamespace::IPreviewBeatmapLevel*), "get_songName", 0)); - DECLARE_OVERRIDE_METHOD(Il2CppString*, get_songSubName, il2cpp_utils::FindMethodUnsafe(classof(GlobalNamespace::IPreviewBeatmapLevel*), "get_songSubName", 0)); - DECLARE_OVERRIDE_METHOD(Il2CppString*, get_songAuthorName, il2cpp_utils::FindMethodUnsafe(classof(GlobalNamespace::IPreviewBeatmapLevel*), "get_songAuthorName", 0)); - DECLARE_OVERRIDE_METHOD(Il2CppString*, get_levelAuthorName, il2cpp_utils::FindMethodUnsafe(classof(GlobalNamespace::IPreviewBeatmapLevel*), "get_levelAuthorName", 0)); - DECLARE_OVERRIDE_METHOD(float, get_beatsPerMinute, il2cpp_utils::FindMethodUnsafe(classof(GlobalNamespace::IPreviewBeatmapLevel*), "get_beatsPerMinute", 0)); - DECLARE_OVERRIDE_METHOD(float, get_songDuration, il2cpp_utils::FindMethodUnsafe(classof(GlobalNamespace::IPreviewBeatmapLevel*), "get_songDuration", 0)); - DECLARE_OVERRIDE_METHOD(float, get_songTimeOffset, il2cpp_utils::FindMethodUnsafe(classof(GlobalNamespace::IPreviewBeatmapLevel*), "get_songTimeOffset", 0)); - DECLARE_OVERRIDE_METHOD(float, get_previewDuration, il2cpp_utils::FindMethodUnsafe(classof(GlobalNamespace::IPreviewBeatmapLevel*), "get_previewDuration", 0)); - DECLARE_OVERRIDE_METHOD(float, get_previewStartTime, il2cpp_utils::FindMethodUnsafe(classof(GlobalNamespace::IPreviewBeatmapLevel*), "get_previewStartTime", 0)); - DECLARE_OVERRIDE_METHOD(float, get_shuffle, il2cpp_utils::FindMethodUnsafe(classof(GlobalNamespace::IPreviewBeatmapLevel*), "get_shuffle", 0)); - DECLARE_OVERRIDE_METHOD(float, get_shufflePeriod, il2cpp_utils::FindMethodUnsafe(classof(GlobalNamespace::IPreviewBeatmapLevel*), "get_shufflePeriod", 0)); - DECLARE_OVERRIDE_METHOD(GlobalNamespace::EnvironmentInfoSO*, get_allDirectionsEnvironmentInfo, il2cpp_utils::FindMethodUnsafe(classof(GlobalNamespace::IPreviewBeatmapLevel*), "get_allDirectionsEnvironmentInfo", 0)); - DECLARE_OVERRIDE_METHOD(GlobalNamespace::EnvironmentInfoSO*, get_environmentInfo, il2cpp_utils::FindMethodUnsafe(classof(GlobalNamespace::IPreviewBeatmapLevel*), "get_environmentInfo", 0)); - DECLARE_OVERRIDE_METHOD(Array*, get_previewDifficultyBeatmapSets, il2cpp_utils::FindMethodUnsafe(classof(GlobalNamespace::IPreviewBeatmapLevel*), "get_previewDifficultyBeatmapSets", 0)); - - DECLARE_OVERRIDE_METHOD(System::Threading::Tasks::Task_1*, GetCoverImageAsync, il2cpp_utils::FindMethodUnsafe(classof(GlobalNamespace::IPreviewBeatmapLevel*), "GetCoverImageAsync", 1), System::Threading::CancellationToken cancellationToken); - DECLARE_OVERRIDE_METHOD(System::Threading::Tasks::Task_1*, GetPreviewAudioClipAsync, il2cpp_utils::FindMethodUnsafe(classof(GlobalNamespace::IPreviewBeatmapLevel*), "GetPreviewAudioClipAsync", 1), System::Threading::CancellationToken cancellationToken); - - // C++ Definitions - private: - DownloadableState _downloadable; -) \ No newline at end of file diff --git a/MultiQuestensions/include/CS_DataStore.hpp b/MultiQuestensions/include/CS_DataStore.hpp deleted file mode 100644 index 5f9034a..0000000 --- a/MultiQuestensions/include/CS_DataStore.hpp +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once -#include "custom-types/shared/macros.hpp" -#include -#include "GlobalNamespace/IPreviewBeatmapLevel.hpp" -#include "GlobalNamespace/BeatmapDifficulty.hpp" -#include "GlobalNamespace/BeatmapCharacteristicSO.hpp" -#include "GlobalNamespace/IDifficultyBeatmap.hpp" -#include "GlobalNamespace/GameplayModifiers.hpp" - -DECLARE_CLASS_CODEGEN(MultiQuestensions, DataStore, Il2CppObject, - - DECLARE_CTOR(New, GlobalNamespace::IPreviewBeatmapLevel*, GlobalNamespace::BeatmapCharacteristicSO*, GlobalNamespace::IDifficultyBeatmap*, GlobalNamespace::GameplayModifiers*); - - DECLARE_INSTANCE_FIELD(GlobalNamespace::IPreviewBeatmapLevel*, loadingPreviewBeatmapLevel); - DECLARE_INSTANCE_FIELD(GlobalNamespace::BeatmapCharacteristicSO*, loadingBeatmapCharacteristic); - DECLARE_INSTANCE_FIELD(GlobalNamespace::IDifficultyBeatmap*, loadingDifficultyBeatmap); - DECLARE_INSTANCE_FIELD(GlobalNamespace::GameplayModifiers*, loadingGameplayModifiers); - - static DataStore* instance; - -public: - std::optional loadingBeatmapDifficulty; - - static DataStore* get_Instance(); - static DataStore* CS_Ctor(GlobalNamespace::IPreviewBeatmapLevel* previewBeatmap, GlobalNamespace::BeatmapCharacteristicSO* beatmapCharacteristic, GlobalNamespace::IDifficultyBeatmap* difficultyBeatmap, GlobalNamespace::GameplayModifiers* gameplayModifiers, GlobalNamespace::BeatmapDifficulty beatmapDifficulty); - static void Clear(); -) \ No newline at end of file diff --git a/MultiQuestensions/include/Config.hpp b/MultiQuestensions/include/Config.hpp new file mode 100644 index 0000000..34c027e --- /dev/null +++ b/MultiQuestensions/include/Config.hpp @@ -0,0 +1,142 @@ +#pragma once +#include "main.hpp" +#include "UnityEngine/Color.hpp" +#include "CodegenExtensions/ColorUtility.hpp" + +namespace MultiQuestensions { + Configuration& getConfig(); + Configuration& getLegacyConfig(); + + struct Config { + static constexpr UnityEngine::Color DefaultPlayerColor = UnityEngine::Color(0.031f, 0.752f, 1.0f, 1.0f); + + protected: + bool Hologram = true; + bool LagReducer = false; + bool MissLighting = true; + bool NoMetaZone = false; + UnityEngine::Color PlayerColor = DefaultPlayerColor; + UnityEngine::Color MissColor = UnityEngine::Color(1, 0, 0, 1); + + public: + bool getHologram() const { return Hologram; } + + void setHologram(bool value) { + Hologram = value; + getConfig().config["Hologram"].SetBool(Hologram); + getConfig().Write(); + } + + bool getLagReducer() const { return LagReducer; } + + void setLagReducer(bool value) { + LagReducer = value; + getConfig().config["LagReducer"].SetBool(LagReducer); + getConfig().Write(); + } + + bool getMissLighting() const { return MissLighting; } + + void setMissLighting(bool value) { + MissLighting = value; + getConfig().config["MissLighting"].SetBool(MissLighting); + getConfig().Write(); + } + + bool getNoMetaZone() const { return NoMetaZone; } + + void setNoMetaZone(bool value) { + NoMetaZone = value; + getConfig().config["NoMetaZone"].SetBool(NoMetaZone); + getConfig().Write(); + } + + UnityEngine::Color getPlayerColor() const { + return PlayerColor; + } + + void setPlayerColor(const UnityEngine::Color& value) { + PlayerColor = value; + ConfigDocument& configDoc = getConfig().config; + auto& allocator = configDoc.GetAllocator(); + configDoc["PlayerColor"].SetString(UnityEngine::ColorUtility::ToHtmlStringRGB_CPP(PlayerColor), allocator); + getConfig().Write(); + } + + UnityEngine::Color getMissColor() const { + return MissColor; + } + + void setMissColor(const UnityEngine::Color& value) { + MissColor = value; + ConfigDocument& configDoc = getConfig().config; + auto& allocator = configDoc.GetAllocator(); + configDoc["MissColor"].SetString(UnityEngine::ColorUtility::ToHtmlStringRGB_CPP(MissColor), allocator); + getConfig().Write(); + } + + void Initialize() { + getLogger().info("Checking for legacy config file..."); + // Checking legacy config file + ConfigDocument& legacyConfig = getLegacyConfig().config; + if (legacyConfig.IsObject() && !legacyConfig.HasMember("Converted")) { + // Legacy Conversion + getLogger().info("Legacy config file found. Converting..."); + if (legacyConfig.HasMember("LagReducer") && legacyConfig["LagReducer"].IsBool()) { + LagReducer = legacyConfig["LagReducer"].GetBool(); + } + if (legacyConfig.HasMember("color") && legacyConfig["color"].IsString()) { + UnityEngine::ColorUtility::TryParseHtmlString(legacyConfig["color"].GetString(), PlayerColor); + } + legacyConfig.AddMember("Converted", true, legacyConfig.GetAllocator()); + } + // TODO: Delete config on a later version + /* + else if (legacyConfig.IsObject() && legacyConfig.HasMember("Converted")) { + remove(Configuration::getConfigFilePath({"multiquestensions", modInfo.version}).c_str()); + } + */ + + getLogger().info("Reading config file..."); + ConfigDocument& configDoc = getConfig().config; + bool parseError = false; + if (configDoc.IsObject()) { + (configDoc.HasMember("Hologram") && configDoc["Hologram"].IsBool()) ? (Hologram = configDoc["Hologram"].GetBool()) : parseError = true; + (configDoc.HasMember("LagReducer") && configDoc["LagReducer"].IsBool()) ? (LagReducer = configDoc["LagReducer"].GetBool()) : parseError = true; + (configDoc.HasMember("MissLighting") && configDoc["MissLighting"].IsBool()) ? (MissLighting = configDoc["MissLighting"].GetBool()) : parseError = true; + (configDoc.HasMember("NoMetaZone") && configDoc["NoMetaZone"].IsBool()) ? (NoMetaZone = configDoc["NoMetaZone"].GetBool()) : parseError = true; + if (!(configDoc.HasMember("PlayerColor") && configDoc["PlayerColor"].IsString() && + UnityEngine::ColorUtility::TryParseHtmlString(configDoc["PlayerColor"].GetString(), PlayerColor))) { + parseError = true; + } + if (!(configDoc.HasMember("MissColor") && configDoc["MissColor"].IsString() && + UnityEngine::ColorUtility::TryParseHtmlString(configDoc["MissColor"].GetString(), MissColor))) { + parseError = true; + } + } else parseError = true; + + if (parseError) SaveDefaultConfig(); + } + + void SaveDefaultConfig() { + getLogger().info("Creating config file..."); + ConfigDocument& configDoc = getConfig().config; + configDoc.RemoveAllMembers(); + if (!configDoc.IsObject()) + configDoc.SetObject(); + auto& allocator = configDoc.GetAllocator(); + + configDoc.AddMember("Hologram", Hologram, allocator); + configDoc.AddMember("LagReducer", LagReducer, allocator); + configDoc.AddMember("MissLighting", MissLighting, allocator); + configDoc.AddMember("NoMetaZone", NoMetaZone, allocator); + configDoc.AddMember("PlayerColor", UnityEngine::ColorUtility::ToHtmlStringRGB_CPP(PlayerColor), allocator); + configDoc.AddMember("MissColor", UnityEngine::ColorUtility::ToHtmlStringRGB_CPP(MissColor), allocator); + + getConfig().Write(); + getLogger().info("Config file created."); + } + }; + + extern MultiQuestensions::Config config; +} \ No newline at end of file diff --git a/MultiQuestensions/include/Environments/LobbyAvatarNameTag.hpp b/MultiQuestensions/include/Environments/MQEAvatarNameTag.hpp similarity index 51% rename from MultiQuestensions/include/Environments/LobbyAvatarNameTag.hpp rename to MultiQuestensions/include/Environments/MQEAvatarNameTag.hpp index c67a53b..85fbd34 100644 --- a/MultiQuestensions/include/Environments/LobbyAvatarNameTag.hpp +++ b/MultiQuestensions/include/Environments/MQEAvatarNameTag.hpp @@ -1,6 +1,8 @@ #pragma once #include "custom-types/shared/macros.hpp" -#include "Extensions/ExtendedPlayer.hpp" +#include "Players/MpexPlayerData.hpp" +#include "multiplayer-core/shared/Players/MpPlayerData.hpp" +#include "Players/MpexPlayerManager.hpp" #include "UnityEngine/MonoBehaviour.hpp" #include "HMUI/ImageView.hpp" #include "HMUI/CurvedTextMeshPro.hpp" @@ -8,7 +10,7 @@ #include "UnityEngine/Sprite.hpp" #include -DECLARE_CLASS_CODEGEN(MultiQuestensions::Environments, LobbyAvatarNameTag, UnityEngine::MonoBehaviour, +DECLARE_CLASS_CODEGEN(MultiQuestensions::Environments, MQEAvatarNameTag, UnityEngine::MonoBehaviour, DECLARE_INSTANCE_FIELD(GlobalNamespace::IConnectedPlayer*, _playerInfo); DECLARE_INSTANCE_FIELD(HMUI::ImageView*, _bg); @@ -18,10 +20,13 @@ DECLARE_CLASS_CODEGEN(MultiQuestensions::Environments, LobbyAvatarNameTag, Unity DECLARE_INSTANCE_METHOD(void, Awake); DECLARE_INSTANCE_METHOD(void, OnEnable); + DECLARE_INSTANCE_METHOD(void, OnDisable); DECLARE_INSTANCE_METHOD(void, SetPlayerInfo, GlobalNamespace::IConnectedPlayer* player); - DECLARE_INSTANCE_METHOD(void, SetExtendedPlayerInfo, MultiQuestensions::Extensions::ExtendedPlayer* extendedPlayer); + DECLARE_INSTANCE_METHOD(void, SetPlatformData, MultiplayerCore::Players::MpPlayerData* data); DECLARE_INSTANCE_METHOD(void, SetSimplePlayerInfo, GlobalNamespace::IConnectedPlayer* simplePlayer); + DECLARE_INSTANCE_METHOD(void, HandlePlatformData, GlobalNamespace::IConnectedPlayer* player, MultiplayerCore::Players::MpPlayerData* data); + DECLARE_INSTANCE_METHOD(void, HandleMpexData, GlobalNamespace::IConnectedPlayer* player, MultiQuestensions::Players::MpexPlayerData* data); enum PlayerIconSlot { Platform = 0 @@ -29,6 +34,10 @@ DECLARE_CLASS_CODEGEN(MultiQuestensions::Environments, LobbyAvatarNameTag, Unity bool _enabled; + MultiplayerCore::event_handler _platformDataEventHandler; + MultiplayerCore::event_handler _mpexPlayerDataEventHandler; + + std::map _playerIcons; void SetIcon(PlayerIconSlot slot, UnityEngine::Sprite* sprite); diff --git a/MultiQuestensions/include/Environments/LobbyAvatarPlaceLighting.hpp b/MultiQuestensions/include/Environments/MQEAvatarPlaceLighting.hpp similarity index 75% rename from MultiQuestensions/include/Environments/LobbyAvatarPlaceLighting.hpp rename to MultiQuestensions/include/Environments/MQEAvatarPlaceLighting.hpp index dd0f1ad..771ad02 100644 --- a/MultiQuestensions/include/Environments/LobbyAvatarPlaceLighting.hpp +++ b/MultiQuestensions/include/Environments/MQEAvatarPlaceLighting.hpp @@ -4,7 +4,10 @@ #include "GlobalNamespace/TubeBloomPrePassLight.hpp" #include "UnityEngine/Color.hpp" -DECLARE_CLASS_CODEGEN(MultiQuestensions::Environments, LobbyAvatarPlaceLighting, UnityEngine::MonoBehaviour, +#include "Players/MpexPlayerManager.hpp" +#include "multiplayer-core/shared/Utils/event.hpp" + +DECLARE_CLASS_CODEGEN(MultiQuestensions::Environments, MQEAvatarPlaceLighting, UnityEngine::MonoBehaviour, std::vector lights; DECLARE_INSTANCE_FIELD(float, smoothTime); DECLARE_INSTANCE_FIELD_DEFAULT(UnityEngine::Color, targetColor, UnityEngine::Color::get_black()); @@ -12,9 +15,10 @@ DECLARE_CLASS_CODEGEN(MultiQuestensions::Environments, LobbyAvatarPlaceLighting, DECLARE_CTOR(New); + // DECLARE_INSTANCE_METHOD(void, Awake); DECLARE_INSTANCE_METHOD(void, OnEnable); DECLARE_INSTANCE_METHOD(void, OnDisable); - DECLARE_INSTANCE_METHOD(void, Update); + DECLARE_INSTANCE_METHOD(void, FixedUpdate); DECLARE_INSTANCE_METHOD(UnityEngine::Color, GetColor); DECLARE_INSTANCE_METHOD(bool, IsColorVeryCloseToColor, UnityEngine::Color color0, UnityEngine::Color color1); diff --git a/MultiQuestensions/include/Extensions/ExtendedPlayer.hpp b/MultiQuestensions/include/Extensions/ExtendedPlayer.hpp deleted file mode 100644 index 84eaf34..0000000 --- a/MultiQuestensions/include/Extensions/ExtendedPlayer.hpp +++ /dev/null @@ -1,120 +0,0 @@ -#pragma once -#include "custom-types/shared/macros.hpp" -#include "GlobalNamespace/IConnectedPlayer.hpp" -#include "UnityEngine/Color.hpp" -#include "GlobalNamespace/MultiplayerAvatarData.hpp" -#include "GlobalNamespace/DisconnectedReason.hpp" - -#include "LiteNetLib/Utils/NetDataReader.hpp" -#include "LiteNetLib/Utils/NetDataWriter.hpp" -#include "LiteNetLib/Utils/INetSerializable.hpp" -#include "GlobalNamespace/ThreadStaticPacketPool_1.hpp" -#include "GlobalNamespace/PacketPool_1.hpp" -#include "GlobalNamespace/IPoolablePacket.hpp" - -#include - -namespace MultiQuestensions::Extensions { - enum class Platform - { - Unknown = 0, - Steam = 1, - OculusPC = 2, - OculusQuest = 3, - PS4 = 4 - }; -} - -#define EPInterfaces { classof(GlobalNamespace::IConnectedPlayer*) } -___DECLARE_TYPE_WRAPPER_INHERITANCE(MultiQuestensions::Extensions, ExtendedPlayer, Il2CppTypeEnum::IL2CPP_TYPE_CLASS, - Il2CppObject, "MultiplayerExtensions.Extensions", - EPInterfaces, 0, nullptr, - - DECLARE_INSTANCE_FIELD(GlobalNamespace::IConnectedPlayer*, _connectedPlayer); - - /// - /// Platform User ID - /// - DECLARE_INSTANCE_FIELD(Il2CppString*, platformID); - DECLARE_INSTANCE_METHOD(Il2CppString*, get_platformID); - - /// - /// MultiplayerExtensions version reported by BSIPA. - /// - DECLARE_INSTANCE_FIELD(Il2CppString*, mpexVersion); - DECLARE_INSTANCE_METHOD(Il2CppString*, get_mpexVersion); - - /// - /// Player's color set in the plugin config. - /// - DECLARE_INSTANCE_FIELD(UnityEngine::Color, playerColor); - - DECLARE_INSTANCE_FIELD(Il2CppString*, isPartyOwner); - DECLARE_INSTANCE_FIELD(Il2CppString*, hasRecommendBeatmapPermission); - DECLARE_INSTANCE_FIELD(Il2CppString*, hasRecommendModifiersPermission); - DECLARE_INSTANCE_FIELD(Il2CppString*, hasKickVotePermission); - - DECLARE_CTOR(ExtendedPlayer_ctor, GlobalNamespace::IConnectedPlayer* player, Il2CppString* platformID, int platform, UnityEngine::Color playerColor, Il2CppString* mpexVersion); - - DECLARE_INSTANCE_METHOD(bool, get_isMe); - DECLARE_INSTANCE_METHOD(Il2CppString*, get_userId); - DECLARE_INSTANCE_METHOD(Il2CppString*, get_userName); - DECLARE_INSTANCE_METHOD(float, get_currentLatency); - DECLARE_INSTANCE_METHOD(bool, get_isConnected); - DECLARE_INSTANCE_METHOD(bool, get_isConnectionOwner); - DECLARE_INSTANCE_METHOD(float, get_offsetSyncTime); - DECLARE_INSTANCE_METHOD(int, get_sortIndex); - DECLARE_INSTANCE_METHOD(bool, get_isKicked); - DECLARE_INSTANCE_METHOD(GlobalNamespace::MultiplayerAvatarData, get_multiplayerAvatarData); - DECLARE_INSTANCE_METHOD(GlobalNamespace::DisconnectedReason, get_disconnectedReason); - DECLARE_INSTANCE_METHOD(bool, HasState, Il2CppString* state); - -public: - static const constexpr UnityEngine::Color DefaultColor = UnityEngine::Color(0.031f, 0.752f, 1.0f); - - /// - /// Platform - /// - Platform platform = Platform::OculusQuest; - Platform get_platform(); - - static ExtendedPlayer* CS_ctor(GlobalNamespace::IConnectedPlayer* player); - static ExtendedPlayer* CS_ctor(GlobalNamespace::IConnectedPlayer* player, Il2CppString* platformID, Platform platform, UnityEngine::Color playerColor); - static ExtendedPlayer* CS_ctor(GlobalNamespace::IConnectedPlayer* player, Il2CppString* platformID, Platform platform, Il2CppString* mpexVersion, UnityEngine::Color playerColor); - - constexpr const UnityEngine::Color& get_playerColor() const { - return playerColor; - } - - constexpr bool HasState_CPP(std::string& state) const { - return _connectedPlayer->HasState(il2cpp_utils::newcsstr(state)); - } - - constexpr ExtendedPlayer* get_self() { - return this; - } - -) -#undef EPInterfaces - -#define EPPInterfaces { classof(LiteNetLib::Utils::INetSerializable*), classof(GlobalNamespace::IPoolablePacket*) } -___DECLARE_TYPE_WRAPPER_INHERITANCE(MultiQuestensions::Extensions, ExtendedPlayerPacket, Il2CppTypeEnum::IL2CPP_TYPE_CLASS, - Il2CppObject, "MultiplayerExtensions.Extensions", - EPPInterfaces, 0, nullptr, - DECLARE_CTOR(New); - - // Player and platform information - DECLARE_INSTANCE_FIELD(Il2CppString*, platformID); - DECLARE_INSTANCE_FIELD(Il2CppString*, mpexVersion); - DECLARE_INSTANCE_FIELD(UnityEngine::Color, playerColor); - - DECLARE_OVERRIDE_METHOD(void, Serialize, il2cpp_utils::FindMethodUnsafe(classof(LiteNetLib::Utils::INetSerializable*), "Serialize", 1), LiteNetLib::Utils::NetDataWriter* writer); - DECLARE_OVERRIDE_METHOD(void, Deserialize, il2cpp_utils::FindMethodUnsafe(classof(LiteNetLib::Utils::INetSerializable*), "Deserialize", 1), LiteNetLib::Utils::NetDataReader* reader); - DECLARE_OVERRIDE_METHOD(void, Release, il2cpp_utils::FindMethodUnsafe(classof(GlobalNamespace::IPoolablePacket*), "Release", 0)); - - public: - Platform platform; - - static ExtendedPlayerPacket* Init(Il2CppString* platformID, Platform platform, UnityEngine::Color playerColor); -) -#undef EPPInterfaces \ No newline at end of file diff --git a/MultiQuestensions/include/GlobalFields.hpp b/MultiQuestensions/include/GlobalFields.hpp index ca5611c..86f315a 100644 --- a/MultiQuestensions/include/GlobalFields.hpp +++ b/MultiQuestensions/include/GlobalFields.hpp @@ -1,20 +1,18 @@ #pragma once //#include "main.hpp" -#include "GlobalNamespace/MultiplayerSessionManager.hpp" +//#include "GlobalNamespace/MultiplayerSessionManager.hpp" #include "GlobalNamespace/LobbyPlayersDataModel.hpp" #include "GlobalNamespace/LobbyGameStateController.hpp" // MQE Includes -#include "Packets/PacketManager.hpp" +// #include "Packets/PacketManager.hpp" //#include "System/Collections/Generic/Dictionary_2.hpp" namespace MultiQuestensions { // Plugin setup stuff - extern GlobalNamespace::MultiplayerSessionManager* sessionManager; + //extern GlobalNamespace::MultiplayerSessionManager* sessionManager; extern GlobalNamespace::LobbyPlayersDataModel* lobbyPlayersDataModel; extern GlobalNamespace::LobbyGameStateController* lobbyGameStateController; - - extern Il2CppString* getCustomLevelSongPackMaskStr(); } //extern System::Collections::Generic::Dictionary_2 extendedPlayers; \ No newline at end of file diff --git a/MultiQuestensions/include/Hooks/EnvironmentAndAvatarHooks.hpp b/MultiQuestensions/include/Hooks/EnvironmentAndAvatarHooks.hpp index bedd418..0417f37 100644 --- a/MultiQuestensions/include/Hooks/EnvironmentAndAvatarHooks.hpp +++ b/MultiQuestensions/include/Hooks/EnvironmentAndAvatarHooks.hpp @@ -4,5 +4,10 @@ namespace MultiQuestensions { extern void SetPlayerPlaceColor(GlobalNamespace::IConnectedPlayer* player, const UnityEngine::Color& color, bool priority); - extern void CreateOrUpdateNameTag(GlobalNamespace::IConnectedPlayer* player); + extern void UpdateNameTagIcons(); +} + +#include "GlobalNamespace/MultiplayerSessionManager.hpp" +namespace MultiplayerCore { + extern GlobalNamespace::MultiplayerSessionManager* _multiplayerSessionManager; } \ No newline at end of file diff --git a/MultiQuestensions/include/Hooks/Hooks.hpp b/MultiQuestensions/include/Hooks/Hooks.hpp index c6e9895..c975ecf 100644 --- a/MultiQuestensions/include/Hooks/Hooks.hpp +++ b/MultiQuestensions/include/Hooks/Hooks.hpp @@ -47,47 +47,18 @@ #include "questui/shared/CustomTypes/Components/MainThreadScheduler.hpp" -#include "songdownloader/shared/BeatSaverAPI.hpp" - -#include "songloader/shared/API.hpp" - namespace MultiQuestensions { - constexpr bool IsCustomLevel(const std::string& levelId) { - return levelId.starts_with(RuntimeSongLoader::API::GetCustomLevelsPrefix()); - } - - extern bool HasSong(const std::string& levelId); - - extern Il2CppString* getModdedStateStr(); - extern Il2CppString* getMEStateStr(); - extern Il2CppString* getNEStateStr(); - extern Il2CppString* getChromaStateStr(); - extern GlobalNamespace::LobbyGameStateController* lobbyGameStateController; - extern std::map> entitlementDictionary; - - extern GlobalNamespace::IPreviewBeatmapLevel* loadingPreviewBeatmapLevel; - extern GlobalNamespace::BeatmapDifficulty loadingBeatmapDifficulty; - extern GlobalNamespace::BeatmapCharacteristicSO* loadingBeatmapCharacteristic; - extern GlobalNamespace::IDifficultyBeatmap* loadingDifficultyBeatmap; - extern GlobalNamespace::GameplayModifiers* loadingGameplayModifiers; - class Hooks { - static void NetworkplayerEntitlementChecker(); - static void MaxPlayerHooks(); static void EnvironmentHooks(); - static void QuickplayHooks(); static void SessionManagerAndExtendedPlayerHooks(); static void InstallerHooks(); public: static const void Install_Hooks() { - NetworkplayerEntitlementChecker(); - MaxPlayerHooks(); EnvironmentHooks(); - QuickplayHooks(); SessionManagerAndExtendedPlayerHooks(); InstallerHooks(); } diff --git a/MultiQuestensions/include/Hooks/SessionManagerAndExtendedPlayerHooks.hpp b/MultiQuestensions/include/Hooks/SessionManagerAndExtendedPlayerHooks.hpp index f9f3de0..128f43b 100644 --- a/MultiQuestensions/include/Hooks/SessionManagerAndExtendedPlayerHooks.hpp +++ b/MultiQuestensions/include/Hooks/SessionManagerAndExtendedPlayerHooks.hpp @@ -1,7 +1,18 @@ #pragma once -#include "Extensions/ExtendedPlayer.hpp" +#include "Players/MpexPlayerData.hpp" +#include "GlobalNamespace/IConnectedPlayer.hpp" +#include "GlobalNamespace/DisconnectedReason.hpp" +#include "multiplayer-core/shared/Networking/MpPacketSerializer.hpp" +#include "multiplayer-core/shared/Networking/MpNetworkingEvents.hpp" -extern SafePtr localExtendedPlayer; -extern std::map> _extendedPlayers; +extern MultiQuestensions::Players::MpexPlayerData* localMpexPlayerData; +extern std::unordered_map _mpexPlayerData; -static void HandlePreviewBeatmapPacket(MultiQuestensions::Beatmaps::PreviewBeatmapPacket* packet, GlobalNamespace::IConnectedPlayer* player); \ No newline at end of file +extern void HandlePlayerConnected(GlobalNamespace::IConnectedPlayer* player); +extern void HandlePlayerDisconnected(GlobalNamespace::IConnectedPlayer* player); +extern void HandleDisconnect(GlobalNamespace::DisconnectedReason reason, GlobalNamespace::IConnectedPlayer* localPlayer); +extern void HandleConnecting(GlobalNamespace::IConnectedPlayer* localPlayer); + + +extern void HandleRegisterMpexPackets(MultiplayerCore::Networking::MpPacketSerializer* _mpPacketSerializer); +extern void HandleUnRegisterMpexPackets(MultiplayerCore::Networking::MpPacketSerializer* _mpPacketSerializer); \ No newline at end of file diff --git a/MultiQuestensions/include/Packets/Callback.hpp b/MultiQuestensions/include/Packets/Callback.hpp deleted file mode 100644 index 7e01a77..0000000 --- a/MultiQuestensions/include/Packets/Callback.hpp +++ /dev/null @@ -1,63 +0,0 @@ -#pragma once -#include "main.hpp" - -#include "Beatmaps/PreviewBeatmapPacket.hpp" -#include "GlobalNamespace/ThreadStaticPacketPool_1.hpp" -#include "GlobalNamespace/PacketPool_1.hpp" -#include "LiteNetLib/Utils/NetDataReader.hpp" - -template -using PacketCallback = void (*)(T, GlobalNamespace::IConnectedPlayer*); - -namespace MultiQuestensions { - class CallbackBase { - public: - virtual ~CallbackBase() {}; - virtual void Invoke(LiteNetLib::Utils::NetDataReader* reader, int size, GlobalNamespace::IConnectedPlayer* player) = 0; - }; - - template class CallbackWrapper : public CallbackBase { - private: - PacketCallback action = nullptr; - - public: - CallbackWrapper(PacketCallback callback) { - action = callback; - } - - void Invoke(LiteNetLib::Utils::NetDataReader* reader, int size, GlobalNamespace::IConnectedPlayer* player) { - getLogger().debug("Running Invoke creating packet"); - //TPacket packet = THROW_UNLESS(il2cpp_utils::New()); - //getLogger().debug("Assigning from ThreadStaticPacketPool"); - //packet = GlobalNamespace::ThreadStaticPacketPool_1::get_pool()->Obtain(); - TPacket packet = GlobalNamespace::ThreadStaticPacketPool_1::get_pool()->Obtain(); - if (packet == nullptr) { - reader->SkipBytes(size); - } - else { - try { - packet->Deserialize(reader); - } - catch (const std::exception& e) { - getLogger().debug("Exception Deserializing Packet"); - getLogger().error("REPORT TO ENDER: %s", e.what()); - } - catch (...) { - getLogger().debug("REPORT TO ENDER: Unknown exception Deserializing Packet"); - } - } - if (action != nullptr) { - try { - action(packet, player); - } - catch (const std::exception& e) { - getLogger().error("Exception running action"); - getLogger().error("REPORT TO ENDER: %s", e.what()); - } - catch (...) { - getLogger().debug("REPORT TO ENDER: Unknown exception Deserializing Packet"); - } - } - } - }; -} \ No newline at end of file diff --git a/MultiQuestensions/include/Packets/PacketManager.hpp b/MultiQuestensions/include/Packets/PacketManager.hpp deleted file mode 100644 index b60508a..0000000 --- a/MultiQuestensions/include/Packets/PacketManager.hpp +++ /dev/null @@ -1,62 +0,0 @@ -#pragma once -#include "main.hpp" -#include "Callback.hpp" -#include "PacketSerializer.hpp" - -#include -#include -#include "GlobalNamespace/MultiplayerSessionManager_MessageType.hpp" -#include "GlobalNamespace/PacketPool_1.hpp" - -namespace MultiQuestensions { - class PacketManager { - private: - GlobalNamespace::MultiplayerSessionManager* _sessionManager; - PacketSerializer* packetSerializer; - - public: - PacketManager(GlobalNamespace::MultiplayerSessionManager* sessionManager); - void Send(LiteNetLib::Utils::INetSerializable* message); - void SendUnreliable(LiteNetLib::Utils::INetSerializable* message); - - template - void RegisterCallback(std::string identifier, PacketCallback callback) { - CallbackWrapper* newCallback = new CallbackWrapper(callback); - if (identifier.empty()) { - getLogger().error("Cannot register callback: Identifier null."); - return; - } - else { - packetSerializer->RegisterCallback(identifier, newCallback); - } - } - - template - void RegisterCallback(PacketCallback callback) { - Il2CppReflectionType* packetType = csTypeOf(TPacket); - if (packetType == nullptr) { - getLogger().info("Packet Type null."); - return; - } - - std::string identifier = to_utf8(csstrtostr(packetType->ToString())); - int pos = 0; - while ((pos = identifier.find("::")) != std::string::npos) - { - identifier.replace(pos, 2, "."); - pos++; - } - - RegisterCallback(identifier, callback); - } - - template - void UnregisterCallback() { - packetSerializer->UnregisterCallback(); - } - - void UnregisterCallback(std::string identifier); - }; - - extern PacketManager* packetManager; -} \ No newline at end of file diff --git a/MultiQuestensions/include/Packets/PacketSerializer.hpp b/MultiQuestensions/include/Packets/PacketSerializer.hpp deleted file mode 100644 index affeba1..0000000 --- a/MultiQuestensions/include/Packets/PacketSerializer.hpp +++ /dev/null @@ -1,79 +0,0 @@ -#pragma once -#include "main.hpp" -#include "Callback.hpp" -#include "custom-types/shared/types.hpp" -#include "custom-types/shared/macros.hpp" - -#include -#include "System/Action_3.hpp" -#include "LiteNetLib/Utils/NetDataWriter.hpp" -#include "System/Collections/Generic/Dictionary_2.hpp" -#include "System/Collections/Generic/List_1.hpp" -#include "GlobalNamespace/INetworkPacketSubSerializer_1.hpp" -#include "GlobalNamespace/IConnectedPlayer.hpp" -#include "System/Type.hpp" - -/*DECLARE_CLASS_INTERFACES(Il2CppNamespace, MyCustomBeatmapLevelPackCollection, "System", "Object", sizeof(Il2CppObject), - il2cpp_utils::GetClassFromName("", "IBeatmapLevelPackCollection"), - DECLARE_INSTANCE_FIELD(Il2CppArray*, wrappedArr); - - DECLARE_OVERRIDE_METHOD(Il2CppArray*, get_beatmapLevelPacks, il2cpp_utils::FindMethod("", "IBeatmapLevelPackCollection", "get_beatmapLevelPacks")); - DECLARE_CTOR(ctor, Il2CppArray* originalArray); - REGISTER_FUNCTION(MyCustomBeatmapLevelPackCollection, - modLogger().debug("Registering MyCustomBeatmapLevelPackCollection!"); - REGISTER_METHOD(get_beatmapLevelPacks); - REGISTER_METHOD(ctor); - REGISTER_FIELD(wrappedArr); - ) -)*/ - -using CallbackDictionary = std::map; -using TypeDictionary = std::map; - -DECLARE_CLASS_INTERFACES(MultiQuestensions, PacketSerializer, "System", "Object", sizeof(Il2CppObject), - { classof(GlobalNamespace::INetworkPacketSubSerializer_1*) }, - DECLARE_OVERRIDE_METHOD(void, Serialize, il2cpp_utils::FindMethodUnsafe(classof(GlobalNamespace::INetworkPacketSubSerializer_1*), "Serialize", 2), LiteNetLib::Utils::NetDataWriter* writer, LiteNetLib::Utils::INetSerializable* packet); - DECLARE_OVERRIDE_METHOD(void, Deserialize, il2cpp_utils::FindMethodUnsafe(classof(GlobalNamespace::INetworkPacketSubSerializer_1*), "Deserialize", 3), LiteNetLib::Utils::NetDataReader* reader, int length, GlobalNamespace::IConnectedPlayer* data); - DECLARE_OVERRIDE_METHOD(bool, HandlesType, il2cpp_utils::FindMethodUnsafe(classof(GlobalNamespace::INetworkPacketSubSerializer_1*), "HandlesType", 1), Il2CppReflectionType* type); - - //DECLARE_INSTANCE_METHOD(void, Serialize, LiteNetLib::Utils::NetDataWriter* writer, LiteNetLib::Utils::INetSerializable* packet); - //DECLARE_INSTANCE_METHOD(void, Deserialize, LiteNetLib::Utils::NetDataReader* reader, int length, GlobalNamespace::IConnectedPlayer* data); - //DECLARE_INSTANCE_METHOD(bool, HandlesType, Il2CppReflectionType* type); - - DECLARE_CTOR(Construct); - DECLARE_DTOR(Deconstruct); - - //REGISTER_FUNCTION(PacketSerializer, - // REGISTER_METHOD(Serialize); - // REGISTER_METHOD(Deserialize); - // REGISTER_METHOD(HandlesType); - // REGISTER_METHOD(Construct); - //) - - private: - CallbackDictionary packetHandlers; - TypeDictionary registeredTypes; - - public: - template - void RegisterCallback(std::string identifier, CallbackWrapper* callback) { - registeredTypes[csTypeOf(TPacket)] = identifier; - packetHandlers[identifier] = callback; - } - - template - void UnregisterCallback() { - getLogger().debug("UnregisterCallback called"); - - auto it = registeredTypes.find(csTypeOf(TPacket)); - if (it != registeredTypes.end()) registeredTypes.erase(it); - - auto itr = packetHandlers.find(csTypeOf(TPacket)->ToString()); - if (itr != packetHandlers.end()) { - delete itr->second; - packetHandlers.erase(itr); - } - } - - void UnregisterCallback(std::string identifier); -) \ No newline at end of file diff --git a/MultiQuestensions/include/UI/CenterScreenLoading.hpp b/MultiQuestensions/include/UI/CenterScreenLoading.hpp deleted file mode 100644 index 9267a7d..0000000 --- a/MultiQuestensions/include/UI/CenterScreenLoading.hpp +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once -#include "custom-types/shared/macros.hpp" -#include "UnityEngine/MonoBehaviour.hpp" -#include "GlobalNamespace/LoadingControl.hpp" -#include "GlobalNamespace/CenterStageScreenController.hpp" - -DECLARE_CLASS_CODEGEN(MultiQuestensions::UI, CenterScreenLoading, UnityEngine::MonoBehaviour, - DECLARE_INSTANCE_FIELD(GlobalNamespace::LoadingControl*, loadingControl); - DECLARE_INSTANCE_FIELD(GlobalNamespace::CenterStageScreenController*, screenController); - DECLARE_INSTANCE_FIELD(GlobalNamespace::LobbyGameStateController*, gameStateController); - - DECLARE_INSTANCE_METHOD(void, Awake); - DECLARE_INSTANCE_METHOD(void, OnDisable); - DECLARE_INSTANCE_METHOD(void, ShowLoading); - // Note: downloadingProgress 1.0f = Full - DECLARE_INSTANCE_METHOD(void, ShowDownloadingProgress, float downloadingProgress); - //DECLARE_INSTANCE_METHOD(void, HideLoading); - DECLARE_INSTANCE_METHOD(void, FixedUpdate); - - bool isDownloading; - - static CenterScreenLoading* instance; -public: - static int playersReady; - static CenterScreenLoading* get_Instance() { return instance; } - static void HideLoading(); - //static void UpdatePlayersReady(int playerReady); -) diff --git a/MultiQuestensions/include/UI/DownloadedSongsGSM.hpp b/MultiQuestensions/include/UI/DownloadedSongsGSM.hpp deleted file mode 100644 index 21dd6a4..0000000 --- a/MultiQuestensions/include/UI/DownloadedSongsGSM.hpp +++ /dev/null @@ -1,37 +0,0 @@ -#pragma once -#include "custom-types/shared/macros.hpp" -#include "UnityEngine/MonoBehaviour.hpp" -#include "questui/shared/CustomTypes/Components/List/CustomListTableData.hpp" -#include "HMUI/CurvedTextMeshPro.hpp" -#include "HMUI/ImageView.hpp" -#include "HMUI/ModalView.hpp" - -#include "GlobalNamespace/CustomPreviewBeatmapLevel.hpp" -#include "System/Threading/Tasks/Task_1.hpp" -#include "UnityEngine/Sprite.hpp" - - -DECLARE_CLASS_CODEGEN(MultiQuestensions::UI, DownloadedSongsGSM, UnityEngine::MonoBehaviour, - DECLARE_INSTANCE_FIELD(HMUI::ModalView*, modal); - DECLARE_INSTANCE_FIELD(QuestUI::CustomListTableData*, list); - DECLARE_INSTANCE_FIELD(GlobalNamespace::CustomPreviewBeatmapLevel*, lastDownloaded); - - DECLARE_INSTANCE_METHOD(void, DidActivate, bool firstActivation); - DECLARE_INSTANCE_METHOD(void, OnEnable); - DECLARE_INSTANCE_METHOD(void, Refresh); - DECLARE_INSTANCE_METHOD(void, CreateCell, System::Threading::Tasks::Task_1* coverTask, GlobalNamespace::CustomPreviewBeatmapLevel* level); - DECLARE_INSTANCE_METHOD(void, Delete); - - static DownloadedSongsGSM* instance; - - int selectedIdx; - bool needSongRefresh; - public: - static DownloadedSongsGSM* get_Instance() { return instance; } - - static std::vector mapQueue; - -public: - void InsertCell(std::string hash); - -) \ No newline at end of file diff --git a/MultiQuestensions/include/UI/LobbySetupPanel.hpp b/MultiQuestensions/include/UI/LobbySetupPanel.hpp index bbe1338..e9a46a7 100644 --- a/MultiQuestensions/include/UI/LobbySetupPanel.hpp +++ b/MultiQuestensions/include/UI/LobbySetupPanel.hpp @@ -3,6 +3,7 @@ #include "HMUI/CurvedTextMeshPro.hpp" #include "UnityEngine/UI/Toggle.hpp" #include "UnityEngine/RectTransform.hpp" +#include "GlobalNamespace/LobbySetupViewController.hpp" namespace MultiQuestensions::UI { class LobbySetupPanel { @@ -14,7 +15,7 @@ namespace MultiQuestensions::UI { static bool needRefresh; public: - static void AddSetupPanel(UnityEngine::RectTransform* parent, GlobalNamespace::MultiplayerSessionManager* sessionManager); + static void AddSetupPanel(UnityEngine::RectTransform* parent, GlobalNamespace::MultiplayerSessionManager* sessionManager, GlobalNamespace::LobbySetupViewController* lobbySetupViewController); }; //void SetCustomSongs(bool value); } \ No newline at end of file diff --git a/MultiQuestensions/include/main.hpp b/MultiQuestensions/include/main.hpp index 43b60f3..2cc41ae 100644 --- a/MultiQuestensions/include/main.hpp +++ b/MultiQuestensions/include/main.hpp @@ -36,24 +36,6 @@ #include // Define these functions here so that we can easily read configuration and log information from other files -Configuration& getConfig(); Logger& getLogger(); -// Utilities -Il2CppString* LevelIdToHash(Il2CppString*); - -extern std::string GetHash(const std::string& levelId); - -extern ModInfo modInfo; - -extern std::vector DownloadedSongIds; - -extern bool gotSongPackOverrides; - -extern std::string missingLevelText; - -namespace MultiQuestensions { - extern bool AllPlayersHaveNE(); - extern bool AllPlayersHaveME(); - extern bool AllPlayersHaveChroma(); -} \ No newline at end of file +extern ModInfo modInfo; \ No newline at end of file diff --git a/MultiQuestensions/mod.json b/MultiQuestensions/mod.json deleted file mode 100644 index a269238..0000000 --- a/MultiQuestensions/mod.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "_QPVersion": "0.1.1", - "name": "MultiQuestensions", - "id": "multiquestensions", - "author": "EnderdracheLP and Goobie", - "version": "0.1.1", - "packageId": "com.beatgames.beatsaber", - "packageVersion": "1.17.1", - "description": "Lightweight port of MultiplayerExtensions, the PC mod that adds features to official Multiplayer.", - "modFiles": [ - "libmultiquestensions.so" - ], - "libraryFiles": [ - "libbeatsaber-hook_2_3_2.so" - ], - "dependencies": [ - { - "id": "BeatTogether", - "version": "^1.3.0", - "downloadIfMissing": "https://github.com/pythonology/BeatTogether.Quest/releases/download/v1.3.0/BeatTogether_v1.3.0.qmod" - } - ] -} diff --git a/MultiQuestensions/mod.template.json b/MultiQuestensions/mod.template.json new file mode 100644 index 0000000..3b3555d --- /dev/null +++ b/MultiQuestensions/mod.template.json @@ -0,0 +1,16 @@ +{ + "_QPVersion": "0.1.1", + "name": "${mod_name}", + "id": "${mod_id}", + "author": "EnderdracheLP and Goobie", + "version": "${version}", + "packageId": "com.beatgames.beatsaber", + "packageVersion": "1.22.0", + "description": "Lightweight port of MultiplayerExtensions, the PC mod that adds features to Multiplayer.!", + "dependencies": [ + ], + "modFiles": [], + "libraryFiles": [], + "fileCopies": [], + "copyExtensions": [] +} diff --git a/MultiQuestensions/qpm.json b/MultiQuestensions/qpm.json index b0267c0..9f809db 100644 --- a/MultiQuestensions/qpm.json +++ b/MultiQuestensions/qpm.json @@ -3,44 +3,27 @@ "dependenciesDir": "extern", "info": { "name": "MultiQuestensions", - "id": "multiquestensions", - "version": "0.1.0", + "id": "MultiQuestensions", + "version": "1.0.0", "url": "", - "additionalData": {} + "additionalData": { + "overrideSoName": "libMultiQuestensions.so" + } }, "dependencies": [ - { - "id": "beatsaber-hook", - "versionRange": "^2.3.2", - "additionalData": { - "extraFiles": [ - "src/inline-hook" - ] - } - }, { "id": "codegen", - "versionRange": "^0.14.0", + "versionRange": "^0.23.0", "additionalData": {} }, { "id": "custom-types", - "versionRange": "=0.12.7", - "additionalData": {} - }, - { - "id": "songloader", - "versionRange": "^0.7.1", + "versionRange": "^0.15.10", "additionalData": {} }, { "id": "questui", - "versionRange": "^0.11.1", - "additionalData": {} - }, - { - "id": "songdownloader", - "versionRange": "^0.3.2", + "versionRange": "^0.15.2", "additionalData": {} }, { @@ -49,9 +32,18 @@ "additionalData": {} }, { - "id": "cpp-semver", + "id": "modloader", "versionRange": "*", "additionalData": {} + }, + { + "id": "beatsaber-hook", + "versionRange": "^3.8.5", + "additionalData": { + "extraFiles": [ + "src/inline-hook" + ] + } } ], "additionalData": {} diff --git a/MultiQuestensions/shared/CodegenExtensions/ColorUtility.hpp b/MultiQuestensions/shared/CodegenExtensions/ColorUtility.hpp index 915c48a..d81d9ae 100644 --- a/MultiQuestensions/shared/CodegenExtensions/ColorUtility.hpp +++ b/MultiQuestensions/shared/CodegenExtensions/ColorUtility.hpp @@ -1,33 +1,29 @@ #pragma once #include "main.hpp" #include "beatsaber-hook/shared/utils/byref.hpp" -#include "UnityEngine/UnityString.hpp" #include "UnityEngine/Color32.hpp" +#include "UnityEngine/Color.hpp" namespace UnityEngine { // Forward declaration of UnityEngine.Color - class Color; - //// Forward declaration of UnityEngine.Color32 - //class Color32; - //// Forward declaration of UnityEngine.UnityString - //class UnityString; + // class Color; class ColorUtility { ColorUtility() = delete; public: - static bool TryParseHtmlString(Il2CppString* htmlString, Color& color) { + static bool TryParseHtmlString(StringW htmlString, Color& color) { Color32 c; bool result = DoTryParseHtmlColor(htmlString, byref(c)); color = { (float)c.r / 255.0f, (float)c.g / 255.0f, (float)c.b / 255.0f, 1 }; return result; } - static bool TryParseHtmlString(std::string htmlString, Color& color) { - return TryParseHtmlString(il2cpp_utils::newcsstr(htmlString), color); - } + //static bool TryParseHtmlString(std::string htmlString, Color& color) { + // return TryParseHtmlString(htmlString.c_str(), color); + //} - static bool DoTryParseHtmlColor(Il2CppString* htmlString, ByRef color) { - using DoTryParseHtmlColor = function_ptr_t>; + static bool DoTryParseHtmlColor(StringW htmlString, ByRef color) { + using DoTryParseHtmlColor = function_ptr_t>; static DoTryParseHtmlColor doTryParseHtmlColor = reinterpret_cast(il2cpp_functions::resolve_icall("UnityEngine.ColorUtility::DoTryParseHtmlColor")); return doTryParseHtmlColor(htmlString, byref(color)); } @@ -36,9 +32,9 @@ namespace UnityEngine { return std::clamp(static_cast(round(colorVal * 255.0f)), 0, UINT8_MAX); } - static Il2CppString* ToHtmlStringRGB(Color color) { + static StringW ToHtmlStringRGB(Color color) { Color32 color2(conv(color.r), conv(color.g), conv(color.b), 1); - return il2cpp_utils::newcsstr(string_format("#%02X%02X%02X", color2.r, color2.g, color2.b)); + return string_format("#%02X%02X%02X", color2.r, color2.g, color2.b); } static std::string ToHtmlStringRGB_CPP(Color color) { diff --git a/MultiQuestensions/shared/Players/MpexPlayerData.hpp b/MultiQuestensions/shared/Players/MpexPlayerData.hpp new file mode 100644 index 0000000..ffeb205 --- /dev/null +++ b/MultiQuestensions/shared/Players/MpexPlayerData.hpp @@ -0,0 +1,18 @@ +#pragma once +#include "custom-types/shared/macros.hpp" +#include "GlobalNamespace/IConnectedPlayer.hpp" +#include "multiplayer-core/shared/Networking/Abstractions/MpPacket.hpp" +#include "UnityEngine/Color.hpp" + +DECLARE_CLASS_CUSTOM_DLL(MultiQuestensions::Players, MpexPlayerData, MultiplayerCore::Networking::Abstractions::MpPacket, "MultiplayerExtensions.Players", + + DECLARE_DEFAULT_CTOR(); + + /// + /// Player's color set in the mod config. + /// + DECLARE_INSTANCE_FIELD(UnityEngine::Color, Color); + + DECLARE_OVERRIDE_METHOD(void, Serialize, il2cpp_utils::FindMethodUnsafe(classof(MultiplayerCore::Networking::Abstractions::MpPacket*), "Serialize", 1), LiteNetLib::Utils::NetDataWriter* writer); + DECLARE_OVERRIDE_METHOD(void, Deserialize, il2cpp_utils::FindMethodUnsafe(classof(MultiplayerCore::Networking::Abstractions::MpPacket*), "Deserialize", 1), LiteNetLib::Utils::NetDataReader* reader); +) \ No newline at end of file diff --git a/MultiQuestensions/shared/Players/MpexPlayerManager.hpp b/MultiQuestensions/shared/Players/MpexPlayerManager.hpp new file mode 100644 index 0000000..5f1287d --- /dev/null +++ b/MultiQuestensions/shared/Players/MpexPlayerManager.hpp @@ -0,0 +1,17 @@ +#pragma once +#include "MpexPlayerData.hpp" +#include "multiplayer-core/shared/Utils/event.hpp" +#include "multiplayer-core/shared/Players/MpPlayerData.hpp" + + +namespace MultiQuestensions::Players { + class MpexPlayerManager { + public: + + + static bool TryGetMpexPlayerData(std::string const& playerId, MultiQuestensions::Players::MpexPlayerData*& player); + static MpexPlayerData* GetMpexPlayerData(std::string const& playerId); + + static MultiplayerCore::event ReceivedMpExPlayerData; + }; +} \ No newline at end of file diff --git a/MultiQuestensions/shared/Utils/CustomData.hpp b/MultiQuestensions/shared/Utils/CustomData.hpp deleted file mode 100644 index 5642c28..0000000 --- a/MultiQuestensions/shared/Utils/CustomData.hpp +++ /dev/null @@ -1,7 +0,0 @@ -#pragma once -#include -#include "GlobalNamespace/CustomPreviewBeatmapLevel.hpp" - -namespace MultiQuestensions::Utils { - bool HasRequirement(const std::optional& beatmapOpt); -} diff --git a/MultiQuestensions/shared/Utils/SemVerChecker.hpp b/MultiQuestensions/shared/Utils/SemVerChecker.hpp deleted file mode 100644 index ca075f0..0000000 --- a/MultiQuestensions/shared/Utils/SemVerChecker.hpp +++ /dev/null @@ -1,39 +0,0 @@ -#pragma once -#include "main.hpp" -#include "cpp-semver/shared/cpp-semver.hpp" - -namespace MultiQuestensions::Utils { - static const std::string ChromaID = "Chroma"; - static const std::string ChromaVersionRange = ">=2.5.7"; - - static bool IsInstalled(std::string const& modName) { - auto const modList = Modloader::getMods(); - auto modInfo = modList.find(modName); - return (modInfo != modList.end()); - } - - static bool MatchesVersion(std::string const& modName, std::string const& versionRange) { - auto const modList = Modloader::getMods(); - auto modLoaded = Modloader::requireMod(modName); - try { - if (modLoaded) { - auto modInfo = modList.find(modName); - if (modInfo != modList.end()) { - - getLogger().debug("Checking %s version '%s'", modName.c_str(), modInfo->second.info.version.c_str()); - if (semver::satisfies(modInfo->second.info.version, versionRange)) { - getLogger().debug("%s version valid", modName.c_str()); - return true; - } - getLogger().debug("%s version outdated", modName.c_str()); - // mod is outdated - } - } - return false; - } - catch (const semver::semver_error &e) { - getLogger().error("SemVer Exception checking %s: %s", modName.c_str(), e.what()); - return false; - } - } -} \ No newline at end of file diff --git a/MultiQuestensions/src/Assets/Sprites.cpp b/MultiQuestensions/src/Assets/Sprites.cpp index 4a0030d..e1b34ca 100644 --- a/MultiQuestensions/src/Assets/Sprites.cpp +++ b/MultiQuestensions/src/Assets/Sprites.cpp @@ -11,23 +11,38 @@ using namespace QuestUI::BeatSaberUI; namespace MultiQuestensions { + Logger& getLogger() { + static Logger* myLogger = new Logger(modInfo, LoggerOptions(false, true)); + return *myLogger; + } + std::string Sprites::Oculus64 = "iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAAHdbkFIAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAAN1sAADdbAZmDAsYAAATaSURBVGhD7ZrNq1ZFHMefey21yDSxNyIKKtEyfIEgWgkhIaUFERFKK1GQJHvHZatCFymE/0ALk9zUImpViwiM3l8X1yRSVCKvZaWm5u3znfnd6zNz3u9zjtxb84EvvzNn3ufMnDMvpzcwQ2Z7Y2NjMzHb0AU0PDQ09LLuO/B8E+UxYzyA+M45DLun1JxjkXeHOM8pQH87PIm5xbt6H9AOH+nCBSgqMYF8AnkBxu/VS+G/BHU9rwrX4GmLchHzcNitDHjN8iEcR3Vv2PlE4LkKLYyF1x4fwuESCCDQalSJBU9MGTLDiae0CPMoivvIn2gPI/CYd3omEiDi1Zjfvauc/nHcn0CjDhK8DIi7VgkYG+12BvyO+iBRZrh3+9vlpcD7Jh/qYjhXDNzXY8Yb5zy6y18GXIYmPgBBFQSJXHBJ1+NrixaCx1/ev5RPLHgikaiCATOMXkHnNHoG4DN0pyVbDYFnokOK2QGaxgUELyQCOOMcWc6hA/6yFMWfg252riyv8x7cYtchFGC7K2fIIfNuDHHv90kEnDVvR/zdy853er1riTTXrpuy1mw/Z8w6wm8CkNlpzGzv6oRlPIKv7Do7O8TzCszz3tUqo2hef+aV0Bqz0TNoBDXlLNqHVlhyiUQikUjkkvkcx/AxuQFzO7oHaRns9/Py0ddVy8PDaD86yNcvmIDUhowXI00oB0HbT6+i3E2hQoiwRbFbRAVR61VDwPUuSvscRtrkDYhnxdrQ1TPsin30icfs2hEXYCcmux3oUcc6iao6rqbld6DLnSsLZShIggIcQTHHUONJKnE2uNhZHrEgWfDUPC5mlXk3hrg/+yQCdpi3Ix4e2sqJ+cHsZPjDbD9XmXXEBdCGXMxLZhtBTTXsbvOugBGzjrgTfo5Z7l0B7yCdkdQdIVoXvofyWvRGOuHEjmNcgLsx+Ztg7fA3mQcdOngEeH6D6aoAGp55rRtCK8xFx1HbNFvuEWG/jzcw2r5cbck2g4gr0Fso7/1QxQH0LOpypZ1IJBKJRCKRSCSmMVXbLIWw0NCpmn4TWYnuQ8uQVsN5K+E20cpch6g6dfsYfYi+ZT2rY8buoeIPoU/RKTRVUFlUpjVWzHYhYS3UX0BdLNbbZhS9iOZZ8UupHAIktBizFy1Bkx4ylxhtwehvrccZGt+7OwWUVojKL8Vor3+WuzH90PnEvTTCF96ZpbABqLwOR95F1ZtZHr2c3ka7kbb+9JtJm+jlqjJtRg+jov3vmC/RgzTCEe+sCQ2gXxVOoirOoF2o3hlIC5DXHLQTnUZVqA7PWdQMZadHOmUItrIL+BHtoIV1enFJIC9tu2uf/6C7UY7qoGO+XMoaQJnU2Q3Xk6//b1h7KM86vU51yDt2KIduswblHRnloWOkTajzXVjlgTZannVQHfJ+4HKUvQRlXkNbddEAtfgvSK1edqrcBPVUdeXrUNOZ5i60lWHjXRFVn0Edm76BnnA3ph+av6yj8v945ySgEfQ75zqUd+A4VdHvpzpwzxyMTxoS06dnMxr0h9ouUdmeQvp9tRalQyAPEtd41CdyPXoAaaLU9QqwCL1vNNF5H2mojtDdG713GjdADA2iGdl8pMXHNehWtABdidQwg+aht7Eqegr9in5CJ9BvaJQKtz3jTCQSiUQi8b+g1/sXRmYDomhz93gAAAAASUVORK5CYII="; + std::string Sprites::Steam64 = "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"; + std::string Sprites::Meta64 = "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"; Sprite* CreateSpriteFromBase64(std::string& base64) { - Array* bytes = System::Convert::FromBase64String(il2cpp_utils::newcsstr(base64)); + ArrayW bytes = System::Convert::FromBase64String(base64.c_str()); Texture2D* texture = Texture2D::New_ctor(2, 2); if (ImageConversion::LoadImage(texture, bytes, false)) { return Sprite::Create(texture, UnityEngine::Rect(0.0f, 0.0f, (float)texture->get_width(), (float)texture->get_height()), UnityEngine::Vector2(0, 0), 10.0f, 0u, SpriteMeshType::Tight, UnityEngine::Vector4(0.0f, 0.0f, 0.0f, 0.0f), false); } + getLogger().debug("Failed to load image texture"); return nullptr; } Sprite* Sprites::IconOculus64() { + //getLogger().debug("CreatingOculusImage"); return CreateSpriteFromBase64(Oculus64); } Sprite* Sprites::IconSteam64() { + //getLogger().debug("CreatingSteamIcon"); return CreateSpriteFromBase64(Steam64); } + + Sprite* Sprites::IconMeta64() { + //getLogger().debug("CreatingMetaIcon"); + return CreateSpriteFromBase64(Meta64); + } } \ No newline at end of file diff --git a/MultiQuestensions/src/Beatmaps/PreviewBeatmapPacket.cpp b/MultiQuestensions/src/Beatmaps/PreviewBeatmapPacket.cpp deleted file mode 100644 index 6e81c67..0000000 --- a/MultiQuestensions/src/Beatmaps/PreviewBeatmapPacket.cpp +++ /dev/null @@ -1,58 +0,0 @@ -#include "Beatmaps/PreviewBeatmapPacket.hpp" -#include "custom-types/shared/register.hpp" -#include "GlobalNamespace/VarIntExtensions.hpp" - -DEFINE_TYPE(MultiQuestensions::Beatmaps, PreviewBeatmapPacket); - -namespace MultiQuestensions::Beatmaps { - - //void PreviewBeatmapPacket::New_ctor() {} - //PreviewBeatmapPacket::PreviewBeatmapPacket() {} - void PreviewBeatmapPacket::New() {} - - void PreviewBeatmapPacket::Release() { - getLogger().debug("PreviewBeatmapPacket::Release"); - GlobalNamespace::ThreadStaticPacketPool_1::get_pool()->Release(this); - } - - void PreviewBeatmapPacket::Serialize(LiteNetLib::Utils::NetDataWriter* writer) { - getLogger().debug("PreviewBeatmapPacket::Serialize"); - - writer->Put(levelId); - writer->Put(levelHash); - writer->Put(songName); - writer->Put(songSubName); - writer->Put(songAuthorName); - writer->Put(levelAuthorName); - writer->Put(beatsPerMinute); - writer->Put(songDuration); - - writer->Put(characteristic); - GlobalNamespace::VarIntExtensions::PutVarUInt(writer, difficulty); - //writer->Put(difficulty); - } - - void PreviewBeatmapPacket::Deserialize(LiteNetLib::Utils::NetDataReader* reader) { - getLogger().debug("PreviewBeatmapPacket::Deserialize"); - - levelId = reader->GetString(); - //getLogger().debug("levelID: %s", to_utf8(csstrtostr(levelId)).c_str()); - levelHash = reader->GetString(); - //getLogger().debug("levelHash: %s", to_utf8(csstrtostr(levelHash)).c_str()); - songName = reader->GetString(); - songSubName = reader->GetString(); - songAuthorName = reader->GetString(); - levelAuthorName = reader->GetString(); - //getLogger().debug("songName: %s\n songSubName: %s\n songAuthorName: %s\n levelAuthorName: %s", - // to_utf8(csstrtostr(songName)).c_str(), - // to_utf8(csstrtostr(songSubName)).c_str(), - // to_utf8(csstrtostr(songAuthorName)).c_str(), - // to_utf8(csstrtostr(levelAuthorName)).c_str()); - beatsPerMinute = reader->GetFloat(); - songDuration = reader->GetFloat(); - - characteristic = reader->GetString(); - difficulty = GlobalNamespace::VarIntExtensions::GetVarUInt(reader); - getLogger().debug("Deserialize PreviewBeatmapPacket done"); - } -} diff --git a/MultiQuestensions/src/Beatmaps/PreviewBeatmapStub.cpp b/MultiQuestensions/src/Beatmaps/PreviewBeatmapStub.cpp deleted file mode 100644 index 4001b49..0000000 --- a/MultiQuestensions/src/Beatmaps/PreviewBeatmapStub.cpp +++ /dev/null @@ -1,176 +0,0 @@ -#include "Beatmaps/PreviewBeatmapStub.hpp" -#include "beatsaber-hook/shared/utils/il2cpp-utils.hpp" -#include "System/Threading/Tasks/TaskStatus.hpp" -#include "UnityEngine/ImageConversion.hpp" -#include "GlobalNamespace/MediaAsyncLoader.hpp" -#include "songdownloader/shared/BeatSaverAPI.hpp" -#include "questui/shared/CustomTypes/Components/MainThreadScheduler.hpp" -#include - -DEFINE_TYPE(MultiQuestensions::Beatmaps, PreviewBeatmapStub); - -namespace MultiQuestensions::Beatmaps { - - void PreviewBeatmapStub::FromPreviewPacket(Il2CppString* levelhash, GlobalNamespace::IPreviewBeatmapLevel* level, PreviewBeatmapPacket* lvl) { - if (levelhash && level) { - getLogger().debug("PreviewBeatmapStub::FromPreview"); - - _preview = level; - isDownloaded = true; - _downloadable = DownloadableState::Unchecked; - - levelID = level->get_levelID(); - levelHash = levelhash; - //getLogger().debug("levelID: %s, levelHash: %s", to_utf8(csstrtostr(levelID)).c_str(), to_utf8(csstrtostr(levelHash)).c_str()); - - songName = level->get_songName(); - songSubName = level->get_songSubName(); - songAuthorName = level->get_songAuthorName(); - levelAuthorName = level->get_levelAuthorName(); - //getLogger().debug("songName: %s, songSubName: %s, songAuthorName: %s, levelAuthorName: %s", to_utf8(csstrtostr(songName)).c_str(), to_utf8(csstrtostr(songSubName)).c_str(), to_utf8(csstrtostr(songAuthorName)).c_str(), to_utf8(csstrtostr(levelAuthorName)).c_str()); - - beatsPerMinute = level->get_beatsPerMinute(); - songDuration = level->get_songDuration(); - //getLogger().debug("beatsPerMinute: %f, songDuration: %f", beatsPerMinute, songDuration); - } - else { - getLogger().debug("PreviewBeatmapStub::FromPacket"); - - //getLogger().debug("Set downloadstates"); - isDownloaded = false; - _downloadable = DownloadableState::Unchecked; - - //getLogger().debug("Get levelId and Hash"); - levelID = lvl->levelId; - levelHash = lvl->levelHash; - //levelHash = LevelIdToHash(levelID)/*packet->levelHash*/; - //getLogger().debug("levelID: %s\n Hash: %s", - // to_utf8(csstrtostr(levelID)).c_str(), - // to_utf8(csstrtostr(levelHash)).c_str()); - //getLogger().debug("SongInfo strings"); - songName = lvl->songName; - songSubName = lvl->songSubName; - songAuthorName = lvl->songAuthorName; - levelAuthorName = lvl->levelAuthorName; - //getLogger().debug("songName: %s\n songSubName: %s\n songAuthorName: %s\n levelAuthorName: %s", - // to_utf8(csstrtostr(songName)).c_str(), - // to_utf8(csstrtostr(songSubName)).c_str(), - // to_utf8(csstrtostr(songAuthorName)).c_str(), - // to_utf8(csstrtostr(levelAuthorName)).c_str()); - - //getLogger().debug("SongInfo floats"); - beatsPerMinute = lvl->beatsPerMinute; - songDuration = lvl->songDuration; - //getLogger().debug("beatsPerMinute: %f\n songDuration: %f", - // beatsPerMinute, - // songDuration); - } - - } - - System::Threading::Tasks::Task_1* PreviewBeatmapStub::GetCoverImageAsync(System::Threading::CancellationToken cancellationToken) { - if (_preview) { - getLogger().debug("Use localPreview"); - return _preview->GetCoverImageAsync(cancellationToken); - } - if (coverImage) - return System::Threading::Tasks::Task_1::New_ctor(coverImage); - else { - return System::Threading::Tasks::Task_1::New_ctor(static_cast(nullptr)); - } - } - - System::Threading::Tasks::Task_1* PreviewBeatmapStub::GetPreviewAudioClipAsync(System::Threading::CancellationToken cancellationToken) { - if (_preview) { - return _preview->GetPreviewAudioClipAsync(cancellationToken); - } - else { - return nullptr; - } - } - - MultiQuestensions::Beatmaps::PreviewBeatmapPacket* PreviewBeatmapStub::GetPacket(Il2CppString* characteristic, GlobalNamespace::BeatmapDifficulty difficulty) { - getLogger().debug("Start PreviewBeatmapStub::GetPacket"); - MultiQuestensions::Beatmaps::PreviewBeatmapPacket* packet = THROW_UNLESS(il2cpp_utils::New()); - - packet->levelId = levelID; - packet->levelHash = levelHash; - - packet->songName = songName; - packet->songSubName = songSubName; - packet->songAuthorName = songAuthorName; - packet->levelAuthorName = levelAuthorName; - - packet->beatsPerMinute = beatsPerMinute; - packet->songDuration = songDuration; - - packet->characteristic = characteristic; - packet->difficulty = difficulty; - return packet; - } - -#pragma region All the pain in form of getter functions - - Il2CppString* PreviewBeatmapStub::get_levelID() { - return levelID; - } - - Il2CppString* PreviewBeatmapStub::get_songName() { - return songName; - } - - Il2CppString* PreviewBeatmapStub::get_songSubName() { - return songSubName; - } - - Il2CppString* PreviewBeatmapStub::get_songAuthorName() { - return songAuthorName; - } - - Il2CppString* PreviewBeatmapStub::get_levelAuthorName() { - return levelAuthorName; - } - - float PreviewBeatmapStub::get_beatsPerMinute() { - return beatsPerMinute; - } - - float PreviewBeatmapStub::get_songDuration() { - return songDuration; - } - - float PreviewBeatmapStub::get_songTimeOffset() { - return songTimeOffset; - } - - float PreviewBeatmapStub::get_previewDuration() { - return previewDuration; - } - - float PreviewBeatmapStub::get_previewStartTime() { - return previewStartTime; - } - - float PreviewBeatmapStub::get_shuffle() { - return shuffle; - } - - float PreviewBeatmapStub::get_shufflePeriod() { - return shufflePeriod; - } - - GlobalNamespace::EnvironmentInfoSO* PreviewBeatmapStub::get_allDirectionsEnvironmentInfo() { - return allDirectionsEnvironmentInfo; - } - - GlobalNamespace::EnvironmentInfoSO* PreviewBeatmapStub::get_environmentInfo() { - return environmentInfo; - } - - Array* PreviewBeatmapStub::get_previewDifficultyBeatmapSets() { - return previewDifficultyBeatmapSets; - } - -#pragma endregion - -} \ No newline at end of file diff --git a/MultiQuestensions/src/CS_DataStore.cpp b/MultiQuestensions/src/CS_DataStore.cpp deleted file mode 100644 index 95ff462..0000000 --- a/MultiQuestensions/src/CS_DataStore.cpp +++ /dev/null @@ -1,35 +0,0 @@ -#include "main.hpp" -#include "CS_DataStore.hpp" - -DEFINE_TYPE(MultiQuestensions, DataStore); - -namespace MultiQuestensions { - void DataStore::New(GlobalNamespace::IPreviewBeatmapLevel* previewBeatmap, GlobalNamespace::BeatmapCharacteristicSO* beatmapCharacteristic, GlobalNamespace::IDifficultyBeatmap* difficultyBeatmap, GlobalNamespace::GameplayModifiers* gameplayModifiers) { - loadingPreviewBeatmapLevel = previewBeatmap; - loadingBeatmapCharacteristic = beatmapCharacteristic; - loadingDifficultyBeatmap = difficultyBeatmap; - loadingGameplayModifiers = gameplayModifiers; - } - - DataStore* DataStore::instance; - - DataStore* DataStore::get_Instance() { - return instance; - } - - DataStore* DataStore::CS_Ctor(GlobalNamespace::IPreviewBeatmapLevel* previewBeatmap, GlobalNamespace::BeatmapCharacteristicSO* beatmapCharacteristic, GlobalNamespace::IDifficultyBeatmap* difficultyBeatmap, GlobalNamespace::GameplayModifiers* gameplayModifiers, GlobalNamespace::BeatmapDifficulty beatmapDifficulty) { - instance = THROW_UNLESS(il2cpp_utils::New(previewBeatmap, beatmapCharacteristic, difficultyBeatmap, gameplayModifiers)); - instance->loadingBeatmapDifficulty = beatmapDifficulty; - return instance; - } - - void DataStore::Clear() { - if (instance) { - instance->loadingPreviewBeatmapLevel = nullptr; - instance->loadingBeatmapCharacteristic = nullptr; - instance->loadingDifficultyBeatmap = nullptr; - instance->loadingGameplayModifiers = nullptr; - instance->loadingBeatmapDifficulty = std::nullopt; - } - } -} \ No newline at end of file diff --git a/MultiQuestensions/src/Environments/LobbyAvatarNameTag.cpp b/MultiQuestensions/src/Environments/LobbyAvatarNameTag.cpp deleted file mode 100644 index bbd011c..0000000 --- a/MultiQuestensions/src/Environments/LobbyAvatarNameTag.cpp +++ /dev/null @@ -1,176 +0,0 @@ -#include "main.hpp" -#include "Environments/LobbyAvatarNameTag.hpp" -#include "Assets/Sprites.hpp" -#include "UnityEngine/CanvasRenderer.hpp" -#include "UnityEngine/RectTransform.hpp" -#include "UnityEngine/UI/HorizontalLayoutGroup.hpp" -#include "GlobalNamespace/ConnectedPlayerName.hpp" -#include "UnityEngine/Object.hpp" -using MultiQuestensions::Extensions::ExtendedPlayer; -using namespace GlobalNamespace; -using namespace UnityEngine::UI; -using namespace UnityEngine; -using namespace HMUI; - -DEFINE_TYPE(MultiQuestensions::Environments, LobbyAvatarNameTag); - -namespace MultiQuestensions::Environments { - static Il2CppString* BG() { - static auto* bg = il2cpp_utils::newcsstr("BG"); - return bg; - } - - static Il2CppString* Name() { - static auto* name = il2cpp_utils::newcsstr("Name"); - return name; - } - - static Il2CppString* Player() { - static auto* player = il2cpp_utils::newcsstr("Player"); - return player; - } - - - void LobbyAvatarNameTag::New() { - _enabled = false; - _playerInfo = nullptr; - _playerIcons.clear(); - } - - void LobbyAvatarNameTag::Awake() { - // Get references - _bg = get_transform()->Find(BG())->GetComponent(); - _nameText = get_transform()->Find(Name())->GetComponent(); - - // Add horizontal layout to bg - HorizontalLayoutGroup* _; - if (!_bg->TryGetComponent(ByRef(_))) - { - auto hLayout = _bg->get_gameObject()->AddComponent(); - hLayout->set_childAlignment(TextAnchor::MiddleCenter); - hLayout->set_childForceExpandWidth(false); - hLayout->set_childForceExpandHeight(false); - hLayout->set_childScaleWidth(false); - hLayout->set_childScaleHeight(false); - hLayout->set_spacing(4); - } - - // Re-nest name onto bg - _nameText->get_transform()->SetParent(_bg->get_transform(), false); - - // Take control of name tag - ConnectedPlayerName* nativeNameScript; - if (_nameText->TryGetComponent(ByRef(nativeNameScript))) - Object::Destroy(nativeNameScript); - _nameText->set_text(Player()); - } - - void LobbyAvatarNameTag::OnEnable() - { - _enabled = true; - - if (_playerInfo != nullptr) - SetPlayerInfo(_playerInfo); - } - - #pragma region Set Player Info - void LobbyAvatarNameTag::SetPlayerInfo(IConnectedPlayer* player) - { - if (il2cpp_utils::AssignableFrom(reinterpret_cast(player)->klass)) - SetExtendedPlayerInfo(reinterpret_cast(player)); - else - SetSimplePlayerInfo(player); - } - - void LobbyAvatarNameTag::SetExtendedPlayerInfo(ExtendedPlayer* extendedPlayer) - { - _playerInfo = reinterpret_cast(extendedPlayer); - - if (!_enabled) - return; - - _nameText->set_text(extendedPlayer->get_userName()); - _nameText->set_color(extendedPlayer->get_playerColor()); - - //getLogger().debug("SetExtendedPlayerInfo platform: %d", (int)extendedPlayer->get_platform()); - - switch (extendedPlayer->get_platform()) - { - case Extensions::Platform::Steam: - SetIcon(PlayerIconSlot::Platform, Sprites::IconSteam64()); - break; - case Extensions::Platform::OculusQuest: - case Extensions::Platform::OculusPC: - SetIcon(PlayerIconSlot::Platform, Sprites::IconOculus64()); - break; - default: - RemoveIcon(PlayerIconSlot::Platform); - break; - } - } - - void LobbyAvatarNameTag::SetSimplePlayerInfo(IConnectedPlayer* simplePlayer) - { - _playerInfo = simplePlayer; - - if (!_enabled) - return; - - _nameText->set_text(simplePlayer->get_userName()); - _nameText->set_color(Color::get_white()); - - RemoveIcon(PlayerIconSlot::Platform); - } - #pragma endregion - - #pragma region Set Icons - void LobbyAvatarNameTag::SetIcon(PlayerIconSlot slot, Sprite* sprite) - { - getLogger().debug("SetIcon Sprite %p", sprite); - - if (!_enabled) - return; - - HMUI::ImageView* imageView; - if (!_playerIcons.contains(slot)) - { - //getLogger().debug("SetIcon, create new Icon"); - auto iconObj = GameObject::New_ctor(il2cpp_utils::newcsstr(string_format("MQEPlayerIcon(%d)", (int)slot))); - iconObj->get_transform()->SetParent(_bg->get_transform(), false); - iconObj->get_transform()->SetSiblingIndex((int)slot); - iconObj->set_layer(5); - - iconObj->AddComponent(); - - imageView = iconObj->AddComponent(); - imageView->set_maskable(true); - imageView->set_fillCenter(true); - imageView->set_preserveAspect(true); - imageView->set_material(_bg->get_material()); // No Glow Billboard material - _playerIcons.insert_or_assign(slot, imageView); - - auto rectTransform = iconObj->GetComponent(); - rectTransform->set_localScale({ 3.2f, 3.2f }); - } - else imageView = _playerIcons.at(slot); - - imageView->set_sprite(sprite); - - _nameText->get_transform()->SetSiblingIndex(999); - } - - void LobbyAvatarNameTag::RemoveIcon(PlayerIconSlot slot) - { - //getLogger().debug("RemoveIcon"); - - if (!_enabled) - return; - if (_playerIcons.contains(slot)) - { - HMUI::ImageView* imageView = _playerIcons[slot]; - GameObject::Destroy(imageView->get_gameObject()); - _playerIcons.erase(slot); - } - } - #pragma endregion -} \ No newline at end of file diff --git a/MultiQuestensions/src/Environments/LobbyAvatarPlaceLighting.cpp b/MultiQuestensions/src/Environments/LobbyAvatarPlaceLighting.cpp deleted file mode 100644 index 23a5f59..0000000 --- a/MultiQuestensions/src/Environments/LobbyAvatarPlaceLighting.cpp +++ /dev/null @@ -1,73 +0,0 @@ -#include "main.hpp" -#include "Environments/LobbyAvatarPlaceLighting.hpp" -#include "UnityEngine/Time.hpp" -using namespace GlobalNamespace; -using namespace UnityEngine; - -DEFINE_TYPE(MultiQuestensions::Environments, LobbyAvatarPlaceLighting); - -namespace MultiQuestensions::Environments { - - void LobbyAvatarPlaceLighting::New() { - smoothTime = 2.0f; - } - - void LobbyAvatarPlaceLighting::OnEnable() { - auto lightsArr = this->GetComponentsInChildren(); - for (int i = 0; i < lightsArr->Length(); i++) { - this->lights.emplace_back(lightsArr->get(i)); - } - } - - void LobbyAvatarPlaceLighting::OnDisable() { - lights.clear(); - } - - inline static Color Lerp(Color const& a, Color const& b, float const& t) - { - float t_clamp = std::clamp(t, 0.0f, 1.0f); - return Color(a.r + (b.r - a.r) * t_clamp, a.g + (b.g - a.g) * t_clamp, a.b + (b.b - a.b) * t_clamp, a.a + (b.a - a.a) * t_clamp); - } - - void LobbyAvatarPlaceLighting::Update() { - Color current = GetColor(); - - if (current == targetColor) - return; - - SetColor(targetColor); - - if (IsColorVeryCloseToColor(current, targetColor)) - SetColor(targetColor); - else - //SetColor(Color::Lerp(current, targetColor, Time::get_deltaTime() * smoothTime)); - SetColor(Lerp(current, targetColor, Time::get_deltaTime() * smoothTime)); - } - - void LobbyAvatarPlaceLighting::SetColor(const Color& color, bool immediate) { - targetColor = color; - - if (immediate) - { - SetColor(color); - } - } - - Color LobbyAvatarPlaceLighting::GetColor() { - if (lights.size() > 0) - return lights[0]->get_color(); - return Color::get_black(); - } - - bool LobbyAvatarPlaceLighting::IsColorVeryCloseToColor(Color color0, Color color1) - { - return (fabs(color0.r - color1.r) < 0.002f && fabs(color0.g - color1.g) < 0.002f && fabs(color0.b - color1.b) < 0.002f && fabs(color0.a - color1.a) < 0.002f); - } - - void LobbyAvatarPlaceLighting::SetColor(const Color& color) { - for (TubeBloomPrePassLight* light : lights) { - light->set_color(color); - light->Refresh(); - } - } -} \ No newline at end of file diff --git a/MultiQuestensions/src/Environments/MQEAvatarPlaceLighting.cpp b/MultiQuestensions/src/Environments/MQEAvatarPlaceLighting.cpp new file mode 100644 index 0000000..81d1e63 --- /dev/null +++ b/MultiQuestensions/src/Environments/MQEAvatarPlaceLighting.cpp @@ -0,0 +1,114 @@ +#include "main.hpp" +#include "Environments/MQEAvatarPlaceLighting.hpp" +#include "UnityEngine/Time.hpp" +#include "Config.hpp" +using namespace GlobalNamespace; +using namespace UnityEngine; + +DEFINE_TYPE(MultiQuestensions::Environments, MQEAvatarPlaceLighting); + +namespace MultiQuestensions::Environments { + + Logger& getLogger() { + static Logger* myLogger = new Logger(modInfo, LoggerOptions(false, true)); + return *myLogger; + } + + void MQEAvatarPlaceLighting::New() { + //getLogger().debug("MQEAvatarPlaceLighting::New()"); + smoothTime = 2.0f; + // if (targetColor != UnityEngine::Color::get_black()) + targetColor = UnityEngine::Color::get_black(); + } + + // void MQEAvatarPlaceLighting::Awake() { + // // auto lightsArr = this->GetComponentsInChildren(); + // // for (int i = 0; i < lightsArr.Length(); i++) { + // // this->lights.emplace_back(lightsArr.get(i)); + // // } + + // // if (_sessionManager == nullptr || _sessionManager->get_localPlayer() == nullptr) + // // return; + + // // if (_sessionManager->get_localPlayer()->get_sortIndex() == SortIndex) { + // // this->SetColor(config.PlayerColor, true); + // // return; + // // } + + // // for (int i = 0; i < _sessionManager->connectedPlayers->get_Count(); i++) { + // // if (_sessionManager->connectedPlayers->get_Item(i)->get_sortIndex() == SortIndex) { + // // auto player = _sessionManager->connectedPlayers->get_Item(i); + // // auto mpexPlayerData = MpexPlayerManager::GetPlayer(player->get_playerId()); + // // this->SetColor(mpexPlayerData ? mpexPlayerData->get_color() : Config::DefaultPlayerColor, true); + // // return; + // // } + // // } + // } + + void MQEAvatarPlaceLighting::OnEnable() { + //getLogger().debug("MQEAvatarPlaceLighting::OnEnable()"); + auto lightsArr = this->GetComponentsInChildren(); + for (int i = 0; i < lightsArr.Length(); i++) { + this->lights.emplace_back(lightsArr.get(i)); + } + } + + void MQEAvatarPlaceLighting::OnDisable() { + //getLogger().debug("MQEAvatarPlaceLighting::OnDisable()"); + lights.clear(); + } + + inline static Color Lerp(Color const& a, Color const& b, float const& t) + { + //getLogger().debug("Color Lerp"); + float t_clamp = std::clamp(t, 0.0f, 1.0f); + return Color(a.r + (b.r - a.r) * t_clamp, a.g + (b.g - a.g) * t_clamp, a.b + (b.b - a.b) * t_clamp, a.a + (b.a - a.a) * t_clamp); + } + + void MQEAvatarPlaceLighting::FixedUpdate() { + //getLogger().debug("MQEAvatarPlaceLighting::Update()"); + Color current = GetColor(); + + if (current == targetColor) + return; + + // SetColor(targetColor); + + if (IsColorVeryCloseToColor(current, targetColor)) + SetColor(targetColor); + else + //SetColor(Color::Lerp(current, targetColor, Time::get_deltaTime() * smoothTime)); + SetColor(Lerp(current, targetColor, Time::get_deltaTime() * smoothTime)); + } + + void MQEAvatarPlaceLighting::SetColor(const Color& color, bool immediate) { + //getLogger().debug("MQEAvatarPlaceLighting::SetColor(const Color& color, bool immediate)"); + targetColor = color; + + if (immediate) + { + SetColor(color); + } + } + + Color MQEAvatarPlaceLighting::GetColor() { + //getLogger().debug("MQEAvatarPlaceLighting::GetColor()"); + if (lights.size() > 0) + return lights[0]->get_color(); + return Color::get_black(); + } + + bool MQEAvatarPlaceLighting::IsColorVeryCloseToColor(Color color0, Color color1) + { + //getLogger().debug("MQEAvatarPlaceLighting::IsColorVeryCloseToColor"); + return (fabs(color0.r - color1.r) < 0.002f && fabs(color0.g - color1.g) < 0.002f && fabs(color0.b - color1.b) < 0.002f && fabs(color0.a - color1.a) < 0.002f); + } + + void MQEAvatarPlaceLighting::SetColor(const Color& color) { + //getLogger().debug("MQEAvatarPlaceLighting::SetColor(const Color& color)"); + for (TubeBloomPrePassLight* light : lights) { + light->set_color(color); + light->Refresh(); + } + } +} \ No newline at end of file diff --git a/MultiQuestensions/src/Environments/MQEAvaterNameTag.cpp b/MultiQuestensions/src/Environments/MQEAvaterNameTag.cpp new file mode 100644 index 0000000..dbba406 --- /dev/null +++ b/MultiQuestensions/src/Environments/MQEAvaterNameTag.cpp @@ -0,0 +1,229 @@ +#include "main.hpp" +#include "Config.hpp" +#include "Environments/MQEAvatarNameTag.hpp" +#include "Assets/Sprites.hpp" +#include "Hooks/SessionManagerAndExtendedPlayerHooks.hpp" +#include "UnityEngine/CanvasRenderer.hpp" +#include "UnityEngine/RectTransform.hpp" +#include "UnityEngine/UI/HorizontalLayoutGroup.hpp" +#include "GlobalNamespace/ConnectedPlayerName.hpp" +#include "UnityEngine/Object.hpp" +#include "Players/MpexPlayerManager.hpp" +#include "multiplayer-core/shared/Players/MpPlayerData.hpp" +#include "multiplayer-core/shared/Players/MpPlayerManager.hpp" +#include "multiplayer-core/shared/Utils/event.hpp" + +using namespace GlobalNamespace; +using namespace UnityEngine::UI; +using namespace UnityEngine; +using namespace HMUI; + +DEFINE_TYPE(MultiQuestensions::Environments, MQEAvatarNameTag); + +namespace MultiQuestensions::Environments { + static StringW BG() { + static ConstString bg("BG"); + return bg; + } + + static StringW Name() { + static ConstString name("Name"); + return name; + } + + static StringW Player() { + static ConstString player("Player"); + return player; + } + + + void MQEAvatarNameTag::New() { + //getLogger().debug("MQEAvatarNameTag::New()"); + _enabled = false; + _playerInfo = nullptr; + _playerIcons.clear(); + } + + void MQEAvatarNameTag::Awake() { + //getLogger().debug("MQEAvatarNameTag::Awake()"); + // Get references + _bg = get_transform()->Find(BG())->GetComponent(); + _nameText = get_transform()->Find(Name())->GetComponent(); + + // Add horizontal layout to bg + HorizontalLayoutGroup* _; + if (!_bg->TryGetComponent(ByRef(_))) + { + auto hLayout = _bg->get_gameObject()->AddComponent(); + hLayout->set_childAlignment(TextAnchor::MiddleCenter); + hLayout->set_childForceExpandWidth(false); + hLayout->set_childForceExpandHeight(false); + hLayout->set_childScaleWidth(false); + hLayout->set_childScaleHeight(false); + hLayout->set_spacing(8); + } + + // Re-nest name onto bg + _nameText->get_transform()->SetParent(_bg->get_transform(), false); + + // Take control of name tag + ConnectedPlayerName* nativeNameScript; + if (_nameText->TryGetComponent(byref(nativeNameScript))) + Object::Destroy(nativeNameScript); + _nameText->set_text(Player()); + + // Create Event handlers + _platformDataEventHandler = MultiplayerCore::event_handler( + [this](GlobalNamespace::IConnectedPlayer* player, MultiplayerCore::Players::MpPlayerData* data) { + HandlePlatformData(player, data); + }); + _mpexPlayerDataEventHandler = MultiplayerCore::event_handler( + [this](GlobalNamespace::IConnectedPlayer* player, Players::MpexPlayerData* data) { + HandleMpexData(player, data); + }); + } + + void MQEAvatarNameTag::OnEnable() + { + //getLogger().debug("MQEAvatarNameTag::OnEnable()"); + _enabled = true; + + // Subscribe to events + MultiplayerCore::Players::MpPlayerManager::ReceivedPlayerData += _platformDataEventHandler; + Players::MpexPlayerManager::ReceivedMpExPlayerData += _mpexPlayerDataEventHandler; + + // Set player info + if (_playerInfo != nullptr) + SetPlayerInfo(_playerInfo); + } + + void MQEAvatarNameTag::OnDisable() + { + //getLogger().debug("MQEAvatarNameTag::OnDisable()"); + _enabled = false; + + // Unsubscribe from events + MultiplayerCore::Players::MpPlayerManager::ReceivedPlayerData -= _platformDataEventHandler; + Players::MpexPlayerManager::ReceivedMpExPlayerData -= _mpexPlayerDataEventHandler; + } + + #pragma region Set Player Info + void MQEAvatarNameTag::SetPlayerInfo(IConnectedPlayer* player) + { + //getLogger().debug("MQEAvatarNameTag::SetPlayerInfo"); + _playerInfo = player; + + if (!_enabled) + return; + + _nameText->set_text(player->get_userName()); + std::string userId = static_cast(player->get_userId()); + if (_mpexPlayerData.contains(userId)) + _nameText->set_color(_mpexPlayerData.at(userId)->Color); + + RemoveIcon(PlayerIconSlot::Platform); + + MultiplayerCore::Players::MpPlayerData* data; + if (MultiplayerCore::Players::MpPlayerManager::TryGetMpPlayerData(userId, data)) + SetPlatformData(data); + } + + void MQEAvatarNameTag::SetPlatformData(MultiplayerCore::Players::MpPlayerData* data) { + //getLogger().debug("MQEAvatarNameTag::SetPlatformData"); + using MultiplayerCore::Players::Platform; + switch (data->platform) + { + case Platform::Steam: + SetIcon(PlayerIconSlot::Platform, Sprites::IconSteam64()); + break; + case Platform::OculusQuest: + if (!config.getNoMetaZone()) { + SetIcon(PlayerIconSlot::Platform, Sprites::IconMeta64()); + break; + } + case Platform::OculusPC: + SetIcon(PlayerIconSlot::Platform, Sprites::IconOculus64()); + break; + case Platform::Unknown: + case Platform::PS4: + default: + break; + } + } + + void MQEAvatarNameTag::SetSimplePlayerInfo(IConnectedPlayer* simplePlayer) + { + //getLogger().debug("SetSimplePlayerInfo"); + _playerInfo = simplePlayer; + + if (!_enabled) + return; + + SetPlayerInfo(simplePlayer); + } + + using MultiplayerCore::Players::MpPlayerData; + void MQEAvatarNameTag::HandlePlatformData(IConnectedPlayer* player, MpPlayerData* data) { + getLogger().debug("HandlePlatformData"); + if (data && player && _playerInfo && player->get_userId() == _playerInfo->get_userId()) + SetPlatformData(data); + } + + using Players::MpexPlayerData; + void MQEAvatarNameTag::HandleMpexData(IConnectedPlayer* player, MpexPlayerData* data) { + getLogger().debug("HandleMpexData"); + if (data && player && _playerInfo && player->get_userId() == _playerInfo->get_userId()) + _nameText->set_color(data->Color); + } + #pragma endregion + + #pragma region Set Icons + void MQEAvatarNameTag::SetIcon(PlayerIconSlot slot, Sprite* sprite) + { + getLogger().debug("SetIcon Sprite %p", sprite); + + if (!_enabled) + return; + + HMUI::ImageView* imageView; + if (!_playerIcons.contains(slot)) + { + getLogger().debug("SetIcon, create new Icon"); + auto iconObj = GameObject::New_ctor(string_format("MQEPlayerIcon(%d)", (int)slot).c_str()); + iconObj->get_transform()->SetParent(_bg->get_transform(), false); + iconObj->get_transform()->SetSiblingIndex((int)slot); + iconObj->set_layer(5); + + iconObj->AddComponent(); + + imageView = iconObj->AddComponent(); + imageView->set_maskable(true); + imageView->set_fillCenter(true); + imageView->set_preserveAspect(true); + imageView->set_material(_bg->get_material()); // No Glow Billboard material + _playerIcons.insert_or_assign(slot, imageView); + + auto rectTransform = iconObj->GetComponent(); + rectTransform->set_localScale({ 3.2f, 3.2f, 0.0f }); + } + else imageView = _playerIcons.at(slot); + + imageView->set_sprite(sprite); + + _nameText->get_transform()->SetSiblingIndex(999); + } + + void MQEAvatarNameTag::RemoveIcon(PlayerIconSlot slot) + { + //getLogger().debug("MQEAvatarNameTag::RemoveIcon"); + if (!_enabled) + return; + if (_playerIcons.contains(slot)) + { + HMUI::ImageView* imageView = _playerIcons[slot]; + GameObject::Destroy(imageView->get_gameObject()); + _playerIcons.erase(slot); + } + } + #pragma endregion +} \ No newline at end of file diff --git a/MultiQuestensions/src/Extensions/ExtendedPlayer.cpp b/MultiQuestensions/src/Extensions/ExtendedPlayer.cpp deleted file mode 100644 index edd10ae..0000000 --- a/MultiQuestensions/src/Extensions/ExtendedPlayer.cpp +++ /dev/null @@ -1,144 +0,0 @@ -#include "Extensions/ExtendedPlayer.hpp" -#include "CodegenExtensions/ColorUtility.hpp" - -DEFINE_TYPE(MultiQuestensions::Extensions, ExtendedPlayer) - -DEFINE_TYPE(MultiQuestensions::Extensions, ExtendedPlayerPacket) - -namespace MultiQuestensions::Extensions { - - static Il2CppString* MQE_Protocol() { - static Il2CppString* protocol = il2cpp_utils::newcsstr(MPEX_PROTOCOL); - return protocol; - } - -#pragma region ExtendedPlayer - void ExtendedPlayer::ExtendedPlayer_ctor(GlobalNamespace::IConnectedPlayer* player, Il2CppString* platformID, int platform, UnityEngine::Color playerColor, Il2CppString* mpexVersion) { - getLogger().debug("Creating ExtendedPlayer"); - _connectedPlayer = player; - if (mpexVersion) this->mpexVersion = mpexVersion; - else this->mpexVersion = MQE_Protocol(); - if (platformID) { - this->platformID = platformID; - this->platform = (Platform)platform; - this->playerColor = playerColor; - } - getLogger().debug("Finished Creating ExtendedPlayer"); - } - - ExtendedPlayer* ExtendedPlayer::CS_ctor(GlobalNamespace::IConnectedPlayer* player) { - return THROW_UNLESS(il2cpp_utils::New(player, static_cast(nullptr), 3, DefaultColor, static_cast(nullptr))); - } - ExtendedPlayer* ExtendedPlayer::CS_ctor(GlobalNamespace::IConnectedPlayer* player, Il2CppString* platformID, Platform platform, UnityEngine::Color playerColor) { - return THROW_UNLESS(il2cpp_utils::New(player, platformID, (int)platform, playerColor, static_cast(nullptr))); - } - ExtendedPlayer* ExtendedPlayer::CS_ctor(GlobalNamespace::IConnectedPlayer* player, Il2CppString* platformID, Platform platform, Il2CppString* mpexVersion, UnityEngine::Color playerColor) { - return THROW_UNLESS(il2cpp_utils::New(player, platformID, (int)platform, playerColor, mpexVersion)); - } -#pragma endregion - -#pragma region Getters - Il2CppString* ExtendedPlayer::get_platformID() { - return platformID; - } - Platform ExtendedPlayer::get_platform() { - return platform; - } - Il2CppString* ExtendedPlayer::get_mpexVersion() { - return mpexVersion; - } - - bool ExtendedPlayer::get_isMe() { - return _connectedPlayer->get_isMe(); - } - - Il2CppString* ExtendedPlayer::get_userId() { - return _connectedPlayer->get_userId(); - } - - Il2CppString* ExtendedPlayer::get_userName() { - return _connectedPlayer->get_userName(); - } - - float ExtendedPlayer::get_currentLatency() { - return _connectedPlayer->get_currentLatency(); - } - - bool ExtendedPlayer::get_isConnected() { - return _connectedPlayer->get_isConnected(); - } - - bool ExtendedPlayer::get_isConnectionOwner() { - return _connectedPlayer->get_isConnectionOwner(); - } - - float ExtendedPlayer::get_offsetSyncTime() { - return _connectedPlayer->get_offsetSyncTime(); - } - - int ExtendedPlayer::get_sortIndex() { - return _connectedPlayer->get_sortIndex(); - } - - bool ExtendedPlayer::get_isKicked() { - return _connectedPlayer->get_isKicked(); - } - - GlobalNamespace::MultiplayerAvatarData ExtendedPlayer::get_multiplayerAvatarData() { - return _connectedPlayer->get_multiplayerAvatarData(); - } - - GlobalNamespace::DisconnectedReason ExtendedPlayer::get_disconnectedReason() { - return _connectedPlayer->get_disconnectedReason(); - } - - bool ExtendedPlayer::HasState(Il2CppString* state) { - return _connectedPlayer->HasState(state); - } -#pragma endregion - -#pragma region ExtendedPlayerPacket - void ExtendedPlayerPacket::New() {} - - void ExtendedPlayerPacket::Release() { - getLogger().debug("ExtendedPlayerPacket::Release"); - GlobalNamespace::ThreadStaticPacketPool_1::get_pool()->Release(this); - } - - void ExtendedPlayerPacket::Serialize(LiteNetLib::Utils::NetDataWriter* writer) { - getLogger().debug("ExtendedPlayerPacket::Serialize"); - - writer->Put(platformID); - writer->Put(mpexVersion); - writer->Put(UnityEngine::ColorUtility::ToHtmlStringRGB(playerColor)); - //writer->Put(Il2CppString::Concat(il2cpp_utils::newcsstr("#"), UnityEngine::ColorUtility::ToHtmlStringRGB(playerColor))); - writer->Put((int)platform); - getLogger().debug("Serialize ExtendedPlayerPacket done"); - } - - void ExtendedPlayerPacket::Deserialize(LiteNetLib::Utils::NetDataReader* reader) { - getLogger().debug("ExtendedPlayerPacket::Deserialize"); - - platformID = reader->GetString(); - mpexVersion = reader->GetString(); - if (!UnityEngine::ColorUtility::TryParseHtmlString(reader->GetString(), playerColor)) - this->playerColor = ExtendedPlayer::DefaultColor; - if (reader->get_AvailableBytes() >= 4) // Verify this works when the platform int exists. - this->platform = (Platform)reader->GetInt(); - else - this->platform = Platform::Unknown; - getLogger().debug("Deserialize ExtendedPlayerPacket done"); - } - - ExtendedPlayerPacket* ExtendedPlayerPacket::Init(Il2CppString* platformID, Platform platform, UnityEngine::Color playerColor) - { - auto extendedPlayer = THROW_UNLESS(il2cpp_utils::New()); - - extendedPlayer->platformID = platformID; - extendedPlayer->platform = (Platform)platform; - extendedPlayer->playerColor = playerColor; - extendedPlayer->mpexVersion = MQE_Protocol(); - return extendedPlayer; - } -#pragma endregion -} \ No newline at end of file diff --git a/MultiQuestensions/src/Hooks/EnvironmentAndAvatarHooks.cpp b/MultiQuestensions/src/Hooks/EnvironmentAndAvatarHooks.cpp index d444e17..84e744e 100644 --- a/MultiQuestensions/src/Hooks/EnvironmentAndAvatarHooks.cpp +++ b/MultiQuestensions/src/Hooks/EnvironmentAndAvatarHooks.cpp @@ -1,10 +1,15 @@ #include "main.hpp" #include "Hooks/Hooks.hpp" #include "GlobalFields.hpp" +#include "multiplayer-core/shared/GlobalFields.hpp" #include "Hooks/EnvironmentAndAvatarHooks.hpp" #include "Hooks/SessionManagerAndExtendedPlayerHooks.hpp" -#include "Environments/LobbyAvatarPlaceLighting.hpp" -#include "Environments/LobbyAvatarNameTag.hpp" +#include "Environments/MQEAvatarPlaceLighting.hpp" +#include "Environments/MQEAvatarNameTag.hpp" +#include "Players/MpexPlayerManager.hpp" +#include "Config.hpp" +#include "multiplayer-core/shared/Players/MpPlayerManager.hpp" +#include "multiplayer-core/shared/Utils/EnumUtils.hpp" #include "GlobalNamespace/MultiplayerLobbyController.hpp" #include "GlobalNamespace/LightWithIdMonoBehaviour.hpp" @@ -19,7 +24,16 @@ #include "GlobalNamespace/MultiplayerLobbyAvatarManager.hpp" #include "GlobalNamespace/IConnectedPlayer.hpp" #include "GlobalNamespace/ConnectedPlayerManager_ConnectedPlayer.hpp" -#include "GlobalNamespace/AvatarPoseRestrictions.hpp" +#include "GlobalNamespace/GameplayServerConfiguration.hpp" +#include "GlobalNamespace/MultiplayerGameplayAnimator.hpp" +#include "GlobalNamespace/MultiplayerController.hpp" +#include "GlobalNamespace/SimpleColorSO.hpp" +#include "GlobalNamespace/ConnectedPlayerHelpers.hpp" +#include "GlobalNamespace/LightsAnimator.hpp" +#include "GlobalNamespace/EaseType.hpp" + +#include "Tweening/Tween_1.hpp" +#include "Tweening/ColorTween.hpp" #include "System/Collections/Generic/List_1.hpp" @@ -29,18 +43,12 @@ using namespace GlobalNamespace; using namespace UnityEngine; using namespace MultiQuestensions::Environments; -using namespace MultiQuestensions::Extensions; using namespace System::Collections::Generic; namespace MultiQuestensions { #pragma region Fields - ILobbyStateDataModel* _lobbyStateDataModel; - MenuEnvironmentManager* _menuEnvironmentManager; - MultiplayerLobbyAvatarPlaceManager* _placeManager; - MultiplayerLobbyCenterStageManager* _stageManager; - - std::vector avatarPlaces; + std::vector avatarPlaces; float innerCircleRadius; float minOuterCircleRadius; @@ -51,14 +59,14 @@ namespace MultiQuestensions { MultiplayerLobbyAvatarManager* _avatarManager; - Dictionary_2* _refPlayerIdToAvatarMap; + Dictionary_2* _refPlayerIdToAvatarMap; #pragma endregion #pragma region LobbyAvatarPlaceLigting - LobbyAvatarPlaceLighting* GetConnectedPlayerPlace(IConnectedPlayer* player) + MQEAvatarPlaceLighting* GetConnectedPlayerPlace(IConnectedPlayer* player) { - //getLogger().debug("GetConnectedPlayerPlace"); - int sortIndex = _lobbyStateDataModel->get_localPlayer()->get_sortIndex(); + //getLogger().debug("GetConnectedPlayerPlace, lighting"); + int sortIndex = MultiplayerCore::_multiplayerSessionManager->get_localPlayer()->get_sortIndex(); //getLogger().debug("GetConnectedPlayerPlace sortIndex %d, angleBetweenPlayersWithEvenAdjustment %f", sortIndex, angleBetweenPlayersWithEvenAdjustment); //static auto* sortIndexMethod = THROW_UNLESS(il2cpp_utils::FindMethodUnsafe(classof(IConnectedPlayer*), "get_sortIndex", 0)); //int playerSortIndex = il2cpp_utils::RunMethodThrow(player, sortIndexMethod); @@ -68,46 +76,50 @@ namespace MultiQuestensions { for (auto* place : avatarPlaces) { if (place->get_transform()->get_position() == playerWorldPosition && place->get_isActiveAndEnabled()) return place; } + //getLogger().debug("GetConnectedPlayerPlace, lighting is NULLptr"); return nullptr; //return Array::Find(_avatarPlaces, place => place.transform.position == playerWorldPosition && place.isActiveAndEnabled); } void SetPlayerPlaceColor(IConnectedPlayer* player, const Color& color, bool priority) { + //getLogger().debug("SetPlayerPlaceColor"); if (!initialized) return; - //if (il2cpp_utils::AssignableFrom(reinterpret_cast(player)->klass)) - // getLogger().debug("SetPlayerPlaceColor ExtendedPlayer"); + MQEAvatarPlaceLighting* place = GetConnectedPlayerPlace(player); - //else if (il2cpp_utils::AssignableFrom(reinterpret_cast(player)->klass)) getLogger().debug("CreateOrUpdateNameTag SimplePlayer"); - //else { - // getLogger().debug("SetPlayerPlaceColor unknown type: %s", il2cpp_utils::ClassStandardName(reinterpret_cast(player)->klass).c_str()); - // return; - //} - if (!(il2cpp_utils::AssignableFrom(reinterpret_cast(player)->klass) || il2cpp_utils::AssignableFrom(reinterpret_cast(player)->klass))) { - getLogger().error("SetPlayerPlaceColor unknown type: %s", il2cpp_utils::ClassStandardName(reinterpret_cast(player)->klass).c_str()); + if (place == nullptr){ + //getLogger().debug("Place was null, returning"); return; } - //getLogger().debug("SetPlayerPlaceColor player type: %s", il2cpp_utils::ClassStandardName(reinterpret_cast(player)->klass).c_str()); - - //getLogger().debug("SetPlayerPlaceColor"); + // getLogger().debug("SetPlayerPlaceColor player %s userId %s", static_cast(player->get_userName()).c_str(), static_cast(player->get_userId()).c_str()); - LobbyAvatarPlaceLighting* place = GetConnectedPlayerPlace(player); + // getLogger().debug("SetPlayerPlaceColor new TargetColor %f, %f, %f, %f", color.r, color.g, color.b, color.a); - if (place == nullptr) - return; + // getLogger().debug("SetPlayerPlaceColor current TargetColor %f, %f, %f, %f", place->targetColor.r, place->targetColor.g, place->targetColor.b, place->targetColor.a); - if (!priority && place->TargetColor != Color::get_black() && place->TargetColor != ExtendedPlayer::DefaultColor) + if (!priority && place->targetColor != Color::get_black() && place->targetColor != Config::DefaultPlayerColor) { + //getLogger().debug("Default color, returning"); + // getLogger().debug("SetPlayerPlaceColor not priority and TargetColor check is priority '%s' check is black '%s', check is DefaultPlayerColor '%s' check full eval '%s'", !priority ? "true" : "false", place->targetColor != Color::get_black() ? "true" : "false", place->targetColor != Config::DefaultPlayerColor ? "true" : "false", (!priority && place->targetColor != Color::get_black() && place->targetColor != Config::DefaultPlayerColor) ? "true" : "false"); + + // getLogger().debug("SetPlayerPlaceColor TargetColor %f, %f, %f, %f", place->targetColor.r, place->targetColor.g, place->targetColor.b, place->targetColor.a); + + // getLogger().debug("SetPlayerPlaceColor black %f, %f, %f, %f", Color::get_black().r, Color::get_black().g, Color::get_black().b, Color::get_black().a); + + // getLogger().debug("SetPlayerPlaceColor DefaultPlayerColor %f, %f, %f, %f", Config::DefaultPlayerColor.r, Config::DefaultPlayerColor.g, Config::DefaultPlayerColor.b, Config::DefaultPlayerColor.a); // Priority colors are always set; non-priority colors can only override default black/blue return; + } + //getLogger().debug("SetPlayerPlaceColor setting color"); place->SetColor(color, false); } void SetAllPlayerPlaceColors(Color color, bool immediate = false) { - for (LobbyAvatarPlaceLighting* place : avatarPlaces) + //getLogger().debug("SetAllPlayerPlaceColors"); + for (MQEAvatarPlaceLighting* place : avatarPlaces) { place->SetColor(color, immediate); } @@ -116,162 +128,220 @@ namespace MultiQuestensions { static void SetDefaultPlayerPlaceColors() { SetAllPlayerPlaceColors(Color::get_black(), true); - SetPlayerPlaceColor(sessionManager->get_localPlayer(), localExtendedPlayer->get_playerColor(), true); + //getLogger().debug("SetDefaultPlayerPlaceColors, set local player color"); + SetPlayerPlaceColor(MultiplayerCore::_multiplayerSessionManager->get_localPlayer(), config.getPlayerColor(), true); using System::Collections::Generic::List_1; - for (int i = 0; i < reinterpret_cast*>(sessionManager->get_connectedPlayers())->get_Count(); i++) { - auto player = sessionManager->get_connectedPlayers()->get_Item(i); - SetPlayerPlaceColor(player, ExtendedPlayer::DefaultColor, false); - } - - for (auto& [key, extendedPlayer] : _extendedPlayers) { - SetPlayerPlaceColor(reinterpret_cast(extendedPlayer->get_self()), extendedPlayer->get_playerColor(), true); + for (int i = 0; i < reinterpret_cast*>(MultiplayerCore::_multiplayerSessionManager->get_connectedPlayers())->get_Count(); i++) { + auto player = MultiplayerCore::_multiplayerSessionManager->get_connectedPlayers()->get_Item(i); + std::string userId = player->get_userId(); + // If user is us, we skip + if (userId == static_cast(MultiplayerCore::_multiplayerSessionManager->get_localPlayer()->get_userId())) continue; + if (_mpexPlayerData.contains(userId)) { + SetPlayerPlaceColor(player, _mpexPlayerData.at(userId)->Color, true); + } else { + SetPlayerPlaceColor(player, Config::DefaultPlayerColor, true); + } } } - void HandleLobbyEnvironmentLoaded() { + void HandleLobbyEnvironmentLoaded(ILobbyStateDataModel* _lobbyStateDataModel, MenuEnvironmentManager* _menuEnvironmentManager, MultiplayerLobbyAvatarPlaceManager* _placeManager, MultiplayerLobbyCenterStageManager* _stageManager) { initialized = false; - //getLogger().debug("HandleLobbyEnvironmentLoaded Started"); - auto* nativeAvatarPlaces = Resources::FindObjectsOfTypeAll(); - for (int i = 0; i < nativeAvatarPlaces->Length(); i++) + getLogger().debug("HandleLobbyEnvironmentLoaded start"); + auto nativeAvatarPlaces = Resources::FindObjectsOfTypeAll(); + for (int i = 0; i < nativeAvatarPlaces.Length(); i++) { - auto* nativeAvatarPlace = nativeAvatarPlaces->values[i]; + auto nativeAvatarPlace = nativeAvatarPlaces[i]; - auto avatarPlace = nativeAvatarPlace->GetComponent(); + auto avatarPlace = nativeAvatarPlace->GetComponent(); if (avatarPlace == nullptr) - avatarPlace = nativeAvatarPlace->get_gameObject()->AddComponent(); + avatarPlace = nativeAvatarPlace->get_gameObject()->AddComponent(); avatarPlaces.push_back(avatarPlace); } - innerCircleRadius = _placeManager->dyn__innerCircleRadius(); - minOuterCircleRadius = _placeManager->dyn__minOuterCircleRadius(); - //getLogger().debug("innerCircleRadius %f, minOuterCircleRadius %f", innerCircleRadius, minOuterCircleRadius); - angleBetweenPlayersWithEvenAdjustment = MultiplayerPlayerPlacement::GetAngleBetweenPlayersWithEvenAdjustment(_lobbyStateDataModel->get_maxPartySize(), MultiplayerPlayerLayout::Circle); - outerCircleRadius = fmax(MultiplayerPlayerPlacement::GetOuterCircleRadius(angleBetweenPlayersWithEvenAdjustment, innerCircleRadius), minOuterCircleRadius); - //getLogger().debug("angleBetweenPlayersWithEvenAdjustment %f, outerCircleRadius %f", angleBetweenPlayersWithEvenAdjustment, outerCircleRadius); - - bool buildingsEnabled = (sessionManager->dyn__maxPlayerCount() <= 18); - auto* Construction_tr = _menuEnvironmentManager->get_transform()->Find(il2cpp_utils::newcsstr("Construction")); - if (Construction_tr && Construction_tr->get_gameObject()) { - Construction_tr->get_gameObject()->SetActive(buildingsEnabled); - } - - auto* Construction_1_tr = _menuEnvironmentManager->get_transform()->Find(il2cpp_utils::newcsstr("Construction (1)")); - if (Construction_1_tr && Construction_1_tr->get_gameObject()) { - Construction_1_tr->get_gameObject()->SetActive(buildingsEnabled); - } + innerCircleRadius = _placeManager->innerCircleRadius; + minOuterCircleRadius = _placeManager->minOuterCircleRadius; - float centerScreenScale = outerCircleRadius / minOuterCircleRadius; - _stageManager->get_transform()->set_localScale({ centerScreenScale, centerScreenScale, centerScreenScale }); + angleBetweenPlayersWithEvenAdjustment = MultiplayerPlayerPlacement::GetAngleBetweenPlayersWithEvenAdjustment(_lobbyStateDataModel->get_configuration().maxPlayerCount, MultiplayerPlayerLayout::Circle); + outerCircleRadius = fmax(MultiplayerPlayerPlacement::GetOuterCircleRadius(angleBetweenPlayersWithEvenAdjustment, innerCircleRadius), minOuterCircleRadius); initialized = true; SetDefaultPlayerPlaceColors(); - //getLogger().debug("HandleLobbyEnvironmentLoaded Finished"); + getLogger().debug("HandleLobbyEnvironmentLoaded Finished"); } MAKE_HOOK_MATCH(MultiplayerLobbyController_ActivateMultiplayerLobby, &MultiplayerLobbyController::ActivateMultiplayerLobby, void, MultiplayerLobbyController* self) { - if (!_placeManager) _placeManager = Resources::FindObjectsOfTypeAll()->values[0]; - if (!_menuEnvironmentManager) _menuEnvironmentManager = Resources::FindObjectsOfTypeAll()->values[0]; - if (!_stageManager) _stageManager = Resources::FindObjectsOfTypeAll()->values[0]; - if (!_lobbyStateDataModel) _lobbyStateDataModel = _placeManager->lobbyStateDataModel; + getLogger().debug("MultiplayerLobbyController::ActivateMultiplayerLobby"); + MultiplayerLobbyAvatarPlaceManager* _placeManager = self->multiplayerLobbyAvatarPlaceManager; + MenuEnvironmentManager* _menuEnvironmentManager = self->menuEnvironmentManager; + MultiplayerLobbyCenterStageManager* _stageManager = self->multiplayerLobbyCenterStageManager; + ILobbyStateDataModel* _lobbyStateDataModel = _placeManager->lobbyStateDataModel; - self->dyn__innerCircleRadius() = 1; - self->dyn__minOuterCircleRadius() = 4.4f; MultiplayerLobbyController_ActivateMultiplayerLobby(self); - HandleLobbyEnvironmentLoaded(); + HandleLobbyEnvironmentLoaded(_lobbyStateDataModel, _menuEnvironmentManager, _placeManager, _stageManager); } MAKE_HOOK_MATCH(LightWithIdMonoBehaviour_RegisterLight, &LightWithIdMonoBehaviour::RegisterLight, void, LightWithIdMonoBehaviour* self) { - if (!(self->get_transform()->get_parent() != nullptr && self->get_transform()->get_parent()->get_name()->Contains(il2cpp_utils::newcsstr("LobbyAvatarPlace")))) LightWithIdMonoBehaviour_RegisterLight(self); + if ((self && self->get_transform() && self->get_transform()->get_parent() && self->get_transform()->get_parent()->get_name()->Contains("LobbyAvatarPlace"))){ + if(!self->isRegistered){ + //getLogger().debug("MQE registering a MQEAvatarPlaceLighting or light that has the lobby avatar place as a parent"); + LightWithIdMonoBehaviour_RegisterLight(self); + } + } + else{ + LightWithIdMonoBehaviour_RegisterLight(self); + } } - #pragma endregion #pragma region LobbyAvatarNameTag - MultiplayerLobbyAvatarController* GetAvatarController(Il2CppString* userId) + MultiplayerLobbyAvatarController* GetAvatarController(StringW userId) { //getLogger().debug("Start GetAvatarController: _refPlayerIdToAvatarMap"); if (_refPlayerIdToAvatarMap == nullptr && _avatarManager) - _refPlayerIdToAvatarMap = _avatarManager->dyn__playerIdToAvatarMap(); + _refPlayerIdToAvatarMap = _avatarManager->playerIdToAvatarMap; //getLogger().debug("Start GetAvatarController: _refPlayerIdToAvatarMap Done"); if (_refPlayerIdToAvatarMap != nullptr) { MultiplayerLobbyAvatarController* value; //getLogger().debug("Start GetAvatarController return MultiplayerLobbyAvatarController"); - return _refPlayerIdToAvatarMap->TryGetValue(userId, ByRef(value)) ? value : nullptr; + return _refPlayerIdToAvatarMap->TryGetValue(userId, byref(value)) ? value : nullptr; } - //getLogger().debug("GetAvatarController return nullptr, this part should never be triggered"); + getLogger().debug("GetAvatarController return nullptr, this part should never be triggered"); return nullptr; } - GameObject* GetAvatarCaptionObject(Il2CppString* userId) + GameObject* GetAvatarCaptionObject(StringW userId) { auto avatarController = GetAvatarController(userId); - if (avatarController) return avatarController->get_transform()->Find(il2cpp_utils::newcsstr("AvatarCaption"))->get_gameObject(); + if (avatarController) return avatarController->get_transform()->Find("AvatarCaption")->get_gameObject(); else return nullptr; } - void CreateOrUpdateNameTag(IConnectedPlayer* player) - { - try { - //getLogger().debug("Start CreateOrUpdateNameTag: GetAvatarCaptionObject"); - //Il2CppString* userId; - //if (il2cpp_utils::AssignableFrom(reinterpret_cast(player)->klass)) - // getLogger().debug("CreateOrUpdateNameTag ExtendedPlayer"); - - //else if (il2cpp_utils::AssignableFrom(reinterpret_cast(player)->klass)) getLogger().debug("CreateOrUpdateNameTag SimplePlayer"); - //else { - // getLogger().debug("CreateOrUpdateNameTag unknown type: %s", il2cpp_utils::ClassStandardName(reinterpret_cast(player)->klass).c_str()); - // return; - //} - //getLogger().debug("CreateOrUpdateNameTag player type: %s", il2cpp_utils::ClassStandardName(reinterpret_cast(player)->klass).c_str()); - - if (!(il2cpp_utils::AssignableFrom(reinterpret_cast(player)->klass) || il2cpp_utils::AssignableFrom(reinterpret_cast(player)->klass))) { - getLogger().error("CreateOrUpdateNameTag unknown type: %s", il2cpp_utils::ClassStandardName(reinterpret_cast(player)->klass).c_str()); - return; - } + void HandleLobbyAvatarCreated(IConnectedPlayer* player) { + auto objAvatarCaption = GetAvatarCaptionObject(player->get_userId()); + //getLogger().debug("Finding GetAvatarCaptionObject"); + if (objAvatarCaption == nullptr) + return; + + MQEAvatarNameTag* nameTag; + if (!objAvatarCaption->TryGetComponent(byref(nameTag))) { + getLogger().debug("Adding new LobbyAvatarNameTag Component"); + nameTag = objAvatarCaption->AddComponent(); + } + nameTag->SetPlayerInfo(player); + } - auto objAvatarCaption = GetAvatarCaptionObject(player->get_userId()/*userId*/); - if (objAvatarCaption == nullptr) - return; + void UpdateNameTagIcons() { + getLogger().debug("Start UpdateNameTagIcons"); + for (int i = 0; i < MultiplayerCore::_multiplayerSessionManager->connectedPlayers->get_Count(); i++) { + auto player = MultiplayerCore::_multiplayerSessionManager->connectedPlayers->get_Item(i); + auto objAvatarCaption = GetAvatarCaptionObject(player->get_userId()); + getLogger().debug("Finding GetAvatarCaptionObject"); + if (objAvatarCaption == nullptr) { + //getLogger().debug("GetAvatarCaptionObject is nullptr, returning"); + continue; + } //getLogger().debug("Found GetAvatarCaptionObject"); - LobbyAvatarNameTag* nameTag; - if (!objAvatarCaption->TryGetComponent(ByRef(nameTag))) { - //getLogger().debug("Adding new LobbyAvatarNameTag Component"); - nameTag = objAvatarCaption->AddComponent(); + MQEAvatarNameTag* nameTag; + if (objAvatarCaption->TryGetComponent(byref(nameTag))) { + MultiplayerCore::Players::MpPlayerData* mpPlayerData; + if (MultiplayerCore::Players::MpPlayerManager::TryGetMpPlayerData(player->get_userId(), mpPlayerData)) { + nameTag->SetPlatformData(mpPlayerData); + } else { + getLogger().debug("UpdateNameTagIcons: mpPlayerData is nullptr"); + } } - - //getLogger().debug("SetPlayerInfo"); - nameTag->SetPlayerInfo(player); - } - catch (const std::runtime_error& e) { - getLogger().error("REPORT TO ENDER: CreateOrUpdateNameTag Failed: %s", e.what()); } } - void HandleLobbyAvatarCreated(IConnectedPlayer* player) { - const std::string userId = to_utf8(csstrtostr(player->get_userId())); - if (_extendedPlayers.contains(userId)) - player = reinterpret_cast(_extendedPlayers.at(userId)->get_self()); - CreateOrUpdateNameTag(player); - } - MAKE_HOOK_MATCH(MultiplayerLobbyAvatarManager_AddPlayer, &MultiplayerLobbyAvatarManager::AddPlayer, void, MultiplayerLobbyAvatarManager* self, IConnectedPlayer* connectedPlayer) { + //getLogger().debug("MultiplayerLobbyAvatarManager::AddPlayer"); MultiplayerLobbyAvatarManager_AddPlayer(self, connectedPlayer); - if (!_avatarManager) _avatarManager = self; + _avatarManager = self; HandleLobbyAvatarCreated(connectedPlayer); } - MAKE_HOOK_MATCH(AvatarPoseRestrictions_HandleAvatarPoseControllerPositionsWillBeSet, &AvatarPoseRestrictions::HandleAvatarPoseControllerPositionsWillBeSet, void, AvatarPoseRestrictions* self, Quaternion headRotation, Vector3 headPosition, Vector3 leftHandPosition, Vector3 rightHandPosition, ByRef newHeadPosition, ByRef newLeftHandPosition, ByRef newRightHandPosition) { - newHeadPosition.heldRef = headPosition; - newLeftHandPosition.heldRef = self->LimitHandPositionRelativeToHead(leftHandPosition, headPosition); - newRightHandPosition.heldRef = self->LimitHandPositionRelativeToHead(rightHandPosition, headPosition); - //AvatarPoseRestrictions_HandleAvatarPoseControllerPositionsWillBeSet(self, headRotation, headPosition, leftHandPosition, rightHandPosition, newHeadPosition, newLeftHandPosition, newRightHandPosition); +#pragma region Lighting hooks + + // Utilities for working with colors + ColorSO* getColorSO(UnityEngine::Color color) { + SimpleColorSO* colorSO = UnityEngine::ScriptableObject::CreateInstance(); + colorSO->SetColor(color); + return colorSO; + } + + MAKE_HOOK_MATCH(MultiplayerGameplayAnimator_HandleStateChanged, &MultiplayerGameplayAnimator::HandleStateChanged, void, MultiplayerGameplayAnimator* self, MultiplayerController::State state) { + getLogger().debug("MultiplayerGameplayAnimator::HandleStateChanged: Checking State %s", MultiplayerCore::EnumUtils::GetEnumName(state).c_str()); + getLogger().debug("MultiplayerGameplayAnimator::HandleStateChanged: Checking Player IsFailed %s", ConnectedPlayerHelpers::IsFailed(self->connectedPlayer) ? "true" : "false"); + + static bool skippedFirst = false; + + if (!ConnectedPlayerHelpers::IsFailed(self->connectedPlayer)) { + getLogger().debug("MultiplayerGameplayAnimator::HandleStateChanged: Player is not Failed"); + if (self && self->connectedPlayer && self->connectedPlayer->get_isMe()) { + if (state == MultiplayerController::State::Gameplay) + { + getLogger().debug("MultiplayerGameplayAnimator::HandleStateChanged: State is Gameplay and Player is not Failed (Me)"); + self->activeLightsColor = getColorSO(config.getPlayerColor()); + self->leadingLightsColor = getColorSO(config.getPlayerColor()); + // self->failedLightsColor = getColorSO(config.getPlayerColor()); + skippedFirst = false; + } + else if (state == MultiplayerController::State::Intro) + { + getLogger().debug("MultiplayerGameplayAnimator::HandleStateChanged: State is Intro and Player is not Failed (Me)"); + // if (skippedFirst) + // { + // TODO: Figure out how to animate the lights to the new color without the old value mixing itself in + for(LightsAnimator* lightsAnimator : self->allLightsAnimators) + { + // lightsAnimator->tween->set_delay(1.0f); + // lightsAnimator->tween->fromValue = {0, 0, 0, 0}; + // lightsAnimator->tween->toValue = config.getPlayerColor(); + lightsAnimator->AnimateToColor(config.getPlayerColor() * 1.1, 3, EaseType::Linear); + } + // } + // else skippedFirst = true; + + } + } + else if (self && self->connectedPlayer) { + StringW userId = self->connectedPlayer->get_userId(); + if (userId && _mpexPlayerData.contains(userId)) { + if (state == MultiplayerController::State::Gameplay) + { + getLogger().debug("MultiplayerGameplayAnimator::HandleStateChanged: State is Gameplay and Player is not Failed (Other Player)"); + self->activeLightsColor = getColorSO(_mpexPlayerData.at(userId)->Color); + self->leadingLightsColor = getColorSO(_mpexPlayerData.at(userId)->Color); + // self->failedLightsColor = getColorSO(_mpexPlayerData.at(userId)->Color); + skippedFirst = false; + } + else if (state == MultiplayerController::State::Intro) + { + getLogger().debug("MultiplayerGameplayAnimator::HandleStateChanged: State is Intro and Player is not Failed (Other Player)"); + // if (skippedFirst) + // { + // TODO: Figure out how to animate the lights to the new color without the old value mixing itself in + for(LightsAnimator* lightsAnimator : self->allLightsAnimators) + { + // lightsAnimator->tween->set_delay(1.0f); + // lightsAnimator->tween->fromValue = {0, 0, 0, 0}; + // lightsAnimator->tween->toValue = _mpexPlayerData.at(userId)->Color; + lightsAnimator->AnimateToColor(_mpexPlayerData.at(userId)->Color * 1.1, 3, EaseType::Linear); + } + // } + // else skippedFirst = true; + } + } + } + } + MultiplayerGameplayAnimator_HandleStateChanged(self, state); } #pragma endregion @@ -280,6 +350,6 @@ namespace MultiQuestensions { INSTALL_HOOK(getLogger(), MultiplayerLobbyController_ActivateMultiplayerLobby); INSTALL_HOOK(getLogger(), LightWithIdMonoBehaviour_RegisterLight); INSTALL_HOOK(getLogger(), MultiplayerLobbyAvatarManager_AddPlayer); - INSTALL_HOOK_ORIG(getLogger(), AvatarPoseRestrictions_HandleAvatarPoseControllerPositionsWillBeSet); + INSTALL_HOOK(getLogger(), MultiplayerGameplayAnimator_HandleStateChanged); } } \ No newline at end of file diff --git a/MultiQuestensions/src/Hooks/InstallerHooks.cpp b/MultiQuestensions/src/Hooks/InstallerHooks.cpp index 71a7b37..c62110b 100644 --- a/MultiQuestensions/src/Hooks/InstallerHooks.cpp +++ b/MultiQuestensions/src/Hooks/InstallerHooks.cpp @@ -1,55 +1,20 @@ #include "main.hpp" #include "Hooks/Hooks.hpp" -#include "GlobalNamespace/MultiplayerConnectedPlayerInstaller.hpp" -#include "GlobalNamespace/GameplayCoreSceneSetupData.hpp" -#include "GlobalNamespace/GameplayModifiers.hpp" -#include "GlobalNamespace/IDifficultyBeatmap.hpp" -#include "GlobalNamespace/PlayerSpecificSettings.hpp" -#include "GlobalNamespace/PracticeSettings.hpp" -#include "GlobalNamespace/EnvironmentInfoSO.hpp" -#include "GlobalNamespace/ColorScheme.hpp" +#include "GlobalNamespace/MultiplayerConnectedPlayerBeatmapObjectEventManager.hpp" +#include "Config.hpp" using namespace GlobalNamespace; namespace MultiQuestensions { - MAKE_HOOK_MATCH(MultiplayerConnectedPlayerInstaller_InstallBindings, &MultiplayerConnectedPlayerInstaller::InstallBindings, void, MultiplayerConnectedPlayerInstaller* self) { - getLogger().debug("LagReducer set to '%s'", getConfig().config["LagReducer"].GetBool() ? "true" : "false"); - //self->dyn__sceneSetupData()->dyn_gameplayModifiers()->dyn__zenMode() |= getConfig().config["LagReducer"].GetBool(); - self->dyn__sceneSetupData() = GameplayCoreSceneSetupData::New_ctor( - self->dyn__sceneSetupData()->difficultyBeatmap, - self->dyn__sceneSetupData()->previewBeatmapLevel, - self->dyn__sceneSetupData()->gameplayModifiers->CopyWith( - System::Nullable_1(), - System::Nullable_1(), - System::Nullable_1(), - System::Nullable_1(), - System::Nullable_1(), - System::Nullable_1(), - System::Nullable_1(), - System::Nullable_1(), - System::Nullable_1(), - System::Nullable_1(), - System::Nullable_1(), - System::Nullable_1(), - System::Nullable_1(), - System::Nullable_1(), - System::Nullable_1(), - System::Nullable_1(self->dyn__sceneSetupData()->dyn_gameplayModifiers()->dyn__zenMode() || getConfig().config["LagReducer"].GetBool(), true), - System::Nullable_1() - //zenMode: Plugin.Config.LagReducer - ), - self->dyn__sceneSetupData()->playerSpecificSettings, - self->dyn__sceneSetupData()->practiceSettings, - self->dyn__sceneSetupData()->useTestNoteCutSoundEffects, - self->dyn__sceneSetupData()->environmentInfo, - self->dyn__sceneSetupData()->colorScheme - ); - - MultiplayerConnectedPlayerInstaller_InstallBindings(self); + MAKE_HOOK_MATCH(MultiplayerConnectedPlayerBeatmapObjectEventManager_Start, &MultiplayerConnectedPlayerBeatmapObjectEventManager::Start, void, MultiplayerConnectedPlayerBeatmapObjectEventManager* self) { + //getLogger().debug("MultiplayerConnectedPlayerBeatmapObjectEventManager_Start, checking if lag reducer is enabled"); + if (!config.getLagReducer()) + MultiplayerConnectedPlayerBeatmapObjectEventManager_Start(self); } + void Hooks::InstallerHooks() { - INSTALL_HOOK(getLogger(), MultiplayerConnectedPlayerInstaller_InstallBindings); + INSTALL_HOOK(getLogger(), MultiplayerConnectedPlayerBeatmapObjectEventManager_Start); } } \ No newline at end of file diff --git a/MultiQuestensions/src/Hooks/MaxPlayerHooks.cpp b/MultiQuestensions/src/Hooks/MaxPlayerHooks.cpp deleted file mode 100644 index 723b65c..0000000 --- a/MultiQuestensions/src/Hooks/MaxPlayerHooks.cpp +++ /dev/null @@ -1,210 +0,0 @@ -#include "main.hpp" -#include "Hooks/Hooks.hpp" -//#include "UnityEngine/Debug.hpp" -#include "GlobalNamespace/MultiplayerOutroAnimationController.hpp" -#include "GlobalNamespace/CreateServerFormController.hpp" -#include "GlobalNamespace/FormattedFloatListSettingsController.hpp" - -using namespace GlobalNamespace; -using namespace System; -using namespace System::Collections::Generic; -using namespace System::Linq; -using namespace UnityEngine; -using namespace UnityEngine::Playables; -using namespace UnityEngine::Timeline; - -namespace MultiQuestensions { - - int targetIterations = 0; - - MAKE_HOOK_MATCH(MultiplayerResultsPyramidPatch, &MultiplayerResultsPyramidView::SetupResults, void, MultiplayerResultsPyramidView* self, IReadOnlyList_1* resultsData, UnityEngine::Transform* badgeStartTransform, UnityEngine::Transform* badgeMidTransform) { - try { - static auto* Enumerable_Take_Generic = THROW_UNLESS(il2cpp_utils::FindMethodUnsafe(classof(Enumerable*), "Take", 2)); - static auto* Enumerable_Take = THROW_UNLESS(il2cpp_utils::MakeGenericMethod(Enumerable_Take_Generic, { classof(MultiplayerPlayerResultsData*) })); - auto* takeResult = il2cpp_utils::RunMethodThrow*, false>(static_cast(nullptr), - Enumerable_Take, reinterpret_cast*>(resultsData), 5); - - - static auto* Enumerable_ToList_Generic = THROW_UNLESS(il2cpp_utils::FindMethodUnsafe(classof(Enumerable*), "ToList", 1)); - static auto* Enumerable_ToList = THROW_UNLESS(il2cpp_utils::MakeGenericMethod(Enumerable_ToList_Generic, { classof(MultiplayerPlayerResultsData*) })); - List* newResultsData = il2cpp_utils::RunMethodThrow*, false>(static_cast(nullptr), - Enumerable_ToList, takeResult); - - //List* newResultsData = Enumerable::ToList(Enumerable::Take(reinterpret_cast*>(resultsData), 5)); - MultiplayerResultsPyramidPatch(self, (IReadOnlyList_1*)newResultsData, badgeStartTransform, badgeMidTransform); - } - catch (const std::runtime_error& e) { - getLogger().critical("REPORT TO ENDER: Hook MultiplayerResultsPyramidPatch File " __FILE__ " at Line %d: %s", __LINE__, e.what()); - MultiplayerResultsPyramidPatch(self, resultsData, badgeStartTransform, badgeMidTransform); - } - } - - MAKE_HOOK_MATCH(IntroAnimationPatch, &MultiplayerIntroAnimationController::PlayIntroAnimation, void, MultiplayerIntroAnimationController* self, float maxDesiredIntroAnimationDuration, Action* onCompleted) { - PlayableDirector* realDirector = self->introPlayableDirector; - if (targetIterations == 0) - { - targetIterations = floor((reinterpret_cast*>(self->multiplayerPlayersManager->allActiveAtGameStartPlayers)->get_Count() - 1) / 4) + 1; - } - try { - // Run animation one time for each set of 4 players - if (targetIterations != 1) { - // Create duplicated animations - GameObject* newPlayableGameObject = GameObject::New_ctor(); - self->introPlayableDirector = newPlayableGameObject->AddComponent(); - - using SetPlayableAsset = function_ptr_t; - static SetPlayableAsset setPlayableAsset = reinterpret_cast(il2cpp_functions::resolve_icall("UnityEngine.Playables.PlayableDirector::SetPlayableAsset")); - setPlayableAsset(self->introPlayableDirector, realDirector->get_playableAsset()); - - // Mute duplicated animations except one (otherwise audio is very loud) - TimelineAsset* mutedTimeline = reinterpret_cast(self->introPlayableDirector->get_playableAsset()); - - static auto* Enumerable_ToList_Generic = THROW_UNLESS(il2cpp_utils::FindMethodUnsafe(classof(Enumerable*), "ToList", 1)); - static auto* Enumerable_ToList = THROW_UNLESS(il2cpp_utils::MakeGenericMethod(Enumerable_ToList_Generic, { classof(TrackAsset*) })); - - //List* outputTracks = Enumerable::ToList(animationTimeline->GetOutputTracks()); - List* outputTracks = il2cpp_utils::RunMethodThrow*, false>(static_cast(nullptr), - Enumerable_ToList, mutedTimeline->GetOutputTracks()); - - for (int i = 0; i < outputTracks->get_Count(); i++) { - TrackAsset* currentTrack = outputTracks->get_Item(i); - bool isAudio = il2cpp_utils::AssignableFrom(reinterpret_cast(currentTrack)->klass); - currentTrack->set_muted(isAudio); - } - } - - self->bindingFinished = false; - IntroAnimationPatch(self, maxDesiredIntroAnimationDuration, onCompleted); - // Reset director to real director - self->introPlayableDirector = realDirector; - targetIterations--; - if (targetIterations != 0) - self->PlayIntroAnimation(maxDesiredIntroAnimationDuration, onCompleted); - } - catch (const std::runtime_error& e) { - // Reset director to real director - self->introPlayableDirector = realDirector; - getLogger().critical("REPORT TO ENDER: Hook IntroAnimationPatch" __FILE__ " at Line %d: %s", __LINE__, e.what()); - IntroAnimationPatch(self, maxDesiredIntroAnimationDuration, onCompleted); - } - } - - enum CalculatePlayerIndexSequencePatchType { - None, BindTimeline, BindOutroTimeline - }; - - CalculatePlayerIndexSequencePatchType cpispt = None; - - MAKE_HOOK_MATCH(MultiplayerIntroAnimationController_BindTimeline, &MultiplayerIntroAnimationController::BindTimeline, void, MultiplayerIntroAnimationController* self) { - getLogger().debug("Start: MultiplayerIntroAnimationController_BindTimeline"); - cpispt = BindTimeline; - MultiplayerIntroAnimationController_BindTimeline(self); - } - - MAKE_HOOK_MATCH(MultiplayerOutroAnimationController_BindOutroTimeline, &MultiplayerOutroAnimationController::BindOutroTimeline, void, MultiplayerOutroAnimationController* self) { - getLogger().debug("Start: MultiplayerOutroAnimationController_BindOutroTimeline"); - cpispt = BindOutroTimeline; - MultiplayerOutroAnimationController_BindOutroTimeline(self); - } - - MAKE_HOOK_MATCH(MultiplayerPlayersManager_get_allActiveAtGameStartPlayers, &MultiplayerPlayersManager::get_allActiveAtGameStartPlayers, IReadOnlyList_1*, MultiplayerPlayersManager* self) { - getLogger().debug("Start: MultiplayerPlayersManager_get_allActiveAtGameStartPlayers"); - static auto* Enumerable_ToList_Generic = THROW_UNLESS(il2cpp_utils::FindMethodUnsafe(classof(Enumerable*), "ToList", 1)); - static auto* Enumerable_ToList = THROW_UNLESS(il2cpp_utils::MakeGenericMethod(Enumerable_ToList_Generic, { classof(IConnectedPlayer*) })); - - static auto* Enumerable_Take_Generic = THROW_UNLESS(il2cpp_utils::FindMethodUnsafe(classof(Enumerable*), "Take", 2)); - static auto* Enumerable_Take = THROW_UNLESS(il2cpp_utils::MakeGenericMethod(Enumerable_Take_Generic, { classof(IConnectedPlayer*) })); - - if (cpispt == BindTimeline) { - cpispt = None; - try { - List_1* listActivePlayers = il2cpp_utils::RunMethodThrow*, false>(static_cast(nullptr), - Enumerable_ToList, reinterpret_cast*>(self->dyn__allActiveAtGameStartPlayers())); - //List* listActivePlayers = Enumerable::ToList(reinterpret_cast*>(allActivePlayer)); - IConnectedPlayer* localPlayer = nullptr; - - // Check if active players contains local player and remove local player - for (int i = 0; i < listActivePlayers->get_Count(); i++) { - IConnectedPlayer* currentPlayer = listActivePlayers->get_Item(i); - if (currentPlayer->get_isMe()) { - listActivePlayers->RemoveAt(i); - localPlayer = currentPlayer; - } - } - - // Skip x amount of players and then take 4 - static auto* Enumerable_Skip_Generic = THROW_UNLESS(il2cpp_utils::FindMethodUnsafe(classof(Enumerable*), "Skip", 2)); - static auto* Enumerable_Skip = THROW_UNLESS(il2cpp_utils::MakeGenericMethod(Enumerable_Skip_Generic, { classof(IConnectedPlayer*) })); - auto* skipResult = il2cpp_utils::RunMethodThrow*, false>(static_cast(nullptr), - Enumerable_Skip, reinterpret_cast*>(listActivePlayers), (targetIterations - 1) * 4); - - auto* takeResult = il2cpp_utils::RunMethodThrow*, false>(static_cast(nullptr), - Enumerable_Take, skipResult, 4); - - List_1* selectedActivePlayers = il2cpp_utils::RunMethodThrow*, false>(static_cast(nullptr), - Enumerable_ToList, takeResult); - - //List_1* selectedActivePlayers = Enumerable::ToList(Enumerable::Take(Enumerable::Skip(reinterpret_cast*>(listActivePlayers), (targetIterations - 1) * 4), 4)); - //List_1* selectedActivePlayers = Enumerable::ToList(Enumerable::Take(skipResult, 4)); - - // Add back local player if not null - if (targetIterations == 1 && localPlayer != nullptr) { - selectedActivePlayers->Add(localPlayer); - } - - getLogger().debug("Finish: MultiplayerPlayersManager_get_allActiveAtGameStartPlayers"); - - //return new list of players - return reinterpret_cast*>(selectedActivePlayers); - } - catch (const std::runtime_error& e) { - getLogger().critical("REPORT TO ENDER: Hook MultiplayerPlayersManager_get_allActiveAtGameStartPlayers" __FILE__ " at Line %d: %s", __LINE__, e.what()); - } - } - else if (cpispt == BindOutroTimeline) { - cpispt = None; - - auto* result = il2cpp_utils::RunMethodThrow*, false>(static_cast(nullptr), - Enumerable_Take, reinterpret_cast*>(self->dyn__allActiveAtGameStartPlayers()), 4); - - return reinterpret_cast*>(il2cpp_utils::RunMethodThrow*, false>(static_cast(nullptr), - Enumerable_ToList, result)); - } - cpispt = None; - return self->dyn__allActiveAtGameStartPlayers(); - } - - MAKE_HOOK_MATCH(CreateServerFormController_get_formData, &CreateServerFormController::get_formData, CreateServerFormData, CreateServerFormController* self) { - CreateServerFormData result = CreateServerFormController_get_formData(self); - result.maxPlayers = std::clamp(self->dyn__maxPlayersList()->get_value(), 2, std::clamp(getConfig().config["MaxPlayers"].GetInt(), 2, 100)); - return result; - } - - MAKE_HOOK_MATCH(CreateServerFormController_Setup, &CreateServerFormController::Setup, void, CreateServerFormController* self, int selectedNumberOfPlayers, bool netDiscoverable) { - try { - std::vector rangeVec; - static auto* Enumerable_ToArray_Generic = THROW_UNLESS(il2cpp_utils::FindMethodUnsafe(classof(Enumerable*), "ToArray", 1)); - static auto* Enumerable_ToArray = THROW_UNLESS(il2cpp_utils::MakeGenericMethod(Enumerable_ToArray_Generic, { classof(int) })); - il2cpp_utils::RunMethodThrow<::Array*, false>(static_cast(nullptr), - Enumerable_ToArray, Enumerable::Range(2, std::clamp(getConfig().config["MaxPlayers"].GetInt(), 2, 100) - 1))->copy_to(rangeVec); - //Enumerable::ToArray(Enumerable::Range(2, 9))->copy_to(rangeVec); - std::vector resultVec(rangeVec.begin(), rangeVec.end()); - self->dyn__maxPlayersList()->dyn__values() = il2cpp_utils::vectorToArray(resultVec); - } catch (const std::runtime_error& e) { - getLogger().critical("REPORT TO ENDER: Hook CreateServerFormController_Setup caught on" __FILE__ " at Line %d: %s", __LINE__, e.what()); - } - CreateServerFormController_Setup(self, selectedNumberOfPlayers, netDiscoverable); - } - - void Hooks::MaxPlayerHooks() { - INSTALL_HOOK(getLogger(), MultiplayerResultsPyramidPatch); - INSTALL_HOOK(getLogger(), IntroAnimationPatch); - INSTALL_HOOK(getLogger(), MultiplayerIntroAnimationController_BindTimeline); - INSTALL_HOOK(getLogger(), MultiplayerOutroAnimationController_BindOutroTimeline); - - INSTALL_HOOK_ORIG(getLogger(), MultiplayerPlayersManager_get_allActiveAtGameStartPlayers); - INSTALL_HOOK(getLogger(), CreateServerFormController_get_formData); - INSTALL_HOOK(getLogger(), CreateServerFormController_Setup); - - } -} \ No newline at end of file diff --git a/MultiQuestensions/src/Hooks/NetworkPlayerEntitlementCheckerHooks.cpp b/MultiQuestensions/src/Hooks/NetworkPlayerEntitlementCheckerHooks.cpp deleted file mode 100644 index 2249fe8..0000000 --- a/MultiQuestensions/src/Hooks/NetworkPlayerEntitlementCheckerHooks.cpp +++ /dev/null @@ -1,183 +0,0 @@ -#include "main.hpp" -#include "Hooks/Hooks.hpp" -#include "Utils/CustomData.hpp" -#include "CS_DataStore.hpp" -using namespace GlobalNamespace; -using namespace System::Threading::Tasks; - -std::string missingLevelText; - -namespace MultiQuestensions { - -#pragma region Fields - std::map> entitlementDictionary; - - //static std::multimap entitlementDictionary; - - GlobalNamespace::IPreviewBeatmapLevel* loadingPreviewBeatmapLevel; - GlobalNamespace::BeatmapDifficulty loadingBeatmapDifficulty; - GlobalNamespace::BeatmapCharacteristicSO* loadingBeatmapCharacteristic; - GlobalNamespace::IDifficultyBeatmap* loadingDifficultyBeatmap; - GlobalNamespace::GameplayModifiers* loadingGameplayModifiers; - - //System::Action_3<::Il2CppString*, ::Il2CppString*, EntitlementsStatus>* entitlementAction; -#pragma endregion - - // For debugging purposes - const char* entitlementText(EntitlementsStatus entitlement) { - switch (entitlement.value) { - case EntitlementsStatus::Unknown: - return "Unknown"; - case EntitlementsStatus::NotOwned: - return "NotOwned"; - case EntitlementsStatus::NotDownloaded: - return "NotDownloaded"; - case EntitlementsStatus::Ok: - return "Ok"; - } - return ""; - } - - // Subscribe this method to 'menuRpcManager.setIsEntitledToLevelEvent' when on NetworkPlayerEntitlementChecker.Start, unsub on destroy - static void HandleEntitlementReceived(Il2CppString* userId, Il2CppString* levelId, EntitlementsStatus entitlement) { - std::string cUserId = to_utf8(csstrtostr(userId)); - std::string cLevelId = to_utf8(csstrtostr(levelId)); - - getLogger().debug("[HandleEntitlementReceived] Received Entitlement from user '%s' for level '%s' with status '%s'", - cUserId.c_str(), - cLevelId.c_str(), - entitlementText(entitlement) - ); - - if (entitlement == EntitlementsStatus::NotOwned && MultiQuestensions::Utils::HasRequirement(RuntimeSongLoader::API::GetLevelById(cLevelId))) { - IConnectedPlayer* player = sessionManager->GetPlayerByUserId(userId); - if (player) { - if (!player->HasState(getMEStateStr())) - missingLevelText = "One or more players are missing the following Requirement: Mapping Extensions"; - else if (!player->HasState(getNEStateStr())) - missingLevelText = "One or more players are missing the following Requirement: Noodle Extensions"; - else if (!player->HasState(getChromaStateStr())) - missingLevelText = "One or more players are missing the following Requirement: Chroma"; - } - else { - missingLevelText = "Error Checking Requirement: Player not found"; - } - } - entitlementDictionary[cUserId][cLevelId] = entitlement.value; - if (lobbyGameStateController != nullptr && lobbyGameStateController->get_state() == MultiplayerLobbyState::GameStarting) { - DataStore* instance = DataStore::get_Instance(); - if (instance && instance->loadingPreviewBeatmapLevel && instance->loadingBeatmapCharacteristic && instance->loadingDifficultyBeatmap && instance->loadingGameplayModifiers && instance->loadingBeatmapDifficulty) { - getLogger().debug("[HandleEntitlementReceived] GameStarting, running 'HandleMultiplayerLevelLoaderCountdownFinished'"); - lobbyGameStateController->HandleMultiplayerLevelLoaderCountdownFinished(instance->loadingPreviewBeatmapLevel, instance->loadingBeatmapDifficulty.value(), instance->loadingBeatmapCharacteristic, instance->loadingDifficultyBeatmap, instance->loadingGameplayModifiers); - return; - } - else if (lobbyGameStateController->dyn__multiplayerLevelLoader()) { - getLogger().debug("[HandleEntitlementReceived] GameStarting, DataStore empty trying to run 'HandleMultiplayerLevelLoaderCountdownFinished' with lvlLoader Data"); - MultiplayerLevelLoader* lvlLoader = lobbyGameStateController->dyn__multiplayerLevelLoader(); - if (lvlLoader->dyn__previewBeatmapLevel() && lvlLoader->dyn__beatmapId() && lvlLoader->dyn__beatmapCharacteristic() && lvlLoader->dyn__difficultyBeatmap() && lvlLoader->dyn__gameplayModifiers()) { - lobbyGameStateController->HandleMultiplayerLevelLoaderCountdownFinished(lvlLoader->dyn__previewBeatmapLevel(), lvlLoader->dyn__beatmapId()->get_difficulty(), lvlLoader->dyn__beatmapCharacteristic(), lvlLoader->dyn__difficultyBeatmap(), lvlLoader->dyn__gameplayModifiers()); - return; - } - } - getLogger().error("[HandleEntitlementReceived] GameStarting but level data not available"); - //getLogger().debug("[HandleEntitlementReceived] checking pointers: loadingPreviewBeatmapLevel='%p', loadingBeatmapDifficulty set to '%d', loadingBeatmapCharacteristic='%p', loadingDifficultyBeatmap='%p', loadingGameplayModifiers='%p'", loadingPreviewBeatmapLevel, static_cast(loadingBeatmapDifficulty), loadingBeatmapCharacteristic, loadingDifficultyBeatmap, loadingGameplayModifiers); - } - } - -#pragma region Hooks - MAKE_HOOK_MATCH(NetworkPlayerEntitlementChecker_GetEntitlementStatus, &NetworkPlayerEntitlementChecker::GetEntitlementStatus, Task_1*, NetworkPlayerEntitlementChecker* self, Il2CppString* levelIdCS) { - missingLevelText.clear(); - std::string levelId = to_utf8(csstrtostr(levelIdCS)); - getLogger().info("NetworkPlayerEntitlementChecker_GetEntitlementStatus: %s", levelId.c_str()); - if (IsCustomLevel(levelId)) { - if (HasSong(levelId)) { - //std::optional level = RuntimeSongLoader::API::GetLevelById(levelId); - if (MultiQuestensions::Utils::HasRequirement(RuntimeSongLoader::API::GetLevelById(levelId))) - return Task_1::New_ctor(EntitlementsStatus::Ok); - else return Task_1::New_ctor(EntitlementsStatus::NotOwned); - } - else { - auto task = Task_1::New_ctor(); - BeatSaver::API::GetBeatmapByHashAsync(GetHash(levelId), - [task, levelId](std::optional beatmaps) { - QuestUI::MainThreadScheduler::Schedule( - [task, beatmaps, levelId] { - if (beatmaps.has_value()) { - // TODO: Server side check, possibly something better mod side as well, this would just prevent downloading - // Possibly taking a look at this https://github.com/BSMGPink/PinkCore/blob/master/include/Utils/RequirementUtils.hpp - //for (auto& beatmap : beatmaps->GetVersions()) { - auto& beatmap = beatmaps->GetVersions().front(); - std::string mapHash = beatmap.GetHash(); - std::transform(mapHash.begin(), mapHash.end(), mapHash.begin(), toupper); - if (mapHash == GetHash(levelId)) { - for (auto& diff : beatmap.GetDiffs()) { - if (diff.GetChroma() && !AllPlayersHaveChroma() && Modloader::getMods().find("Chroma") != Modloader::getMods().end()) { - task->TrySetResult(EntitlementsStatus::NotOwned); - getLogger().warning("Map contains Chroma difficulty and Chroma is installed, returning NotOwned as Chroma currently causes issues in Multiplayer"); - missingLevelText = "Chroma Requirement block, please uninstall Chroma"; - return; - } - else if (diff.GetNE() && !AllPlayersHaveNE() && Modloader::getMods().find("NoodleExtensions") == Modloader::getMods().end()) { - task->TrySetResult(EntitlementsStatus::NotOwned); - getLogger().warning("Map contains NE difficulty but NoodleExtensions doesn't seem to be installed, returning NotOwned"); - missingLevelText = "You or another Player is Missing the following Requirement: Noodle Extensions"; - return; - } - else if (diff.GetME() && !AllPlayersHaveME() && Modloader::getMods().find("MappingExtensions") == Modloader::getMods().end()) { - task->TrySetResult(EntitlementsStatus::NotOwned); - getLogger().warning("Map contains ME difficulty but MappingExtensions doesn't seem to be installed, returning NotOwned"); - missingLevelText = "You or another Player is Missing the following Requirement: Mapping Extensions"; - return; - } - } - task->TrySetResult(EntitlementsStatus::NotDownloaded); - return; - } - else { - getLogger().error("Hash returned by BeatSaver doesn't match requested hash: Expected: '%s' got '%s'", GetHash(levelId).c_str(), mapHash.c_str()); - missingLevelText = string_format("Hash returned by BeatSaver doesn't match requested hash: Expected: '%s' got '%s'", GetHash(levelId).c_str(), mapHash.c_str()); - } - //} - task->TrySetResult(EntitlementsStatus::NotOwned); - } - else { - task->TrySetResult(EntitlementsStatus::NotOwned); - } - } - ); - } - ); - return task; - } - } - else { - return NetworkPlayerEntitlementChecker_GetEntitlementStatus(self, levelIdCS); - } - } - - MAKE_HOOK_MATCH(NetworkPlayerEntitlementChecker_Start, &NetworkPlayerEntitlementChecker::Start, void, NetworkPlayerEntitlementChecker* self) { - //entitlementAction = il2cpp_utils::MakeDelegate*>(classof(System::Action_3<::Il2CppString*, ::Il2CppString*, EntitlementsStatus>*), (std::function) [&](Il2CppString* userId, Il2CppString* beatmapId, EntitlementsStatus status) { - // HandleEntitlementReceived(userId, beatmapId, status); - // }); - //self->rpcManager->add_setIsEntitledToLevelEvent(entitlementAction); - self->dyn__rpcManager()->add_setIsEntitledToLevelEvent( - il2cpp_utils::MakeDelegate*>(classof(System::Action_3<::Il2CppString*, ::Il2CppString*, EntitlementsStatus>*), (std::function) [&](Il2CppString* userId, Il2CppString* beatmapId, EntitlementsStatus status) { - HandleEntitlementReceived(userId, beatmapId, status); - })); - NetworkPlayerEntitlementChecker_Start(self); - } - - // Causes crash when being called - //MAKE_HOOK_MATCH(NetworkPlayerEntitlementChecker_OnDestroy, &NetworkPlayerEntitlementChecker::OnDestroy, void, NetworkPlayerEntitlementChecker* self) { - // if (entitlementAction) - // self->rpcManager->remove_setIsEntitledToLevelEvent(entitlementAction); - // NetworkPlayerEntitlementChecker_OnDestroy(self); - //} -#pragma endregion - - void Hooks::NetworkplayerEntitlementChecker() { - INSTALL_HOOK_ORIG(getLogger(), NetworkPlayerEntitlementChecker_GetEntitlementStatus); - INSTALL_HOOK(getLogger(), NetworkPlayerEntitlementChecker_Start); - //INSTALL_HOOK(getLogger(), NetworkPlayerEntitlementChecker_OnDestroy); - } -} \ No newline at end of file diff --git a/MultiQuestensions/src/Hooks/QuickplayHooks.cpp b/MultiQuestensions/src/Hooks/QuickplayHooks.cpp deleted file mode 100644 index 52833a4..0000000 --- a/MultiQuestensions/src/Hooks/QuickplayHooks.cpp +++ /dev/null @@ -1,113 +0,0 @@ -#include "main.hpp" -#include "Hooks/Hooks.hpp" -#include "GlobalFields.hpp" - -#include "GlobalNamespace/MasterServerQuickPlaySetupData_QuickPlaySongPacksOverride_PredefinedPack.hpp" -#include "GlobalNamespace/MasterServerQuickPlaySetupData_QuickPlaySongPacksOverride_LocalizedCustomPack.hpp" -#include "GlobalNamespace/MasterServerQuickPlaySetupData_QuickPlaySongPacksOverride_LocalizedCustomPackName.hpp" -#include "GlobalNamespace/MasterServerQuickPlaySetupData_QuickPlaySongPacksOverride.hpp" -#include "GlobalNamespace/MasterServerQuickPlaySetupData.hpp" - -//#include "GlobalNamespace/MasterServerQuickPlaySetupModel.hpp" -#include "GlobalNamespace/SongPackMaskModelSO.hpp" - -#include "GlobalNamespace/MultiplayerModeSelectionFlowCoordinator.hpp" -#include "GlobalNamespace/JoinQuickPlayViewController.hpp" -#include "GlobalNamespace/SimpleDialogPromptViewController.hpp" -#include "GlobalNamespace/MultiplayerModeSettings.hpp" - -#include "HMUI/ViewController_AnimationDirection.hpp" -#include "HMUI/ViewController_AnimationType.hpp" - -#include "Polyglot/Localization.hpp" -#include "Polyglot/LanguageExtensions.hpp" - -#include "GlobalNamespace/QuickPlaySongPacksDropdown.hpp" - -using namespace GlobalNamespace; - -using MSQSD_QPSPO_PredefinedPack = MasterServerQuickPlaySetupData::QuickPlaySongPacksOverride::PredefinedPack; -using MSQSD_QPSPO_LocalizedCustomPack = MasterServerQuickPlaySetupData::QuickPlaySongPacksOverride::LocalizedCustomPack; -using MSQD_QPSPO_LocalizedCustomPackName = MasterServerQuickPlaySetupData::QuickPlaySongPacksOverride::LocalizedCustomPackName; - -bool gotSongPackOverrides = false; - -namespace MultiQuestensions { - // Check for our custom packs - MAKE_HOOK_MATCH(QuickPlaySongPacksDropdown_LazyInit, &QuickPlaySongPacksDropdown::LazyInit, void, QuickPlaySongPacksDropdown* self) { - gotSongPackOverrides = (self->dyn__quickPlaySongPacksOverride() != nullptr); - QuickPlaySongPacksDropdown_LazyInit(self); - } - - MAKE_HOOK_MATCH(MultiplayerModeSelectionFlowCoordinator_HandleJoinQuickPlayViewControllerDidFinish, &MultiplayerModeSelectionFlowCoordinator::HandleJoinQuickPlayViewControllerDidFinish, void, MultiplayerModeSelectionFlowCoordinator* self, bool success) { - Il2CppString* levelPackName = self->dyn__joinQuickPlayViewController()->dyn__multiplayerModeSettings()->dyn_quickPlaySongPackMaskSerializedName(); - if (success && - self->dyn__songPackMaskModel()->ToSongPackMask( - levelPackName - ).Contains( - getCustomLevelSongPackMaskStr()) - ) { - self->dyn__simpleDialogPromptViewController()->Init( - il2cpp_utils::newcsstr("Custom Song Quickplay"), - il2cpp_utils::newcsstr("This category includes songs of varying difficulty.\nIt may be more enjoyable to play in a private lobby with friends."), - il2cpp_utils::newcsstr("Continue"), - il2cpp_utils::newcsstr("Cancel"), - il2cpp_utils::MakeDelegate*>(classof(System::Action_1*), (std::function)[self, success](int btnId) { - switch (btnId) - { - default: - case 0: // Continue - MultiplayerModeSelectionFlowCoordinator_HandleJoinQuickPlayViewControllerDidFinish(self, success); - return; - case 1: // Cancel - //self->DismissViewController(self->dyn__simpleDialogPromptViewController(), HMUI::ViewController::AnimationDirection::Vertical, nullptr, false); - self->ReplaceTopViewController(self->dyn__joinQuickPlayViewController(), nullptr, HMUI::ViewController::AnimationType::In, HMUI::ViewController::AnimationDirection::Vertical); - return; - } - } - ) - ); - self->ReplaceTopViewController(self->dyn__simpleDialogPromptViewController(), nullptr, HMUI::ViewController::AnimationType::In, HMUI::ViewController::AnimationDirection::Vertical); - } else MultiplayerModeSelectionFlowCoordinator_HandleJoinQuickPlayViewControllerDidFinish(self, success); - } - - //MAKE_HOOK_MATCH(MasterServerQuickPlaySetupModel_GetQuickPlaySetupInternal, &MasterServerQuickPlaySetupModel::GetQuickPlaySetupInternal, System::Threading::Tasks::Task_1*, MasterServerQuickPlaySetupModel* self) { - // getLogger().debug("MasterServerQuickPlaySetupModel::GetQuickPlaySetupInternal"); - // using T1_MasterServerQuickplaySetupData = System::Threading::Tasks::Task_1*; - // T1_MasterServerQuickplaySetupData result = MasterServerQuickPlaySetupModel_GetQuickPlaySetupInternal(self); - // auto action = il2cpp_utils::MakeDelegate*>(classof(System::Action_1*), (std::function)[result] { - // MasterServerQuickPlaySetupData* data = result->get_ResultOnSuccess(); - // if (data) { - // MasterServerQuickPlaySetupData::QuickPlaySongPacksOverride* overrides = data->dyn_quickPlayAvailablePacksOverride(); - // getLogger().debug("MasterServerQuickplaySetupData: hasOverride: '%s', Check _quickPlayAvailablePacksOverride Pointer: '%p'", data->get_hasOverride() ? "true" : "false", overrides); - // using PredefinedPack = MasterServerQuickPlaySetupData::QuickPlaySongPacksOverride::PredefinedPack; - // for (int i = 0; i < overrides->dyn_predefinedPackIds()->get_Count(); i++) { - // PredefinedPack* pack = overrides->dyn_predefinedPackIds()->get_Item(i); - // getLogger().debug("predefinedPackIds: order: '%d', packId: '%s'", pack->dyn_order(), to_utf8(csstrtostr(pack->dyn_packId())).c_str()); - // } - // using LocalizedCustomPack = MasterServerQuickPlaySetupData::QuickPlaySongPacksOverride::LocalizedCustomPack; - // for (int i = 0; i < overrides->dyn_localizedCustomPacks()->get_Count(); i++) { - // LocalizedCustomPack* pack = overrides->dyn_localizedCustomPacks()->get_Item(i); - // getLogger().debug("predefinedPackIds: order: '%d', serializedName: '%s'", pack->dyn_order(), to_utf8(csstrtostr(pack->dyn_serializedName())).c_str()); - // for (int j = 0; j < pack->dyn_packIds()->get_Count(); j++) { - // getLogger().debug("predefinedPackIds packId '%d': packId: '%s'", j, to_utf8(csstrtostr(pack->dyn_packIds()->get_Item(j))).c_str()); - // } - // } - // } - // else { - // getLogger().debug("MasterServerQuickPlaySetupModel::GetQuickPlaySetupInternal: Task does not have Results"); - // } - // } - // ); - // reinterpret_cast(result)->ContinueWith(action); - - // return result; - //} - - void Hooks::QuickplayHooks() { - INSTALL_HOOK(getLogger(), QuickPlaySongPacksDropdown_LazyInit); - INSTALL_HOOK(getLogger(), MultiplayerModeSelectionFlowCoordinator_HandleJoinQuickPlayViewControllerDidFinish); - //INSTALL_HOOK(getLogger(), MasterServerQuickPlaySetupModel_GetQuickPlaySetupInternal); - } -} - diff --git a/MultiQuestensions/src/Hooks/SessionManagerAndExtendedPlayerHooks.cpp b/MultiQuestensions/src/Hooks/SessionManagerAndExtendedPlayerHooks.cpp deleted file mode 100644 index ed6d0f4..0000000 --- a/MultiQuestensions/src/Hooks/SessionManagerAndExtendedPlayerHooks.cpp +++ /dev/null @@ -1,239 +0,0 @@ -#include "main.hpp" -#include "Hooks/Hooks.hpp" -#include "Hooks/SessionManagerAndExtendedPlayerHooks.hpp" -#include "GlobalFields.hpp" -#include "Hooks/EnvironmentAndAvatarHooks.hpp" -#include "UI/CenterScreenLoading.hpp" -#include "Utils/SemVerChecker.hpp" - -#include "Beatmaps/PreviewBeatmapStub.hpp" - -#include "GlobalNamespace/IPlatformUserModel.hpp" -#include "GlobalNamespace/LocalNetworkPlayerModel.hpp" -#include "UnityEngine/Resources.hpp" -#include "GlobalNamespace/UserInfo.hpp" - -#include "CodegenExtensions/ColorUtility.hpp" - -#include "questui/shared/BeatSaberUI.hpp" -#include "questui/shared/CustomTypes/Components/MainThreadScheduler.hpp" -#include "songdownloader/shared/BeatSaverAPI.hpp" -using namespace MultiQuestensions; -using namespace GlobalNamespace; - -// Handles a PreviewBeatmapPacket used to transmit data about a custom song. -static void HandlePreviewBeatmapPacket(MultiQuestensions::Beatmaps::PreviewBeatmapPacket* packet, GlobalNamespace::IConnectedPlayer* player) { - getLogger().debug("'%s' selected song '%s'", to_utf8(csstrtostr(player->get_userId())).c_str(), to_utf8(csstrtostr(packet->levelHash)).c_str()); - IPreviewBeatmapLevel* localPreview = lobbyPlayersDataModel->beatmapLevelsModel->GetLevelPreviewForLevelId(packet->levelId); - MultiQuestensions::Beatmaps::PreviewBeatmapStub* preview; - try { - if (localPreview == nullptr) { - Il2CppString* nullString = nullptr; - IPreviewBeatmapLevel* nullLvl = nullptr; - preview = THROW_UNLESS(il2cpp_utils::New(nullString, nullLvl, packet)); - getLogger().info("Try getting CoverImage from BeatSaver"); - std::string levelid = to_utf8(csstrtostr(preview->get_levelID())); - BeatSaver::API::GetBeatmapByHashAsync(GetHash(levelid), - [preview, player, packet](std::optional beatmap) { - if (beatmap.has_value()) { - BeatSaver::API::GetCoverImageAsync(*beatmap, [preview, player, packet](std::vector bytes) { - QuestUI::MainThreadScheduler::Schedule([packet, player, preview, bytes] { - if (packet && player && preview && lobbyPlayersDataModel) { - preview->coverImage = QuestUI::BeatSaberUI::VectorToSprite(bytes); - BeatmapCharacteristicSO* characteristic = lobbyPlayersDataModel->beatmapCharacteristicCollection->GetBeatmapCharacteristicBySerializedName(packet->characteristic); - lobbyPlayersDataModel->SetPlayerBeatmapLevel(player->get_userId(), reinterpret_cast(preview), packet->difficulty, characteristic); - } - else { - getLogger().error("Error nullptr: packet='%p', player='%p', preview='%p', lobbyPlayersDataModel='%p'", packet, player, preview, lobbyPlayersDataModel); - } - } - ); - } - ); - } - else { - QuestUI::MainThreadScheduler::Schedule([packet, player, preview] { - if (packet && player && preview) { - BeatmapCharacteristicSO* characteristic = lobbyPlayersDataModel->beatmapCharacteristicCollection->GetBeatmapCharacteristicBySerializedName(packet->characteristic); - lobbyPlayersDataModel->SetPlayerBeatmapLevel(player->get_userId(), reinterpret_cast(preview), packet->difficulty, characteristic); - } - else { - getLogger().error("Error nullptr: packet='%p', player='%p', preview='%p', lobbyPlayersDataModel='%p'", packet, player, preview, lobbyPlayersDataModel); - } - } - ); - } - } - ); - return; - } - else { - MultiQuestensions::Beatmaps::PreviewBeatmapPacket* nullpacket = nullptr; - preview = THROW_UNLESS(il2cpp_utils::New(packet->levelHash, localPreview, nullpacket)); - } - BeatmapCharacteristicSO* characteristic = lobbyPlayersDataModel->beatmapCharacteristicCollection->GetBeatmapCharacteristicBySerializedName(packet->characteristic); - //getLogger().debug("Check difficulty as unsigned int: %u", packet->difficulty); - lobbyPlayersDataModel->SetPlayerBeatmapLevel(player->get_userId(), reinterpret_cast(preview), packet->difficulty, characteristic); - } - catch (const std::runtime_error& e) { - getLogger().error("REPORT TO ENDER: %s", e.what()); - } - catch (...) { - getLogger().debug("REPORT TO ENDER: Unknown exception in HandlePreviewBeatmapPacket"); - } -} - - -std::map> _extendedPlayers; -//System::Collections::Generic::Dictionary_2* extendedPlayers; -//SafePtr*> extendedPlayersSPTR; -//SafePtr localExtendedPlayerSPTR; -SafePtr localExtendedPlayer; -IPlatformUserModel* platformUserModel; - -static void HandleExtendedPlayerPacket(MultiQuestensions::Extensions::ExtendedPlayerPacket* packet, IConnectedPlayer* player) { - const std::string userId = to_utf8(csstrtostr(player->get_userId())); - //if (extendedPlayers->ContainsKey(player->get_userId())) { - if (_extendedPlayers.contains(userId)) { - SafePtr extendedPlayer = _extendedPlayers.at(userId); - //Extensions::ExtendedPlayer* extendedPlayer = extendedPlayers->get_Item(player->get_userId()); - extendedPlayer->_connectedPlayer = player; - extendedPlayer->platformID = packet->platformID; - extendedPlayer->platform = packet->platform; - extendedPlayer->playerColor = packet->playerColor; - extendedPlayer->mpexVersion = packet->mpexVersion; - - // Updates Color and NameTag - SetPlayerPlaceColor(reinterpret_cast(extendedPlayer->get_self()), extendedPlayer->get_playerColor(), true); - CreateOrUpdateNameTag(reinterpret_cast(extendedPlayer->get_self())); - } - else { - getLogger().info("Received 'ExtendedPlayerPacket' from '%s' with platformID: '%s' mpexVersion: '%s'", - to_utf8(csstrtostr(player->get_userId())).c_str(), - to_utf8(csstrtostr(packet->platformID)).c_str(), - to_utf8(csstrtostr(packet->mpexVersion)).c_str() - ); - Extensions::ExtendedPlayer* extendedPlayer; - try { - extendedPlayer = Extensions::ExtendedPlayer::CS_ctor(player, packet->platformID, packet->platform, packet->mpexVersion, packet->playerColor); - if (to_utf8(csstrtostr(extendedPlayer->mpexVersion)) != MPEX_PROTOCOL) - { - getLogger().warning( - "###################################################################\r\n" - "Different MultiplayerExtensions protocol detected!\r\n" - "The player '%s' is using MpEx protocol version %s while you are using MpEx protocol " MPEX_PROTOCOL "\r\n" - "For best compatibility all players should use a compatible version of MultiplayerExtensions/MultiQuestensions.\r\n" - "###################################################################", - to_utf8(csstrtostr(player->get_userName())).c_str(), - to_utf8(csstrtostr(extendedPlayer->mpexVersion)).c_str() - ); - } - } - catch (const std::runtime_error& e) { - getLogger().error("REPORT TO ENDER: Exception while trying to create ExtendedPlayer: %s", e.what()); - } - if (extendedPlayer) { - _extendedPlayers.emplace(userId, extendedPlayer); - //extendedPlayers->Add(player->get_userId(), extendedPlayer); - //if (!extendedPlayersSPTR) extendedPlayersSPTR = extendedPlayers; - - //getLogger().debug("SetPlayerPlaceColor"); - SetPlayerPlaceColor(reinterpret_cast(extendedPlayer->get_self()), extendedPlayer->get_playerColor(), true); - //getLogger().debug("CreateOrUpdateNameTag"); - // This packet is usually received before the avatar is actually created - CreateOrUpdateNameTag(reinterpret_cast(extendedPlayer->get_self())); - getLogger().debug("ExtendedPlayerPacket done"); - - //extendedPlayerConnectedEvent::Invoke(extendedPlayer); - } - } -} - -void HandlePlayerConnected(IConnectedPlayer* player) { - try { - getLogger().debug("HandlePlayerConnected"); - if (player) { - const std::string userId = to_utf8(csstrtostr(player->get_userId())); - getLogger().info("Player '%s' joined", userId.c_str()); - getLogger().debug("Sending ExtendedPlayerPacket"); - if (localExtendedPlayer->get_platformID() != nullptr) - { - Extensions::ExtendedPlayerPacket* localPlayerPacket = Extensions::ExtendedPlayerPacket::Init(localExtendedPlayer->get_platformID(), localExtendedPlayer->get_platform(), localExtendedPlayer->get_playerColor()); - //getLogger().debug("LocalPlayer Color is, R: %f G: %f B: %f", localPlayerPacket->playerColor.r, localPlayerPacket->playerColor.g, localPlayerPacket->playerColor.b); - packetManager->Send(reinterpret_cast(localPlayerPacket)); - } - getLogger().debug("ExtendedPlayerPacket sent"); - - SetPlayerPlaceColor(player, Extensions::ExtendedPlayer::DefaultColor, false); - } - } - catch (const std::runtime_error& e) { - getLogger().error("REPORT TO ENDER: %s", e.what()); - } -} - -void HandlePlayerDisconnected(IConnectedPlayer* player) { - getLogger().info("Player '%s' left", to_utf8(csstrtostr(player->get_userId())).c_str()); - getLogger().debug("Reseting platform lights"); - SetPlayerPlaceColor(player, UnityEngine::Color::get_black(), true); - _extendedPlayers.erase(to_utf8(csstrtostr(player->get_userId())).c_str()); -} - -//void HandleDisconnect(DisconnectedReason* reason) { -//} - -MAKE_HOOK_MATCH(SessionManagerStart, &MultiplayerSessionManager::Start, void, MultiplayerSessionManager* self) { - - sessionManager = self; - SessionManagerStart(sessionManager); - packetManager = new PacketManager(sessionManager); - - - packetManager->RegisterCallback("MultiplayerExtensions.Beatmaps.PreviewBeatmapPacket", HandlePreviewBeatmapPacket); - packetManager->RegisterCallback("MultiplayerExtensions.Extensions.ExtendedPlayerPacket", HandleExtendedPlayerPacket); -} - -MAKE_HOOK_FIND_VERBOSE(SessionManager_StartSession, il2cpp_utils::FindMethodUnsafe("", "MultiplayerSessionManager", "StartSession", 1), void, MultiplayerSessionManager* self, ConnectedPlayerManager* connectedPlayerManager) { - SessionManager_StartSession(self, connectedPlayerManager); - getLogger().debug("MultiplayerSessionManager.StartSession, creating localExtendedPlayerPacket"); - //try { - localExtendedPlayer = Extensions::ExtendedPlayer::CS_ctor(self->get_localPlayer()); - //localExtendedPlayerSPTR = localExtendedPlayer; - - if (!UnityEngine::ColorUtility::TryParseHtmlString(il2cpp_utils::newcsstr(getConfig().config["color"].GetString()), localExtendedPlayer->playerColor)) - localExtendedPlayer->playerColor = UnityEngine::Color(0.031f, 0.752f, 1.0f); - - static auto localNetworkPlayerModel = UnityEngine::Resources::FindObjectsOfTypeAll()->get(0); - static auto UserInfoTask = localNetworkPlayerModel->platformUserModel->GetUserInfo(); - static auto action = il2cpp_utils::MakeDelegate*>(classof(System::Action_1*), (std::function*)>)[&](System::Threading::Tasks::Task_1* userInfoTask) { - auto userInfo = userInfoTask->get_Result(); - if (userInfo) { - localExtendedPlayer->platformID = userInfo->platformUserId; - localExtendedPlayer->platform = (Extensions::Platform)userInfo->platform.value; - } - else getLogger().error("Failed to get local network player!"); - } - ); - reinterpret_cast(UserInfoTask)->ContinueWith(action); - - using namespace MultiQuestensions::Utils; - self->SetLocalPlayerState(getModdedStateStr(), true); - self->SetLocalPlayerState(getMEStateStr(), MatchesVersion("MappingExtensions", "*")); - self->SetLocalPlayerState(getNEStateStr(), MatchesVersion("NoodleExtensions", "*")); - self->SetLocalPlayerState(getChromaStateStr(), MatchesVersion(ChromaID, ChromaVersionRange)); - // TODO: Do this check correctly once Chroma got an Update for Multiplayer - - //} - //catch (const std::runtime_error& e) { - // getLogger().error("%s", e.what()); - //} - self->add_playerConnectedEvent(il2cpp_utils::MakeDelegate*>(classof(System::Action_1*), static_cast(nullptr), HandlePlayerConnected)); - self->add_playerDisconnectedEvent(il2cpp_utils::MakeDelegate*>(classof(System::Action_1*), static_cast(nullptr), HandlePlayerDisconnected)); - //self->add_disconnectedEvent(il2cpp_utils::MakeDelegate*>*>(classof(System::Action_1*>*), static_cast(nullptr), HandleDisconnect)); -} - - -void MultiQuestensions::Hooks::SessionManagerAndExtendedPlayerHooks() { - INSTALL_HOOK(getLogger(), SessionManagerStart); - INSTALL_HOOK(getLogger(), SessionManager_StartSession); -} \ No newline at end of file diff --git a/MultiQuestensions/src/Hooks/SessionManagerAndMpexPlayerHooks.cpp b/MultiQuestensions/src/Hooks/SessionManagerAndMpexPlayerHooks.cpp new file mode 100644 index 0000000..bc8dd18 --- /dev/null +++ b/MultiQuestensions/src/Hooks/SessionManagerAndMpexPlayerHooks.cpp @@ -0,0 +1,180 @@ +#include "main.hpp" +#include "Hooks/Hooks.hpp" +#include "Hooks/SessionManagerAndExtendedPlayerHooks.hpp" +#include "GlobalFields.hpp" +#include "Hooks/EnvironmentAndAvatarHooks.hpp" +#include "Players/MpexPlayerManager.hpp" +#include "Config.hpp" + +#include "GlobalNamespace/LocalNetworkPlayerModel.hpp" +#include "UnityEngine/Resources.hpp" +#include "GlobalNamespace/UserInfo.hpp" +#include "GlobalNamespace/MultiplayerSessionManager_SessionType.hpp" +#include "GlobalNamespace/DisconnectedReason.hpp" + + +#include "CodegenExtensions/ColorUtility.hpp" + +#include "questui/shared/BeatSaberUI.hpp" +#include "questui/shared/CustomTypes/Components/MainThreadScheduler.hpp" + +#include "multiplayer-core/shared/Networking/MpPacketSerializer.hpp" +#include "multiplayer-core/shared/Networking/MpNetworkingEvents.hpp" + +#include "multiplayer-core/shared/Utils/EnumUtils.hpp" +#include "Players/MpexPlayerManager.hpp" +#include "multiplayer-core/shared/Players/MpPlayerData.hpp" +#include "multiplayer-core/shared/Players/MpPlayerManager.hpp" +#include "multiplayer-core/shared/GlobalFields.hpp" + +using namespace MultiQuestensions; +using namespace MultiplayerCore::Networking; +using namespace GlobalNamespace; + +Players::MpexPlayerData* localMpexPlayerData; +// TODO: Check the Players::MpexPlayerData* instances don't get GC'd before getting placed in their CT +std::unordered_map _mpexPlayerData; + +MultiplayerCore::event MultiQuestensions::Players::MpexPlayerManager::ReceivedMpExPlayerData; + +MultiplayerCore::event_handler _RegisterMpexPacketsHandler = MultiplayerCore::event_handler(HandleRegisterMpexPackets); +MultiplayerCore::event_handler _UnRegisterMpexPacketsHandler = MultiplayerCore::event_handler(HandleUnRegisterMpexPackets); + + +MultiplayerCore::event_handler _PlayerConnectedHandler = MultiplayerCore::event_handler(HandlePlayerConnected); +MultiplayerCore::event_handler _PlayerDisconnectedHandler = MultiplayerCore::event_handler(HandlePlayerDisconnected); +MultiplayerCore::event_handler _ConnectingHandler = MultiplayerCore::event_handler(HandleConnecting); +MultiplayerCore::event_handler _DisconnectedHandler = MultiplayerCore::event_handler(HandleDisconnect); + + +bool MultiQuestensions::Players::MpexPlayerManager::TryGetMpexPlayerData(std::string const& playerId, MultiQuestensions::Players::MpexPlayerData*& player) { + if (_mpexPlayerData.find(playerId) != _mpexPlayerData.end()) { + player = _mpexPlayerData.at(playerId); + return true; + } + return false; +} + +MultiQuestensions::Players::MpexPlayerData* MultiQuestensions::Players::MpexPlayerManager::GetMpexPlayerData(std::string const& playerId) { + if (_mpexPlayerData.find(playerId) != _mpexPlayerData.end()) { + return _mpexPlayerData.at(playerId); + } + return nullptr; +} + +static void HandleMpexData(Players::MpexPlayerData* packet, IConnectedPlayer* player) { + if(player){ + std::string const& userId = player->get_userId(); + if (_mpexPlayerData.contains(userId)) { + getLogger().info("Received 'MpexPlayerData', player already exists"); + // Explicitly free previous MpexPlayerData + gc_free_specific( _mpexPlayerData[userId]); + _mpexPlayerData[userId] = packet; + } + else { + getLogger().info("Received 'MpexPlayerData' from '%s' with color: '%f, %f, %f, %f'", + userId.c_str(), + packet->Color.r, packet->Color.g, packet->Color.b, packet->Color.a + ); + _mpexPlayerData.emplace(userId, packet); + } + SetPlayerPlaceColor(player, packet->Color, true); + getLogger().info("Calling event 'ReceivedMpExPlayerData'"); + MultiQuestensions::Players::MpexPlayerManager::ReceivedMpExPlayerData(player, packet); + } +} + +void HandleRegisterMpexPackets(MultiplayerCore::Networking::MpPacketSerializer* _mpPacketSerializer) { + // Explicitly allocate instance when packet is received, we will manually clear it + _mpPacketSerializer->RegisterCallback(HandleMpexData); + // _mpPacketSerializer->RegisterCallback(HandleMpexData); + getLogger().debug("Callback HandleMpexData Registered"); +} +void HandleUnRegisterMpexPackets(MultiplayerCore::Networking::MpPacketSerializer* _mpPacketSerializer) { + _mpPacketSerializer->UnregisterCallback(); + getLogger().debug("Callback HandleMpexData UnRegistered"); +} + +void HandlePlayerConnected(IConnectedPlayer* player) { + try { + getLogger().debug("MpEx HandlePlayerConnected"); + if (player) { + getLogger().info("MpExPlayer '%s' joined", static_cast(player->get_userId()).c_str()); + getLogger().debug("Sending MpexPlayerData"); + if (localMpexPlayerData) + { + MultiplayerCore::mpPacketSerializer->Send(localMpexPlayerData); + getLogger().debug("MpexPlayerData sent"); + } + SetPlayerPlaceColor(player, Config::DefaultPlayerColor, false); + } + } + catch (const std::runtime_error& e) { + getLogger().error("REPORT TO ENDER: %s", e.what()); + } +} + +void HandlePlayerDisconnected(IConnectedPlayer* player) { + try { + getLogger().debug("MpEx HandlePlayerDisconnected"); + if (player) { + std::string const& userId = player->get_userId(); + getLogger().info("Player '%s' left", userId.c_str()); + if (_mpexPlayerData.contains(userId)) { + getLogger().info("Reseting platform lights for Player '%s'", userId.c_str()); + SetPlayerPlaceColor(player, UnityEngine::Color::get_black(), true); + // Explicitly free previous MpexPlayerData + gc_free_specific( _mpexPlayerData[userId]); + _mpexPlayerData.erase(userId); + } + } + } + catch (const std::runtime_error& e) { + getLogger().error("REPORT TO ENDER: %s", e.what()); + } +} + + +void HandleDisconnect(DisconnectedReason reason, GlobalNamespace::IConnectedPlayer* localPlayer) { + getLogger().info("Disconnected from server reason: '%s'", MultiplayerCore::EnumUtils::GetEnumName(reason).c_str()); + getLogger().info("Clearing MPEX player data"); + // TODO: Clear each instance manually + for (const auto & [ key, value ] : _mpexPlayerData) { + gc_free_specific(value); + } + _mpexPlayerData.clear(); + getLogger().info("MQE Removing connected/disconnected/disconnect events"); + MultiplayerCore::Players::MpPlayerManager::playerConnectedEvent -= _PlayerConnectedHandler; + MultiplayerCore::Players::MpPlayerManager::playerDisconnectedEvent -= _PlayerDisconnectedHandler; + MultiplayerCore::Players::MpPlayerManager::disconnectedEvent -= _DisconnectedHandler; + + MultiplayerCore::Networking::MpNetworkingEvents::RegisterPackets -= _RegisterMpexPacketsHandler; + MultiplayerCore::Networking::MpNetworkingEvents::UnRegisterPackets -= _UnRegisterMpexPacketsHandler; + + // Clear our manually created localMpexPlayerData instance + if(localMpexPlayerData){ + gc_free_specific(localMpexPlayerData); + localMpexPlayerData = nullptr; + } +} + +void HandleConnecting(GlobalNamespace::IConnectedPlayer* localPlayer){ + getLogger().debug("HandleConnecting, creating localMpexPlayerData and registering events"); + + if(!localMpexPlayerData){ + localMpexPlayerData = Players::MpexPlayerData::New_ctor<::il2cpp_utils::CreationType::Manual>(); + localMpexPlayerData->Color = config.getPlayerColor(); + } + MultiplayerCore::Networking::MpNetworkingEvents::RegisterPackets += _RegisterMpexPacketsHandler; + MultiplayerCore::Networking::MpNetworkingEvents::UnRegisterPackets += _UnRegisterMpexPacketsHandler; + + MultiplayerCore::Players::MpPlayerManager::playerConnectedEvent += _PlayerConnectedHandler; + MultiplayerCore::Players::MpPlayerManager::playerDisconnectedEvent += _PlayerDisconnectedHandler; + MultiplayerCore::Players::MpPlayerManager::disconnectedEvent += _DisconnectedHandler; + +} + +void MultiQuestensions::Hooks::SessionManagerAndExtendedPlayerHooks() { + + MultiplayerCore::Players::MpPlayerManager::connectingEvent += _ConnectingHandler; +} \ No newline at end of file diff --git a/MultiQuestensions/src/Packets/PacketManager.cpp b/MultiQuestensions/src/Packets/PacketManager.cpp deleted file mode 100644 index a14aca8..0000000 --- a/MultiQuestensions/src/Packets/PacketManager.cpp +++ /dev/null @@ -1,43 +0,0 @@ -#include "Packets/PacketManager.hpp" -#include "Packets/PacketSerializer.hpp" -#include "beatsaber-hook/shared/utils/il2cpp-utils.hpp" -#include "UnityEngine/GameObject.hpp" - -#include "GlobalNamespace/StandardScoreSyncStateNetSerializable.hpp" - -namespace MultiQuestensions { - PacketManager::PacketManager(GlobalNamespace::MultiplayerSessionManager* sessionManager) { - //if (packetSerializer == nullptr) { - getLogger().info("Creating Packet serializer."); - - _sessionManager = sessionManager; - packetSerializer = CRASH_UNLESS(il2cpp_utils::New()); - GlobalNamespace::MultiplayerSessionManager_MessageType messageType = (GlobalNamespace::MultiplayerSessionManager_MessageType)100; - - _sessionManager->RegisterSerializer(messageType, reinterpret_cast*>(packetSerializer)); - //} else { - // getLogger().info("Packet serializer already exists."); - //} - } - - void PacketManager::Send(LiteNetLib::Utils::INetSerializable* message) { - getLogger().debug("Running send"); - if (_sessionManager != nullptr && message != nullptr) { - _sessionManager->Send(message); - getLogger().debug("Sent message"); - } - else getLogger().error("Failed to Send message"); - } - - void PacketManager::SendUnreliable(LiteNetLib::Utils::INetSerializable* message) { - if (_sessionManager != nullptr && message != nullptr) { - _sessionManager->SendUnreliable(message); - getLogger().debug("Sent message"); - } - else getLogger().error("Failed to SendUnreliable message"); - } - - void PacketManager::UnregisterCallback(std::string identifier) { - packetSerializer->UnregisterCallback(identifier); - } -} \ No newline at end of file diff --git a/MultiQuestensions/src/Packets/PacketSerializer.cpp b/MultiQuestensions/src/Packets/PacketSerializer.cpp deleted file mode 100644 index 88847f0..0000000 --- a/MultiQuestensions/src/Packets/PacketSerializer.cpp +++ /dev/null @@ -1,79 +0,0 @@ -#include "Packets/PacketSerializer.hpp" -#include "GlobalNamespace/BeatmapIdentifierNetSerializable.hpp" - -DEFINE_TYPE(MultiQuestensions, PacketSerializer); - -namespace MultiQuestensions { - void PacketSerializer::Construct() { - registeredTypes = std::move(TypeDictionary()); - packetHandlers = std::move(CallbackDictionary()); - } - - void PacketSerializer::Deconstruct() { - getLogger().debug("PacketSerializer::Deconstruct"); - for (auto it = packetHandlers.begin(); it != packetHandlers.end(); it++) { - delete it->second; - } - packetHandlers.clear(); - } - - void PacketSerializer::Serialize(LiteNetLib::Utils::NetDataWriter* writer, LiteNetLib::Utils::INetSerializable* packet) { - Il2CppReflectionType* packetType = il2cpp_utils::GetSystemType(il2cpp_functions::object_get_class(reinterpret_cast(packet))); - getLogger().debug("Serialize packetType is: %s", to_utf8(csstrtostr(packetType->ToString())).c_str()); - getLogger().debug("Registered types check: %s", registeredTypes[packetType].c_str()); - writer->Put(il2cpp_utils::newcsstr(registeredTypes[packetType])); - //writer->Put(packetType->ToString()); - getLogger().debug("Writer Put"); - getLogger().debug("Nullptr checking: packet: %p, writer: %p", packet, writer); - packet->Serialize(writer); - getLogger().debug("Serialize Finished"); - } - - void PacketSerializer::Deserialize(LiteNetLib::Utils::NetDataReader* reader, int length, GlobalNamespace::IConnectedPlayer* data) { - getLogger().debug("PacketSerializer::Deserialize"); - int prevPosition = reader->get_Position(); - std::string packetType = to_utf8(csstrtostr(reader->GetString())); - getLogger().debug("packetType: %s", packetType.c_str()); - length -= reader->get_Position() - prevPosition; - getLogger().debug("length: %d", length); - prevPosition = reader->get_Position(); - if (packetHandlers.find(packetType) != packetHandlers.end()) { - getLogger().debug("packetHandlers found PacketType, try Invoke"); - try { - packetHandlers[packetType]->Invoke(reader, length, data); - } - catch (const std::exception& e) { - getLogger().warning("An C++ exception was thrown while processing custom packet"); - getLogger().error("REPORT TO ENDER: %s", e.what()); - } - catch (...) { - getLogger().warning("REPORT TO ENDER: An Unknown exception was thrown while processing custom packet"); - } - } - int processedBytes = reader->get_Position() - prevPosition; - reader->SkipBytes(length - processedBytes); - } - - bool PacketSerializer::HandlesType(Il2CppReflectionType* type) { - auto it = registeredTypes.find(type); - if (it != registeredTypes.end()) { - getLogger().debug("HandlesType: %s", it->second.c_str()); - return true; - } - return false; - } - - void PacketSerializer::UnregisterCallback(std::string identifier) { - getLogger().debug("UnregisterCallback called"); - - for (auto it = registeredTypes.begin(); it != registeredTypes.end(); it++) { - if (it->second == identifier) registeredTypes.erase(it); - } - - auto itr = packetHandlers.find(identifier); - if (itr != packetHandlers.end()) { - delete itr->second; - packetHandlers.erase(itr); - } - } -} \ No newline at end of file diff --git a/MultiQuestensions/src/Players/MpexPlayerData.cpp b/MultiQuestensions/src/Players/MpexPlayerData.cpp new file mode 100644 index 0000000..a60762a --- /dev/null +++ b/MultiQuestensions/src/Players/MpexPlayerData.cpp @@ -0,0 +1,23 @@ +#include "CodegenExtensions/ColorUtility.hpp" +#include "Players/MpexPlayerData.hpp" +#include "Config.hpp" +using namespace UnityEngine; +using namespace LiteNetLib::Utils; + +DEFINE_TYPE(MultiQuestensions::Players, MpexPlayerData) + +namespace MultiQuestensions::Players +{ + void MpexPlayerData::Serialize(NetDataWriter* writer) + { + writer->Put(ColorUtility::ToHtmlStringRGB(Color)); + } + + void MpexPlayerData::Deserialize(NetDataReader* reader) + { + UnityEngine::Color color; + if (!ColorUtility::TryParseHtmlString(reader->GetString(), color)) + color = Config::DefaultPlayerColor; + Color = color; + } +} diff --git a/MultiQuestensions/src/UI/CenterScreenLoading.cpp b/MultiQuestensions/src/UI/CenterScreenLoading.cpp deleted file mode 100644 index 21898e9..0000000 --- a/MultiQuestensions/src/UI/CenterScreenLoading.cpp +++ /dev/null @@ -1,98 +0,0 @@ -#include "main.hpp" -#include "GlobalFields.hpp" -#include "UI/CenterScreenLoading.hpp" -#include "UnityEngine/GameObject.hpp" -#include "UnityEngine/Resources.hpp" -#include "UnityEngine/Transform.hpp" -#include "UnityEngine/RectOffset.hpp" -#include "UnityEngine/UI/LayoutElement.hpp" -using namespace GlobalNamespace; -using namespace UnityEngine; -using namespace UnityEngine::UI; - -#include "questui/shared/BeatSaberUI.hpp" - -DEFINE_TYPE(MultiQuestensions::UI, CenterScreenLoading); - -namespace MultiQuestensions::UI{ - - CenterScreenLoading* CenterScreenLoading::instance; - int CenterScreenLoading::playersReady; - - void CenterScreenLoading::Awake() { - getLogger().debug("CenterScreenLoading::Awake"); - instance = this; - screenController = get_gameObject()->GetComponentInParent(); - gameStateController = lobbyGameStateController; - - UnityEngine::UI::VerticalLayoutGroup* vertical = QuestUI::BeatSaberUI::CreateVerticalLayoutGroup(get_transform()); - vertical->get_rectTransform()->set_sizeDelta({ 60, 60 }); - vertical->get_rectTransform()->set_anchoredPosition({ 0.0f, -30.0f }); - auto layout = vertical->get_gameObject()->AddComponent(); - //layout->set_preferredHeight(GameListSizeDelta.y); - layout->set_minWidth(60); - //vertical->set_padding(UnityEngine::RectOffset::New_ctor(0, 0, 30, 10)); - - GameObject* existingLoadingControl = Resources::FindObjectsOfTypeAll()->values[0]->get_gameObject(); - GameObject* loadingControlGameObject = UnityEngine::GameObject::Instantiate(existingLoadingControl, vertical->get_transform()); - loadingControl = loadingControlGameObject->GetComponent(); - loadingControl->Hide(); - } - - void CenterScreenLoading::OnDisable() { - getLogger().debug("CenterScreenLoading::OnDisable"); - isDownloading = false; - if (loadingControl) { - loadingControl->Hide(); - } - playersReady = 0; - } - - void CenterScreenLoading::ShowLoading() { - int maxPlayers = sessionManager ? sessionManager->get_connectedPlayerCount() + 1 : 1; - int readyPlayers = std::min(playersReady + 1, maxPlayers); - getLogger().info("CenterScreenLoading::ShowLoading, players ready (%d of %d)", - readyPlayers, maxPlayers); - if (loadingControl) { - loadingControl->ShowLoading(il2cpp_utils::newcsstr(string_format("%d of %d players ready...", - readyPlayers, maxPlayers))); - } - } - - void CenterScreenLoading::ShowDownloadingProgress(float downloadingProgress) { - //getLogger().debug("CenterScreenLoading::ShowDownloadingProgress: %f2", downloadingProgress); - isDownloading = (downloadingProgress < 100.0f); - if (loadingControl) { - //static Il2CppString* string = il2cpp_utils::newcsstr("Downloading..."); - loadingControl->ShowDownloadingProgress(il2cpp_utils::newcsstr(string_format("Downloading (%.2f%%)...", downloadingProgress)), downloadingProgress / 100); - } - } - - void CenterScreenLoading::HideLoading() { - getLogger().debug("CenterScreenLoading::HideLoading"); - if (get_Instance() && get_Instance()->loadingControl) { - get_Instance()->loadingControl->Hide(); - } - } - - - void CenterScreenLoading::FixedUpdate() { - if (isDownloading) - { - return; - } - else if (screenController->get_countdownShown() && gameStateController && sessionManager->get_syncTime() >= gameStateController->get_startTime() && gameStateController->get_levelStartInitiated()) - { - if (loadingControl) - loadingControl->ShowLoading(il2cpp_utils::newcsstr(string_format("%d of %d players ready...", - playersReady + 1, sessionManager ? sessionManager->get_connectedPlayerCount() + 1 : 1))); - } - else - { - if (loadingControl) - loadingControl->Hide(); - playersReady = 0; - } - } - -} \ No newline at end of file diff --git a/MultiQuestensions/src/UI/DownloadedSongsGSM.cpp b/MultiQuestensions/src/UI/DownloadedSongsGSM.cpp deleted file mode 100644 index 5a6ebc4..0000000 --- a/MultiQuestensions/src/UI/DownloadedSongsGSM.cpp +++ /dev/null @@ -1,159 +0,0 @@ -/* -* DownloadedSongsGameplaySetupMenu -* Copyright (c) EnderdracheLP -*/ - -#include "main.hpp" -#include "UI/DownloadedSongsGSM.hpp" - -#include "questui/shared/BeatSaberUI.hpp" -#include "songloader/shared/API.hpp" - -#include "HMUI/ModalView.hpp" -#include "System/Threading/Tasks/Task.hpp" -#include "System/Action_1.hpp" - -#include "GlobalFields.hpp" -using namespace GlobalNamespace; -using namespace QuestUI; -using namespace RuntimeSongLoader::API; -using namespace HMUI; -using namespace MultiQuestensions; - - -DEFINE_TYPE(MultiQuestensions::UI, DownloadedSongsGSM); - -namespace MultiQuestensions::UI { - bool cellIsSelected = false; - DownloadedSongsGSM* DownloadedSongsGSM::instance; - std::vector DownloadedSongsGSM::mapQueue; - - void DownloadedSongsGSM::CreateCell(System::Threading::Tasks::Task_1* coverTask, CustomPreviewBeatmapLevel* level) { - getLogger().debug("CreateCell"); - UnityEngine::Sprite* cover = coverTask->get_Result(); - if (cover && level) { - // "" + map.GetMetadata().GetSongAuthorName() + "" + " [" + map.GetMetadata().GetLevelAuthorName() + "]" - list->data.emplace_back(CustomListTableData::CustomCellInfo{ - level->get_songName() ? to_utf8(csstrtostr(level->get_songName())) : "Error: songName null", - (level->get_songAuthorName() ? to_utf8(csstrtostr(level->get_songAuthorName())) : std::string()) + " [" + (level->get_levelAuthorName() ? to_utf8(csstrtostr(level->get_levelAuthorName())) : std::string()) + "]", - cover - }); - } - else if (level) { - list->data.emplace_back(CustomListTableData::CustomCellInfo{ - level->get_songName() ? to_utf8(csstrtostr(level->get_songName())) : "Error: songName null", - (level->get_songAuthorName() ? to_utf8(csstrtostr(level->get_songAuthorName())) : std::string()) + " [" + (level->get_levelAuthorName() ? to_utf8(csstrtostr(level->get_levelAuthorName())) : std::string()) + "]", - level->get_defaultCoverImage() - }); - } else getLogger().error("Nullptr in UI: cover '%p', level '%p'", cover, level); - if (!mapQueue.empty()) { - InsertCell(mapQueue.back()); - mapQueue.pop_back(); - } - if (list && list->tableView) - list->tableView->RefreshCellsContent(); - else getLogger().error("Nullptr in UI: list '%p', list->tableView '%p'", list, list->tableView); - getLogger().debug("CreateCell Finished"); - } - - // TODO: Add index check, check if index is out of bounds - void DownloadedSongsGSM::Delete() { - try { - needSongRefresh = false; - auto level = GetLevelByHash(DownloadedSongIds.at(selectedIdx)); - if (level.has_value()) { - std::string songPath = to_utf8(csstrtostr(level.value()->customLevelPath)); - getLogger().info("Deleting Song: %s", songPath.c_str()); - DeleteSong(songPath, [&] { - if (needSongRefresh) { - RefreshSongs(false); - } - }); - if (lobbyGameStateController) lobbyGameStateController->menuRpcManager->SetIsEntitledToLevel(level.value()->get_levelID(), EntitlementsStatus::NotOwned); - } - needSongRefresh = true; - DownloadedSongIds.erase(DownloadedSongIds.begin() + selectedIdx); - } - catch (const std::exception& e) { - getLogger().critical("REPORT TO ENDER: Exception encountered trying to delete song: %s", e.what()); - } - list->tableView->ClearSelection(); - list->data.erase(list->data.begin() + selectedIdx); - Refresh(); - modal->Hide(true, nullptr); - } - - // TODO: Add keep all and delete all option - void DownloadedSongsGSM::DidActivate(bool firstActivation) { - if (firstActivation) { - instance = this; - - modal = BeatSaberUI::CreateModal(get_transform(), { 55, 25 }, [this](HMUI::ModalView* self) { - list->tableView->ClearSelection(); - }); - auto wrapper = QuestUI::BeatSaberUI::CreateHorizontalLayoutGroup(modal->get_transform()); - auto container = QuestUI::BeatSaberUI::CreateVerticalLayoutGroup(wrapper->get_transform()); - container->set_childAlignment(UnityEngine::TextAnchor::MiddleCenter); - QuestUI::BeatSaberUI::CreateText(container->get_transform(), "Do you want to delete this song?")->set_alignment(TMPro::TextAlignmentOptions::Center); - - auto horizon = QuestUI::BeatSaberUI::CreateHorizontalLayoutGroup(container->get_transform()); - - QuestUI::BeatSaberUI::CreateUIButton(horizon->get_transform(), "Delete", [this]() -> void { - Delete(); - cellIsSelected = false; - }); - - QuestUI::BeatSaberUI::CreateUIButton(horizon->get_transform(), "Keep", [this]() -> void { - DownloadedSongIds.erase(DownloadedSongIds.begin() + selectedIdx); - list->tableView->ClearSelection(); - list->data.erase(list->data.begin() + selectedIdx); - Refresh(); - modal->Hide(true, nullptr); - cellIsSelected = false; - }); - - list = BeatSaberUI::CreateScrollableList(get_transform(), { 80, 60 }, [this](int idx) { - getLogger().debug("Cell with idx %d clicked", idx); - cellIsSelected = true; - selectedIdx = idx; - modal->Show(true, true, nullptr); - }); - } - getLogger().debug("DownloadedSongsGSM::DidActivate"); - Refresh(); - } - - void DownloadedSongsGSM::InsertCell(std::string hash) { - std::optional levelOpt = GetLevelByHash(hash); - if (levelOpt.has_value()) { - lastDownloaded = levelOpt.value(); - getLogger().info("Song with Hash '%s' added to list", hash.c_str()); - System::Threading::Tasks::Task_1* coverTask = lastDownloaded->GetCoverImageAsync(System::Threading::CancellationToken::get_None()); - auto action = il2cpp_utils::MakeDelegate*>(classof(System::Action_1*), (std::function)[coverTask, this] { - CreateCell(coverTask, lastDownloaded/*, list->NumberOfCells() + 1*/); - } - ); - reinterpret_cast(coverTask)->ContinueWith(action); - } - else { - getLogger().error("Song with Hash '%s' not found, was it already deleted?", hash.c_str()); - } - } - - void DownloadedSongsGSM::Refresh() { - list->tableView->ReloadData(); - list->tableView->RefreshCellsContent(); - } - - void DownloadedSongsGSM::OnEnable() { - if (list && list->tableView) { - if (!mapQueue.empty()) { - InsertCell(mapQueue.back()); - mapQueue.pop_back(); - } - if (cellIsSelected) list->tableView->ClearSelection(); - list->tableView->ReloadData(); - list->tableView->RefreshCellsContent(); - } - } -} \ No newline at end of file diff --git a/MultiQuestensions/src/UI/LobbySetupPanel.cpp b/MultiQuestensions/src/UI/LobbySetupPanel.cpp index 0382747..67c71cb 100644 --- a/MultiQuestensions/src/UI/LobbySetupPanel.cpp +++ b/MultiQuestensions/src/UI/LobbySetupPanel.cpp @@ -1,5 +1,6 @@ #include "main.hpp" #include "UI/LobbySetupPanel.hpp" +#include "Config.hpp" #include "questui/shared/BeatSaberUI.hpp" #include "UnityEngine/UI/ContentSizeFitter.hpp" #include "UnityEngine/UI/LayoutElement.hpp" @@ -9,22 +10,22 @@ #include "GlobalFields.hpp" #include "Hooks/SessionManagerAndExtendedPlayerHooks.hpp" #include "Hooks/EnvironmentAndAvatarHooks.hpp" -#include "songloader/shared/API.hpp" -#include "UI/DownloadedSongsGSM.hpp" -#include "Utils/SemVerChecker.hpp" + +#include "multiplayer-core/shared/Networking/MpPacketSerializer.hpp" using namespace UnityEngine::UI; +using namespace MultiQuestensions; namespace MultiQuestensions::UI { UnityEngine::UI::Toggle* LobbySetupPanel::lagReducerToggle; bool LobbySetupPanel::needRefresh; - void SetLagReducer(bool value) { - getConfig().config["LagReducer"].SetBool(value); - getConfig().Write(); + void SetNoMetaZone(bool value) { + config.setNoMetaZone(value); + UpdateNameTagIcons(); } - void LobbySetupPanel::AddSetupPanel(UnityEngine::RectTransform* parent, GlobalNamespace::MultiplayerSessionManager* sessionManager) { + void LobbySetupPanel::AddSetupPanel(UnityEngine::RectTransform* parent, GlobalNamespace::MultiplayerSessionManager* sessionManager, GlobalNamespace::LobbySetupViewController* lobbySetupViewController) { auto vertical = QuestUI::BeatSaberUI::CreateVerticalLayoutGroup(parent); auto horizontal = QuestUI::BeatSaberUI::CreateHorizontalLayoutGroup(vertical->get_transform()); @@ -47,51 +48,64 @@ namespace MultiQuestensions::UI { vertical4->get_gameObject()->AddComponent() ->set_minWidth(45); - using namespace MultiQuestensions::Utils; - if (IsInstalled(ChromaID) && !MatchesVersion(ChromaID, ChromaVersionRange)) { - //HMUI::ModalView* modal = QuestUI::BeatSaberUI::CreateModal(parent, { 55, 25 }, std::nullptr_t()); - //auto wrapper = QuestUI::BeatSaberUI::CreateHorizontalLayoutGroup(modal->get_transform()); - //auto container = QuestUI::BeatSaberUI::CreateVerticalLayoutGroup(wrapper->get_transform()); - //container->set_childAlignment(UnityEngine::TextAnchor::MiddleCenter); - //QuestUI::BeatSaberUI::CreateText(modal->get_transform(), "Chroma detected!\r\nChroma may cause issues such as crashes,\r\nif you're experiencing issues like these,\r\nthen it may be best to try disabling Chroma")->set_alignment(TMPro::TextAlignmentOptions::Center); - //modal->Show(true, true, nullptr); - QuestUI::BeatSaberUI::CreateText(vertical2->get_transform(), - "Chroma outdated!\r\nPlease update to the latest version of Chroma.", - { -40, 0 })->set_alignment(TMPro::TextAlignmentOptions::Left); - getLogger().warning("Chroma outdated"); - } - else getLogger().debug("Chroma not installed or version compatible"); + // using namespace MultiQuestensions::Utils; + // if (IsInstalled(ChromaID) && !MatchesVersion(ChromaID, ChromaVersionRange)) { + // //HMUI::ModalView* modal = QuestUI::BeatSaberUI::CreateModal(parent, { 55, 25 }, std::nullptr_t()); + // //auto wrapper = QuestUI::BeatSaberUI::CreateHorizontalLayoutGroup(modal->get_transform()); + // //auto container = QuestUI::BeatSaberUI::CreateVerticalLayoutGroup(wrapper->get_transform()); + // //container->set_childAlignment(UnityEngine::TextAnchor::MiddleCenter); + // //QuestUI::BeatSaberUI::CreateText(modal->get_transform(), "Chroma detected!\r\nChroma may cause issues such as crashes,\r\nif you're experiencing issues like these,\r\nthen it may be best to try disabling Chroma")->set_alignment(TMPro::TextAlignmentOptions::Center); + // //modal->Show(true, true, nullptr); + // QuestUI::BeatSaberUI::CreateText(vertical2->get_transform(), + // "Chroma outdated!\r\nPlease update to the latest version of Chroma.", + // { -40, 0 })->set_alignment(TMPro::TextAlignmentOptions::Left); + // getLogger().warning("Chroma outdated"); + // } + // else getLogger().debug("Chroma not installed or version incompatible"); // //QuestUI::BeatSaberUI::CreateText(vertical4->get_transform(), "THESE TOGGLES ARE JUST\r\nPLACEHOLDERS!"); - lagReducerToggle = QuestUI::BeatSaberUI::CreateToggle(vertical4->get_transform(), "Lag Reducer", getConfig().config["LagReducer"].GetBool(), SetLagReducer); - QuestUI::BeatSaberUI::AddHoverHint(lagReducerToggle->get_gameObject(), "Makes multiplayer easier for the quest to handle."); + lagReducerToggle = QuestUI::BeatSaberUI::CreateToggle(vertical4->get_transform(), "Lag Reducer", config.getLagReducer(), [](bool value) { config.setLagReducer(value); }); + QuestUI::BeatSaberUI::AddHoverHint(lagReducerToggle->get_gameObject(), "Makes Multiplayer easier for the Quest to handle."); + // lagReducerToggle->set_interactable(false); + // QuestUI::BeatSaberUI::AddHoverHint(lagReducerToggle->get_gameObject(), "Currently Broken."); + + auto noMetaToggle = QuestUI::BeatSaberUI::CreateToggle(vertical4->get_transform(), "No Meta Icons", config.getNoMetaZone(), SetNoMetaZone); + QuestUI::BeatSaberUI::AddHoverHint(noMetaToggle->get_gameObject(), "Shows the Oculus Icon for Quest Players instead."); + - UnityEngine::Color playerColor; - UnityEngine::ColorUtility::TryParseHtmlString(getConfig().config["color"].GetString(), playerColor); + UnityEngine::Color playerColor = config.getPlayerColor(); + // UnityEngine::ColorUtility::TryParseHtmlString(getConfig().config["color"].GetString(), playerColor); //QuestUI::BeatSaberUI::CreateColorPickerModal(parent->get_transform(), "Player Color Selection", playerColor); auto colorPicker = QuestUI::BeatSaberUI::CreateColorPickerModal(parent, "Player Color Selection", playerColor, + //Done [&playerColor, sessionManager](UnityEngine::Color value) { playerColor = value; - getConfig().config["color"].SetString(UnityEngine::ColorUtility::ToHtmlStringRGB_CPP(value), getConfig().config.GetAllocator()); - getConfig().Write(); - localExtendedPlayer->playerColor = value; + config.setPlayerColor(value); + // config.PlayerColor = value; + // getConfig().config["PlayerColor"].SetString(UnityEngine::ColorUtility::ToHtmlStringRGB_CPP(value), getConfig().config.GetAllocator()); + // getConfig().Write(); + // localExtendedPlayer->playerColor = value; + localMpexPlayerData->Color = value; SetPlayerPlaceColor(sessionManager->get_localPlayer(), value, true); - Extensions::ExtendedPlayerPacket* localPlayerPacket = Extensions::ExtendedPlayerPacket::Init(localExtendedPlayer->get_platformID(), localExtendedPlayer->get_platform(), localExtendedPlayer->get_playerColor()); - getLogger().debug("LocalPlayer Color is, R: %f G: %f B: %f", localPlayerPacket->playerColor.r, localPlayerPacket->playerColor.g, localPlayerPacket->playerColor.b); - packetManager->Send(reinterpret_cast(localPlayerPacket)); + // Extensions::ExtendedPlayerPacket* localPlayerPacket = Extensions::ExtendedPlayerPacket::Init(localExtendedPlayer->get_platformID(), localExtendedPlayer->get_platform(), localExtendedPlayer->get_playerColor()); + getLogger().debug("LocalPlayer Color is, R: %f G: %f B: %f", localMpexPlayerData->Color.r, localMpexPlayerData->Color.g, localMpexPlayerData->Color.b); + // packetManager->Send(reinterpret_cast(localPlayerPacket)); + MultiplayerCore::mpPacketSerializer->Send(localMpexPlayerData); }, + //Cancel [sessionManager] { - SetPlayerPlaceColor(sessionManager->get_localPlayer(), localExtendedPlayer->get_playerColor(), true); + SetPlayerPlaceColor(sessionManager->get_localPlayer(), localMpexPlayerData->Color, true); //Extensions::ExtendedPlayerPacket* localPlayerPacket = Extensions::ExtendedPlayerPacket::Init(localExtendedPlayer->get_platformID(), localExtendedPlayer->get_platform(), localExtendedPlayer->get_playerColor()); //getLogger().debug("LocalPlayer Color is, R: %f G: %f B: %f", localPlayerPacket->playerColor.r, localPlayerPacket->playerColor.g, localPlayerPacket->playerColor.b); //packetManager->Send(reinterpret_cast(localPlayerPacket)); }, + //OnChange [sessionManager](UnityEngine::Color value) { SetPlayerPlaceColor(sessionManager->get_localPlayer(), value, true); // TODO: Uncomment when MpEx supports live platform color updates or maybe not because that might be too much packets sent @@ -107,11 +121,11 @@ namespace MultiQuestensions::UI { } ); - auto autoDelete = QuestUI::BeatSaberUI::CreateToggle(vertical4->get_transform(), "Auto-Delete Songs", getConfig().config["autoDelete"].GetBool(), [](bool value) { - getConfig().config["autoDelete"].SetBool(value); - getConfig().Write(); - }); - QuestUI::BeatSaberUI::AddHoverHint(autoDelete->get_gameObject(), "Automatically deletes downloaded songs after playing them."); + // auto autoDelete = QuestUI::BeatSaberUI::CreateToggle(vertical4->get_transform(), "Auto-Delete Songs", getConfig().config["autoDelete"].GetBool(), [](bool value) { + // getConfig().config["autoDelete"].SetBool(value); + // getConfig().Write(); + // }); + // QuestUI::BeatSaberUI::AddHoverHint(autoDelete->get_gameObject(), "Automatically deletes downloaded songs after playing them."); //auto deleteDownloadedSongs = QuestUI::BeatSaberUI::CreateUIButton(vertical2->get_transform(), "Delete Downloaded", [] { // using namespace RuntimeSongLoader::API; @@ -142,6 +156,8 @@ namespace MultiQuestensions::UI { ); QuestUI::BeatSaberUI::AddHoverHint(colorPickerButton->get_gameObject(), "Lets you pick your own personal platform and Name Tag color for everyone to see."); + lobbySetupViewController->spectatorWarningTextWrapper->get_transform()->SetParent(parent->get_transform()); + lobbySetupViewController->spectatorWarningTextWrapper->get_transform()->set_localPosition({ 0, -20, 0 }); //customSongsToggle = QuestUI::BeatSaberUI::CreateToggle(vertical2->get_transform(), "Custom Songs", getConfig().config["customsongs"].GetBool(), SetCustomSongs); //QuestUI::BeatSaberUI::AddHoverHint(customSongsToggle->get_gameObject(), "Toggles custom songs for all players"); } diff --git a/MultiQuestensions/src/Utils/CustomData.cpp b/MultiQuestensions/src/Utils/CustomData.cpp deleted file mode 100644 index 7249116..0000000 --- a/MultiQuestensions/src/Utils/CustomData.cpp +++ /dev/null @@ -1,73 +0,0 @@ -#include "main.hpp" -#include "Utils/CustomData.hpp" -#include "Utils/SemVerChecker.hpp" - -namespace MultiQuestensions::Utils { - bool HasRequirement(const std::optional& beatmapOpt) { - using namespace MultiQuestensions::Utils; - if (beatmapOpt) - { - std::string infodatPath = to_utf8(csstrtostr(beatmapOpt.value()->get_customLevelPath())); - if (fileexists(infodatPath + "/Info.dat")) { - infodatPath += "/Info.dat"; - } - else if (fileexists(infodatPath + "/info.dat")) { - infodatPath += "/info.dat"; - } - else { - getLogger().error("Could not find Info.dat or info.dat"); - return false; - } - rapidjson::Document document; - document.Parse(readfile(infodatPath)); - if (!document.HasParseError() && document.IsObject() && - document.HasMember("_difficultyBeatmapSets") && document["_difficultyBeatmapSets"].IsArray() && !document["_difficultyBeatmapSets"].Empty()) { - for (auto& dBS : document["_difficultyBeatmapSets"].GetArray()) - if (dBS.HasMember("_difficultyBeatmaps") && dBS["_difficultyBeatmaps"].IsArray() && !dBS["_difficultyBeatmaps"].Empty()) { - for (auto& dB : dBS["_difficultyBeatmaps"].GetArray()) { - if (dB.HasMember("_customData") && dB["_customData"].IsObject()) { - if (dB["_customData"].HasMember("_requirements") && dB["_customData"]["_requirements"].IsArray() && - !dB["_customData"]["_requirements"].Empty()) { - for (auto& req : dB["_customData"]["_requirements"].GetArray()) { - getLogger().debug("Found requirement: %s", req.GetString()); - if (strcmp(req.GetString(), "Chroma") == 0 && (!AllPlayersHaveChroma() || !MatchesVersion(ChromaID, ChromaVersionRange))) { - getLogger().warning("Requires Chroma"); - missingLevelText = "Chroma suggestion block, please install or update Chroma"; - return false; - } - else if (strcmp(req.GetString(), "Noodle Extensions") == 0 && (!AllPlayersHaveNE() || !MatchesVersion("NoodleExtensions", "*"))) { - getLogger().warning("Requires Noodle Extensions but it was not installed"); - missingLevelText = "You or another Player are Missing the following Requirement: Noodle Extensions"; - return false; - } - else if (strcmp(req.GetString(), "Mapping Extensions") == 0 && (!AllPlayersHaveME() || !MatchesVersion("MappingExtensions", "*"))) { - getLogger().warning("Requires Mapping Extensions but it was not installed"); - missingLevelText = "You or another Player are Missing the following Requirement: Mapping Extensions"; - return false; - } - } - } - if (dB["_customData"].HasMember("_suggestions") && dB["_customData"]["_suggestions"].IsArray() && - !dB["_customData"]["_suggestions"].Empty()) { - for (auto& sug : dB["_customData"]["_suggestions"].GetArray()) { - getLogger().debug("Found suggestion: %s", sug.GetString()); - if (strcmp(sug.GetString(), "Chroma") == 0 && (!AllPlayersHaveChroma() || !MatchesVersion(ChromaID, ChromaVersionRange))) { - getLogger().warning("Suggestion Chroma and Chroma outdated or missing"); - missingLevelText = "Chroma suggestion block, please install or update Chroma"; - return false; - } - getLogger().debug("Checking bools 'All players have chroma': %s, 'MatchesVersion': %s 'Combined check': %s", AllPlayersHaveChroma() ? "true" : "false", MatchesVersion(ChromaID, ChromaVersionRange) ? "true" : "false", (!AllPlayersHaveChroma() || !MatchesVersion(ChromaID, ChromaVersionRange)) ? "true" : "false"); - } - } - } - else getLogger().warning("No _customData or _requirements/_suggestions empty/not an array"); - } - } - else getLogger().error("_difficultyBeatmaps not found, empty or not an array"); - } - else getLogger().error("Failed to parse beatmap or _difficultyBeatmapSets empty/not an array"); - } - missingLevelText.clear(); - return true; - } -} diff --git a/MultiQuestensions/src/main.cpp b/MultiQuestensions/src/main.cpp index 98fb8ce..8541e9a 100644 --- a/MultiQuestensions/src/main.cpp +++ b/MultiQuestensions/src/main.cpp @@ -1,22 +1,20 @@ #include "main.hpp" #include "Hooks/Hooks.hpp" #include "Hooks/SessionManagerAndExtendedPlayerHooks.hpp" -#include "Beatmaps/PreviewBeatmapPacket.hpp" -#include "Beatmaps/PreviewBeatmapStub.hpp" #include "UI/LobbySetupPanel.hpp" -#include "UI/DownloadedSongsGSM.hpp" -#include "UI/CenterScreenLoading.hpp" -#include "CS_DataStore.hpp" +#include "Config.hpp" + +#include "CodegenExtensions/ColorUtility.hpp" #include "GlobalNamespace/ConnectedPlayerManager.hpp" #include "GlobalNamespace/MultiplayerLevelSelectionFlowCoordinator.hpp" -#include "GlobalNamespace/CenterStageScreenController.hpp" #include "beatsaber-hook/shared/utils/il2cpp-utils.hpp" #include "custom-types/shared/register.hpp" #include "questui/shared/QuestUI.hpp" #include "questui/shared/BeatSaberUI.hpp" +#include "multiplayer-core/shared/GlobalFields.hpp" //#include "GlobalNamespace/LobbySetupViewController.hpp" @@ -24,6 +22,7 @@ using namespace GlobalNamespace; using namespace System::Threading::Tasks; using namespace MultiQuestensions; +using UnityEngine::ColorUtility; #ifndef VERSION #warning No Version set @@ -36,11 +35,20 @@ using namespace MultiQuestensions; ModInfo modInfo; // Stores the ID and version of our mod, and is sent to the modloader upon startup -// Loads the config from disk using our modInfo, then returns it for use -Configuration& getConfig() { - static Configuration config(modInfo); - config.Load(); - return config; +namespace MultiQuestensions { + // Loads the config from disk using our modInfo, then returns it for use + Configuration& getConfig() { + static Configuration config(modInfo); + config.Load(); + return config; + } + + // Loads the old config from disk using our modInfo, then returns it for use + Configuration& getLegacyConfig() { + static Configuration config({"multiquestensions", modInfo.version}); + config.Load(); + return config; + } } // Returns a logger, useful for printing debug messages @@ -49,114 +57,23 @@ Logger& getLogger() { return *myLogger; } - -// Converts a levelId to a levelHash -Il2CppString* LevelIdToHash(Il2CppString* levelId) { - if (Il2CppString::IsNullOrWhiteSpace(levelId)) { - return nullptr; - } - Array* ary = levelId->Split('_', ' '); - Il2CppString* hash = nullptr; - if (ary->Length() > 2) { - hash = ary->values[2]; - } - return (hash != nullptr && hash->get_Length() == 40) ? hash : nullptr; -} - - +Config MultiQuestensions::config; namespace MultiQuestensions { // Plugin setup stuff - GlobalNamespace::MultiplayerSessionManager* sessionManager; + //GlobalNamespace::MultiplayerSessionManager* sessionManager; GlobalNamespace::LobbyPlayersDataModel* lobbyPlayersDataModel; - MultiQuestensions::PacketManager* packetManager; GlobalNamespace::LobbyGameStateController* lobbyGameStateController; LobbySetupViewController* lobbySetupView; - std::string moddedState = "modded"; - - Il2CppString* getCustomLevelSongPackMaskStr() { - static Il2CppString* songPackMaskStr = il2cpp_utils::newcsstr("custom_levelpack_CustomLevels"); - return songPackMaskStr; - } - - Il2CppString* getModdedStateStr() { - static Il2CppString* moddedStateStr = il2cpp_utils::newcsstr("modded"); - return moddedStateStr; - } - - Il2CppString* getMEStateStr() { - static Il2CppString* moddedStateStr = il2cpp_utils::newcsstr("ME_Installed"); - return moddedStateStr; - } - - Il2CppString* getNEStateStr() { - static Il2CppString* moddedStateStr = il2cpp_utils::newcsstr("ME_Installed"); - return moddedStateStr; - } - - Il2CppString* getChromaStateStr() { - static Il2CppString* moddedStateStr = il2cpp_utils::newcsstr("Chroma_Installed"); - return moddedStateStr; - } - bool AllPlayersModded() { - for (int i = 0; i < sessionManager->connectedPlayers->get_Count(); i++) { - if (!sessionManager->connectedPlayers->get_Item(i)->HasState(getModdedStateStr())) return false; - } - return true; - } - - bool AllPlayersHaveNE() { - for (int i = 0; i < sessionManager->connectedPlayers->get_Count(); i++) { - if (!sessionManager->connectedPlayers->get_Item(i)->HasState(getNEStateStr())) return false; - } - return true; - } - - bool AllPlayersHaveME() { - for (int i = 0; i < sessionManager->connectedPlayers->get_Count(); i++) { - if (!sessionManager->connectedPlayers->get_Item(i)->HasState(getMEStateStr())) return false; + getLogger().debug("MQE AllPlayersModded"); + for (int i = 0; i < MultiplayerCore::_multiplayerSessionManager->connectedPlayers->get_Count(); i++) { + if (!MultiplayerCore::_multiplayerSessionManager->connectedPlayers->get_Item(i)->HasState("modded")) return false; } return true; } - - bool AllPlayersHaveChroma() { - for (int i = 0; i < sessionManager->connectedPlayers->get_Count(); i++) { - if (!sessionManager->connectedPlayers->get_Item(i)->HasState(getChromaStateStr())) return false; - } - return true; - } - - -} - -//using PD_ValueCollection = System::Collections::Generic::Dictionary_2::ValueCollection; - -MultiQuestensions::Beatmaps::PreviewBeatmapStub* GetExistingPreview(Il2CppString* levelId) { - getLogger().debug("GetExistingPreview PlayerCount: %d", sessionManager->connectedPlayers->get_Count()); - for (int i = 0; i < sessionManager->connectedPlayers->get_Count(); i++) { - ILevelGameplaySetupData* playerData = reinterpret_cast(lobbyPlayersDataModel->playersData-> - get_Item(sessionManager->connectedPlayers->get_Item(i)->get_userId())); - if (playerData->get_beatmapLevel() != nullptr && csstrtostr(playerData->get_beatmapLevel()->get_levelID()) == csstrtostr(levelId)) { - getLogger().debug("GetExistingPreview type: %s", il2cpp_utils::ClassStandardName(reinterpret_cast(playerData->get_beatmapLevel())->klass).c_str()); - if (il2cpp_utils::AssignableFrom(reinterpret_cast(playerData->get_beatmapLevel())->klass)) { - getLogger().debug(__FILE__ " Line: %d", __LINE__); - return reinterpret_cast(playerData->get_beatmapLevel()); - } - } - else { - if (playerData->get_beatmapLevel() != nullptr) - getLogger().debug("Check expected levelId: %s, \r\nactual levelId: %s, \r\nlevelIDs match? %s", - to_utf8(csstrtostr(levelId)).c_str(), - to_utf8(csstrtostr(playerData->get_beatmapLevel()->get_levelID())).c_str(), - csstrtostr(playerData->get_beatmapLevel()->get_levelID()) == csstrtostr(levelId) ? "true" : "false"); - else getLogger().debug("beatmapLevel is null"); - } - } - getLogger().debug("GetExistingPreview return nullptr"); - return nullptr; } // LobbyPlayersDataModel Activate @@ -165,321 +82,11 @@ MAKE_HOOK_MATCH(LobbyPlayersActivate, &LobbyPlayersDataModel::Activate, void, Lo LobbyPlayersActivate(lobbyPlayersDataModel); } -// LobbyPlayersDataModel SetLocalPlayerBeatmapLevel -MAKE_HOOK_MATCH(LobbyPlayersSetLocalBeatmap, &LobbyPlayersDataModel::SetLocalPlayerBeatmapLevel, void, LobbyPlayersDataModel* self, Il2CppString* levelId, BeatmapDifficulty beatmapDifficulty, BeatmapCharacteristicSO* characteristic) { - - Il2CppString* hash = LevelIdToHash(levelId); - if (hash != nullptr) { - getLogger().info("Local user selected song '%s'.", to_utf8(csstrtostr(hash)).c_str()); - MultiQuestensions::Beatmaps::PreviewBeatmapStub* preview = GetExistingPreview(levelId); - if (preview != nullptr) { - self->SetPlayerBeatmapLevel(self->get_localUserId(), reinterpret_cast(preview), beatmapDifficulty, characteristic); - self->menuRpcManager->RecommendBeatmap(BeatmapIdentifierNetSerializable::New_ctor(levelId, characteristic->get_serializedName(), beatmapDifficulty)); - return; - } - else { - IPreviewBeatmapLevel* localIPreview = self->beatmapLevelsModel->GetLevelPreviewForLevelId(levelId); - if (localIPreview != nullptr) { - try { - using namespace MultiQuestensions::Beatmaps; - PreviewBeatmapStub* previewStub = THROW_UNLESS(il2cpp_utils::New(hash, localIPreview, static_cast(nullptr))); - //MultiQuestensions::Beatmaps::PreviewBeatmapStub* previewStub = MultiQuestensions::Beatmaps::PreviewBeatmapStub::New_ctor(hash, localIPreview); - getLogger().debug("Check 'LobbyPlayersSetLocalBeatmap' levelID: %s", to_utf8(csstrtostr(reinterpret_cast(previewStub)->get_levelID())).c_str()); - self->SetPlayerBeatmapLevel(self->get_localUserId(), reinterpret_cast(previewStub), beatmapDifficulty, characteristic); - PreviewBeatmapPacket* packet = previewStub->GetPacket(characteristic->get_serializedName(), beatmapDifficulty); - self->menuRpcManager->RecommendBeatmap(BeatmapIdentifierNetSerializable::New_ctor(levelId, characteristic->get_serializedName(), beatmapDifficulty)); - packetManager->Send(packet->ToINetSerializable()); - return; - } - catch (const std::runtime_error& e) { - getLogger().critical("REPORT TO ENDER: An Exception occured during class creation: %s", e.what()); - } - } - } - } - LobbyPlayersSetLocalBeatmap(self, levelId, beatmapDifficulty, characteristic); -} - -std::string GetHash(const std::string& levelId) { - return levelId.substr(RuntimeSongLoader::API::GetCustomLevelsPrefix().length(), levelId.length() - RuntimeSongLoader::API::GetCustomLevelsPrefix().length()); -} - -// LobbyPlayersDataModel HandleMenuRpcManagerSelectedBeatmap (DONT REMOVE THIS, without it a player's selected map will be cleared) -MAKE_HOOK_MATCH(LobbyPlayersSelectedBeatmap, &LobbyPlayersDataModel::HandleMenuRpcManagerRecommendBeatmap, void, LobbyPlayersDataModel* self, Il2CppString* userId, BeatmapIdentifierNetSerializable* beatmapId) { - getLogger().debug("HandleMenuRpcManagerRecommendBeatmap: LevelID: %s", to_utf8(csstrtostr(beatmapId->get_levelID())).c_str()); - MultiQuestensions::Beatmaps::PreviewBeatmapStub* preview = GetExistingPreview(beatmapId->get_levelID()); - if (preview != nullptr) { - getLogger().debug("HandleMenuRpcManagerRecommendBeatmap: Preview exists, SetPlayerBeatmapLevel"); - BeatmapCharacteristicSO* characteristic = self->beatmapCharacteristicCollection->GetBeatmapCharacteristicBySerializedName(beatmapId->get_beatmapCharacteristicSerializedName()); - self->SetPlayerBeatmapLevel(userId, reinterpret_cast(preview), beatmapId->get_difficulty(), characteristic); - return; - } - else { - getLogger().debug("HandleMenuRpcManagerRecommendBeatmap: Preview doesn't exist use localPreview, SetPlayerBeatmapLevel"); - IPreviewBeatmapLevel* localPreview = self->beatmapLevelsModel->GetLevelPreviewForLevelId(beatmapId->levelID); - if (localPreview != nullptr) { - BeatmapCharacteristicSO* characteristic = self->beatmapCharacteristicCollection->GetBeatmapCharacteristicBySerializedName(beatmapId->get_beatmapCharacteristicSerializedName()); - //self->SetPlayerBeatmapLevel(userId, localPreview, beatmapId->get_difficulty(), characteristic); - LobbyPlayersSelectedBeatmap(self, userId, beatmapId); - } - else { - // TODO: fetch beatmap data from beatsaver - getLogger().debug("No Info found, need to fetch from BeatSaver"); - //BeatSaver::API::GetBeatmapByHashAsync(GetHash((beatmapId->levelID), - // [](std::optional beatmap) { - // QuestUI::MainThreadScheduler::Schedule( - // [beatmap] { - // if (beatmap.has_value()) { - // self->SetPlayerBeatmapLevel(userId, bmPreview, beatmapId); - // // Create our own Preview or something? - // } - // else { - // // Well doesn't exist on BeatSaver then - // } - // } - // ); - // } - //); - } - } -} - MAKE_HOOK_MATCH(LobbySetupViewController_DidActivate, &LobbySetupViewController::DidActivate, void, LobbySetupViewController* self, bool firstActivation, bool addedToHierarchy, bool screenSystemEnabling) { lobbySetupView = self; LobbySetupViewController_DidActivate(self, firstActivation, addedToHierarchy, screenSystemEnabling); - if (getConfig().config["autoDelete"].GetBool() && !DownloadedSongIds.empty()) { - using namespace RuntimeSongLoader::API; - std::string hash = DownloadedSongIds.back(); - getLogger().debug("AutoDelete Song with Hash '%s'", hash.c_str()); - std::optional levelOpt = GetLevelByHash(hash); - if (levelOpt.has_value()) { - std::string songPath = to_utf8(csstrtostr(levelOpt.value()->dyn__customLevelPath())); - getLogger().info("Deleting Song: %s", songPath.c_str()); - DeleteSong(songPath, [&] { - RefreshSongs(false); - DownloadedSongIds.pop_back(); - }); - if (lobbyGameStateController) lobbyGameStateController->menuRpcManager->SetIsEntitledToLevel(levelOpt.value()->get_levelID(), EntitlementsStatus::NotOwned); - } - } if (firstActivation) { - MultiQuestensions::UI::LobbySetupPanel::AddSetupPanel(self->get_rectTransform(), sessionManager); - } -} - -MAKE_HOOK_MATCH(MultiplayerLobbyConnectionController_CreateParty, &MultiplayerLobbyConnectionController::CreateParty, void, MultiplayerLobbyConnectionController* self, CreateServerFormData data) { - data.songPacks = SongPackMask::get_all() | SongPackMask(getCustomLevelSongPackMaskStr()); - MultiplayerLobbyConnectionController_CreateParty(self, data); -} - -//MAKE_HOOK_MATCH(MultiplayerLobbyConnectionController_ConnectToMatchmaking, &MultiplayerLobbyConnectionController::ConnectToMatchmaking, void, MultiplayerLobbyConnectionController* self, BeatmapDifficultyMask beatmapDifficultyMask, SongPackMask songPackMask, bool allowSongSelection) { -// if (!gotSongPackOverrides) { -// songPackMask = songPackMask | SongPackMask(getCustomLevelSongPackMaskStr()); -// } -// MultiplayerLobbyConnectionController_ConnectToMatchmaking(self, beatmapDifficultyMask, songPackMask, allowSongSelection); -//} - -// Show the custom levels tab in multiplayer -MAKE_HOOK_MATCH(LevelSelectionNavigationController_Setup, &LevelSelectionNavigationController::Setup, void, LevelSelectionNavigationController* self, - SongPackMask songPackMask, BeatmapDifficultyMask allowedBeatmapDifficultyMask, Array* notAllowedCharacteristics, - bool hidePacksIfOneOrNone, bool hidePracticeButton, bool showPlayerStatsInDetailView, Il2CppString* actionButtonText, IBeatmapLevelPack* levelPackToBeSelectedAfterPresent, - SelectLevelCategoryViewController::LevelCategory startLevelCategory, IPreviewBeatmapLevel* beatmapLevelToBeSelectedAfterPresent, bool enableCustomLevels) { - getLogger().info("LevelSelectionNavigationController_Setup setting custom songs . . ."); - LevelSelectionNavigationController_Setup(self, songPackMask, allowedBeatmapDifficultyMask, notAllowedCharacteristics, hidePacksIfOneOrNone, hidePracticeButton, showPlayerStatsInDetailView, - actionButtonText, levelPackToBeSelectedAfterPresent, startLevelCategory, beatmapLevelToBeSelectedAfterPresent, songPackMask.Contains(getCustomLevelSongPackMaskStr())); -} - -static bool isMissingLevel = false; - -// This hook makes sure to grey-out the play button so that players can't start a level that someone doesn't have. -// This prevents crashes. -MAKE_HOOK_MATCH(LobbySetupViewController_SetPlayersMissingLevelText , &LobbySetupViewController::SetPlayersMissingLevelText, void, LobbySetupViewController* self, Il2CppString* playersMissingLevelText) { - getLogger().info("LobbySetupViewController_SetPlayersMissingLevelText"); - if (!missingLevelText.empty()) { - getLogger().info("Disabling start game as entitlements missing level text exists . . ."); - isMissingLevel = true; - playersMissingLevelText = il2cpp_utils::newcsstr(missingLevelText); - self->SetStartGameEnabled(CannotStartGameReason::DoNotOwnSong); - LobbySetupViewController_SetPlayersMissingLevelText(self, playersMissingLevelText); - return; - } - else if (playersMissingLevelText) { - getLogger().info("Disabling start game as missing level text exists . . ."); - isMissingLevel = true; - self->SetStartGameEnabled(CannotStartGameReason::DoNotOwnSong); - } - else { - getLogger().info("Enabling start game as missing level text does not exist . . ."); - isMissingLevel = false; - self->SetStartGameEnabled(CannotStartGameReason::None); - } - - - LobbySetupViewController_SetPlayersMissingLevelText(self, playersMissingLevelText); -} - -// Prevent the button becoming shown when we're force disabling it, as pressing it would crash -MAKE_HOOK_MATCH(LobbySetupViewController_SetStartGameEnabled, &LobbySetupViewController::SetStartGameEnabled, void, LobbySetupViewController* self, CannotStartGameReason cannotStartGameReason) { - getLogger().info("LobbySetupViewController_SetStartGameEnabled. Reason: %d", (int)cannotStartGameReason); - if (isMissingLevel && cannotStartGameReason == CannotStartGameReason::None) { - getLogger().info("Game attempted to enable the play button when the level was missing, stopping it!"); - cannotStartGameReason = CannotStartGameReason::DoNotOwnSong; - } - LobbySetupViewController_SetStartGameEnabled(self, cannotStartGameReason); -} - -namespace MultiQuestensions { - bool HasSong(const std::string& levelId) { - return static_cast(RuntimeSongLoader::API::GetLevelById(levelId)); - } -} - -std::vector DownloadedSongIds; - -MAKE_HOOK_MATCH(MultiplayerLevelLoader_LoadLevel, &MultiplayerLevelLoader::LoadLevel, void, MultiplayerLevelLoader* self, BeatmapIdentifierNetSerializable* beatmapId, GameplayModifiers* gameplayModifiers, float initialStartTime) { - std::string levelId = to_utf8(csstrtostr(beatmapId->levelID)); - getLogger().info("MultiplayerLevelLoader_LoadLevel: %s", levelId.c_str()); - MultiQuestensions::UI::CenterScreenLoading* cslInstance = MultiQuestensions::UI::CenterScreenLoading::get_Instance(); - if (IsCustomLevel(levelId)) { - if (HasSong(levelId)) { - getLogger().debug("MultiplayerLevelLoader_LoadLevel, HasSong, calling original"); - MultiplayerLevelLoader_LoadLevel(self, beatmapId, gameplayModifiers, initialStartTime); - return; - } - else { - std::string hash = GetHash(levelId); - BeatSaver::API::GetBeatmapByHashAsync(hash, - [self, beatmapId, gameplayModifiers, initialStartTime, hash, cslInstance](std::optional beatmapOpt) { - if (beatmapOpt.has_value()) { - auto beatmap = beatmapOpt.value(); - auto beatmapName = beatmap.GetName(); - getLogger().info("Downloading map: %s", beatmap.GetName().c_str()); - BeatSaver::API::DownloadBeatmapAsync(beatmap, - [self, beatmapId, gameplayModifiers, initialStartTime, beatmapName, hash, beatmap, cslInstance](bool error) { - if (error) { - getLogger().info("Failed downloading map retrying: %s", beatmapName.c_str()); - BeatSaver::API::DownloadBeatmapAsync(beatmap, - [self, beatmapId, gameplayModifiers, initialStartTime, beatmapName, hash](bool error) { - if (error) { - getLogger().info("Failed downloading map: %s", beatmapName.c_str()); - } - else { - getLogger().info("Downloaded map: %s", beatmapName.c_str()); - DownloadedSongIds.emplace_back(hash); - QuestUI::MainThreadScheduler::Schedule( - [self, beatmapId, gameplayModifiers, initialStartTime, hash] { - RuntimeSongLoader::API::RefreshSongs(false, - [self, beatmapId, gameplayModifiers, initialStartTime, hash](const std::vector& songs) { - auto* downloadedSongsGSM = UI::DownloadedSongsGSM::get_Instance(); - if (!getConfig().config["autoDelete"].GetBool() && downloadedSongsGSM) downloadedSongsGSM->InsertCell(hash); - else { - getLogger().warning("DownloadedSongsGSM was null, adding to queue"); - UI::DownloadedSongsGSM::mapQueue.push_back(hash); - } - getLogger().debug("Pointer Check before loading level: self='%p', beatmapId='%p', gameplayModifiers='%p'", self, beatmapId, gameplayModifiers); - self->loaderState = MultiplayerLevelLoader::MultiplayerBeatmapLoaderState::NotLoading; - //getLogger().debug("MultiplayerLevelLoader_LoadLevel, Downloaded, calling original"); - MultiplayerLevelLoader_LoadLevel(self, beatmapId, gameplayModifiers, initialStartTime); - return; - } - ); - } - ); - } - }, [cslInstance](float downloadProgress) { - if (cslInstance) { - QuestUI::MainThreadScheduler::Schedule( - [cslInstance, downloadProgress] { - cslInstance->ShowDownloadingProgress(downloadProgress); - }); - } - } - ); - } - else { - getLogger().info("Downloaded map: %s", beatmapName.c_str()); - DownloadedSongIds.emplace_back(hash); - QuestUI::MainThreadScheduler::Schedule( - [self, beatmapId, gameplayModifiers, initialStartTime, hash] { - RuntimeSongLoader::API::RefreshSongs(false, - [self, beatmapId, gameplayModifiers, initialStartTime, hash](const std::vector& songs) { - auto* downloadedSongsGSM = UI::DownloadedSongsGSM::get_Instance(); - if (!getConfig().config["autoDelete"].GetBool() && downloadedSongsGSM) downloadedSongsGSM->InsertCell(hash); - else { - getLogger().warning("DownloadedSongsGSM was null, adding to queue"); - UI::DownloadedSongsGSM::mapQueue.push_back(hash); - } - getLogger().debug("Pointer Check before loading level: self='%p', beatmapId='%p', gameplayModifiers='%p'", self, beatmapId, gameplayModifiers); - self->dyn__loaderState() = MultiplayerLevelLoader::MultiplayerBeatmapLoaderState::NotLoading; - //getLogger().debug("MultiplayerLevelLoader_LoadLevel, Downloaded, calling original"); - MultiplayerLevelLoader_LoadLevel(self, beatmapId, gameplayModifiers, initialStartTime); - return; - } - ); - } - ); - } - }, [cslInstance](float downloadProgress) { - if (cslInstance) { - QuestUI::MainThreadScheduler::Schedule( - [cslInstance, downloadProgress] { - cslInstance->ShowDownloadingProgress(downloadProgress); - }); - } - } - ); - } - } - ); - } - } - else { - getLogger().debug("MultiplayerLevelLoader_LoadLevel, calling original"); - MultiplayerLevelLoader_LoadLevel(self, beatmapId, gameplayModifiers, initialStartTime); - } -} - -// Checks entitlement and stalls lobby until fullfilled, unless a game is already in progress. -MAKE_HOOK_MATCH(LobbyGameStateController_HandleMultiplayerLevelLoaderCountdownFinished, &LobbyGameStateController::HandleMultiplayerLevelLoaderCountdownFinished, void, LobbyGameStateController* self, GlobalNamespace::IPreviewBeatmapLevel* previewBeatmapLevel, GlobalNamespace::BeatmapDifficulty beatmapDifficulty, GlobalNamespace::BeatmapCharacteristicSO* beatmapCharacteristic, GlobalNamespace::IDifficultyBeatmap* difficultyBeatmap, GlobalNamespace::GameplayModifiers* gameplayModifiers) { - getLogger().debug("LobbyGameStateController_HandleMultiplayerLevelLoaderCountdownFinished"); - lobbyGameStateController = self; - - DataStore* instance = DataStore::get_Instance(); - if (!instance) instance = DataStore::CS_Ctor(previewBeatmapLevel, beatmapCharacteristic, difficultyBeatmap, gameplayModifiers, beatmapDifficulty); - - instance->loadingPreviewBeatmapLevel = previewBeatmapLevel; - instance->loadingBeatmapDifficulty = beatmapDifficulty; - instance->loadingBeatmapCharacteristic = beatmapCharacteristic; - instance->loadingDifficultyBeatmap = difficultyBeatmap; - instance->loadingGameplayModifiers = gameplayModifiers; - - //self->dyn__multiplayerLevelLoader()->dyn__loaderState() = MultiplayerLevelLoader::MultiplayerBeatmapLoaderState::NotLoading; - bool entitlementStatusOK = true; - std::string LevelID = to_utf8(csstrtostr(self->startedBeatmapId->get_levelID())); - // Checks each player, to see if they're in the lobby, and if they are, checks their entitlement status. - MultiQuestensions::UI::CenterScreenLoading::playersReady = 0; - for (int i = 0; i < sessionManager->connectedPlayers->get_Count(); i++) { - Il2CppString* csUserID = sessionManager->connectedPlayers->get_Item(i)->get_userId(); - std::string UserID = to_utf8(csstrtostr(csUserID)); - if (self->dyn__lobbyPlayersDataModel()->GetPlayerIsInLobby(csUserID)) { - if (entitlementDictionary[UserID][LevelID] != EntitlementsStatus::Ok) entitlementStatusOK = false; - else MultiQuestensions::UI::CenterScreenLoading::playersReady++; - } - } - getLogger().debug("[HandleMultiplayerLevelLoaderCountdownFinished] Players ready: '%d'", MultiQuestensions::UI::CenterScreenLoading::playersReady + 1); - self->menuRpcManager->SetIsEntitledToLevel(previewBeatmapLevel->get_levelID(), EntitlementsStatus::Ok); - if (entitlementStatusOK) { - //if (cslInstance) cslInstance->HideLoading(); - //loadingPreviewBeatmapLevel = nullptr; - ////loadingBeatmapDifficulty.dyn_value__() = 1000; - //loadingBeatmapCharacteristic = nullptr; - //loadingDifficultyBeatmap = nullptr; - //loadingGameplayModifiers = nullptr; - DataStore::Clear(); - - // call original method - LobbyGameStateController_HandleMultiplayerLevelLoaderCountdownFinished(self, previewBeatmapLevel, beatmapDifficulty, beatmapCharacteristic, difficultyBeatmap, gameplayModifiers); - //entitlementDictionary.clear(); + MultiQuestensions::UI::LobbySetupPanel::AddSetupPanel(self->get_rectTransform(), MultiplayerCore::_multiplayerSessionManager, self); } } @@ -488,91 +95,12 @@ MAKE_HOOK_MATCH(LobbyGameStateController_Activate, &LobbyGameStateController::Ac LobbyGameStateController_Activate(self); } -//MAKE_HOOK_MATCH(MenuRpcManager_InvokeSetCountdownEndTime, &MenuRpcManager::InvokeSetCountdownEndTime, void, MenuRpcManager* self, ::Il2CppString* userId, float newTime) { -// getLogger().debug("InvokeSetCountdownEndTime: newTime: %f", newTime); -// MenuRpcManager_InvokeSetCountdownEndTime(self, userId, newTime); -//} - -//MAKE_HOOK_MATCH(MenuRpcManager_InvokeStartLevel, &MenuRpcManager::InvokeStartLevel, void, MenuRpcManager* self, ::Il2CppString* userId, GlobalNamespace::BeatmapIdentifierNetSerializable* beatmapId, GlobalNamespace::GameplayModifiers* gameplayModifiers, float startTime) { -// getLogger().debug("StartLevel: startTime: %f", startTime); -// MenuRpcManager_InvokeStartLevel(self, userId, beatmapId, gameplayModifiers, startTime); -//} -// -//MAKE_HOOK_MATCH(LobbyGameStateController_HandleMenuRpcManagerSetCountdownEndTime, &LobbyGameStateController::HandleMenuRpcManagerSetCountdownEndTime, void, LobbyGameStateController* self, ::Il2CppString* userId, float countdownTime) { -// getLogger().debug("SetCountdownEndTime: raw=%f, synctime=%f, r-s=%f", countdownTime, self->multiplayerSessionManager->get_syncTime(), countdownTime - self->multiplayerSessionManager->get_syncTime()); -// LobbyGameStateController_HandleMenuRpcManagerSetCountdownEndTime(self, userId, countdownTime); -//} - -//MAKE_HOOK_MATCH(ConnectedPlayerManager_HandleSyncTimePacket, &ConnectedPlayerManager::HandleSyncTimePacket, void, ConnectedPlayerManager* self, GlobalNamespace::ConnectedPlayerManager::SyncTimePacket* packet, GlobalNamespace::IConnectedPlayer* player) { -// //getLogger().debug("SetCountdownEndTime: raw=%f, synctime=%f, r-s=%f", countdownTime, self->multiplayerSessionManager->get_syncTime(), countdownTime - self->multiplayerSessionManager->get_syncTime()); -// getLogger().debug("HandleSyncTimePacket: syncTime=%f", packet->syncTime); -// -// ConnectedPlayerManager_HandleSyncTimePacket(self, packet, player); -//} - -// TODO: This allows for overwriting entitlement as it doesn't check entitlement for other players selection -MAKE_HOOK_MATCH(GameServerPlayerTableCell_SetData, &GameServerPlayerTableCell::SetData, void, GameServerPlayerTableCell* self, IConnectedPlayer* connectedPlayer, ILobbyPlayerData* playerData, bool hasKickPermissions, bool allowSelection, System::Threading::Tasks::Task_1* getLevelEntitlementTask) { - getLogger().debug("GameServerPlayerTableCell_SetData Set Entitlement Owned"); - getLevelEntitlementTask = Task_1::New_ctor(AdditionalContentModel::EntitlementStatus::Owned); - //if (playerData && reinterpret_cast(playerData)->get_beatmapLevel()) { - // std::string UserID = to_utf8(csstrtostr(connectedPlayer->get_userId())); - // std::string LevelID = to_utf8(csstrtostr(reinterpret_cast(playerData)->get_beatmapLevel()->get_levelID())); - // if (!playerData->get_isInLobby() && entitlementDictionary[UserID][LevelID] != EntitlementsStatus::Ok) getLevelEntitlementTask->TrySetResult(AdditionalContentModel::EntitlementStatus::NotOwned); - //} - GameServerPlayerTableCell_SetData(self, connectedPlayer, playerData, hasKickPermissions, allowSelection, getLevelEntitlementTask); -} - -MAKE_HOOK_MATCH(CenterStageScreenController_Setup, &CenterStageScreenController::Setup, void, CenterStageScreenController* self, bool showModifiers) { - CenterStageScreenController_Setup(self, showModifiers); - if (!self->get_gameObject()->GetComponent()) - self->get_gameObject()->AddComponent(); -} - - -void saveDefaultConfig() { - getLogger().info("Creating config file..."); - ConfigDocument& config = getConfig().config; - - if (config.HasMember("color") && config["color"].IsString() && - config.HasMember("autoDelete") && config["autoDelete"].IsBool() && - config.HasMember("LagReducer") && config["LagReducer"].IsBool() && - config.HasMember("MaxPlayers") && config["MaxPlayers"].IsInt()) { - getLogger().info("Config file already exists."); - return; - } - - //config.RemoveAllMembers(); - if (!config.IsObject()) - config.SetObject(); - auto& allocator = config.GetAllocator(); - - if (!(config.HasMember("MaxPlayers") && config["MaxPlayers"].IsInt())) - config.AddMember("MaxPlayers", 10, allocator); - if (!(config.HasMember("LagReducer") && config["LagReducer"].IsBool())) - config.AddMember("LagReducer", false, allocator); - if (!(config.HasMember("autoDelete") && config["autoDelete"].IsBool())) - config.AddMember("autoDelete", false, allocator); - if (!(config.HasMember("color") && config["color"].IsString())) - config.AddMember("color", "#08C0FF", allocator); - - //config.AddMember("freemod", false, allocator); - //config.AddMember("hostpick", true, allocator); - //config["customsongs"].SetBool(true); - //config["enforcemods"].SetBool(true); - - getConfig().Write(); - getLogger().info("Config file created."); -} - // Called at the early stages of game loading extern "C" void setup(ModInfo& info) { info.id = ID; info.version = VERSION; modInfo = info; - //getConfig().Load(); - saveDefaultConfig(); - getLogger().info("Completed setup!"); } @@ -580,44 +108,21 @@ extern "C" void setup(ModInfo& info) { extern "C" void load() { il2cpp_functions::Init(); - custom_types::Register::AutoRegister(); + // Initialize Config + config.Initialize(); + + // if (readConfig()) { + // getLogger().error("Config file is invalid."); + // saveDefaultConfig(); + // } - QuestUI::Register::RegisterGameplaySetupMenu(modInfo, "MP Downloaded", QuestUI::Register::Online); + custom_types::Register::AutoRegister(); getLogger().info("Installing hooks..."); Hooks::Install_Hooks(); INSTALL_HOOK(getLogger(), LobbyPlayersActivate); - INSTALL_HOOK(getLogger(), LobbyPlayersSetLocalBeatmap); - INSTALL_HOOK(getLogger(), LobbyPlayersSelectedBeatmap); - INSTALL_HOOK_ORIG(getLogger(), MultiplayerLevelLoader_LoadLevel); - //INSTALL_HOOK(getLogger(), NetworkPlayerEntitlementChecker_GetPlayerLevelEntitlementsAsync); - if (Modloader::getMods().find("BeatTogether") != Modloader::getMods().end()) { - getLogger().info("Hello BeatTogether!"); - } - else getLogger().warning("BeatTogether was not found! Is Multiplayer modded?"); - - INSTALL_HOOK_ORIG(getLogger(), LobbyGameStateController_HandleMultiplayerLevelLoaderCountdownFinished); INSTALL_HOOK(getLogger(), LobbyGameStateController_Activate); - INSTALL_HOOK(getLogger(), LobbySetupViewController_SetPlayersMissingLevelText); - INSTALL_HOOK(getLogger(), LobbySetupViewController_SetStartGameEnabled); INSTALL_HOOK(getLogger(), LobbySetupViewController_DidActivate); - - INSTALL_HOOK(getLogger(), MultiplayerLobbyConnectionController_CreateParty); - //INSTALL_HOOK(getLogger(), MultiplayerLobbyConnectionController_ConnectToMatchmaking); - - INSTALL_HOOK(getLogger(), LevelSelectionNavigationController_Setup); - INSTALL_HOOK(getLogger(), CenterStageScreenController_Setup); - -#pragma region Debug Hooks - //INSTALL_HOOK(getLogger(), MenuRpcManager_InvokeSetCountdownEndTime); - //INSTALL_HOOK(getLogger(), MenuRpcManager_InvokeStartLevel); - //INSTALL_HOOK(getLogger(), LobbyGameStateController_HandleMenuRpcManagerSetCountdownEndTime); - //INSTALL_HOOK(getLogger(), ConnectedPlayerManager_HandleSyncTimePacket); -#pragma endregion - - - INSTALL_HOOK(getLogger(), GameServerPlayerTableCell_SetData); - getLogger().info("Installed all hooks!"); } \ No newline at end of file