diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ecd5528..423785a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -21,9 +21,9 @@ jobs: with: dotnet-version: 7.0.x - name: Restore dependencies - run: dotnet restore + run: dotnet restore NeoModLoader.csproj - name: Build - run: dotnet build + run: dotnet build NeoModLoader.csproj - name: Archive production artifacts uses: actions/upload-artifact@v4 with: diff --git a/.github/workflows/build_mobile.yml b/.github/workflows/build_mobile.yml new file mode 100644 index 0000000..5b1a9e2 --- /dev/null +++ b/.github/workflows/build_mobile.yml @@ -0,0 +1,32 @@ +# This workflow will build a .NET project +# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-net + +name: Build-NML + +on: + push: + branches: [ "master" ] + pull_request: + branches: [ "master" ] + +jobs: + Windows: + + runs-on: windows-2025 + + steps: + - uses: actions/checkout@v3 + - name: Setup .NET + uses: actions/setup-dotnet@v3 + with: + dotnet-version: 7.0.x + - name: Restore dependencies + run: dotnet restore NeoModLoader_mobile.csproj + - name: Build + run: dotnet build NeoModLoader_mobile.csproj + - name: Archive production artifacts + uses: actions/upload-artifact@v4 + with: + name: NeoModLoader + path: | + bin\Debug\net8.0\NeoModLoader.* diff --git a/.gitignore b/.gitignore index 6272041..6c0e52e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ -.* +.* !.github !.gitignore !.gitattributes @@ -11,8 +11,7 @@ assemblies !resources/assemblies resources/commit -*.csproj *.user *.snk App.config -constants/Setting.cs \ No newline at end of file +constants/Setting.cs diff --git a/NeoModLoader.csproj b/NeoModLoader.csproj index 81a8395..f076afe 100644 --- a/NeoModLoader.csproj +++ b/NeoModLoader.csproj @@ -125,7 +125,7 @@ - + @@ -199,12 +199,16 @@ - - - - - - + + + + + + + + + + @@ -220,6 +224,13 @@ + + + + + + + diff --git a/NeoModLoader_mobile.csproj b/NeoModLoader_mobile.csproj new file mode 100644 index 0000000..e7eb525 --- /dev/null +++ b/NeoModLoader_mobile.csproj @@ -0,0 +1,234 @@ + + + + IL2CPP + net8.0 + enable + disable + 14 + True + WorldBoxOpenMods + https://github.com/WorldBoxOpenMods + https://github.com/WorldBoxOpenMods/ModLoader + Git + 1 + portable + true + NeoModLoader_mobile + + + + true + wbopenmods.snk + + + + + + android-assembly-dependencies\0Harmony.dll + + + ..\..\..\Desktop\melon_data\MelonLoader\net8\Il2CppInterop.Common.dll + + + android-assembly-dependencies\Il2CppInterop.Runtime.dll + + + android-assembly-dependencies\Il2Cppmscorlib.dll + + + android-assembly-dependencies\Il2CppSystem.Core.dll + + + android-assembly-dependencies\MelonLoader.dll + + + android-assembly-dependencies\Mono.Cecil.dll + + + android-assembly-dependencies\Mono.Cecil.Pdb.dll + + + android-assembly-dependencies\MonoMod.Core.dll + + + android-assembly-dependencies\MonoMod.RuntimeDetour.dll + + + android-assembly-dependencies\MonoMod.Utils.dll + + + assembly-dependencies\Newtonsoft.Json.dll + + + android-assembly-dependencies\Il2CppRSG.dll + + + android-assembly-dependencies\UnityEngine.UnityWebRequestModule.dll + + + + + + + resources\assemblies\Microsoft.CodeAnalysis.dll + + + resources\assemblies\Microsoft.CodeAnalysis.CSharp.dll + + + + + + + + + + + + + + android-assembly-dependencies\Assembly-CSharp.dll + + + android-assembly-dependencies\Assembly-CSharp-firstpass.dll + + + android-assembly-dependencies\Il2Cppstrings.dll + + + android-assembly-dependencies\Il2CppDOTween.dll + + + android-assembly-dependencies\Il2CppFMODUnity.dll + + + android-assembly-dependencies\UnityEngine.AudioModule.dll + + + android-assembly-dependencies\UnityEngine.CoreModule.dll + + + android-assembly-dependencies\UnityEngine.ImageConversionModule.dll + + + android-assembly-dependencies\UnityEngine.InputLegacyModule.dll + + + android-assembly-dependencies\UnityEngine.JSONSerializeModule.dll + + + android-assembly-dependencies\UnityEngine.TextRenderingModule.dll + + + android-assembly-dependencies\UnityEngine.UI.dll + + + android-assembly-dependencies\UnityEngine.UIModule.dll + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/NeoModLoader_mobile.sln b/NeoModLoader_mobile.sln new file mode 100644 index 0000000..7b44f39 --- /dev/null +++ b/NeoModLoader_mobile.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 18 +VisualStudioVersion = 18.3.11520.95 d18.3 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NeoModLoader_mobile", "NeoModLoader_mobile.csproj", "{6F122670-343B-3FB6-5FFB-66339AF7C496}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {6F122670-343B-3FB6-5FFB-66339AF7C496}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6F122670-343B-3FB6-5FFB-66339AF7C496}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6F122670-343B-3FB6-5FFB-66339AF7C496}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6F122670-343B-3FB6-5FFB-66339AF7C496}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {6DD92563-9AEB-4E85-8F45-46F78A0329E1} + EndGlobalSection +EndGlobal diff --git a/README.md b/README.md index 203e3fc..1f16793 100644 --- a/README.md +++ b/README.md @@ -49,7 +49,10 @@ If you're trying to play mods that aren't compatible with the latest version of 3. Start the game with experimental mode enabled It is recommended to subscribe to the NML item on the Steam Workshop to receive automatic updates - +## Installing on Android +1. Download the latest version of lemon loader https://gamebanana.com/mods/656702 +follow instructions there on how to install +2. Move neomodloader_mobile.dll to MelonLoader/com.mkarpenko.worldbox/Mods

Others

@@ -64,7 +67,6 @@ Report BUG

How to compile NML

