同一项目的 GameScripts 与 EditorScripts 共同构成一个创作代际。即使只修改 Editor 源码,也一起经过候选、Scene/Settings 状态恢复、原子激活与严格卸载验证。原因是 Editor 中的泛型扩展闭合于项目 runtime 类型时,CLR loader allocator 可以形成双向依赖;单向程序集引用并不保证两个 collectible context 可独立卸载。编译仍可复用未修改的 GameScripts 产物,不要求重编全部源码。Plugin 按公开模块依赖图处理,领域扩展使用同一候选和退休协议。
Scripting 索引 · Compiler · Extensibility · 目标 reload 强制标准
ScriptReloadHost:把成功编译结果准备成 Module/Type/feature candidates;Plugin availability 改变而无法产出替代编译时,准备显式的 module retirement candidate;两者都只在安全点原子激活。ScriptReloadOptions:reload boundary 和诊断策略。IScriptReloadCoordinator:Editor/Scene/Rendering 等生产 feature 的事务协调 contract。
ScriptReloadHost 构造时要求组合入口提供 Func<ScriptModuleDeployment, IModuleSource>。工厂只为不可变产物选择模块来源,不能自行激活或发布;共同 host 仍负责候选事务、依赖选择、状态迁移和退休检查。Editor/Build 的桌面组合使用 DotNet Adapter,Compiler 与 Reload 程序集不引用具体 Adapter。
模块激活事务不重新编译源文件:编译先产出冻结候选,再进入激活。普通 Project 编译失败没有 candidate,active generation 保持不变;但 active Plugin 被删除、结构失效或更新失败时,安装集合变化本身是有效事实,不能因为依赖脚本编译失败而保留旧 Plugin。Host 会以 active/candidate Plugin ID 与 content hash 计算 retired Plugin modules,再沿 upstreamModuleNames 取得完整反向依赖闭包,原子移除 Plugin、Runtime Scripts 和 Editor Scripts。Scene participant 在同一事务内把已退休类型变成保留 Stable ID 与状态的 Missing,并在类型返回时恢复。
Plugin 文件系统删除触发的第一次自动 reload 就必须完成上述切换,不要求用户再次执行 Reload Plugins。成功编译出的每个 replacement request 使用编译快照给出的显式依赖清单;空清单不会重新绑定仍处于 previous generation 的 Plugin。提交后 unload verification 按帧协作式等待 CLR 完成 collectible ALC 回收。只有超时后仍真实可达时才发布唯一的 INNO-ALC-UNLOAD Diagnostic;Console 不再额外写入一条内容重复的普通 Error log。超时后当前 Host 必须重启,不能在 Faulted 进程里开始新的 reload 来清除此诊断。
当前 verification 是 Success-or-Exception barrier:旧 ALC 未被 GC 确认回收前 operation 不成功,达到失败阈值后进入 Faulted,并阻止重叠 reload、Play、Build 与 Export。成功 commit、rollback candidate、Plugin uninstall、显式 unload 和 shutdown 都受同一规则约束。完整要求见 Identity、可恢复引用与热重载强制标准。
只有 participant Prepare/Activate、Scene 状态迁移或外部 generation 同步本身失败时,事务才 rollback 到仍完整存在的 immutable previous snapshot。Complete 清理失败不能撤销已经提交的 generation,必须 Fault 共享 gate;不能只记录诊断后继续。调用者负责 Dispose host,释放 catalog registrations 与 retiring generation lease。
IScriptReloadCoordinator.Execute(AssemblyReloadSession, IGenerationChange? externalChange = null) 用统一五阶段 owner 替代两段无所有权 callback。Prepare 在 gate 内执行;参与者 Capture 失败也会回滚已尝试的 prepare,不对未准备的 participant 执行恢复。外部内容 Apply 在 Editor domain 恢复前完成,rollback 先恢复旧 assembly/catalog 与外部内容,再恢复 Editor 属性。ScriptReloadHost 使用 PluginEnvironment.CreateReloadChange,不在末尾另做非原子的 CommitPending。
关闭 ScriptReloadHost 时先撤销 observation、取消并等待编译,然后完成共享 GenerationCoordinator 的上一批 GC barrier,才提交活动模块卸载。退出不是绕过 AwaitingCollection 的特殊入口;Faulted/timeout 仍会阻止新的卸载事务。
自动编译只排除本 ScriptReloadHost 同步发布候选时产生的 SourceMountsChanged 通知回声。Assembly Catalog 会在每次脚本发布时重新发布隔离 Asset Catalog,这不是一次新的源文件编辑,不能由此排队下一轮 reload。真实文件变化、显式编译请求和外部 source-mount 发布仍然正常入队;不关闭 watcher、不取消全局通知,也不跳过 GC barrier。
ScriptReloadHost.progress 是通过一次原子发布获得的 ScriptReloadProgress。排队、后台编译、候选应用及退休各自带着同一请求身份;下一请求及其进度不会被旧请求或旧阶段的迟到回调覆盖。CancelCompilation(operationId) 在同一 owner 锁内校验身份,并在发出取消信号前发布取消状态。
进度只观察既有流程,不改变候选发布、Missing、GC/finalizer/weak-monitor 或 Faulted 协议。canCancel 来自实际编译阶段和资源所有权,不由百分比推测。已捕获快照不可变,不携带 Type、delegate 或插件实例。
此清单对应当前 SDK 求值与源码符号;内部类型不构成公开 API。
| API | 职责与参数 | 返回语义 | 失败条件 |
|---|---|---|---|
public interface Inno.Scripting.Reload.IScriptReloadCoordinator |
Coordinates host-owned state with an atomic script assembly generation transition. | — | — |
Inno.Extensibility.Reload.IAssemblyUnloadProbe Inno.Scripting.Reload.IScriptReloadCoordinator.Execute(Inno.Extensibility.Modules.AssemblyReloadSession reload, Inno.Extensibility.Reload.IGenerationChange? externalChange = null) |
Commits a prepared assembly reload together with dependent host state. reload: The prepared assembly reload session to activate and complete.externalChange: Optional five-phase content change; captured before publication and restored before domain property recovery. |
A monitor observing cooperative unload of assemblies retired by the committed generation. | — |
void Inno.Scripting.Reload.IScriptReloadCoordinator.RefreshDiagnostics() |
Requests diagnostics derived from the active host generation to be republished. | — | — |
public sealed class Inno.Scripting.Reload.ScriptReloadHost : System.IDisposable |
Watches, compiles, and atomically activates one project's C# script assemblies. | — | — |
public Inno.Scripting.Reload.ScriptReloadHost.ScriptReloadHost(Inno.Scripting.Reload.ScriptReloadOptions options, Inno.Scripting.Compiler.ScriptCompiler compiler, Inno.Assets.Pipeline.AssetPipeline assets, Inno.Plugins.Authoring.PluginEnvironment plugins, Inno.Extensibility.Modules.ModuleHost modules, Inno.Core.Settings.ProjectSettingsStore settings, Inno.Scripting.Reload.IScriptReloadCoordinator reloads, System.Func<Inno.Scripting.Compiler.ScriptModuleDeployment, Inno.Extensibility.Modules.IModuleSource> moduleSourceFactory) |
Creates a reload host for one compiler and runtime module owner. options: The automatic compilation, debounce, and long-running warning policy.compiler: The compiler that produces immutable candidate script generations.assets: The authoring asset pipeline that supplies committed source artifacts.plugins: The Plugin environment that owns active and candidate source generations.modules: The module host that atomically activates compiled assemblies.settings: The project settings store rebuilt with each committed Plugin generation.reloads: The host-owned coordinator that commits dependent state together with script generations.moduleSourceFactory: The composition-owned factory that chooses the loading policy for each immutable script module. It must return a fresh source without activating or publishing the module. |
— | System.ArgumentNullException: Thrown when any required dependency is null.System.ArgumentOutOfRangeException: Thrown when the configured debounce duration is negative. |
public bool Inno.Scripting.Reload.ScriptReloadHost.isCompiling { get; } |
Gets whether a compilation currently owns the compiler gate. | — | — |
public bool Inno.Scripting.Reload.ScriptReloadHost.isCompilationPending { get; } |
Gets whether source or plugin changes are waiting to be compiled. | — | — |
public Inno.Scripting.Reload.ScriptReloadProgress Inno.Scripting.Reload.ScriptReloadHost.progress { get; } |
Gets one atomically published operation, fraction, stage and cancellation snapshot. | — | — |
public System.TimeSpan Inno.Scripting.Reload.ScriptReloadHost.compilationElapsed { get; } |
Gets the elapsed duration of the active or most recently completed compilation. | — | — |
public bool Inno.Scripting.Reload.ScriptReloadHost.isCompilationTakingLong { get; } |
Gets whether the active compilation exceeded the configured warning duration. | — | — |
public Inno.Scripting.Compiler.ScriptCompilationResult? Inno.Scripting.Reload.ScriptReloadHost.lastCompilation { get; } |
Gets the most recently completed compilation. | — | — |
public bool Inno.Scripting.Reload.ScriptReloadHost.isUnloadVerificationPending { get; } |
Gets whether retired collectible assembly generations are still being verified for unload. | — | — |
public bool Inno.Scripting.Reload.ScriptReloadHost.isFaulted { get; } |
Gets whether a retired generation exceeded the retention threshold and permanently stopped this host. | — | — |
public void Inno.Scripting.Reload.ScriptReloadHost.Start() |
Subscribes to committed asset changes and queues a cache-aware initial compilation request. | — | — |
public bool Inno.Scripting.Reload.ScriptReloadHost.CancelCompilation(long operationId) |
Requests cancellation of the active compilation without changing the active script generation. operationId: The observed request; a stale cancellation is rejected. |
true when an active compilation received the request. | System.ObjectDisposedException: Thrown after the manager has been disposed. |
public void Inno.Scripting.Reload.ScriptReloadHost.RecompileScripting() |
Queues incremental recompilation of changed script assemblies. | — | System.ObjectDisposedException: Thrown after the manager has been disposed. |
public void Inno.Scripting.Reload.ScriptReloadHost.ReloadScripting() |
Queues a complete rebuild of both scripting load contexts while retaining the plugin generation. Valid cached artifacts are reused. | — | System.ObjectDisposedException: Thrown after the manager has been disposed. |
public void Inno.Scripting.Reload.ScriptReloadHost.ReloadPlugins() |
Queues replacement of the unified plugin generation and both dependent scripting generations. Valid script artifacts are reused when plugin reference fingerprints are unchanged. | — | System.ObjectDisposedException: Thrown after the manager has been disposed. |
public bool Inno.Scripting.Reload.ScriptReloadHost.TryCompilePending(out System.Threading.Tasks.Task<Inno.Scripting.Compiler.ScriptCompilationResult>? compilation) |
Starts a pending compilation after the configured quiet period has elapsed. compilation: The started compilation task, or null when none was ready. |
true when a pending compilation was started. | — |
public bool Inno.Scripting.Reload.ScriptReloadHost.ApplyPendingReload() |
Applies the latest compiled generation, or commits a Plugin-unavailability generation when changed Plugin code cannot produce a replacement, at a caller-controlled main-thread safe point. | true when modules were replaced or retired; otherwise, false. | System.ObjectDisposedException: Thrown after the manager has been disposed. |
public void Inno.Scripting.Reload.ScriptReloadHost.GenerateProjectFiles() |
Generates standard SDK-style game/editor projects and a solution for IDE tooling. | — | System.ObjectDisposedException: Thrown after the manager has been disposed. |
public void Inno.Scripting.Reload.ScriptReloadHost.Dispose() |
Cancels and waits for active compilation work, stops Asset Database observation, and unloads the active script module. | — | — |
public bool Inno.Scripting.Reload.ScriptReloadHost.AdvanceUnloadVerification(out System.Exception? failure) |
Advances cooperative unload verification for retired script generations. failure: Receives the retention failure after all verification attempts, otherwise null. |
true when no additional verification frame is required. | — |
public sealed class Inno.Scripting.Reload.ScriptReloadOptions |
Configures automatic compilation requests and retired-generation verification. | — | — |
public bool Inno.Scripting.Reload.ScriptReloadOptions.autoCompile { get; init; } |
Gets whether startup and subsequent source changes request automatic compilation. | — | — |
public int Inno.Scripting.Reload.ScriptReloadOptions.debounceMilliseconds { get; init; } |
Gets the quiet period applied to source changes before a compilation starts, in milliseconds. | — | — |
public System.TimeSpan Inno.Scripting.Reload.ScriptReloadOptions.compilationWarningTimeout { get; init; } |
Gets the elapsed duration after which an active compilation is reported as long-running. | — | — |
public System.TimeSpan Inno.Scripting.Reload.ScriptReloadOptions.unloadCollectionInterval { get; init; } |
Gets the minimum interval between full garbage-collection attempts for retired assembly generations. | — | — |
public System.TimeSpan Inno.Scripting.Reload.ScriptReloadOptions.unloadRetentionTimeout { get; init; } |
Gets the duration after which a retained collectible generation permanently faults this reload host. | — | — |
public Inno.Scripting.Reload.ScriptReloadOptions.ScriptReloadOptions() |
由 C# 编译器生成的值契约成员。 | — | — |
public sealed record Inno.Scripting.Reload.ScriptReloadProgress : System.IEquatable<Inno.Scripting.Reload.ScriptReloadProgress> |
Atomically published progress owned by one reload request, containing no runtime or extension objects. operationId: The host-local reload request sequence; zero precedes the first request.fraction: The fraction through compilation, activation and verified retirement.stage: The actual stage or terminal diagnostic.cancellationRequested: Whether cancellation has been requested and still requires owned work to drain.canCancel: Whether this exact operation currently admits cancellation. |
— | — |
public Inno.Scripting.Reload.ScriptReloadProgress.ScriptReloadProgress(long operationId, float fraction, string stage, bool cancellationRequested, bool canCancel = false) |
Atomically published progress owned by one reload request, containing no runtime or extension objects. operationId: The host-local reload request sequence; zero precedes the first request.fraction: The fraction through compilation, activation and verified retirement.stage: The actual stage or terminal diagnostic.cancellationRequested: Whether cancellation has been requested and still requires owned work to drain.canCancel: Whether this exact operation currently admits cancellation. |
— | — |
public long Inno.Scripting.Reload.ScriptReloadProgress.operationId { get; init; } |
The host-local reload request sequence; zero precedes the first request. | — | — |
public float Inno.Scripting.Reload.ScriptReloadProgress.fraction { get; init; } |
The fraction through compilation, activation and verified retirement. | — | — |
public string Inno.Scripting.Reload.ScriptReloadProgress.stage { get; init; } |
The actual stage or terminal diagnostic. | — | — |
public bool Inno.Scripting.Reload.ScriptReloadProgress.cancellationRequested { get; init; } |
Whether cancellation has been requested and still requires owned work to drain. | — | — |
public bool Inno.Scripting.Reload.ScriptReloadProgress.canCancel { get; init; } |
Whether this exact operation currently admits cancellation. | — | — |
public override string Inno.Scripting.Reload.ScriptReloadProgress.ToString() |
Returns a string that represents the current object. | A string that represents the current object. | — |
public static bool Inno.Scripting.Reload.ScriptReloadProgress.operator !=(Inno.Scripting.Reload.ScriptReloadProgress? left, Inno.Scripting.Reload.ScriptReloadProgress? right) |
由 C# 编译器生成的值契约成员。 | — | — |
public static bool Inno.Scripting.Reload.ScriptReloadProgress.operator ==(Inno.Scripting.Reload.ScriptReloadProgress? left, Inno.Scripting.Reload.ScriptReloadProgress? right) |
由 C# 编译器生成的值契约成员。 | — | — |
public override int Inno.Scripting.Reload.ScriptReloadProgress.GetHashCode() |
Serves as the default hash function. | A hash code for the current object. | — |
public override bool Inno.Scripting.Reload.ScriptReloadProgress.Equals(object? obj) |
Determines whether the specified object is equal to the current object. obj: The object to compare with the current object. |
true if the specified object is equal to the current object; otherwise, false. | — |
public bool Inno.Scripting.Reload.ScriptReloadProgress.Equals(Inno.Scripting.Reload.ScriptReloadProgress? other) |
Indicates whether the current object is equal to another object of the same type. other: An object to compare with this object. |
true if the current object is equal to the other parameter; otherwise, false. | — |
public void Inno.Scripting.Reload.ScriptReloadProgress.Deconstruct(out long operationId, out float fraction, out string stage, out bool cancellationRequested, out bool canCancel) |
由 C# 编译器生成的值契约成员。 | — | — |
- Inno.Extensibility.Types:实现依赖,PrivateAssets="compile"。
- Inno.Extensibility.Reload:公开引用边界由实际签名核对。
- Inno.Plugins.Authoring:公开引用边界由实际签名核对。
- Inno.Core.Settings:公开引用边界由实际签名核对。
- Inno.Assets:公开引用边界由实际签名核对。
- Inno.Extensibility.Modules:公开引用边界由实际签名核对。
- Inno.Scripting.Compiler:公开引用边界由实际签名核对。
- Inno.Assets.Pipeline:公开引用边界由实际签名核对。
- Inno.Core.Diagnostics:公开引用边界由实际签名核对。
- Inno.Extensibility.Catalogs:公开引用边界由实际签名核对。