{"url":"https://levimc.org/mods/github.com/HosihikariFramework/CSharpProvider/","id":"github.com/HosihikariFramework/CSharpProvider","name":"星光 · C# Provider","description":"星光框架的 C# 型插件 Provider：装载时编译脚本，交给 collectible ALC","owner":"HosihikariFramework","logo":"https://levimc.org/mods/icons/02d0a8d0f793d0cd-288.webp","repository":"https://github.com/HosihikariFramework/CSharpLoader","issues":"https://github.com/HosihikariFramework/CSharpLoader/issues","releases":"https://github.com/HosihikariFramework/CSharpLoader/releases","stars":0,"downloads":107,"updated":"2026-11-09T10:49:06Z","published":"2026-10-19T04:41:02Z","created":"2026-10-17T05:30:58Z","license":"LGPL-3.0","side":["server","client"],"platform":["android-arm64","linux-arm64","linux-x64","win-arm64","win-x64"],"gameVersions":[],"tags":["platform:levilamina","platform:endstone","type:mod"],"latest":{"variant":"","version":"0.2.3","install":"lip install github.com/HosihikariFramework/CSharpProvider@0.2.3"},"readmes":[{"file":"README.md","lang":"en","text":"# Hosihikari.PluginProvider.CSharp\n\n[English](README.md) | [简体中文](README.zh_hans.md)\n\nThe **C# plugin Provider** of the Hosihikari framework. Like F#, C# runs on the CLR, so this\nprovider **compiles at load time**: Roslyn turns a `.cs` file into a real assembly, which then goes\ninto its own collectible ALC.\n\nThat buys C# plugins the same promises as any assembly plugin: per-plugin dependency isolation, and an\nunload that is verified rather than assumed. This is a first-class provider: it AOT-compiles source\nto IL before the plugin runs, and the provider loads that IL into a collectible ALC.\n\n**In-process .NET.** The framework keeps the assembly provider, this C# provider and the F# provider\nin-process. Other languages go through the generic out-of-process provider\n(`Hosihikari.PluginProvider.Rpc`, ADR-0031).\n\n## Templates\n\nStart from [CSharpPluginTemplate](https://github.com/HosihikariFramework/CSharpPluginTemplate): copy\nit into `plugins/`, rename the plugin, and restart. For a precompiled assembly, use the\n[`dotnet new` template](https://www.nuget.org/packages/Hosihikari.Plugin.Template).\n\n## What a plugin package looks like\n\n```\nplugins/Hello/\n  manifest.json\n    {\n      \"name\": \"Hello\",\n      \"entry\": \"main.cs\",\n      \"type\": \"hosihikari\",\n      \"version\": \"1.0.0\",\n      \"dependencies\": [\n        { \"name\": \"hosihikari\" },\n        { \"name\": \"hosihikari-csharp\" }\n      ],\n      \"extraInfo\": { \"api\": \"2\", \"format_version\": \"1\", \"provider\": \"csharp\" }\n    }\n  main.cs\n  Hello.conf          ← the author's defaults, shipped with the package\n```\n\nThe file defines a type implementing `IPlugin` with a parameterless constructor; finding it is the\nprovider's job.\n\n```csharp\nusing System.IO;\nusing Hosihikari;\n\npublic sealed class HelloPlugin : IPlugin\n{\n    private string _data = \"\";\n\n    public void OnLoad(IPluginContext context)\n    {\n        _data = context.DataDirectory;\n        File.WriteAllText(Path.Combine(_data, \"body.txt\"), \"body:\" + context.Configuration[\"greeting\"]);\n    }\n\n    public PluginHookResult OnEnable()\n    {\n        File.WriteAllText(Path.Combine(_data, \"enabled.txt\"), \"enabled\");\n        return PluginHookResult.Allow();\n    }\n\n    public PluginHookResult OnDisable() => PluginHookResult.Allow();\n    public void OnUnload() { }\n}\n```\n\nBecause the result is a real assembly referencing the contract, a C# plugin reaches everything any\nplugin does — including the type-keyed channels.\n\n## File-based app directives\n\n`main.cs` may start with `#:package Id@Version` and `#:include path.cs`, matching .NET file-based\napps. The provider strips them before Roslyn compiles. The packer restores packages at pack time\ninto the plugin package and copies included files; the server stays offline. `#:include` supports\n`*`, `**`, and `?` globs. `#:project`, `#:property`, `#:sdk`, and top-level statements are not\nsupported.\n\n## How it compiles\n\n`CSharpCompilation` from Roslyn (`Microsoft.CodeAnalysis.CSharp`). The references are the host's\ntrusted platform assemblies plus the assemblies in the **framework's own load context** — the host is\nloaded into an isolated ALC, so the contract and its abstractions are not in the default context.\nPrivate DLLs in the plugin package root are also referenced at compile time and resolved from that\ndirectory at runtime; shared framework assemblies keep their existing identity.\nParsing passes an encoding, without which Roslyn refuses to emit debugging information (CS8055).\n\nThe output is cached by content (ADR-0029): `<data directory>/bin/<hash>/<name>.dll`, the hash covering\nthe source text, package-local private DLL names and contents, the Roslyn version and the runtime version. A checksum sits next to it and is verified\nbefore loading — a mismatch means recompile, never load anyway. Compilation goes into a staging\ndirectory and is moved into place only on success, and a portable PDB is emitted alongside so logged\nstacks carry `file:line`.\n\nThe checksum guards against accidents, not against a hostile plugin: plugins share this process and\nare trusted (ADR-0011).\n\n## Running it\n\n```\ndotnet run --project host/Hosihikari.PluginProvider.CSharp.Host.csproj -c Release\n```\n\nThe harness drives this Provider through the core's real `PluginManagerHost`, so sibling\n`Hosihikari.PluginManager`, `Hosihikari.Abstractions` and McMaster fork checkouts are required. It\nchecks these things: the Provider is registered by a plugin package, the artifact lands in a\ncontent-addressed directory with its checksum and PDB, the source is compiled at load and reads the\noperator's override, enable hooks reach it, a channel message from the host reaches it, a\npackage-local third-party dependency is usable, native libraries resolve from the RID directory,\ndisable and unload hooks arrive, the unload verifies, a second load hits the cache, and a tampered\nartifact is detected and recompiled. A separate fixture DLL is built without\nreferencing it from the host, so the dependency check also covers a genuinely private assembly.\n\n## How it reaches the host process\n\nAs a plugin package, named `hosihikari-csharp` — the same way as the other in-process providers. Source packages\ndeclare the dependency, and the dependency graph orders the provider first.\n\n**Registration cannot be undone**: the types this provider hands out live in its load context.\n\n## Packaging\n\n```\npwsh ./pack.ps1 -Version 0.2.3 -OutputRoot artifacts\n```\n\nIt assembles the provider DLL, its private dependencies and the manifest into\n`hosihikari-csharp/`; the shared layer is not copied. Pushing a `v*` tag attaches the zip to the\nGitHub release (ADR-0036).\n\nThe script invokes the core packager (`Hosihikari.PluginManager/tools/Pack`), which owns the\nshared-layer list. Check out the core repository alongside this one, or set\n`HOSIHIKARI_PLUGINMANAGER`.\n\nAfter release: `lip install github.com/HosihikariFramework/CSharpProvider` (install the Shim\nvariant for the carrier first).\n\n## Licence\n\nLGPL-3.0-or-later, see [LICENSE.md](LICENSE.md).\n"},{"file":"README.zh_hans.md","lang":"zh-Hans","text":"# Hosihikari.PluginProvider.CSharp\n\n[English](README.md) | [简体中文](README.zh_hans.md)\n\n星光框架的 **C# 型插件 Provider**。与 F# 一样，C# 直接跑在 CLR 上，所以这个 Provider **装载时编译**：\nRoslyn 把 `.cs` 编成真程序集，再进一个 collectible ALC。\n\nC# 插件因此拿到与其他程序集插件一样的承诺：每插件的依赖互相隔离，卸载是真的验证过而不是假定的。\n这是一等 Provider：在插件开始运行前把源码 AOT 为 IL，Provider 把 IL 装进 collectible ALC。\n\n**进程内只留 .NET。** 框架在进程内保留程序集型、C# 与 F# 三个 Provider；其他语言走\n通用的进程外 Provider（`Hosihikari.PluginProvider.Rpc`，ADR-0031）。\n\n## 模板\n\n从 [CSharpPluginTemplate](https://github.com/HosihikariFramework/CSharpPluginTemplate) 开始：复制到\n`plugins/`，改名后重启。预编译程序集用\n[`dotnet new` 模板](https://www.nuget.org/packages/Hosihikari.Plugin.Template)。\n\n## 插件包长什么样\n\n```\nplugins/Hello/\n  manifest.json\n    {\n      \"name\": \"Hello\",\n      \"entry\": \"main.cs\",\n      \"type\": \"hosihikari\",\n      \"version\": \"1.0.0\",\n      \"dependencies\": [\n        { \"name\": \"hosihikari\" },\n        { \"name\": \"hosihikari-csharp\" }\n      ],\n      \"extraInfo\": { \"api\": \"2\", \"format_version\": \"1\", \"provider\": \"csharp\" }\n    }\n  main.cs\n  Hello.conf          ← 作者给的默认值，随包更新\n```\n\n文件里定义一个实现 `IPlugin`、有无参构造的类型；找到它是 Provider 的事。\n\n```csharp\nusing System.IO;\nusing Hosihikari;\n\npublic sealed class HelloPlugin : IPlugin\n{\n    private string _data = \"\";\n\n    public void OnLoad(IPluginContext context)\n    {\n        _data = context.DataDirectory;\n        File.WriteAllText(Path.Combine(_data, \"body.txt\"), \"body:\" + context.Configuration[\"greeting\"]);\n    }\n\n    public PluginHookResult OnEnable()\n    {\n        File.WriteAllText(Path.Combine(_data, \"enabled.txt\"), \"enabled\");\n        return PluginHookResult.Allow();\n    }\n\n    public PluginHookResult OnDisable() => PluginHookResult.Allow();\n    public void OnUnload() { }\n}\n```\n\n编出来的是一个引用了契约的真程序集，所以 C# 插件拿得到任何插件都拿得到的东西 —— 包括那些按类型做键的通道。\n\n## file-based 指令\n\n`main.cs` 开头可以写 `#:package Id@Version` 与 `#:include path.cs`，与 .NET file-based apps\n对齐。Provider 在 Roslyn 编译前把它们换成空行；打包器在打包期把包还原进插件包并复制被 include\n的文件，服务端保持离线。`#:include` 支持 `*`、`**`、`?` 通配符。`#:project`、`#:property`、\n`#:sdk` 与顶层语句不支持。\n\n## 它怎么编译\n\n用 Roslyn 的 `CSharpCompilation`（`Microsoft.CodeAnalysis.CSharp`）。引用 = 宿主的运行时，加上\n**框架自己那个装载上下文**里的程序集 —— 宿主被装在一个隔离的 ALC 里，契约与它的抽象包不在默认上下文。\n包根目录内的私有 DLL 也参与编译引用，运行时从包目录解析；共享框架程序集仍保持原来的类型身份。\n解析源码时要给编码，否则 Roslyn 拒绝发调试信息（CS8055）。\n\n产物按内容寻址缓存（ADR-0029）：`<数据目录>/bin/<哈希>/<名字>.dll`，哈希覆盖源码内容、Roslyn 版本与\n运行时版本，以及包内私有 DLL 的名字和内容。产物旁存一份校验和，加载前对一遍 —— 对不上就重编，绝不照跑。编译先落到临时目录、成功了才\n改名进去；同时产出 portable PDB，日志里的栈因此带 `file:line`。\n\n校验和防的是**事故**，不防**恶意**：插件与本框架同进程、且完全受信（ADR-0011）。\n\n## 跑一遍\n\n```\ndotnet run --project host/Hosihikari.PluginProvider.CSharp.Host.csproj -c Release\n```\n\n验证宿主借核心的 `PluginManagerHost` 驱动它，所以需要并列的 `Hosihikari.PluginManager`、\n`Hosihikari.Abstractions` 与 McMaster 的 fork checkout。它验证：Provider 由插件包登记、产物按内容寻址落盘且带校验和\n与 PDB、源码装载时编译并读到包外覆盖、启用钩子到达插件、宿主发的通道消息它能收到、包内自带的第三方依赖\n可用、本机库按 RID 目录解析、停用与卸载钩子到达、卸载验证通过、同一份源码再装载命中缓存、以及产物被改坏后会被发现并重编。\n测试还会构建宿主没有引用的独立 DLL，验证真正私有的依赖也能在插件中使用。\n\n## 它怎么进宿主进程\n\n作为插件包，名字是 `hosihikari-csharp` —— 与其他进程内 Provider 同一套。源码包声明依赖，依赖图先排它。\n\n**登记之后不能撤销**：它造出来的类型住在它的装载上下文里。\n\n## 打包\n\n```\npwsh ./pack.ps1 -Version 0.2.3 -OutputRoot artifacts\n```\n\n它把 Provider DLL、私有依赖与清单装进 `hosihikari-csharp/`，共享层不随包走；打 `v*` 标签时 CI 把 zip 附到 release（ADR-0036）。\n\n打包脚本调用核心打包器（`Hosihikari.PluginManager/tools/Pack`），共享层清单由核心统一给出；需要并列检出核心仓，或设置 `HOSIHIKARI_PLUGINMANAGER`。\n\n发布后：`lip install github.com/HosihikariFramework/CSharpProvider`（先按载体装 Shim）。\n\n## 许可\n\nLGPL-3.0-or-later，见 [LICENSE.md](LICENSE.md)。\n"}],"gallery":[],"versions":[{"version":"0.2.3","prerelease":false,"released":"2026-10-21T03:07:15Z","releaseUrl":"https://github.com/HosihikariFramework/CSharpLoader/releases/tag/v0.2.3","manifestUrl":"https://github.com/HosihikariFramework/CSharpLoader/blob/v0.2.3/tooth.json","info":{"name":"星光 · C# Provider","description":"星光框架的 C# 型插件 Provider：装载时编译脚本，交给 collectible ALC","tags":["platform:levilamina","platform:endstone","type:mod"]},"variants":{"":[{"side":"server","platform":"win-x64","deps":{}},{"side":"client","platform":"win-x64","deps":{}},{"side":"server","platform":"win-arm64","deps":{}},{"side":"client","platform":"win-arm64","deps":{}},{"side":"server","platform":"linux-x64","deps":{}},{"side":"client","platform":"linux-x64","deps":{}},{"side":"server","platform":"linux-arm64","deps":{}},{"side":"client","platform":"linux-arm64","deps":{}},{"side":"server","platform":"android-arm64","deps":{}},{"side":"client","platform":"android-arm64","deps":{}}]}},{"version":"0.2.2","prerelease":false,"released":"2026-10-21T02:16:25Z","releaseUrl":"https://github.com/HosihikariFramework/CSharpLoader/releases/tag/v0.2.2","manifestUrl":"https://github.com/HosihikariFramework/CSharpLoader/blob/v0.2.2/tooth.json","info":{"name":"星光 · C# Provider","description":"星光框架的 C# 型插件 Provider：装载时编译脚本，交给 collectible ALC","tags":["platform:levilamina","platform:endstone","type:mod"]},"variants":{"":[{"side":"server","platform":"win-x64","deps":{}},{"side":"client","platform":"win-x64","deps":{}}]}},{"version":"0.2.1","prerelease":false,"released":"2026-10-20T09:44:02Z","releaseUrl":"https://github.com/HosihikariFramework/CSharpLoader/releases/tag/v0.2.1","manifestUrl":"https://github.com/HosihikariFramework/CSharpLoader/blob/v0.2.1/tooth.json","info":{"name":"星光 · C# Provider","description":"星光框架的 C# 型插件 Provider：装载时编译脚本，交给 collectible ALC","tags":["platform:levilamina","platform:endstone","type:mod"]},"variants":{"":[{"side":"server","platform":"win-x64","deps":{}},{"side":"client","platform":"win-x64","deps":{}}]}},{"version":"0.2.0","prerelease":false,"released":"2026-10-20T08:02:23Z","releaseUrl":"https://github.com/HosihikariFramework/CSharpLoader/releases/tag/v0.2.0","manifestUrl":"https://github.com/HosihikariFramework/CSharpLoader/blob/v0.2.0/tooth.json","info":{"name":"星光 · C# Provider","description":"星光框架的 C# 型插件 Provider：装载时编译脚本，交给 collectible ALC","tags":["platform:levilamina","platform:endstone","type:mod"]},"variants":{"":[{"side":"server","platform":"win-x64","deps":{}},{"side":"client","platform":"win-x64","deps":{}}]}},{"version":"0.1.0","prerelease":false,"released":"2026-10-19T04:41:02Z","releaseUrl":"https://github.com/HosihikariFramework/CSharpLoader/releases/tag/v0.1.0","manifestUrl":"https://github.com/HosihikariFramework/CSharpLoader/blob/v0.1.0/tooth.json","info":{"name":"星光 · C# Provider","description":"星光框架的 C# 型插件 Provider：装载时编译脚本，交给 collectible ALC","tags":["platform:levilamina","platform:endstone","type:mod"]},"variants":{"":[{"side":"server","platform":"win-x64","deps":{}},{"side":"client","platform":"win-x64","deps":{}}]}}],"dependencies":{},"requiredBy":[]}