- #### Compile with IDE 1. clone @@ -103,3 +105,4 @@ New features or code reconstruction: 2. Mod one-click install for various platforms 3. Manage vanilla tab buttons 4. Assembly-CSharp mod support(modify game code) + diff --git a/WorldBoxMod.cs b/WorldBoxMod.cs index 5e2c6d5..b9584ec 100644 --- a/WorldBoxMod.cs +++ b/WorldBoxMod.cs @@ -1,5 +1,7 @@ using System.Reflection; using HarmonyLib; +using NeoModLoader.AndroidCompatibilityModule; +using static NeoModLoader.AndroidCompatibilityModule.Converter; using NeoModLoader.api; using NeoModLoader.constants; using NeoModLoader.General; @@ -9,16 +11,27 @@ using NeoModLoader.services; using NeoModLoader.ui; using NeoModLoader.utils; -using NeoModLoader.utils.Builders; using UnityEngine; - +#if IL2CPP +using Il2CppInterop.Runtime.Injection; +#endif namespace NeoModLoader; - /// /// Main class /// +[MelonLoader.RegisterTypeInIl2Cpp] public class WorldBoxMod : MonoBehaviour { +#if IL2CPP + public WorldBoxMod(IntPtr ptr) : base(ptr) + { + } + + public WorldBoxMod() : base(ClassInjector.DerivedConstructorPointer()) + { + ClassInjector.DerivedConstructorBody(this); + } +#endif /// /// All successfully loaded mods. /// @@ -32,7 +45,7 @@ public class WorldBoxMod : MonoBehaviour private bool initialized_successfully = false; private static void UnityExplorerFix() { - Harmony harmony = new Harmony(Others.harmony_id); + HarmonyLib.Harmony harmony = new HarmonyLib.Harmony(Others.harmony_id); MethodInfo original = AccessTools.Method(typeof(Assembly), nameof(Assembly.LoadFrom), new[] { typeof(string) }); MethodInfo standin = AccessTools.Method(typeof(WorldBoxMod), nameof(LoadFrom)); ReversePatcher reversePatcher = harmony.CreateReversePatcher(original, new HarmonyMethod(standin)); @@ -41,26 +54,26 @@ private static void UnityExplorerFix() { } private static Assembly LoadFrom(string path) => Assembly.LoadFrom(path); - private void Start() { Others.unity_player_enabled = true; Transform = transform; - - InactiveTransform = new GameObject("Inactive").transform; + InactiveTransform = CreateGameObject("Inactive").transform; InactiveTransform.SetParent(Transform); InactiveTransform.gameObject.SetActive(false); - + if (Config.isAndroid) + { + GameObject services = GameObject.Find("Services"); + GameObject modloader = new GameObject("ModLoader"); + modloader.transform.parent = services.transform; + } LogService.Init(); - - if (ReflectionHelper.IsAssemblyLoaded("0Harmony")) { + if (ReflectionHelper.IsAssemblyLoaded("0Harmony") && !Config.isAndroid) { UnityExplorerFix(); } - fileSystemInitialize(); LogService.LogInfo($"NeoModLoader Version: {InternalResourcesGetter.GetCommit()}"); } - private void Update() { if (!Config.game_loaded) return; @@ -68,22 +81,23 @@ private void Update() { TabManager._checkNewTabs(); } - + if (initialized) { return; } initialized = true; - ModUploadAuthenticationService.AutoAuth(); + if(!Config.isAndroid) + ModUploadAuthenticationService.AutoAuth(); + HarmonyUtils._init(); - Harmony.CreateAndPatchAll(typeof(LM), Others.harmony_id); - Harmony.CreateAndPatchAll(typeof(ResourcesPatch), Others.harmony_id); - Harmony.CreateAndPatchAll(typeof(CustomAudioManager), Others.harmony_id); - Harmony.CreateAndPatchAll(typeof(AssetPatches), Others.harmony_id); + HarmonyLib.Harmony.CreateAndPatchAll(typeof(LM), Others.harmony_id); ; + HarmonyLib.Harmony.CreateAndPatchAll(typeof(ResourcesPatch), Others.harmony_id); + HarmonyLib.Harmony.CreateAndPatchAll(typeof(CustomAudioManager), Others.harmony_id); + if (!SmoothLoader.isLoading()) SmoothLoader.prepare(); - - SmoothLoader.add(() => + SmoothLoader.add(C(() => { ResourcesPatch.Initialize(); LoadLocales(); @@ -94,10 +108,10 @@ private void Update() WrappedPowersTab._init(); NCMSCompatibleLayer.PreInit(); ModInfoUtils.InitializeModCompileCache(); - }, "Initialize NeoModLoader"); + }), "Initialize NeoModLoader"); List mod_nodes = new(); - SmoothLoader.add(() => + SmoothLoader.add(C(() => { ModCompileLoadService.loadInfoOfBepInExPlugins(); @@ -106,13 +120,13 @@ private void Update() mod_nodes.AddRange(ModDepenSolveService.SolveModDependencies(mods)); ModCompileLoadService.prepareCompile(mod_nodes); - }, "Load Mods Info And Prepare Mods"); - SmoothLoader.add(() => + }), "Load Mods Info And Prepare Mods"); + SmoothLoader.add(C(() => { var mods_to_load = new List(); foreach (var mod in mod_nodes) { - SmoothLoader.add(() => + SmoothLoader.add(C(() => { if (ModCompileLoadService.compileMod(mod)) { @@ -122,68 +136,67 @@ private void Update() { LogService.LogError($"Failed to compile mod {mod.mod_decl.Name}"); } - }, "Compile Mod " + mod.mod_decl.Name); + }), "Compile Mod " + mod.mod_decl.Name); } - MasterBuilder Builder = new(); + AssetLinker Linker = new(); foreach (var mod in mod_nodes) { - SmoothLoader.add(() => + SmoothLoader.add(C(() => { if (mods_to_load.Contains(mod.mod_decl)) { ResourcesPatch.LoadResourceFromFolder(Path.Combine(mod.mod_decl.FolderPath, - Paths.ModResourceFolderName), out List builders); - Builder.AddBuilders(builders); + Paths.ModResourceFolderName), Linker); ResourcesPatch.LoadResourceFromFolder(Path.Combine(mod.mod_decl.FolderPath, - Paths.NCMSAdditionModResourceFolderName), out List builders2); - Builder.AddBuilders(builders2); + Paths.NCMSAdditionModResourceFolderName), Linker); ResourcesPatch.LoadAssetBundlesFromFolder(Path.Combine(mod.mod_decl.FolderPath, Paths.ModAssetBundleFolderName)); } - }, "Load Resources From Mod " + mod.mod_decl.Name); + }), "Load Resources From Mod " + mod.mod_decl.Name); } - SmoothLoader.add(() => + SmoothLoader.add(C(() => { ModCompileLoadService.loadMods(mods_to_load); - Builder.BuildAll(); + Linker.AddAssets(); ModInfoUtils.SaveModRecords(); NCMSCompatibleLayer.Init(); var successfulInit = new Dictionary(); foreach (IMod mod in LoadedMods.Where(mod => mod is IStagedLoad)) { - SmoothLoader.add(() => + SmoothLoader.add(C(() => { successfulInit.Add(mod, ModCompileLoadService.TryInitMod(mod)); - }, "Init Mod " + mod.GetDeclaration().Name); + }), "Init Mod " + mod.GetDeclaration().Name); } foreach (IMod mod in LoadedMods.Where(mod => mod is IStagedLoad)) { - SmoothLoader.add(() => + SmoothLoader.add(C(() => { if (successfulInit.ContainsKey(mod) && successfulInit[mod]) { ModCompileLoadService.PostInitMod(mod); } - }, "Post-Init Mod " + mod.GetDeclaration().Name); + }), "Post-Init Mod " + mod.GetDeclaration().Name); } - }, "Load Mods"); + }), "Load Mods"); - SmoothLoader.add(() => + SmoothLoader.add(C(() => { + #if !IL2CPP ModWorkshopService.Init(); - + #endif UIManager.init(); ModInfoUtils.DealWithBepInExModLinkRequests(); LM.ApplyLocale(); initialized_successfully = true; - }, "NeoModLoader Post Initialize"); - SmoothLoader.add(ExternalModInstallService.CheckExternalModInstall, "Check External Mods to Install"); - }, "Compile Mods And Load resources"); + }), "NeoModLoader Post Initialize"); + SmoothLoader.add(C(ExternalModInstallService.CheckExternalModInstall), "Check External Mods to Install"); + }), "Compile Mods And Load resources"); } - + private void LoadLocales() { string[] resources = NeoModLoaderAssembly.GetManifestResourceNames(); @@ -204,7 +217,6 @@ private void fileSystemInitialize() Directory.CreateDirectory(Paths.ModsPath); LogService.LogInfo($"Create Mods folder at {Paths.ModsPath}"); } - if (!Directory.Exists(Paths.CompiledModsPath)) { Directory.CreateDirectory(Paths.CompiledModsPath); @@ -265,40 +277,43 @@ void extractAssemblies() } } - try + if (!Config.isAndroid) { - using var stream = - NeoModLoaderAssembly.GetManifestResourceStream( - "NeoModLoader.resources.assemblies.Assembly-CSharp-Publicized.dll"); - if (File.Exists(Paths.PublicizedAssemblyPath)) + try { - var modupdate_time = new FileInfo(Paths.NMLModPath).LastWriteTime; - var assemblyupdate_time = new FileInfo(Paths.PublicizedAssemblyPath).CreationTime; - if (modupdate_time > assemblyupdate_time) + using var stream = + NeoModLoaderAssembly.GetManifestResourceStream( + "NeoModLoader.resources.assemblies.Assembly-CSharp-Publicized.dll"); + if (File.Exists(Paths.PublicizedAssemblyPath)) + { + var modupdate_time = new FileInfo(Paths.NMLModPath).LastWriteTime; + var assemblyupdate_time = new FileInfo(Paths.PublicizedAssemblyPath).CreationTime; + if (modupdate_time > assemblyupdate_time) + { + LogService.LogInfo($"NeoModLoader.dll is newer than Assembly-CSharp-Publicized.dll, " + + $"re-extract Assembly-CSharp-Publicized.dll from NeoModLoader.dll"); + File.Delete(Paths.PublicizedAssemblyPath); + using var file = new FileStream(Paths.PublicizedAssemblyPath, FileMode.Create, + FileAccess.Write); + stream.CopyTo(file); + } + } + else { - LogService.LogInfo($"NeoModLoader.dll is newer than Assembly-CSharp-Publicized.dll, " + - $"re-extract Assembly-CSharp-Publicized.dll from NeoModLoader.dll"); - File.Delete(Paths.PublicizedAssemblyPath); - using var file = new FileStream(Paths.PublicizedAssemblyPath, FileMode.Create, FileAccess.Write); + using var file = new FileStream(Paths.PublicizedAssemblyPath, FileMode.CreateNew, FileAccess.Write); stream.CopyTo(file); } } - else + catch (UnauthorizedAccessException) // If the file is hidden, delete it and try again { + File.Delete(Paths.PublicizedAssemblyPath); + using var stream = + NeoModLoaderAssembly.GetManifestResourceStream( + "NeoModLoader.resources.assemblies.Assembly-CSharp-Publicized.dll"); using var file = new FileStream(Paths.PublicizedAssemblyPath, FileMode.CreateNew, FileAccess.Write); stream.CopyTo(file); } } - catch (UnauthorizedAccessException) // If the file is hidden, delete it and try again - { - File.Delete(Paths.PublicizedAssemblyPath); - using var stream = - NeoModLoaderAssembly.GetManifestResourceStream( - "NeoModLoader.resources.assemblies.Assembly-CSharp-Publicized.dll"); - using var file = new FileStream(Paths.PublicizedAssemblyPath, FileMode.CreateNew, FileAccess.Write); - stream.CopyTo(file); - } - foreach (var file_full_path in Directory.GetFiles(Paths.NMLAssembliesPath, "*.dll")) { try @@ -339,10 +354,12 @@ void extractAssemblies() if (!File.Exists(Paths.NMLAutoUpdateModulePath)) { + string name = Config.isAndroid ? "_mobile" : ""; using Stream stream = NeoModLoaderAssembly.GetManifestResourceStream( - "NeoModLoader.resources.assemblies.NeoModLoader.AutoUpdate.dll"); + $"NeoModLoader{name}.resources.assemblies.NeoModLoader.AutoUpdate.dll"); using var file = new FileStream(Paths.NMLAutoUpdateModulePath, FileMode.CreateNew, FileAccess.Write); stream.CopyTo(file); } + } } \ No newline at end of file diff --git a/android-assembly-dependencies/0Harmony.dll b/android-assembly-dependencies/0Harmony.dll new file mode 100644 index 0000000..b9a55b1 Binary files /dev/null and b/android-assembly-dependencies/0Harmony.dll differ diff --git a/android-assembly-dependencies/Assembly-CSharp-firstpass.dll b/android-assembly-dependencies/Assembly-CSharp-firstpass.dll new file mode 100644 index 0000000..9b72b1c Binary files /dev/null and b/android-assembly-dependencies/Assembly-CSharp-firstpass.dll differ diff --git a/android-assembly-dependencies/Assembly-CSharp.dll b/android-assembly-dependencies/Assembly-CSharp.dll new file mode 100644 index 0000000..c338cd0 Binary files /dev/null and b/android-assembly-dependencies/Assembly-CSharp.dll differ diff --git a/android-assembly-dependencies/Il2CppDOTween.dll b/android-assembly-dependencies/Il2CppDOTween.dll new file mode 100644 index 0000000..8621390 Binary files /dev/null and b/android-assembly-dependencies/Il2CppDOTween.dll differ diff --git a/android-assembly-dependencies/Il2CppFMODUnity.dll b/android-assembly-dependencies/Il2CppFMODUnity.dll new file mode 100644 index 0000000..a620f0d Binary files /dev/null and b/android-assembly-dependencies/Il2CppFMODUnity.dll differ diff --git a/android-assembly-dependencies/Il2CppFMODUnityResonance.dll b/android-assembly-dependencies/Il2CppFMODUnityResonance.dll new file mode 100644 index 0000000..0b7b5b9 Binary files /dev/null and b/android-assembly-dependencies/Il2CppFMODUnityResonance.dll differ diff --git a/android-assembly-dependencies/Il2CppInterop.Runtime.dll b/android-assembly-dependencies/Il2CppInterop.Runtime.dll new file mode 100644 index 0000000..0330335 Binary files /dev/null and b/android-assembly-dependencies/Il2CppInterop.Runtime.dll differ diff --git a/android-assembly-dependencies/Il2CppRSG.dll b/android-assembly-dependencies/Il2CppRSG.dll new file mode 100644 index 0000000..31ca956 Binary files /dev/null and b/android-assembly-dependencies/Il2CppRSG.dll differ diff --git a/android-assembly-dependencies/Il2CppSystem.Core.dll b/android-assembly-dependencies/Il2CppSystem.Core.dll new file mode 100644 index 0000000..787bcf4 Binary files /dev/null and b/android-assembly-dependencies/Il2CppSystem.Core.dll differ diff --git a/android-assembly-dependencies/Il2Cppmscorlib.dll b/android-assembly-dependencies/Il2Cppmscorlib.dll new file mode 100644 index 0000000..721a224 Binary files /dev/null and b/android-assembly-dependencies/Il2Cppmscorlib.dll differ diff --git a/android-assembly-dependencies/Il2Cppstrings.dll b/android-assembly-dependencies/Il2Cppstrings.dll new file mode 100644 index 0000000..95b6e8c Binary files /dev/null and b/android-assembly-dependencies/Il2Cppstrings.dll differ diff --git a/android-assembly-dependencies/MelonLoader.dll b/android-assembly-dependencies/MelonLoader.dll new file mode 100755 index 0000000..0bb2067 Binary files /dev/null and b/android-assembly-dependencies/MelonLoader.dll differ diff --git a/android-assembly-dependencies/Mono.Cecil.Pdb.dll b/android-assembly-dependencies/Mono.Cecil.Pdb.dll new file mode 100644 index 0000000..86d3e3a Binary files /dev/null and b/android-assembly-dependencies/Mono.Cecil.Pdb.dll differ diff --git a/android-assembly-dependencies/Mono.Cecil.dll b/android-assembly-dependencies/Mono.Cecil.dll new file mode 100644 index 0000000..553498b Binary files /dev/null and b/android-assembly-dependencies/Mono.Cecil.dll differ diff --git a/android-assembly-dependencies/MonoMod.Core.dll b/android-assembly-dependencies/MonoMod.Core.dll new file mode 100644 index 0000000..dd440bd Binary files /dev/null and b/android-assembly-dependencies/MonoMod.Core.dll differ diff --git a/android-assembly-dependencies/MonoMod.RuntimeDetour.dll b/android-assembly-dependencies/MonoMod.RuntimeDetour.dll new file mode 100644 index 0000000..975178e Binary files /dev/null and b/android-assembly-dependencies/MonoMod.RuntimeDetour.dll differ diff --git a/android-assembly-dependencies/MonoMod.Utils.dll b/android-assembly-dependencies/MonoMod.Utils.dll new file mode 100644 index 0000000..8b4b825 Binary files /dev/null and b/android-assembly-dependencies/MonoMod.Utils.dll differ diff --git a/android-assembly-dependencies/UnityEngine.AudioModule.dll b/android-assembly-dependencies/UnityEngine.AudioModule.dll new file mode 100644 index 0000000..c15285c Binary files /dev/null and b/android-assembly-dependencies/UnityEngine.AudioModule.dll differ diff --git a/android-assembly-dependencies/UnityEngine.CoreModule.dll b/android-assembly-dependencies/UnityEngine.CoreModule.dll new file mode 100644 index 0000000..32ccf99 Binary files /dev/null and b/android-assembly-dependencies/UnityEngine.CoreModule.dll differ diff --git a/android-assembly-dependencies/UnityEngine.ImageConversionModule.dll b/android-assembly-dependencies/UnityEngine.ImageConversionModule.dll new file mode 100644 index 0000000..72b70ab Binary files /dev/null and b/android-assembly-dependencies/UnityEngine.ImageConversionModule.dll differ diff --git a/android-assembly-dependencies/UnityEngine.InputLegacyModule.dll b/android-assembly-dependencies/UnityEngine.InputLegacyModule.dll new file mode 100644 index 0000000..1b7f968 Binary files /dev/null and b/android-assembly-dependencies/UnityEngine.InputLegacyModule.dll differ diff --git a/android-assembly-dependencies/UnityEngine.JSONSerializeModule.dll b/android-assembly-dependencies/UnityEngine.JSONSerializeModule.dll new file mode 100644 index 0000000..b695984 Binary files /dev/null and b/android-assembly-dependencies/UnityEngine.JSONSerializeModule.dll differ diff --git a/android-assembly-dependencies/UnityEngine.TextRenderingModule.dll b/android-assembly-dependencies/UnityEngine.TextRenderingModule.dll new file mode 100644 index 0000000..9e20b66 Binary files /dev/null and b/android-assembly-dependencies/UnityEngine.TextRenderingModule.dll differ diff --git a/android-assembly-dependencies/UnityEngine.UI.dll b/android-assembly-dependencies/UnityEngine.UI.dll new file mode 100644 index 0000000..0277db4 Binary files /dev/null and b/android-assembly-dependencies/UnityEngine.UI.dll differ diff --git a/android-assembly-dependencies/UnityEngine.UIModule.dll b/android-assembly-dependencies/UnityEngine.UIModule.dll new file mode 100644 index 0000000..3896084 Binary files /dev/null and b/android-assembly-dependencies/UnityEngine.UIModule.dll differ diff --git a/android-assembly-dependencies/UnityEngine.UnityWebRequestModule.dll b/android-assembly-dependencies/UnityEngine.UnityWebRequestModule.dll new file mode 100644 index 0000000..4093fac Binary files /dev/null and b/android-assembly-dependencies/UnityEngine.UnityWebRequestModule.dll differ diff --git a/android_compatibility_module/IL2CPP/Attributes.cs b/android_compatibility_module/IL2CPP/Attributes.cs new file mode 100644 index 0000000..e034ec8 --- /dev/null +++ b/android_compatibility_module/IL2CPP/Attributes.cs @@ -0,0 +1,5 @@ +namespace UnityEngine; + +public sealed class SerializeField : Attribute +{ +} \ No newline at end of file diff --git a/android_compatibility_module/IL2CPP/CompilerFix.cs b/android_compatibility_module/IL2CPP/CompilerFix.cs new file mode 100644 index 0000000..2e9e93b --- /dev/null +++ b/android_compatibility_module/IL2CPP/CompilerFix.cs @@ -0,0 +1,13 @@ +namespace System.Runtime.CompilerServices +{ + internal sealed class NullableAttribute : System.Attribute + { + public NullableAttribute(byte _) { } + public NullableAttribute(byte[] _) { } + } + + internal sealed class NullableContextAttribute : System.Attribute + { + public NullableContextAttribute(byte _) { } + } +} \ No newline at end of file diff --git a/android_compatibility_module/IL2CPP/Converter.cs b/android_compatibility_module/IL2CPP/Converter.cs new file mode 100644 index 0000000..9b91b62 --- /dev/null +++ b/android_compatibility_module/IL2CPP/Converter.cs @@ -0,0 +1,188 @@ +using System.Collections.Concurrent; +using System.Reflection; + +namespace NeoModLoader.AndroidCompatibilityModule; +using Il2CppSystem.Collections; +using Il2CppInterop.Runtime.InteropTypes; +using Il2CppInterop.Runtime.InteropTypes.Arrays; +using System = Il2CppSystem; +using Il2CppInterop.Runtime; +using UnityEngine; + +/// +/// collection of tools to allow mods to work on il2cpp and mono on the same code +/// +public static class Converter +{ + public static T GetWrappedComponent(this GameObject obj) + { + return (T) WrapperHelper.GetWrappedComponent(obj, typeof(T)); + } + public static D C(Delegate func) where D : System.Delegate + { + return DelegateSupport.ConvertDelegate(func); + } + + public static System.ValueTuple C(ValueTuple tuple) + { + return new System.ValueTuple(tuple.Item1, tuple.Item2, tuple.Item3); + } + public static System.ValueTuple C(ValueTuple tuple) + { + return new System.ValueTuple(tuple.Item1, tuple.Item2); + } + public static Il2CppEnumeratorWrapper Enumerate(this Il2CppObjectBase Object) where T : System.Object + { + var enumerable = Object.Cast>(); + if (enumerable == null) + { + throw new ArgumentException($"IL2CPP Object of {Object.GetType()} cannot be enumerated!"); + } + + var IEnumerator = enumerable.GetEnumerator(); + return new Il2CppEnumeratorWrapper(IEnumerator.Cast()); + } + + public static IEnumerator ToIL2CPP(this global::System.Collections.IEnumerator enumerator) + { + return new IL2CPPEnumerator(enumerator).Cast(); + } + public static System.Type C (this Type type) + { + return Il2CppType.From(type); + } + + public static Type C(this System.Type type) + { + return Type.GetType(type.AssemblyQualifiedName); + } + + #region Arrays + public static A[] C(this Il2CppArrayBase arr) + { + return arr; + } + //List.Of crashes game for some reason + public static System.Collections.Generic.List CreateList(params T[] arr) + { + System.Collections.Generic.List list = new System.Collections.Generic.List(); + foreach (var t in arr) + { + list.Add(t); + } + + return list; + } + public static System.Nullable Nullify(this A a) where A : new() + { + return new System.Nullable(a); + } + public static Il2CppReferenceArray A(params A[] arr) where A : Il2CppObjectBase + { + return arr; + } + public static Il2CppStringArray A(params string[] arr) + { + return arr; + } + + public static T Cast( Il2CppObjectBase obj) where T : Il2CppObjectBase + { + return obj.Cast(); + } + public static Il2CppReferenceArray C(this A[] arr) where A : Il2CppObjectBase + { + return arr; + } + public static Il2CppStringArray C(this string[] arr) + { + return arr; + } + #endregion + public static System.Exception C(this Exception e) + { + return new System.Exception(e.Message); + } + + public static System.Collections.Generic.HashSet C(this HashSet set) + { + System.Collections.Generic.HashSet hash = new(); + foreach (var VARIABLE in set) + { + hash.Add(VARIABLE); + } + return hash; + } + public static HashSet C(this System.Collections.Generic.HashSet set) + { + HashSet hash = new(); + foreach (var VARIABLE in set) + { + hash.Add(VARIABLE); + } + return hash; + } + public static System.Collections.Generic.List C(this List e) + { + System.Collections.Generic.List list = new System.Collections.Generic.List(); + foreach (var item in e) + { + list.Add(item); + } + return list; + } + public static List C(this System.Collections.Generic.List e) + { + List list = new List(); + foreach (var item in e) + { + list.Add(item); + } + return list; + } + public static Dictionary C(this System.Collections.Generic.Dictionary e) + { + Dictionary dictionary = new Dictionary(); + foreach (var item in e) + { + dictionary.Add(item.Key, item.Value); + } + return dictionary; + } + public static System.Collections.Generic.Dictionary C(this Dictionary e) + { + System.Collections.Generic.Dictionary dictionary = new System.Collections.Generic.Dictionary(); + foreach (var item in e) + { + dictionary.Add(item.Key, item.Value); + } + return dictionary; + } + public static GameObject CreateGameObject(string name, params Type[] types) + { + Il2CppSystem.Type[] Types = new Il2CppSystem.Type[types.Length]; + List WrappedTypes = new List(); + for(int i = 0; i< types.Length; i++) + { + if(typeof(WrappedBehaviour).IsAssignableFrom(types[i])) + { + WrappedTypes.Add(types[i]); + Types[i] = typeof(Il2CPPBehaviour).C(); + } + else + { + Types[i] = types[i].C(); + } + } + GameObject obj = new GameObject(name, Types); + if (WrappedTypes.Count <= 0) return obj; + { + var behs = obj.GetComponents(); + for (int i = 0; i < WrappedTypes.Count; i++) + { + behs[i].CreateWrapperIfNull(WrappedTypes[i]); + } + } + return obj; + } +} \ No newline at end of file diff --git a/android_compatibility_module/IL2CPP/Extentions.cs b/android_compatibility_module/IL2CPP/Extentions.cs new file mode 100644 index 0000000..7815c1a --- /dev/null +++ b/android_compatibility_module/IL2CPP/Extentions.cs @@ -0,0 +1,53 @@ +using Il2CppInterop.Runtime.InteropTypes; +using Il2CppInterop.Runtime.InteropTypes.Arrays; +using UnityEngine; +namespace NeoModLoader.AndroidCompatibilityModule; +public static class Extentions +{ + public static bool IsValid(this Il2CppArrayBase arr) + { + return arr is { Length: > 0 }; + } + //il2cpp array's indexof() is not good, better to just check pointers + public static int GetIndex(this Il2CppReferenceArray arr, T obj) where T : Il2CppObjectBase + { + for (int i = 0; i < arr.Length; i++) + { + if (arr[i].Pointer == obj.Pointer) + { + return i; + } + } + + return -1; + } + public static Il2CppObjectBase Cast(this Il2CppObjectBase obj, Type type) + { + var method = typeof(Il2CppObjectBase) + .GetMethod("Cast") + .MakeGenericMethod(type); + return (Il2CppObjectBase)method.Invoke(obj, null); + } + public static Component GetComponent(this GameObject obj, Type type, int index) + { + var arr = obj.GetComponents(type.C()); + if(!arr.IsValid()) return null; + return (Component)arr[index].Cast(type); + } + public static T Instantiate(T original, Transform parent, bool worldPositionStays = true) where T : WrappedBehaviour + { + Il2CPPBehaviour il2cpp = UnityEngine.Object.Instantiate(original.Wrapper, parent, worldPositionStays); + WrapperResolver.ResolveInstantiate(original.Wrapper.gameObject, il2cpp.gameObject); + return (T)il2cpp.WrappedBehaviour; + } + public static T AddComponent(this GameObject gameObject) where T : WrappedBehaviour + { + Il2CPPBehaviour behaviour = gameObject.AddComponent(); + return behaviour.CreateWrapperIfNull(typeof(T)) as T; + } + public static WrappedBehaviour AddComponent(this GameObject gameObject, Type type) + { + Il2CPPBehaviour behaviour = gameObject.AddComponent(); + return behaviour.CreateWrapperIfNull(type); + } +} \ No newline at end of file diff --git a/android_compatibility_module/IL2CPP/Wrappers/IL2CPPEnumerator.cs b/android_compatibility_module/IL2CPP/Wrappers/IL2CPPEnumerator.cs new file mode 100644 index 0000000..f53b8fb --- /dev/null +++ b/android_compatibility_module/IL2CPP/Wrappers/IL2CPPEnumerator.cs @@ -0,0 +1,91 @@ +using System.Collections; +using System.Reflection.Emit; +using HarmonyLib; +using Il2CppInterop.Runtime; +using Il2CppInterop.Runtime.Attributes; +using Il2CppInterop.Runtime.Injection; +using ArgumentNullException = System.ArgumentNullException; +using Il2CppIEnumerator = Il2CppSystem.Collections.IEnumerator; +using IntPtr = System.IntPtr; +using NotSupportedException = System.NotSupportedException; +using Type = System.Type; +using Object = Il2CppSystem.Object; +namespace NeoModLoader.AndroidCompatibilityModule; +/// +/// source code from bepinex +/// +public class IL2CPPEnumerator : Object +{ + private static readonly Dictionary> boxers = new(); + + private readonly IEnumerator enumerator; + + static IL2CPPEnumerator() + { + ClassInjector.RegisterTypeInIl2Cpp(new RegisterTypeOptions + { + Interfaces = new[] { typeof(Il2CppIEnumerator) } + }); + } + + public IL2CPPEnumerator(IntPtr ptr) : base(ptr) { } + [HideFromIl2Cpp] + + public IL2CPPEnumerator(IEnumerator enumerator) + : base(ClassInjector.DerivedConstructorPointer()) + { + this.enumerator = enumerator ?? throw new ArgumentNullException(nameof(enumerator)); + ClassInjector.DerivedConstructorBody(this); + } + + public Object Current => enumerator.Current switch + { + Il2CppIEnumerator i => i.Cast(), + IEnumerator e => new IL2CPPEnumerator(e), + Object oo => oo, + { } obj => ManagedToIl2CppObject(obj), + null => null + }; + + public bool MoveNext() => enumerator.MoveNext(); + + public void Reset() => enumerator.Reset(); + [HideFromIl2Cpp] + + private static System.Func GetValueBoxer(Type t) + { + if (boxers.TryGetValue(t, out var conv)) + return conv; + + var dm = new DynamicMethod($"Il2CppUnbox_{t.FullDescription()}", typeof(Object), + new[] { typeof(object) }); + var il = dm.GetILGenerator(); + var loc = il.DeclareLocal(t); + var classField = typeof(Il2CppClassPointerStore<>).MakeGenericType(t) + .GetField(nameof(Il2CppClassPointerStore + .NativeClassPtr)); + il.Emit(OpCodes.Ldsfld, classField); + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Unbox_Any, t); + il.Emit(OpCodes.Stloc, loc); + il.Emit(OpCodes.Ldloca, loc); + il.Emit(OpCodes.Call, + typeof(Il2CppInterop.Runtime.IL2CPP).GetMethod(nameof(Il2CppInterop.Runtime.IL2CPP.il2cpp_value_box))); + il.Emit(OpCodes.Newobj, typeof(Object).GetConstructor(new[] { typeof(IntPtr) })); + il.Emit(OpCodes.Ret); + + var converter = dm.CreateDelegate(typeof(System.Func)) as System.Func; + boxers[t] = converter; + return converter; + } + [HideFromIl2Cpp] + private static Object ManagedToIl2CppObject(object obj) + { + var t = obj.GetType(); + if (obj is string s) + return new Object(Il2CppInterop.Runtime.IL2CPP.ManagedStringToIl2Cpp(s)); + if (t.IsPrimitive) + return GetValueBoxer(t)(obj); + throw new NotSupportedException($"Type {t} cannot be converted directly to an Il2Cpp object"); + } +} \ No newline at end of file diff --git a/android_compatibility_module/IL2CPP/Wrappers/Il2CPPBehaviour.cs b/android_compatibility_module/IL2CPP/Wrappers/Il2CPPBehaviour.cs new file mode 100644 index 0000000..2908f4c --- /dev/null +++ b/android_compatibility_module/IL2CPP/Wrappers/Il2CPPBehaviour.cs @@ -0,0 +1,88 @@ +using System.Reflection; +using Il2CppInterop.Runtime.Attributes; +using Il2CppInterop.Runtime.Injection; +using MelonLoader; +using NeoModLoader.services; +using UnityEngine; +using Object = Il2CppSystem.Object; + +namespace NeoModLoader.AndroidCompatibilityModule; +[RegisterTypeInIl2Cpp] +public class Il2CPPBehaviour : MonoBehaviour +{ + public Il2CPPBehaviour(IntPtr ptr) : base(ptr) + { + } + + public Il2CPPBehaviour() : base(ClassInjector.DerivedConstructorPointer()) + { + ClassInjector.DerivedConstructorBody(this); + } + + public void OnEnable() + { + onEnable?.Invoke(WrappedBehaviour, null); + } + + public void Start() + { + start?.Invoke(WrappedBehaviour, null); + } + + public void OnDisable() + { + onDisable?.Invoke(WrappedBehaviour, null); + } + + public void Awake() + { + awake?.Invoke(WrappedBehaviour, null); + } + public void Update() + { + update?.Invoke(WrappedBehaviour, null); + } + [HideFromIl2Cpp] + public MethodInfo GetWrappedMethod(string Method) + { + Type type = WrappedBehaviour.GetType(); + while (type != null) + { + var method = type.GetMethod( + Method, + BindingFlags.Instance | + BindingFlags.Public | + BindingFlags.NonPublic | + BindingFlags.DeclaredOnly); + + if (method != null) + return method; + + type = type.BaseType; + } + return null; + } + [HideFromIl2Cpp] + internal B SetWrappedBehaviour(B Behaviour) where B : WrappedBehaviour + { + WrappedBehaviour = Behaviour; + Behaviour.Wrapper = this; + update = GetWrappedMethod("Update"); + start = GetWrappedMethod("Start"); + awake = GetWrappedMethod("Awake"); + onEnable = GetWrappedMethod("OnEnable"); + onDisable = GetWrappedMethod("OnDisable"); + return Behaviour; + } + [HideFromIl2Cpp] + internal WrappedBehaviour CreateWrapperIfNull(Type WrappedType) + { + return WrappedBehaviour ?? SetWrappedBehaviour((WrappedBehaviour)Activator.CreateInstance(WrappedType)); + } + private MethodInfo update; + private MethodInfo start; + private MethodInfo awake; + private MethodInfo onEnable; + private MethodInfo onDisable; + public WrappedBehaviour WrappedBehaviour { [HideFromIl2Cpp]get; [HideFromIl2Cpp]private set; } +} \ No newline at end of file diff --git a/android_compatibility_module/IL2CPP/Wrappers/Il2CppEnumeratorWrapper.cs b/android_compatibility_module/IL2CPP/Wrappers/Il2CppEnumeratorWrapper.cs new file mode 100644 index 0000000..79c0e17 --- /dev/null +++ b/android_compatibility_module/IL2CPP/Wrappers/Il2CppEnumeratorWrapper.cs @@ -0,0 +1,39 @@ +using Il2CppInterop.Runtime; +using Il2CppSystem.Collections; +using NeoModLoader.services; +using IEnumerable = System.Collections.IEnumerable; + +namespace NeoModLoader.AndroidCompatibilityModule; + +public class Il2CppEnumeratorWrapper : IEnumerator, IEnumerable where T : Il2CppSystem.Object +{ + private IEnumerator _inner; + + public Il2CppEnumeratorWrapper(IEnumerator inner) + { + _inner = inner; + } + + public T Current => (T)_inner.Current; + + object System.Collections.IEnumerator.Current => _inner.Current; + + public bool MoveNext() => _inner.MoveNext(); + + public void Reset() => _inner.Reset(); + + public void Dispose() + { + _inner = null; + } + + public IEnumerator GetEnumerator() + { + return this; + } + + System.Collections.IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } +} \ No newline at end of file diff --git a/android_compatibility_module/IL2CPP/Wrappers/WrappedBehaviour.cs b/android_compatibility_module/IL2CPP/Wrappers/WrappedBehaviour.cs new file mode 100644 index 0000000..c4830b6 --- /dev/null +++ b/android_compatibility_module/IL2CPP/Wrappers/WrappedBehaviour.cs @@ -0,0 +1,43 @@ +using System.Collections; +using UnityEngine; + +namespace NeoModLoader.AndroidCompatibilityModule; + +public class WrappedBehaviour +{ + public Transform transform => Wrapper.transform; + + public GameObject gameObject => Wrapper.gameObject; + public string name + { + get => Wrapper.name; + set => Wrapper.name = value; + } + + public Il2CPPBehaviour Wrapper { get; internal set; } + public C GetComponent() where C : Component + { + return Wrapper.GetComponent(); + } + public static T Instantiate(T original, Transform parent, bool worldPositionStays = true) where T : WrappedBehaviour + { + return Extentions.Instantiate(original, parent, worldPositionStays); + } + public Coroutine StartCoroutine(IEnumerator enumerator) + { + return Wrapper.StartCoroutine(enumerator.ToIL2CPP()); + } + public static GameObject Instantiate(GameObject obj, Transform parent) + { + return GameObject.Instantiate(obj, parent); + } + public static void Destroy(GameObject Object) + { + GameObject.Destroy(Object); + } + + public WrappedBehaviour() + { + + } +} \ No newline at end of file diff --git a/android_compatibility_module/IL2CPP/Wrappers/WrapperHelpers.cs b/android_compatibility_module/IL2CPP/Wrappers/WrapperHelpers.cs new file mode 100644 index 0000000..086fe73 --- /dev/null +++ b/android_compatibility_module/IL2CPP/Wrappers/WrapperHelpers.cs @@ -0,0 +1,254 @@ + +using System.Reflection; +using NeoModLoader.AndroidCompatibilityModule; +using UnityEngine; + +public class ObjectPoolGenericMono where T : WrappedBehaviour +{ + private readonly List _elements_total = new List(); + + public readonly Queue _elements_inactive = new Queue(); + + private readonly T _prefab; + + private readonly Transform _parent_transform; + + public ObjectPoolGenericMono(T pPrefab, Transform pParentTransform) + { + _prefab = pPrefab; + _parent_transform = pParentTransform; + } + + public void clear(bool pDisable = true) + { + _elements_inactive.Clear(); + sortElements(); + foreach (T item in _elements_total) + { + release(item, pDisable); + } + } + + private void sortElements() + { + _elements_total.Sort((T a, T b) => a.transform.GetSiblingIndex().CompareTo(b.transform.GetSiblingIndex())); + } + + public T getFirstActive() + { + return _elements_total[0]; + } + + public IReadOnlyList getListTotal() + { + return _elements_total; + } + + public void disableInactive() + { + foreach (T item in _elements_inactive) + { + if (item.gameObject.activeSelf) + { + item.gameObject.SetActive(value: false); + } + } + } + + public T getNext() + { + T newOrActivate = getNewOrActivate(); + checkActive(newOrActivate); + return newOrActivate; + } + + private T getNewOrActivate() + { + T val; + if (_elements_inactive.Count > 0) + { + val = _elements_inactive.Dequeue(); + } + else + { + val = Extentions.Instantiate(_prefab, _parent_transform); + _elements_total.Add(val); + val.name = typeof(T)?.ToString() + " " + _elements_total.Count + " " + val.transform.GetSiblingIndex(); + } + return val; + } + + public void release(T pElement, bool pDisable = true) + { + if (_parent_transform.gameObject.activeInHierarchy) + { + pElement.transform.SetAsLastSibling(); + } + if (!_elements_inactive.Contains(pElement)) + { + _elements_inactive.Enqueue(pElement); + } + if (pElement.gameObject.activeSelf && pDisable) + { + pElement.gameObject.SetActive(value: false); + } + } + + private void checkActive(T pElement) + { + if (!pElement.gameObject.activeSelf) + { + pElement.gameObject.SetActive(value: true); + } + } + + public int countTotal() + { + return _elements_total.Count; + } + + public int countInactive() + { + return _elements_inactive.Count; + } + + public int countActive() + { + return _elements_total.Count - _elements_inactive.Count; + } + + public void resetParent() + { + foreach (T item in _elements_total) + { + resetParent(item); + } + } + + public void resetParent(T pElement) + { + if (_parent_transform.gameObject.activeInHierarchy) + { + pElement.transform.SetParent(_parent_transform); + } + } +} + +public static class WrapperHelper +{ + public static object GetWrappedComponent(GameObject Object, Type WrappedType) + { + foreach (Il2CPPBehaviour beh in Object.GetComponents()) + { + if (beh.WrappedBehaviour == null) + { + continue; + } + + if (beh.WrappedBehaviour.GetType().IsAssignableTo(WrappedType)) + { + return beh.WrappedBehaviour; + } + } + return null; + } +} +public class WrapperResolver +{ + static void AddChildren(Transform transform, List children) + { + for (int i = 0; i < transform.GetChildCount(); i++) + { + Transform child = transform.GetChild(i); + children.Add(child); + AddChildren(child, children); + } + } + List OrigObjects; + List ClonedObjects; + + public static void ResolveInstantiate(GameObject orig, GameObject clone) + { + WrapperResolver resolver = new WrapperResolver(orig, clone); + resolver.Resolve(); + } + public WrapperResolver(GameObject orig, GameObject clone) + { + OrigObjects = new List{orig.transform}; + ClonedObjects = new List{clone.transform}; + AddChildren(orig.transform, OrigObjects); + AddChildren(clone.transform, ClonedObjects); + } + + public void Resolve() + { + for (int i = 0; i < OrigObjects.Count; i++) + { + Il2CPPBehaviour[] origbeh = OrigObjects[i].GetComponents(); + if (origbeh == null) continue; + Il2CPPBehaviour[] clonedbeh = ClonedObjects[i].GetComponents(); + for (int j = 0; j < origbeh.Length; j++) + { + Clone(origbeh[j], clonedbeh[j]); + } + } + } + public static int Getindex(Component beh, GameObject obj) + { + var arr = obj.GetComponents(beh.GetType().C()); + if (!arr.IsValid()) + { + return -1; + } + int result = arr.GetIndex(beh); + return result; + } + public void Clone(Il2CPPBehaviour orig, Il2CPPBehaviour clone) + { + WrappedBehaviour beh = orig.WrappedBehaviour; + Type WrappedType = orig.WrappedBehaviour.GetType(); + WrappedBehaviour cloned = clone.CreateWrapperIfNull(WrappedType); + var fields = WrappedType.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); + foreach (var field in fields) + { + Type type = field.FieldType; + if (field.GetValue(beh) == null) + { + continue; + } + if (type == typeof(GameObject)) + { + var obj = (GameObject)field.GetValue(beh); + field.SetValue(cloned, ResolveGameObject(obj)); + } + else if (type== typeof(Transform)) + { + var obj = (Transform)field.GetValue(beh); + field.SetValue(cloned, ResolveGameObject(obj.gameObject).transform); + } + else if (typeof(Component).IsAssignableFrom(type)) + { + var obj = (Component)field.GetValue(beh); + var result = ResolveGameObject(obj.gameObject).GetComponent(type, Getindex(obj, obj.gameObject)); + field.SetValue(cloned, result); + } + else if (typeof(WrappedBehaviour).IsAssignableFrom(type)) + { + var obj = (WrappedBehaviour)field.GetValue(beh); + field.SetValue(cloned, ((Il2CPPBehaviour)ResolveGameObject(obj.gameObject).GetComponent(typeof(Il2CPPBehaviour), Getindex(obj.Wrapper, obj.gameObject))).CreateWrapperIfNull(type)); + } + else + { + field.SetValue(cloned, field.GetValue(beh)); + } + } + } + public GameObject ResolveGameObject(GameObject orig) + { + if (!OrigObjects.Contains(orig.transform)) + { + return orig; + } + return ClonedObjects[OrigObjects.IndexOf(orig.transform)].gameObject; + } +} \ No newline at end of file diff --git a/android_compatibility_module/MelonHelper.cs b/android_compatibility_module/MelonHelper.cs new file mode 100644 index 0000000..11f7991 --- /dev/null +++ b/android_compatibility_module/MelonHelper.cs @@ -0,0 +1,47 @@ +namespace NeoModLoader.AndroidCompatibilityModule; +#if IL2CPP +using MelonLoader.Utils; +using MelonLoader; +#endif +public static class MelonHelper +{ + #if IL2CPP + public static string GetPath() + { + return MelonEnvironment.GameRootDirectory; + } + + public static void Log(string msg) + { + UnityEngine.Debug.Log(msg); + MelonLogger.Msg(msg); + } + public static void LogError(string msg) + { + UnityEngine.Debug.LogError(msg); + MelonLogger.Error(msg); + } + public static void LogWarning(string msg) + { + UnityEngine.Debug.LogWarning(msg); + MelonLogger.Warning(msg); + } + #else + public static string GetPath() + { + return ""; + } + public static void Log(string msg) + { + UnityEngine.Debug.Log(msg); + } + public static void LogError(string msg) + { + UnityEngine.Debug.LogError(msg); + } + public static void LogWarning(string msg) + { + UnityEngine.Debug.LogWarning(msg); + } + #endif +} \ No newline at end of file diff --git a/android_compatibility_module/Mono/Converter.cs b/android_compatibility_module/Mono/Converter.cs new file mode 100644 index 0000000..169360c --- /dev/null +++ b/android_compatibility_module/Mono/Converter.cs @@ -0,0 +1,51 @@ +using System.Collections; +using UnityEngine; + +namespace NeoModLoader.AndroidCompatibilityModule; + +public static class Converter +{ + public static A C(this A a) + { + return a; + } + public static A[] A(params A[] a) + { + return a; + } + public static A? Nullify(this A a) where A : struct + { + return a; + } + public static T Cast( object obj) + { + return (T)obj; + } + public static System.Collections.Generic.List CreateList(params T[] arr) + { + System.Collections.Generic.List list = new System.Collections.Generic.List(); + foreach (var t in arr) + { + list.Add(t); + } + + return list; + } + public static D C(Delegate func) where D : System.Delegate + { + return (D)func; + } + public static IEnumerator Enumerate(this IEnumerable Object) + { + return Object.GetEnumerator(); + } + public static IEnumerator ToIL2CPP(this IEnumerator enumerator) + { + return enumerator; + } + public static GameObject CreateGameObject(string name, params Type[] types) + { + return new GameObject(name, types); + } + +} \ No newline at end of file diff --git a/android_compatibility_module/Mono/Extentions.cs b/android_compatibility_module/Mono/Extentions.cs new file mode 100644 index 0000000..1fff0a1 --- /dev/null +++ b/android_compatibility_module/Mono/Extentions.cs @@ -0,0 +1,11 @@ +using UnityEngine; + +namespace NeoModLoader.AndroidCompatibilityModule; + +public static class Extentions +{ + public static T GetWrappedComponent(this GameObject obj) + { + return obj.GetComponent(); + } +} \ No newline at end of file diff --git a/android_compatibility_module/Mono/Stubs/Il2CPPInterop.cs b/android_compatibility_module/Mono/Stubs/Il2CPPInterop.cs new file mode 100644 index 0000000..f1992cc --- /dev/null +++ b/android_compatibility_module/Mono/Stubs/Il2CPPInterop.cs @@ -0,0 +1,7 @@ +namespace Il2CppInterop.Runtime.Attributes; + +[AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Property | + AttributeTargets.Event)] +public class HideFromIl2CppAttribute : Attribute +{ +} \ No newline at end of file diff --git a/android_compatibility_module/Mono/Stubs/MelonLoader.cs b/android_compatibility_module/Mono/Stubs/MelonLoader.cs new file mode 100644 index 0000000..aef9034 --- /dev/null +++ b/android_compatibility_module/Mono/Stubs/MelonLoader.cs @@ -0,0 +1,5 @@ +namespace MelonLoader; + +[AttributeUsage(AttributeTargets.Class)] +public class RegisterTypeInIl2Cpp : Attribute +{} \ No newline at end of file diff --git a/android_compatibility_module/Mono/WrappedBehaviour.cs b/android_compatibility_module/Mono/WrappedBehaviour.cs new file mode 100644 index 0000000..08c9d62 --- /dev/null +++ b/android_compatibility_module/Mono/WrappedBehaviour.cs @@ -0,0 +1,6 @@ + +using UnityEngine; +namespace NeoModLoader.AndroidCompatibilityModule; +public class WrappedBehaviour : MonoBehaviour{ + public MonoBehaviour Wrapper => this; +} \ No newline at end of file diff --git a/api/AbstractListWindow.cs b/api/AbstractListWindow.cs index 10eb8fa..50b4b57 100644 --- a/api/AbstractListWindow.cs +++ b/api/AbstractListWindow.cs @@ -1,14 +1,15 @@ +using NeoModLoader.AndroidCompatibilityModule; using NeoModLoader.General; using UnityEngine; using UnityEngine.UI; - +using NeoModLoader.utils; namespace NeoModLoader.api; /// /// Abstract List Window Item /// /// The type of the parameter passed into to setup the item -public abstract class AbstractListWindowItem : MonoBehaviour +public abstract class AbstractListWindowItem : WrappedBehaviour { /// /// Configure the item with the given object before added to the list diff --git a/api/AbstractWideWindow.cs b/api/AbstractWideWindow.cs index 70871cf..0446808 100644 --- a/api/AbstractWideWindow.cs +++ b/api/AbstractWideWindow.cs @@ -2,7 +2,8 @@ using NeoModLoader.utils; using UnityEngine; using UnityEngine.UI; - +using NeoModLoader.AndroidCompatibilityModule; +using static NeoModLoader.AndroidCompatibilityModule.Converter; namespace NeoModLoader.api; /// @@ -57,8 +58,7 @@ public static T CreateAndInit(string pWindowId, Vector2 pSize = default) //Instance.BackgroundTransform.GetComponent().sizeDelta = new Vector2(600, 280); //Instance.BackgroundTransform.Find("CloseBackgound").localPosition = new Vector3(260, 147); - - var title_bg = new GameObject("TitleBackground", typeof(Image)); + var title_bg = CreateGameObject("TitleBackground", typeof(Image)); title_bg.transform.SetParent(Instance.BackgroundTransform); title_bg.transform.localPosition = new Vector3(0, 145); title_bg.transform.localScale = Vector3.one; diff --git a/api/AbstractWindow.cs b/api/AbstractWindow.cs index f1b70d7..48b77ef 100644 --- a/api/AbstractWindow.cs +++ b/api/AbstractWindow.cs @@ -1,13 +1,15 @@ +using NeoModLoader.AndroidCompatibilityModule; using NeoModLoader.General; +using NeoModLoader.services; using UnityEngine; using UnityEngine.UI; - +using NeoModLoader.utils; namespace NeoModLoader.api; /// /// An abstract window class that should be only one instance. /// /// -public abstract class AbstractWindow : MonoBehaviour where T : AbstractWindow +public abstract class AbstractWindow : WrappedBehaviour where T : AbstractWindow { /// /// The only instance of this class. @@ -38,7 +40,7 @@ public abstract class AbstractWindow : MonoBehaviour where T : AbstractWindow /// public static string WindowId { get; protected set; } /// - /// ÒÔ pWindowId ´´½¨²¢³õʼ»¯Ò»¸ö T ÀàÐ͵Ĵ°¿Ú + /// �� pWindowId ��������ʼ��һ�� T ���͵Ĵ��� /// /// /// diff --git a/api/AttachedModComponent.cs b/api/AttachedModComponent.cs index 90a5dcd..a8054b6 100644 --- a/api/AttachedModComponent.cs +++ b/api/AttachedModComponent.cs @@ -1,3 +1,4 @@ +using NeoModLoader.AndroidCompatibilityModule; using NeoModLoader.constants; using UnityEngine; @@ -6,7 +7,7 @@ namespace NeoModLoader.api; /// /// This class is made for ncms mod to get for themselves /// -public class AttachedModComponent : MonoBehaviour, IMod +public class AttachedModComponent : WrappedBehaviour, IMod { private ModDeclare _declare; diff --git a/api/BasicMod.cs b/api/BasicMod.cs index 065606d..a057921 100644 --- a/api/BasicMod.cs +++ b/api/BasicMod.cs @@ -1,7 +1,8 @@ +using NeoModLoader.AndroidCompatibilityModule; using NeoModLoader.constants; using NeoModLoader.services; using UnityEngine; - +using static NeoModLoader.AndroidCompatibilityModule.Converter; namespace NeoModLoader.api; /// @@ -15,7 +16,7 @@ namespace NeoModLoader.api; /// OnModLoad -> Awake -> OnEnable -> Start -> Update /// /// -public abstract class BasicMod : MonoBehaviour, IMod, ILocalizable, IConfigurable, IFeatureLoadManaged, IStagedLoad +public abstract class BasicMod : WrappedBehaviour, IMod, ILocalizable, IConfigurable, IFeatureLoadManaged, IStagedLoad where T : BasicMod { private ModConfig _config = null!; @@ -45,7 +46,7 @@ public Transform PrefabLibrary _prefab_library = transform.Find("PrefabLibrary"); if (_prefab_library == null) { - _prefab_library = new GameObject("PrefabLibrary").transform; + _prefab_library = CreateGameObject("PrefabLibrary").transform; _prefab_library.SetParent(transform); } } @@ -146,7 +147,7 @@ public ModDeclare GetDeclaration() /// public static GameObject NewPrefab(string name) { - var obj = new GameObject(name); + var obj = CreateGameObject(name); obj.transform.SetParent(Instance.PrefabLibrary); return obj; } diff --git a/api/FeatureLoadException.cs b/api/FeatureLoadException.cs index 0693b4b..be63601 100644 --- a/api/FeatureLoadException.cs +++ b/api/FeatureLoadException.cs @@ -8,7 +8,7 @@ namespace NeoModLoader.api; public class FeatureLoadException : Exception { /// - protected FeatureLoadException([NotNull]SerializationInfo info, StreamingContext context) : base(info, context) + protected FeatureLoadException(SerializationInfo info, StreamingContext context) : base(info, context) { } /// diff --git a/api/ModDeclare.cs b/api/ModDeclare.cs index d7784fc..29d7d7d 100644 --- a/api/ModDeclare.cs +++ b/api/ModDeclare.cs @@ -137,7 +137,7 @@ public ModDeclare(string pFilePath) FolderPath = Path.GetDirectoryName(pFilePath) ?? throw new Exception("Cannot get folder path from input file path"); var pathSegments = FolderPath.Split(Path.DirectorySeparatorChar); - int currentSearchIndex = pathSegments.IndexOf("workshop"); + int currentSearchIndex = Array.IndexOf(pathSegments, "workshop"); if (currentSearchIndex == -1) return; if (currentSearchIndex + 3 >= pathSegments.Length) return; if (pathSegments[++currentSearchIndex] != "content") return; diff --git a/api/ModFeatureManager.cs b/api/ModFeatureManager.cs index f7618fc..76a74f7 100644 --- a/api/ModFeatureManager.cs +++ b/api/ModFeatureManager.cs @@ -1,6 +1,9 @@ using System.Diagnostics; using System.Reflection; using JetBrains.Annotations; +using NeoModLoader.AndroidCompatibilityModule; +using NeoModLoader.utils; + namespace NeoModLoader.api; /// @@ -128,7 +131,6 @@ internal FeatureLoadPathNode(IModFeature modFeature) { ModFeature = modFeature; } - [CanBeNull] internal static FeatureLoadPathNode CreateFeatureLoadPath(FeatureTreeNode[] featureTrees) { FeatureTreeNode rootTreeNode = new FeatureTreeNode(new PlaceholderRootModFeature()); @@ -141,7 +143,7 @@ internal static FeatureLoadPathNode CreateFeatureLoadPath(FeatureTreeNode[] feat var nodesToProcess = new List(rootTreeNode.DependentFeatures); while (nodesToProcess.Count > 0) { - FeatureTreeNode treeNode = nodesToProcess.Pop(); + FeatureTreeNode treeNode = Converter.C(nodesToProcess).Pop(); FeatureLoadPathNode currentLoadPathNode = newestLoadPathNode; while (currentLoadPathNode != null) { diff --git a/api/features/ModAssetFeature.cs b/api/features/ModAssetFeature.cs index a475467..32a7e7f 100644 --- a/api/features/ModAssetFeature.cs +++ b/api/features/ModAssetFeature.cs @@ -1,3 +1,6 @@ +using NeoModLoader.AndroidCompatibilityModule; +using NeoModLoader.utils; + namespace NeoModLoader.api.features; /// @@ -21,7 +24,7 @@ public override bool Init() if (!base.Init()) return false; if (AddToLibrary) { - var library = AssetManager._instance._list.OfType>().FirstOrDefault(); + var library = AssetManager._instance._list.C().OfType>().FirstOrDefault(); if (library == null) throw new FeatureLoadException($"No library found for {typeof(TAsset).Name}"); library.add(Object); } diff --git a/constants/Others.cs b/constants/Others.cs index fa9058c..ee641b7 100644 --- a/constants/Others.cs +++ b/constants/Others.cs @@ -29,4 +29,8 @@ public static bool is_editor return false; } } + /// + /// returns if the game is currently on android. + /// + public static bool IsAndroid => Config.isAndroid; } \ No newline at end of file diff --git a/constants/Paths.cs b/constants/Paths.cs index d67b292..caebd55 100644 --- a/constants/Paths.cs +++ b/constants/Paths.cs @@ -1,5 +1,7 @@ using System.Reflection; using UnityEngine; +using NeoModLoader.AndroidCompatibilityModule; +using NeoModLoader.services; namespace NeoModLoader.constants; @@ -8,6 +10,19 @@ namespace NeoModLoader.constants; /// public static class Paths { + /// + /// path to the root melon folder on android + /// + public static readonly string MelonPath = MelonHelper.GetPath(); + + /// + /// path to the dotnet dlls inside modded woldbox apk assets on android + /// + public static readonly string DotnetAPKPath = "dotnet/shared/Microsoft.NETCore.App/8.0.6/"; + /// + /// path to melon loader assemblies if on android + /// + public static readonly string MelonAssemblies = Combine(MelonPath, "MelonLoader", "net8"); /// /// Path to the mod loader file /// @@ -19,21 +34,23 @@ public static class Paths public static readonly string PersistentDataPath = Combine(Application.persistentDataPath); /// - /// Path to folder StreamingAssets + /// Path to folder StreamingAssets, or base melon path if on android /// - public static readonly string StreamingAssetsPath = Combine(Application.streamingAssetsPath); + public static readonly string StreamingAssetsPath = !Others.IsAndroid ? Application.streamingAssetsPath : MelonPath; /// /// Path to game native Mods folder /// - public static readonly string NativeModsPath = Combine(StreamingAssetsPath, "mods"); + public static readonly string NativeModsPath = Combine(StreamingAssetsPath, Others.IsAndroid ? "mods" : "Mods"); /// - /// Path to game native Managed folder + /// Path to game native Managed folder, or IL2CPP assemblies if on android /// - public static readonly string ManagedPath = Others.is_editor - ? Combine(StreamingAssetsPath, "..", ".Managed") - : Combine(StreamingAssetsPath, "..", "Managed"); + public static readonly string ManagedPath = !Others.IsAndroid + ? Others.is_editor + ? Combine(StreamingAssetsPath, "..", ".Managed") + : Combine(StreamingAssetsPath, "..", "Managed") + : Combine(MelonPath, "MelonLoader", "Il2CppAssemblies"); /// /// Path to folder contains NML's cache @@ -52,9 +69,9 @@ public static class Paths Combine(NativeModsPath, "NeoModLoader.AutoUpdate_memload.dll"); /// - /// Path to the publicized Assembly-CSharp.dll file + /// Path to the publicized Assembly-CSharp.dll file, or just assembly-csharp on android because its already publicized /// - public static readonly string PublicizedAssemblyPath = Combine(NMLPath, "Assembly-CSharp-Publicized.dll"); + public static readonly string PublicizedAssemblyPath = Config.isAndroid ? Combine(ManagedPath, "Assembly-CSharp.dll") : Combine(NMLPath, "Assembly-CSharp-Publicized.dll"); /// /// Path to folder mods config under persistent data folder @@ -70,7 +87,7 @@ public static class Paths /// Path to Mods folder provided by NML /// public static readonly string ModsPath = - Others.is_editor ? Combine(GamePath, "Assets", "Mods") : Combine(GamePath, "Mods"); + Others.is_editor ? Combine(GamePath, "Assets", "Mods") : Combine(GamePath, Others.IsAndroid ? "NMLMods" : "Mods"); /// /// Path to extracted Assemblies cache @@ -154,16 +171,15 @@ static Paths() nml_mod_path = Combine(NativeModsPath, "NeoModLoader.dll"); if (!File.Exists(nml_mod_path)) nml_mod_path = Combine(NativeModsPath, "NeoModLoader_memload.dll"); } - NMLModPath = nml_mod_path; } - /// /// Path to game root folder /// public static string GamePath => Application.platform switch { RuntimePlatform.WindowsPlayer => Combine(StreamingAssetsPath, "..", ".."), + RuntimePlatform.Android => MelonPath, RuntimePlatform.LinuxPlayer => Combine(StreamingAssetsPath, "..", ".."), RuntimePlatform.OSXPlayer => Combine(StreamingAssetsPath, "..", "..", "..", "..", ".."), _ => Combine(StreamingAssetsPath, "..", "..") diff --git a/general/PowerButtonCreator.cs b/general/PowerButtonCreator.cs index ff3e34a..db739a4 100644 --- a/general/PowerButtonCreator.cs +++ b/general/PowerButtonCreator.cs @@ -1,4 +1,5 @@ using JetBrains.Annotations; +using static NeoModLoader.AndroidCompatibilityModule.Converter; using NeoModLoader.services; using UnityEngine; using UnityEngine.Events; @@ -25,8 +26,8 @@ public static class PowerButtonCreator /// Which transform the button attached to /// The button position in /// The PowerButton created - public static PowerButton CreateWindowButton([NotNull] string pId, [NotNull] string pWindowId, - Sprite pIcon, [CanBeNull] Transform pParent = null, Vector2 pLocalPosition = default) + public static PowerButton CreateWindowButton( string pId, string pWindowId, + Sprite pIcon, Transform pParent = null, Vector2 pLocalPosition = default) { PowerButton prefab = ResourcesFinder.FindResource("world_laws"); @@ -80,8 +81,8 @@ public static PowerButton CreateWindowButton([NotNull] string pId, [NotNull] str /// Which transform the button attached to /// The button position in /// The PowerButton created - public static PowerButton CreateSimpleButton([NotNull] string pId, UnityAction pAction, - Sprite pIcon, [CanBeNull] Transform pParent = null, Vector2 pLocalPosition = default) + public static PowerButton CreateSimpleButton( string pId, UnityAction pAction, + Sprite pIcon, Transform pParent = null, Vector2 pLocalPosition = default) { var prefab = ResourcesFinder.FindResource("world_laws"); @@ -130,7 +131,7 @@ public static PowerButton CreateSimpleButton([NotNull] string pId, UnityAction p /// The button position in /// The PowerButton created public static PowerButton CreateGodPowerButton(string pGodPowerId, Sprite pIcon, - [CanBeNull] Transform pParent = null, Vector2 pLocalPosition = default) + Transform pParent = null, Vector2 pLocalPosition = default) { PowerButton prefab = ResourcesFinder.FindResource("inspect"); @@ -181,7 +182,7 @@ public static PowerButton CreateGodPowerButton(string pGodPowerId, Sprite pIcon, /// The button position in /// Not set god power's toggle_action automatically if it's not null /// The PowerButton created - public static PowerButton CreateToggleButton(string pGodPowerId, Sprite pIcon, [CanBeNull] Transform pParent = null, + public static PowerButton CreateToggleButton(string pGodPowerId, Sprite pIcon, Transform pParent = null, Vector2 pLocalPosition = default, bool pNoAutoSetToggleAction = false) { GodPower god_power = AssetManager.powers.get(pGodPowerId); @@ -213,12 +214,12 @@ void toggleOption(string pPower) if (god_power.toggle_action == null) { - god_power.toggle_action = toggleOption; + god_power.toggle_action = C(toggleOption); } else if (!pNoAutoSetToggleAction) { - god_power.toggle_action = (PowerToggleAction)Delegate.Combine(god_power.toggle_action, - new PowerToggleAction(toggleOption)); + god_power.toggle_action = (PowerToggleAction)PowerToggleAction.Combine(god_power.toggle_action, + C(toggleOption)); } if (!PlayerConfig.dict.TryGetValue(god_power.toggle_name, out var option)) diff --git a/general/event/AbstractListener.cs b/general/event/AbstractListener.cs index 7733a14..9053b61 100644 --- a/general/event/AbstractListener.cs +++ b/general/event/AbstractListener.cs @@ -57,7 +57,7 @@ public static void RegisterHandler(THandler handler) Type type = instance.GetType(); try { - Harmony.CreateAndPatchAll(type, type.FullName); + HarmonyLib.Harmony.CreateAndPatchAll(type, type.FullName); } catch(Exception e) { diff --git a/general/event/ListenerManager.cs b/general/event/ListenerManager.cs index ea90427..7e21ed8 100644 --- a/general/event/ListenerManager.cs +++ b/general/event/ListenerManager.cs @@ -14,6 +14,11 @@ internal static class ListenerManager private static readonly HashSet _listeners = new(); public static void _init() { + if (Config.isAndroid) + { + LogService.LogWarning("Warning: listeners are not supported on android"); + return; + } Type[] types = Assembly.GetExecutingAssembly().GetTypes(); foreach (var type in types) { diff --git a/general/event/listeners/ClanCreateListener.cs b/general/event/listeners/ClanCreateListener.cs index 06c2023..b727a1b 100644 --- a/general/event/listeners/ClanCreateListener.cs +++ b/general/event/listeners/ClanCreateListener.cs @@ -4,7 +4,6 @@ using HarmonyLib; using NeoModLoader.General.Event.Handlers; using NeoModLoader.services; - namespace NeoModLoader.General.Event.Listeners; /// /// A listener at the end of method. diff --git a/general/game/extensions/AssetExtension.cs b/general/game/extensions/AssetExtension.cs index d81fb28..25fbf8e 100644 --- a/general/game/extensions/AssetExtension.cs +++ b/general/game/extensions/AssetExtension.cs @@ -1,5 +1,7 @@ using HarmonyLib; +using NeoModLoader.AndroidCompatibilityModule; using NeoModLoader.constants; +using NeoModLoader.utils; namespace NeoModLoader.General.Game.extensions; @@ -39,14 +41,14 @@ public static void ForEach(TLibrary pLibrary, Action pAction) foreach (TAsset asset in pLibrary.list) pAction(asset); state.action = asset => { pAction(asset); }; - state.done.UnionWith(pLibrary.list.Select(x => x.id)); + state.done.UnionWith(pLibrary.list.C().Select(x => x.id)); if (!_states.ContainsKey(pLibrary)) _states.Add(pLibrary, new List()); _states[pLibrary].Add(state); if (_assetlibrary_patched) return; _assetlibrary_patched = true; - new Harmony($"{CoreConstants.ModName}.ForEach").Patch( + new HarmonyLib.Harmony($"{CoreConstants.ModName}.ForEach").Patch( AccessTools.Method(typeof(AssetLibrary), nameof(AssetLibrary.add)), postfix: new HarmonyMethod( AccessTools.FirstMethod(typeof(AssetExtensionInternal), diff --git a/general/ui/prefabs/APrefab.cs b/general/ui/prefabs/APrefab.cs index 4827590..68d02bf 100644 --- a/general/ui/prefabs/APrefab.cs +++ b/general/ui/prefabs/APrefab.cs @@ -1,4 +1,5 @@ using System.Reflection; +using NeoModLoader.AndroidCompatibilityModule; using NeoModLoader.utils; using UnityEngine; @@ -11,7 +12,7 @@ namespace NeoModLoader.General.UI.Prefabs; /// To standard the prefab. You would be better to initialize prefab in '_init' and call 'Setup' for setup an object from prefab. /// /// Type of the actual prefab -public abstract class APrefab : MonoBehaviour where T : APrefab +public abstract class APrefab : WrappedBehaviour where T : APrefab { private static T mPrefab; diff --git a/general/ui/prefabs/SimpleButton.cs b/general/ui/prefabs/SimpleButton.cs index c222f07..f0f17b4 100644 --- a/general/ui/prefabs/SimpleButton.cs +++ b/general/ui/prefabs/SimpleButton.cs @@ -1,8 +1,13 @@ +#if !IL2CPP using DG.Tweening; +#else +using Il2CppDG.Tweening; +#endif +using NeoModLoader.AndroidCompatibilityModule; using UnityEngine; using UnityEngine.Events; using UnityEngine.UI; - +using static NeoModLoader.AndroidCompatibilityModule.Converter; namespace NeoModLoader.General.UI.Prefabs; /// @@ -11,6 +16,7 @@ namespace NeoModLoader.General.UI.Prefabs; /// public class SimpleButton : APrefab { + [SerializeField] private Button button; [SerializeField] private TipButton tipButton; @@ -20,7 +26,6 @@ public class SimpleButton : APrefab [SerializeField] private Image icon; [SerializeField] private Text text; - /// /// The component /// @@ -96,17 +101,17 @@ public void Setup(UnityAction pClickAction, Sprite pIcon, string pText = null, V this.TipButton.type = pTipType; if (string.IsNullOrEmpty(pTipData?.tip_name)) { - TipButton.hoverAction = TipButton.showTooltipDefault; + TipButton.hoverAction =C (TipButton.showTooltipDefault); } else { - TipButton.hoverAction = () => + TipButton.hoverAction =C (() => { Tooltip.show(gameObject, TipButton.type, pTipData); transform.localScale = new Vector3(1.1f, 1.1f, 1.1f); transform.DOKill(); transform.DOScale(1f, 0.1f).SetEase(Ease.InBack); - }; + }); } } } @@ -122,18 +127,18 @@ public override void SetSize(Vector2 pSize) internal static void _init() { - GameObject obj = new GameObject(nameof(SimpleButton), typeof(Button), typeof(Image), typeof(TipButton)); + GameObject obj = CreateGameObject(nameof(SimpleButton), typeof(Button), typeof(Image), typeof(TipButton)); obj.transform.SetParent(WorldBoxMod.Transform); obj.GetComponent().enabled = false; obj.GetComponent().sprite = SpriteTextureLoader.getSprite("ui/special/special_buttonRed"); obj.GetComponent().type = Image.Type.Sliced; - GameObject icon = new GameObject("Icon", typeof(Image)); + GameObject icon = CreateGameObject("Icon", typeof(Image)); icon.transform.SetParent(obj.transform); icon.transform.localPosition = Vector3.zero; icon.transform.localScale = Vector3.one; - GameObject text = new GameObject("Text", typeof(Text)); + GameObject text = CreateGameObject("Text", typeof(Text)); text.transform.SetParent(obj.transform); text.transform.localPosition = Vector3.zero; text.transform.localScale = Vector3.one; diff --git a/general/ui/prefabs/SimpleStatBar.cs b/general/ui/prefabs/SimpleStatBar.cs index d51e830..a52fca0 100644 --- a/general/ui/prefabs/SimpleStatBar.cs +++ b/general/ui/prefabs/SimpleStatBar.cs @@ -1,6 +1,7 @@ +using NeoModLoader.AndroidCompatibilityModule; using UnityEngine; using UnityEngine.UI; - +using static NeoModLoader.AndroidCompatibilityModule.Converter; namespace NeoModLoader.General.UI.Prefabs; /// @@ -9,6 +10,7 @@ namespace NeoModLoader.General.UI.Prefabs; /// public class SimpleStatBar : APrefab { + [SerializeField] private Image _background; [SerializeField] private Image _bar; @@ -98,14 +100,14 @@ public void UpdateBar(float value, float max_value, string pEndText, Color pBarC internal static void _init() { - GameObject stat_bar_obj = new("SimpleStatBar", typeof(Button), typeof(TipButton), + GameObject stat_bar_obj = CreateGameObject("SimpleStatBar", typeof(Button), typeof(TipButton), typeof(Image)); stat_bar_obj.transform.SetParent(WorldBoxMod.Transform); stat_bar_obj.transform.localScale = Vector3.one; stat_bar_obj.GetComponent().sizeDelta = new Vector2(100, 14f); stat_bar_obj.GetComponent().type = Image.Type.Sliced; - GameObject background = new("Background", typeof(Image)); + GameObject background = CreateGameObject("Background", typeof(Image)); background.transform.SetParent(stat_bar_obj.transform); Image image_background = background.GetComponent(); image_background.sprite = SpriteTextureLoader.getSprite("ui/special/windowInnerSliced"); @@ -113,16 +115,15 @@ internal static void _init() image_background.color = new Color(0.49f, 0.49f, 0.49f); - GameObject mask = new("Mask", typeof(Image), typeof(Mask)); + GameObject mask = CreateGameObject("Mask", typeof(Image), typeof(Mask)); mask.transform.SetParent(stat_bar_obj.transform); Mask mask_mask = mask.GetComponent(); mask_mask.showMaskGraphic = false; mask.GetComponent().pivot = new Vector2(0, 0.5f); mask.GetComponent().anchorMax = new Vector2(0, 0.5f); mask.GetComponent().anchorMin = new Vector2(0, 0.5f); - - - GameObject bar = new("Bar", typeof(Image)); + + GameObject bar = CreateGameObject("Bar", typeof(Image)); bar.transform.SetParent(mask.transform); Image image_bar = bar.GetComponent(); image_bar.sprite = SpriteTextureLoader.getSprite("ui/special/windowBar"); @@ -130,12 +131,12 @@ internal static void _init() bar.GetComponent().anchorMax = new Vector2(0, 0.5f); bar.GetComponent().anchorMin = new Vector2(0, 0.5f); - GameObject icon = new("Icon", typeof(Image), typeof(Shadow)); + GameObject icon = CreateGameObject("Icon", typeof(Image), typeof(Shadow)); icon.transform.SetParent(stat_bar_obj.transform); Image image_icon = icon.GetComponent(); image_icon.sprite = SpriteTextureLoader.getSprite("ui/icons/iconHealth"); - GameObject text = new("Text", typeof(Text), typeof(Shadow)); + GameObject text = CreateGameObject("Text", typeof(Text), typeof(Shadow)); text.transform.SetParent(stat_bar_obj.transform); Text text_text = text.GetComponent(); text_text.text = "0/0"; diff --git a/general/ui/prefabs/SliderBar.cs b/general/ui/prefabs/SliderBar.cs index 485d0b1..8616833 100644 --- a/general/ui/prefabs/SliderBar.cs +++ b/general/ui/prefabs/SliderBar.cs @@ -1,7 +1,9 @@ +using NeoModLoader.AndroidCompatibilityModule; +using NeoModLoader.utils; using UnityEngine; using UnityEngine.Events; using UnityEngine.UI; - +using static NeoModLoader.AndroidCompatibilityModule.Converter; namespace NeoModLoader.General.UI.Prefabs; /// @@ -25,10 +27,10 @@ namespace NeoModLoader.General.UI.Prefabs; /// public class SliderBar : APrefab { + [SerializeField] private Slider _slider; [SerializeField] private TipButton _tip_button; - public Slider slider => _slider; /// @@ -81,33 +83,33 @@ public override void SetSize(Vector2 size) internal static void _init() { - GameObject slider_bar = new GameObject("SliderBar", typeof(Slider), typeof(TipButton)); + GameObject slider_bar = CreateGameObject("SliderBar", typeof(Slider), typeof(TipButton)); slider_bar.transform.SetParent(WorldBoxMod.Transform); slider_bar.GetComponent().sizeDelta = new(172, 20); - GameObject background = new GameObject("Background", typeof(Image)); + GameObject background = CreateGameObject("Background", typeof(Image)); background.transform.SetParent(slider_bar.transform); background.transform.localScale = Vector3.one; background.GetComponent().sizeDelta = new(0, 0); background.GetComponent().sprite = SpriteTextureLoader.getSprite("ui/special/special_buttonGray"); background.GetComponent().type = Image.Type.Sliced; - GameObject fill_area = new GameObject("Fill Area", typeof(RectTransform)); + GameObject fill_area = CreateGameObject("Fill Area", typeof(RectTransform)); fill_area.transform.SetParent(slider_bar.transform); fill_area.transform.localScale = Vector3.one; fill_area.GetComponent().sizeDelta = new(-20, 0); - GameObject fill = new GameObject("Fill", typeof(Image)); + GameObject fill = CreateGameObject("Fill", typeof(Image)); fill.transform.SetParent(fill_area.transform); fill.transform.localScale = Vector3.one; fill.GetComponent().sizeDelta = new(10, 0); fill.GetComponent().sprite = SpriteTextureLoader.getSprite("ui/special/special_buttonRed"); fill.GetComponent().type = Image.Type.Sliced; - GameObject handle_area = new GameObject("Handle Slide Area", typeof(RectTransform)); + GameObject handle_area = CreateGameObject("Handle Slide Area", typeof(RectTransform)); handle_area.transform.SetParent(slider_bar.transform); handle_area.transform.localScale = Vector3.one; handle_area.GetComponent().sizeDelta = new(-20, 0); - GameObject handle = new GameObject("Handle", typeof(Image)); + GameObject handle = CreateGameObject("Handle", typeof(Image)); handle.transform.SetParent(handle_area.transform); handle.transform.localScale = Vector3.one; handle.GetComponent().sprite = SpriteTextureLoader.getSprite("ui/special/special_buttonRed"); diff --git a/general/ui/prefabs/SwitchButton.cs b/general/ui/prefabs/SwitchButton.cs index ed32e38..6ce06f6 100644 --- a/general/ui/prefabs/SwitchButton.cs +++ b/general/ui/prefabs/SwitchButton.cs @@ -1,10 +1,14 @@ +using NeoModLoader.AndroidCompatibilityModule; +using NeoModLoader.utils; using UnityEngine; +using UnityEngine.Events; using UnityEngine.UI; - +using static NeoModLoader.AndroidCompatibilityModule.Converter; namespace NeoModLoader.General.UI.Prefabs; public class SwitchButton : APrefab { + [SerializeField] private Button _button; [SerializeField] private Image _icon; @@ -12,7 +16,6 @@ public class SwitchButton : APrefab [SerializeField] private Text _text; [SerializeField] private TipButton _tip_button; - public Button button => _button; public Image icon => _icon; public Text text => _text; @@ -31,16 +34,16 @@ public void Setup(bool value, Action value_update) : SpriteTextureLoader.getSprite("ui/icons/iconOff"); text.text = value ? LM.Get("short_on") : LM.Get("short_off"); button.onClick.RemoveAllListeners(); - button.onClick.AddListener(() => + button.onClick.AddListener(C(() => { value_update(); Setup(!value, value_update); - }); + })); } internal static void _init() { - GameObject switch_button = new GameObject("SwitchButton", typeof(Image), typeof(Button), typeof(TipButton), + GameObject switch_button = CreateGameObject("SwitchButton", typeof(Image), typeof(Button), typeof(TipButton), typeof(HorizontalLayoutGroup)); switch_button.transform.SetParent(WorldBoxMod.Transform); switch_button.transform.localScale = Vector3.one; @@ -53,11 +56,11 @@ internal static void _init() switch_button.GetComponent().sprite = SpriteTextureLoader.getSprite("ui/special/special_buttonRed"); switch_button.GetComponent().type = Image.Type.Sliced; - GameObject switch_button_icon = new GameObject("Icon", typeof(Image)); + GameObject switch_button_icon = CreateGameObject("Icon", typeof(Image)); switch_button_icon.transform.SetParent(switch_button.transform); switch_button_icon.transform.localScale = Vector3.one; switch_button_icon.GetComponent().sizeDelta = new(18, 18); - GameObject switch_button_text = new GameObject("Text", typeof(Text)); + GameObject switch_button_text = CreateGameObject("Text", typeof(Text)); switch_button_text.transform.SetParent(switch_button.transform); switch_button_text.transform.localScale = Vector3.one; switch_button_text.GetComponent().sizeDelta = new(24, 18); diff --git a/general/ui/prefabs/TextInput.cs b/general/ui/prefabs/TextInput.cs index ca360ca..785fbec 100644 --- a/general/ui/prefabs/TextInput.cs +++ b/general/ui/prefabs/TextInput.cs @@ -1,7 +1,9 @@ +using NeoModLoader.AndroidCompatibilityModule; +using NeoModLoader.utils; using UnityEngine; using UnityEngine.Events; using UnityEngine.UI; - +using static NeoModLoader.AndroidCompatibilityModule.Converter; namespace NeoModLoader.General.UI.Prefabs; /// @@ -10,6 +12,7 @@ namespace NeoModLoader.General.UI.Prefabs; /// public class TextInput : APrefab { + [SerializeField] private Image _icon; [SerializeField] private InputField _input; @@ -17,7 +20,6 @@ public class TextInput : APrefab [SerializeField] private Text _text; [SerializeField] private TipButton _tip_button; - public Image icon => _icon; public InputField input => _input; @@ -86,14 +88,14 @@ public override void SetSize(Vector2 size) internal static void _init() { - GameObject text_input = new GameObject("TextInput", typeof(TipButton), typeof(Image)); + GameObject text_input = CreateGameObject("TextInput", typeof(TipButton), typeof(Image)); text_input.transform.SetParent(WorldBoxMod.Transform); Image bg = text_input.GetComponent(); bg.sprite = SpriteTextureLoader.getSprite("ui/special/darkInputFieldEmpty"); bg.type = Image.Type.Sliced; - GameObject input_field = new GameObject("InputField", typeof(Text), typeof(InputField)); + GameObject input_field = CreateGameObject("InputField", typeof(Text), typeof(InputField)); input_field.transform.SetParent(text_input.transform); input_field.transform.localScale = Vector3.one; input_field.GetComponent().pivot = new Vector2(0, 0.5f); @@ -108,7 +110,7 @@ internal static void _init() input.text = ""; input.lineType = InputField.LineType.SingleLine; - GameObject icon = new GameObject("Icon", typeof(Image)); + GameObject icon = CreateGameObject("Icon", typeof(Image)); icon.transform.SetParent(text_input.transform); icon.transform.localScale = Vector3.one; icon.GetComponent().sprite = SpriteTextureLoader.getSprite("ui/special/inputFieldIcon"); diff --git a/general/ui/tab/TabManager.cs b/general/ui/tab/TabManager.cs index f6935d6..34b5e44 100644 --- a/general/ui/tab/TabManager.cs +++ b/general/ui/tab/TabManager.cs @@ -1,12 +1,14 @@ using System.Reflection.Emit; using HarmonyLib; +using NeoModLoader.AndroidCompatibilityModule; using NeoModLoader.constants; +using NeoModLoader.utils; using Newtonsoft.Json; using UnityEngine; -using UnityEngine.EventSystems; +using UnityEngine.Events; +using static NeoModLoader.AndroidCompatibilityModule.Converter; using UnityEngine.UI; using Object = UnityEngine.Object; - namespace NeoModLoader.General.UI.Tab; public static class TabManager @@ -27,7 +29,7 @@ public static class TabManager "CanvasBottom/BottomElements/BottomElementsMover/CanvasScrollView/Scroll View/Viewport/Content/Power Tabs"); private static readonly List