diff --git a/Editor/BuildProcessors/CheckSettings.cs b/Editor/BuildProcessors/CheckSettings.cs index 85dbdd0..2cf0f00 100644 --- a/Editor/BuildProcessors/CheckSettings.cs +++ b/Editor/BuildProcessors/CheckSettings.cs @@ -55,7 +55,7 @@ namespace HybridCLR.Editor.BuildProcessors HybridCLRSettings gs = SettingsUtil.HybridCLRSettings; if (((gs.hotUpdateAssemblies?.Length + gs.hotUpdateAssemblyDefinitions?.Length) ?? 0) == 0) { - throw new Exception($"HybridCLRSettings中未配置任何热更新模块"); + Debug.LogWarning("[CheckSettings] HybridCLRSettings中未配置任何热更新模块"); } } diff --git a/Editor/Commands/DifferentialHybridExecutionCommand.cs b/Editor/Commands/DifferentialHybridExecutionCommand.cs new file mode 100644 index 0000000..6478c7a --- /dev/null +++ b/Editor/Commands/DifferentialHybridExecutionCommand.cs @@ -0,0 +1,56 @@ +using HybridCLR.Editor.DHE; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEditor; +using UnityEngine; + +namespace HybridCLR.Editor.Commands +{ + public static class DifferentialHybridExecutionCommand + { + [MenuItem("HybridCLR/Generate/DHEAssemblyList", priority = 110)] + public static void GenerateAssemblyList() + { + var options = new AssemblyListGenerator.Options() + { + DifferentialHybridAssembyList = (HybridCLRSettings.Instance.differentialHybridAssemblies ?? Array.Empty()).ToList(), + OutputFile = $"{SettingsUtil.LocalIl2CppDir}/libil2cpp/hybridclr/Il2CppCompatibleDef.cpp", + }; + + var g = new AssemblyListGenerator(options); + g.Generate(); + } + + + [MenuItem("HybridCLR/Generate/DHEAssemblyOptionDatas", priority = 111)] + public static void GenerateAssemblyOptionDatas() + { + GenerateAssemblyOptionDatas(true); + } + + public static void GenerateAssemblyOptionDatas(bool compileDll) + { + if (compileDll) + { + CompileDllCommand.CompileDllActiveBuildTarget(); + } + string[] differentialHybridAssemblyList = HybridCLRSettings.Instance.differentialHybridAssemblies; + if (differentialHybridAssemblyList == null || differentialHybridAssemblyList.Length == 0) + { + Debug.Log("[DifferentialHybridExecutionCommand.GenerateAssemblyOptionDatas] differentialHybridAssemblies is empty. skip generation."); + return; + } + var options = new AssemblyOptionDataGenerator.Options() + { + DifferentialHybridAssembyList = differentialHybridAssemblyList.ToList(), + OutputDir = $"{SettingsUtil.ProjectDir}/{HybridCLRSettings.Instance.differentialHybridOptionOutputDir}", + }; + + var g = new AssemblyOptionDataGenerator(options); + g.Generate(); + } + } +} diff --git a/Editor/Commands/DifferentialHybridExecutionCommand.cs.meta b/Editor/Commands/DifferentialHybridExecutionCommand.cs.meta new file mode 100644 index 0000000..c74092b --- /dev/null +++ b/Editor/Commands/DifferentialHybridExecutionCommand.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 998b5f512dc60924e953a558ed250e15 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/Commands/Il2CppDefGeneratorCommand.cs b/Editor/Commands/Il2CppDefGeneratorCommand.cs new file mode 100644 index 0000000..97a407a --- /dev/null +++ b/Editor/Commands/Il2CppDefGeneratorCommand.cs @@ -0,0 +1,28 @@ +using HybridCLR.Editor.Link; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using UnityEditor; +using UnityEngine; + +namespace HybridCLR.Editor.Commands +{ + + public static class Il2CppDefGeneratorCommand + { + + [MenuItem("HybridCLR/Generate/Il2CppDef", priority = 104)] + public static void GenerateIl2CppDef() + { + var options = new Il2CppDef.Il2CppDefGenerator.Options() + { + UnityVersion = UnityEngine.Application.unityVersion, + OutputFile = $"{SettingsUtil.LocalIl2CppDir}/libil2cpp/hybridclr/Il2CppCompatibleDef.h", + }; + + var g = new Il2CppDef.Il2CppDefGenerator(options); + g.Generate(); + } + } +} diff --git a/Editor/Commands/Il2CppDefGeneratorCommand.cs.meta b/Editor/Commands/Il2CppDefGeneratorCommand.cs.meta new file mode 100644 index 0000000..21f9427 --- /dev/null +++ b/Editor/Commands/Il2CppDefGeneratorCommand.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5165a065d05497c43a2fff885f31ed07 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/Commands/PrebuildCommand.cs b/Editor/Commands/PrebuildCommand.cs index a3373d7..d2c97b0 100644 --- a/Editor/Commands/PrebuildCommand.cs +++ b/Editor/Commands/PrebuildCommand.cs @@ -15,6 +15,9 @@ namespace HybridCLR.Editor.Commands [MenuItem("HybridCLR/Generate/All", priority = 200)] public static void GenerateAll() { + Il2CppDefGeneratorCommand.GenerateIl2CppDef(); + DifferentialHybridExecutionCommand.GenerateAssemblyList(); + DifferentialHybridExecutionCommand.GenerateAssemblyOptionDatas(); // 顺序随意 ReversePInvokeWrapperGeneratorCommand.GenerateReversePInvokeWrapper(); diff --git a/Editor/DHE.meta b/Editor/DHE.meta new file mode 100644 index 0000000..d2c3a4c --- /dev/null +++ b/Editor/DHE.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 1e6e11530acf7134796d0a439a6bbe61 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/DHE/AssemblyListGenerator.cs b/Editor/DHE/AssemblyListGenerator.cs new file mode 100644 index 0000000..9b4f9b5 --- /dev/null +++ b/Editor/DHE/AssemblyListGenerator.cs @@ -0,0 +1,44 @@ +using HybridCLR.Editor.Template; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using UnityEngine; + +namespace HybridCLR.Editor.DHE +{ + public class AssemblyListGenerator + { + public class Options + { + public string OutputFile { get; set; } + + public List DifferentialHybridAssembyList { get; set; } + } + + private readonly Options _options; + public AssemblyListGenerator(Options options) + { + _options = options; + } + + public void Generate() + { + var frr = new FileRegionReplace(File.ReadAllText(_options.OutputFile)); + + List lines = new List(); + foreach (var ass in _options.DifferentialHybridAssembyList) + { + lines.Add($"\t\t\"{ass}\","); + } + + frr.Replace("DHE", string.Join("\n", lines)); + + frr.Commit(_options.OutputFile); + Debug.Log($"output:{_options.OutputFile}"); + } + } +} diff --git a/Editor/DHE/AssemblyListGenerator.cs.meta b/Editor/DHE/AssemblyListGenerator.cs.meta new file mode 100644 index 0000000..f6d21a8 --- /dev/null +++ b/Editor/DHE/AssemblyListGenerator.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d6be260bc9a8a40468a82b9ea49b89a1 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/DHE/AssemblyOptionDataGenerator.cs b/Editor/DHE/AssemblyOptionDataGenerator.cs new file mode 100644 index 0000000..f9d7bfe --- /dev/null +++ b/Editor/DHE/AssemblyOptionDataGenerator.cs @@ -0,0 +1,68 @@ +using dnlib.DotNet; +using HybridCLR.Editor.Commands; +using HybridCLR.Editor.Meta; +using HybridCLR.Runtime; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; +using UnityEditor; +using UnityEngine; + +namespace HybridCLR.Editor.DHE +{ + public class AssemblyOptionDataGenerator + { + public class Options + { + public string OutputDir { get; set; } + + public List DifferentialHybridAssembyList { get; set; } + } + + private readonly Options _options; + + public AssemblyOptionDataGenerator(Options options) + { + _options = options; + } + + public void Generate() + { + foreach(string assName in _options.DifferentialHybridAssembyList) + { + using (var assCollector = new AssemblyCache(MetaUtil.CreateBuildTargetAssemblyResolver(EditorUserBuildSettings.activeBuildTarget))) + { + var ass = assCollector.LoadModule(assName); + + var dhaOptions = new DifferentialHybridAssemblyOptions() + { + notChangeMethodTokens = new List(), + }; + + foreach (var type in ass.GetTypes()) + { + foreach (var method in type.Methods) + { + var attr = method.CustomAttributes.Where(a => a.AttributeType.FullName == "HybridCLR.Runtime.UnchangedAttribute").FirstOrDefault(); + if (attr != null) + { + if ((bool)attr.ConstructorArguments[0].Value) + { + dhaOptions.notChangeMethodTokens.Add(method.MDToken.Raw); + Debug.Log($"Unchanged method:{method.MDToken.Raw} {method}"); + } + } + } + } + string outOptionFile = $"{_options.OutputDir}/{assName}.dhao.bytes"; + File.WriteAllBytes(outOptionFile, dhaOptions.Marshal()); + Debug.Log($"[AssemblyOptionDataGenerator] assembly:{ass} output:{outOptionFile}"); + } + } + } + } +} diff --git a/Editor/DHE/AssemblyOptionDataGenerator.cs.meta b/Editor/DHE/AssemblyOptionDataGenerator.cs.meta new file mode 100644 index 0000000..3ccdeaa --- /dev/null +++ b/Editor/DHE/AssemblyOptionDataGenerator.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4a4eda50e51d72d499b439da236a8371 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/Il2CppDef.meta b/Editor/Il2CppDef.meta new file mode 100644 index 0000000..dc951ca --- /dev/null +++ b/Editor/Il2CppDef.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: da46bc9f1a4dece41a5c193166be9a30 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/Il2CppDef/Il2CppDefGenerator.cs b/Editor/Il2CppDef/Il2CppDefGenerator.cs new file mode 100644 index 0000000..cac458a --- /dev/null +++ b/Editor/Il2CppDef/Il2CppDefGenerator.cs @@ -0,0 +1,59 @@ +using HybridCLR.Editor.ABI; +using HybridCLR.Editor.Template; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using UnityEngine; + +namespace HybridCLR.Editor.Il2CppDef +{ + public class Il2CppDefGenerator + { + public class Options + { + public string OutputFile { get; set; } + + public string UnityVersion { get; set; } + } + + private readonly Options _options; + public Il2CppDefGenerator(Options options) + { + _options = options; + } + + + private static readonly Regex s_unityVersionPat = new Regex(@"(\d+)\.(\d+)\.(\d+)"); + + public void Generate() + { + var frr = new FileRegionReplace(File.ReadAllText(_options.OutputFile)); + + List lines = new List(); + + var match = s_unityVersionPat.Matches(_options.UnityVersion)[0]; + int majorVer = int.Parse(match.Groups[1].Value); + int minorVer1 = int.Parse(match.Groups[2].Value); + int minorVer2 = int.Parse(match.Groups[3].Value); + + lines.Add($"#define HYBRIDCLR_UNITY_VERSION {majorVer}{minorVer1.ToString("D2")}{minorVer2.ToString("D2")}"); + lines.Add($"#define HYBRIDCLR_UNITY_{majorVer} 1"); + for (int ver = 2019; ver <= 2024; ver++) + { + if (majorVer >= ver) + { + lines.Add($"#define HYBRIDCLR_UNITY_{ver}_OR_NEW 1"); + } + } + + frr.Replace("UNITY_CONFIG", string.Join("\n", lines)); + + frr.Commit(_options.OutputFile); + Debug.Log($"[HybridCLR.Editor.Il2CppDef.Generator] output:{_options.OutputFile}"); + } + } +} diff --git a/Editor/Il2CppDef/Il2CppDefGenerator.cs.meta b/Editor/Il2CppDef/Il2CppDefGenerator.cs.meta new file mode 100644 index 0000000..b7fe9d7 --- /dev/null +++ b/Editor/Il2CppDef/Il2CppDefGenerator.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 590419ee7e82ac24cbac9b8a48891fe0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/Settings/HybridCLRSettingProvider.cs b/Editor/Settings/HybridCLRSettingProvider.cs index f2926c1..9207783 100644 --- a/Editor/Settings/HybridCLRSettingProvider.cs +++ b/Editor/Settings/HybridCLRSettingProvider.cs @@ -18,6 +18,8 @@ namespace HybridCLR.Editor private SerializedProperty _hotUpdateDllCompileOutputRootDir; private SerializedProperty _strippedAOTDllOutputRootDir; private SerializedProperty _patchAOTAssemblies; + private SerializedProperty _differentialHybridAssemblies; + private SerializedProperty _differentialHybridOptionOutputDir; private SerializedProperty _collectAssetReferenceTypes; private SerializedProperty _outputLinkFile; private SerializedProperty _outputAOTGenericReferenceFile; @@ -44,6 +46,8 @@ namespace HybridCLR.Editor _hotUpdateDllCompileOutputRootDir = _serializedObject.FindProperty("hotUpdateDllCompileOutputRootDir"); _strippedAOTDllOutputRootDir = _serializedObject.FindProperty("strippedAOTDllOutputRootDir"); _patchAOTAssemblies = _serializedObject.FindProperty("patchAOTAssemblies"); + _differentialHybridAssemblies = _serializedObject.FindProperty("differentialHybridAssemblies"); + _differentialHybridOptionOutputDir = _serializedObject.FindProperty("differentialHybridOptionOutputDir"); _collectAssetReferenceTypes = _serializedObject.FindProperty("collectAssetReferenceTypes"); _outputLinkFile = _serializedObject.FindProperty("outputLinkFile"); _outputAOTGenericReferenceFile = _serializedObject.FindProperty("outputAOTGenericReferenceFile"); @@ -131,6 +135,8 @@ namespace HybridCLR.Editor EditorGUILayout.PropertyField(_hotUpdateDllCompileOutputRootDir); EditorGUILayout.PropertyField(_strippedAOTDllOutputRootDir); EditorGUILayout.PropertyField(_patchAOTAssemblies); + EditorGUILayout.PropertyField(_differentialHybridAssemblies); + EditorGUILayout.PropertyField(_differentialHybridOptionOutputDir); EditorGUILayout.PropertyField(_collectAssetReferenceTypes); EditorGUILayout.PropertyField(_outputLinkFile); EditorGUILayout.PropertyField(_outputAOTGenericReferenceFile); diff --git a/Editor/Settings/HybridCLRSettings.cs b/Editor/Settings/HybridCLRSettings.cs index a3d6ffe..053fc53 100644 --- a/Editor/Settings/HybridCLRSettings.cs +++ b/Editor/Settings/HybridCLRSettings.cs @@ -32,6 +32,12 @@ namespace HybridCLR.Editor [Header("补充元数据AOT dlls")] public string[] patchAOTAssemblies; + [Header("差分混合热更新 dlls")] + public string[] differentialHybridAssemblies; + + [Header("差分混合热更新配置数据输出目录")] + public string differentialHybridOptionOutputDir = "Assets/StreamingAssets"; + [Header("生成link.xml时扫描asset中引用的类型")] public bool collectAssetReferenceTypes; diff --git a/Runtime/DifferentialHybridAssemblyOptions.cs b/Runtime/DifferentialHybridAssemblyOptions.cs new file mode 100644 index 0000000..ab76468 --- /dev/null +++ b/Runtime/DifferentialHybridAssemblyOptions.cs @@ -0,0 +1,33 @@ +using System.Collections.Generic; +using System.IO; +using UnityEngine; + +namespace HybridCLR.Runtime +{ + public class DifferentialHybridAssemblyOptions + { + public const uint Signature = 0xABCDABCD; + + public List notChangeMethodTokens; + + + public byte[] Marshal() + { + var stream = new MemoryStream(); + var writer = new BinaryWriter(stream); + writer.Write(Signature); + writer.Write((uint)notChangeMethodTokens.Count); + foreach (uint token in notChangeMethodTokens) + { + writer.Write(token); + } + writer.Flush(); + stream.Flush(); + byte[] result = new byte[stream.Length]; + stream.Position = 0; + stream.Read(result, 0, result.Length); + Debug.Log($"HotPatchAssemblyConfig. options bytes:{result.Length}"); + return result; + } + } +} diff --git a/Runtime/DifferentialHybridAssemblyOptions.cs.meta b/Runtime/DifferentialHybridAssemblyOptions.cs.meta new file mode 100644 index 0000000..d68dd5f --- /dev/null +++ b/Runtime/DifferentialHybridAssemblyOptions.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3394055921ccce643b09466544ea3634 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/RuntimeApi.cs b/Runtime/RuntimeApi.cs index e8fb681..3019212 100644 --- a/Runtime/RuntimeApi.cs +++ b/Runtime/RuntimeApi.cs @@ -31,7 +31,7 @@ namespace HybridCLR #else fixed(byte* data = dllBytes) { - return (LoadImageErrorCode)LoadMetadataForAOTAssembly((IntPtr)data, dllBytes.Length, (int)mode); + return (LoadImageErrorCode)LoadMetadataForAOTAssembly(data, dllBytes.Length, (int)mode); } #endif } @@ -43,7 +43,40 @@ namespace HybridCLR /// /// [DllImport(dllName, EntryPoint = "RuntimeApi_LoadMetadataForAOTAssembly")] - public static extern int LoadMetadataForAOTAssembly(IntPtr dllBytes, int dllSize, int mode); + public static extern unsafe int LoadMetadataForAOTAssembly(byte* dllBytes, int dllSize, int mode); + + + //[DllImport(dllName, EntryPoint = "RuntimeApi_UseDifferentialHybridAOTAssembly")] + //public static unsafe LoadImageErrorCode UseDifferentialHybridAOTAssembly(string assemblyName) + //{ + // byte[] nameBytes = System.Text.Encoding.UTF8.GetBytes(assemblyName); + // fixed(byte* namePtr = nameBytes) + // { + + // } + //} + + [DllImport(dllName, EntryPoint = "RuntimeApi_UseDifferentialHybridAOTAssembly")] + public static extern LoadImageErrorCode UseDifferentialHybridAOTAssembly(string assemblyName); + + + public static unsafe LoadImageErrorCode LoadDifferentialHybridAssembly(byte[] dllBytes, byte[] optionBytes) + { +#if UNITY_EDITOR + return LoadImageErrorCode.OK; +#else + fixed(byte* dllBytesPtr = dllBytes) + { + fixed(byte* tokenPtr = optionBytes) + { + return (LoadImageErrorCode)LoadDifferentialHybridAssembly(dllBytesPtr, dllBytes.Length, tokenPtr, optionBytes.Length); + } + } +#endif + } + + [DllImport(dllName, EntryPoint = "RuntimeApi_LoadDifferentialHybridAssembly")] + public static extern unsafe int LoadDifferentialHybridAssembly(byte* dllBytes, int dllSize, byte* notChangeMethodTokens, int tokenCount); /// /// 获取解释器线程栈的最大StackObject个数(size*8 为最终占用的内存大小) diff --git a/Runtime/UnchangedAttribute.cs b/Runtime/UnchangedAttribute.cs new file mode 100644 index 0000000..fd974b9 --- /dev/null +++ b/Runtime/UnchangedAttribute.cs @@ -0,0 +1,18 @@ +using System; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace HybridCLR.Runtime +{ + [AttributeUsage(AttributeTargets.Method)] + public class UnchangedAttribute : Attribute + { + public bool Unchanged { get; } + + public UnchangedAttribute(bool unchanged = true) + { + Unchanged = unchanged; + } + } +} diff --git a/Runtime/UnchangedAttribute.cs.meta b/Runtime/UnchangedAttribute.cs.meta new file mode 100644 index 0000000..59c7de1 --- /dev/null +++ b/Runtime/UnchangedAttribute.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 704603a2a0551f247a30e331ca476870 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/package.json b/package.json index 1ef69e4..3bfd478 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "com.focus-creative-games.hybridclr_unity", - "version": "0.9.1", + "version": "0.10.0", "displayName": "HybridCLR", "description": "Unity package for HybridCLR. It includes editor and runtime scripts and assets for HybridCLR", "category": "Runtime",