Skip to content

Plugin Development Tutorial zh

3cxc edited this page Sep 13, 2026 · 1 revision

插件开发教程

本教程将指导您使用 NativePluginKit 为Unknown: Site-0 创建一个最小化插件


1. 创建项目

dotnet new classlib -n HelloWorldPlugin -f net8.0
cd HelloWorldPlugin

编辑 HelloWorldPlugin.csproj:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net8.0</TargetFramework>
    <Nullable>enable</Nullable>
    <PublishAot>true</PublishAot>
    <NativeLib>Shared</NativeLib>
    <IlcExportUnmanagedEntrypoints>true</IlcExportUnmanagedEntrypoints>
    <RuntimeIdentifier>win-x64</RuntimeIdentifier>
    <InvariantGlobalization>true</InvariantGlobalization>
  </PropertyGroup>

  <ItemGroup>
    <ProjectReference Include="..\NativePluginKit\NativePluginKit.csproj" />
    <ProjectReference Include="..\NativePluginKit.SourceGenerators\NativePluginKit.SourceGenerators.csproj"
                      OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
    <UnmanagedEntryPointsAssembly Include="NativePluginKit" />
  </ItemGroup>
</Project>

2. 编写插件

创建 HelloWorldPlugin.cs:

using NativePluginKit.Features.Logger;
using NativePluginKit.Loader.Plugins;

namespace HelloWorldPlugin;

public partial class HelloWorldPlugin : Plugin
{
    public override string Name        => "HelloWorldPlugin";
    public override string Description => "一个示例插件";
    public override string Author      => "你的名字";
    public override Version RequiredApiVersion => new(1, 0, 0);

    protected override void OnStart()
    {
        Log.PrintLog("你好,世界!");
    }

    public override void OnStop() { }
}

注意 — 类必须声明为 partial。源生成器会产出 OnInit, OnStop 和 [ModuleInitializer] 引导


3. 使用宿主 API

日志

Log.PrintLog("普通信息");
Log.PrintWarning("警告信息");
Log.PrintError("错误信息");
Log.Debug("调试信息");

场景切换

NativePluginKit.Features.FMOD.Transition.TransitionToScene(
    "res://scenes/menu.tscn", fadeOut: true);

枚举卡牌

using NativePluginKit.Features.Cards;

for (int i = 0; i < Card.GetCardCount(); i++)
{
    string name = Card.GetCardNameAt(i);
    Log.PrintLog($"{name}({Card.GetCardTypeEnum(name)})消耗 {Card.GetCost(name)}");
}

枚举实体

using NativePluginKit.Features.Entities;
using NativePluginKit.Features.Wrappers;

foreach (EntityId id in Entity.GetAllEntityIds())
{
    if (!Entity.Contains(id)) continue;
    Log.PrintLog($"{Entity.GetDisplayName(id)}:{Entity.GetCurrentHP(id)}/{Entity.GetMaxHP(id)}");
}

订阅事件

using NativePluginKit.Events;
using NativePluginKit.Features.Events;

EventBus.On<EntityDamagedEvent>(EventType.EntityDamaged, OnEntityDamaged);

private void OnEntityDamaged(EntityDamagedEvent e)
{
    var id = new EntityId(e.EntityId.Low, e.EntityId.High);
    Log.PrintLog($"{Entity.GetDisplayName(id)} 受到 {e.HpDamage} 点伤害");
}

访问原始 API 表

HostApiTable api = Api;

4. 构建与部署

dotnet publish -c Release -r win-x64

将 bin/Release/net8.0/win-x64/publish/HelloWorldPlugin.dll 复制到主机的 <GameRoot>/plugins/ 目录:

<GameRoot>/
├── ExampleClient.exe
└── plugins/
    └── HelloWorldPlugin.dll

启动客户端,你将看到:

Loading plugin: HelloWorldPlugin v1.0.0.0 by 你的名字
你好,世界!

5. 生命周期

阶段 方法 说明
加载 __GeneratedModuleInit [ModuleInitializer],在 OnInit 之前注册元数据
加载 __GeneratedOnInit 导出为 OnInit,创建实例并注入 Api
启动 OnStart() 业务初始化
停止 OnStop() 释放资源,宿主随后释放实例

6. 最佳实践

  • 不要从 OnStart / OnStop 抛出异常 —— 异常无法安全穿越 ABI 边界
  • 插件保持自包含 —— 只依赖 NativePluginKit 和 BCL
  • 匹配 API 版本 —— 通过 RequiredApiVersion 声明
  • 使用 Log.PrintLog —— Console.WriteLine 输出到进程 stdout,客户端控制台可能看不到
  • 在 OnStop 中释放所有非托管资源 —— 包括通过 EventBus.Off<T> 取消事件订阅

Clone this wiki locally