#nullable enable using System; using System.IO; using System.Linq; using System.Collections.Generic; using Newtonsoft.Json; using UnityEditor; using UnityEditor.Compilation; using UnityEngine; using ShrinkContext; using ShrinkEventBus; namespace ShrinkSDK.Inspection.Editor { public static class ShrinkInspectionExport { private const string DirectoryName = "Artifacts/Inspection"; /// Host extensions add explicitly owned Network/Command/Context instances. Remove handlers when the host shuts down. public static event Action? CaptureHostState; [MenuItem("Tools/ShrinkSDK/Inspection/Export compilation inputs")] public static void ExportCompilation() { RequireStableEditor(); var root = Path.GetFullPath("."); var assemblies = CompilationPipeline.GetAssemblies(AssembliesType.Player); var loaded = AppDomain.CurrentDomain.GetAssemblies().Where(a => !a.IsDynamic).Select(a => a.GetName().Name).ToHashSet(); var identityNames = assemblies.Select(a => a.name).Where(loaded.Contains).OrderBy(n => n, StringComparer.Ordinal).ToArray(); var inputs = assemblies.Select(a => new { Name = a.name, Sources = a.sourceFiles.Select(Path.GetFullPath).ToArray(), References = a.allReferences.Select(Path.GetFullPath).Where(File.Exists).ToArray(), Defines = a.defines, Output = Path.GetFullPath(a.outputPath) }).ToArray(); var ticks = inputs.SelectMany(a => a.Sources.Concat(a.References)).Distinct(StringComparer.OrdinalIgnoreCase) .ToDictionary(path => path, path => File.GetLastWriteTimeUtc(path).Ticks); Directory.CreateDirectory(DirectoryName); File.WriteAllText(DirectoryName + "/compilation.json", JsonConvert.SerializeObject(new { Root = root, BuildId = ShrinkInspectionSnapshot.BuildIdentity(identityNames), IdentityAssemblies = identityNames, Assemblies = inputs, SourceTicks = ticks }, Formatting.Indented)); Debug.Log("Shrink inspection compilation exported: " + Path.GetFullPath(DirectoryName + "/compilation.json")); } [MenuItem("Tools/ShrinkSDK/Inspection/Export runtime snapshot")] public static void ExportRuntime() { RequireStableEditor(); var path = DirectoryName + "/compilation.json"; if (!File.Exists(path)) throw new InvalidOperationException("Export compilation inputs first."); var manifest = JsonConvert.DeserializeObject(File.ReadAllText(path))!; var snapshot = new ShrinkInspectionSnapshot(ShrinkInspectionSnapshot.BuildIdentity(manifest.IdentityAssemblies)); snapshot.AddContext("default-context", ShrinkContextDefaults.Default); var ordinal = 0; foreach (var bus in EventBus.CaptureNamedBuses()) snapshot.AddEventBus("named-bus-" + ordinal++, bus); snapshot.Coverage.Add("Default Context and named EventBus instances only; host-owned instances require CaptureHostState registration."); CaptureHostState?.Invoke(snapshot); File.WriteAllText(DirectoryName + "/runtime.json", snapshot.ToJson()); Debug.Log("Shrink inspection runtime exported: " + Path.GetFullPath(DirectoryName + "/runtime.json")); } private static void RequireStableEditor() { if (EditorApplication.isCompiling || EditorApplication.isUpdating) throw new InvalidOperationException("Wait for Editor compilation/import to finish."); } private sealed class IdentityManifest { public string[] IdentityAssemblies = Array.Empty(); } } }