using System; using System.IO; using System.Linq; using System.Collections; using Newtonsoft.Json; using UnityEngine; public static class InspectHomeCampAot { public static string BuildAndCheck() { var built = AOT.Editor.YooAssetTools.BuildAndStageHotUpdate.BuildAndStage(); if (!built.StartsWith("ok", StringComparison.Ordinal)) throw new Exception(built); return built + " | " + CheckHotUpdate(); } public static string CheckHotUpdate() { var cecil = AppDomain.CurrentDomain.GetAssemblies().First(a => a.GetName().Name == "Mono.Cecil"); var definition = cecil.GetType("Mono.Cecil.AssemblyDefinition"); var stripped = Path.GetFullPath(Path.Combine(Application.dataPath, "../Library/Bee/artifacts/WebGL/ManagedStripped/YooAsset.dll")); object original = definition.GetMethod("ReadAssembly", new[] { typeof(string) }).Invoke(null, new object[] { stripped }); object update = definition.GetMethod("ReadAssembly", new[] { typeof(string) }).Invoke(null, new object[] { Path.Combine(Application.dataPath, "Bundles/Raw/HotUpdate.Runtime.bytes") }); var calls = new System.Collections.Generic.List(); using ((IDisposable)original) using ((IDisposable)update) { var oldModule = Property(original, "MainModule"); var module = Property(update, "MainModule"); var references = (IEnumerable)module.GetType().GetMethod("GetMemberReferences", Type.EmptyTypes).Invoke(module, null); foreach (object reference in references) { var declaring = Property(reference, "DeclaringType"); string typeName = (string)Property(declaring, "FullName"); if (typeName != "YooAsset.SceneHandle" && typeName != "YooAsset.HandleBase") continue; string fullName = (string)Property(reference, "FullName"); object type = oldModule.GetType().GetMethod("GetType", new[] { typeof(string) }).Invoke(oldModule, new object[] { typeName }); var retained = ((IEnumerable)Property(type, "Methods")).Cast().Any(m => (string)Property(m, "FullName") == fullName); if (!retained) throw new Exception("Hot update uses a method absent from the shipped shell: " + fullName); calls.Add(fullName); } } var report = JsonConvert.SerializeObject(new { passed=true, baseline=stripped, retainedCalls=calls }); var output = Path.GetFullPath(Path.Combine(Application.dataPath, "../../Tools/output/homecamp_aot_compatibility")); Directory.CreateDirectory(output); File.WriteAllText(Path.Combine(output, "aot_check.json"), report); return report; } public static string Run() { var directory = Path.GetFullPath(Path.Combine(Application.dataPath, "../Library/Bee/artifacts/WebGL/ManagedStripped")); var cecil = AppDomain.CurrentDomain.GetAssemblies().First(a => a.GetName().Name == "Mono.Cecil"); var definition = cecil.GetType("Mono.Cecil.AssemblyDefinition"); var output = new System.Collections.Generic.List(); foreach (var file in new[] { "YooAsset.dll", "UnityEngine.CoreModule.dll" }) { object assembly = definition.GetMethod("ReadAssembly", new[] { typeof(string) }).Invoke(null, new object[] { Path.Combine(directory, file) }); using ((IDisposable)assembly) { object module = Property(assembly, "MainModule"); foreach (var name in new[] { "YooAsset.SceneHandle", "YooAsset.HandleBase", "UnityEngine.SceneManagement.SceneManager" }) { object type = module.GetType().GetMethod("GetType", new[] { typeof(string) }).Invoke(module, new object[] { name }); if (type != null) output.Add(new { file, written = File.GetLastWriteTimeUtc(Path.Combine(directory, file)), type = name, methods = ((IEnumerable)Property(type, "Methods")).Cast().Select(m => Property(m, "FullName")).ToArray() }); } } } return JsonConvert.SerializeObject(output); } static object Property(object value, string name) => value.GetType().GetProperty(name).GetValue(value); }