载体服务
发现、隔离与生命周期归管理器。其余都来自宿主——管理器所在的那个进程。这一页写的就是两者之间的契约。
carrierServices 是传给构造函数的 IServiceProvider。插件通过 IPluginContext.CarrierServices 取用它,拿宿主拥有的东西:游戏世界句柄、绑在主线程上的调度器、指标汇。框架不检查它里面的内容。
两条规矩:
- 往里放一个
ILoggerFactory。 不放的话插件拿到空日志器,它们写的每一行都会消失,而且没人会报这件事。 - 别把宿主类型塞进插件契约。 这样取到的服务是按你宿主的类型声明的;用它的插件就绑死在你的宿主上,这是逃生舱的用法。只用宿主服务的插件才能跨宿主搬动。
框架自己的五项服务——日志、配置、数据目录、调度、事件总线——不算载体服务。它们由管理器提供,在每个宿主里都一样。
管理器把包路由给认领它的加载器。它只自带一个:处理预编译 IL 的内置程序集加载器。
manager.RegisterLoader(new AssemblyPluginLoader());其余加载器都是插件包,在 OnLoad 里自己登记:
// 某个加载器插件的 OnLoad 里context.RegisterLoader(new MyLoader());管理器记下这条登记;id 已被占用时返回 false。登记不能撤销:加载器交出的类型住在它的装载上下文里,所以更换或移除加载器意味着重启服务端。
登记发生在装载时,因此顺序有意义。需要某个加载器的插件要在清单里声明依赖它,依赖图会让加载器先装:
"dependencies": [ { "name": "hosihikari" }, { "name": "hosihikari-csharp" }]比加载器先到的包没有加载器认领,会被跳过并留下报告。
加载器可以实现 IPluginLoaderDiagnostics,在装载前检查包。管理器在 CanLoad 选中这个加载器之后、Load 开始之前调用 Validate:
public IReadOnlyList<string> Validate(PluginManifest manifest, string packageDirectory){ var entryPath = Path.Combine(packageDirectory, manifest.EntryPath); return File.Exists(entryPath) ? [] : [$"entry not found: {manifest.EntryPath}"];}返回非空列表时,问题并入装载报告并跳过该包;空列表则继续装载。诊断只报告问题——路由仍由 CanLoad 决定。
宿主拿不到的东西
Section titled “宿主拿不到的东西”这份契约不交出插件句柄。要按名字找插件,用生命周期里的状态快照;要对它做事,直接调管理器的方法。
