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/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..b29afe7 --- /dev/null +++ b/android_compatibility_module/IL2CPP/Converter.cs @@ -0,0 +1,173 @@ +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; + } + + 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 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..b5e1dfe --- /dev/null +++ b/android_compatibility_module/Mono/Converter.cs @@ -0,0 +1,37 @@ +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 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