简体中文
English 简体中文 繁體中文
Methodox 首页

插件开发

最简单的插件形式就是一个普通的 C# 类。如果你只想公开一些简单的函数,请使用静态类:

public static class MyPlugin
{
	public static int GetValue() => 15;
}

1. 插件功能标志

宿主通过 PluginFeature 标志公开一组能力。在尝试使用某个子系统之前,插件应调用 GetFeatureSet() 并检查相关标志。可用的功能如下:

  • Menus (1) 宿主可以创建新的顶级菜单和嵌套子菜单。
  • DynamicPackages (2) 宿主可以在运行时向 Divooka 的工具箱中添加新的节点类型(通常称为“包”)。
  • EditorQuery (4) 宿主允许查询和修改图的运行时状态(例如读取节点值、连接/断开引脚)。
  • ConnectorVisualCustomizations (8) 宿主允许自定义节点引脚连线的外观(形状 + 颜色)。
  • CustomNodeSurface (16) [已过时] 已计划但尚未实现:完全自定义节点表面(超出内置的引脚/连接器渲染范围)。
  • PreviewProcessor (32) 宿主支持注册例程,将任意数据转换为 Divooka 可以原生预览的类型(例如图像、文本、图)。
  • CustomPreviewControl (64) [已过时] 已计划但尚未实现:嵌入自定义预览控件(例如自定义的 WPF 或 Godot 控件)。
  • CustomPreviewHandler (128) 宿主支持为任何节点类型提供完全自定义的“弹出式”预览处理程序(插件完全控制预览在单独窗口中的渲染方式)。

插件应通过按位运算组合这些标志(例如 if ((features & PluginFeature.Menus) != 0) { … }),以确定在运行时要注册或启用哪些内容。

插件确认 PluginFeature.Menus 可用后,就可以向 Divooka 的主菜单栏添加菜单项并注册嵌套子菜单。每个菜单项在被单击时调用一个回调。

// Add or remove a top‑level menu item:
void RegisterMenu(
    string menu,                  // unique identifier (e.g. "MyPlugin/Tools")
    string title,                 // user‑facing text
    string? tooltip,              // optional hover text
    Action<IDivookaEngineHostEnvironment> clickCallback,
    bool autoUnregister           // if true, host will remove it on plugin unload
);

void UnregisterMenu(string menu, string title);

// Add or remove a submenu under an existing menu:
void RegisterSubmenu(
    string menu,                  // same “menu” key as above
    string submenu,               // unique identifier for the submenu entry
    string title,
    string? tooltip,
    Action<IDivookaEngineHostEnvironment> clickCallback,
    bool autoUnregister
);

void UnregisterSubMenu(string menu, string submenu, string title);
  • 要点
    • menu 和 submenu 是标识符(不一定是显示的文字)。
    • title 是最终用户看到的内容。
    • autoUnregister 让宿主在插件卸载时进行清理。

3. 节点连接器外观

如果支持 ConnectorVisualCustomizations,插件就可以为任意给定的数据类型自定义引脚(输入/输出端口)和连线的外观。

void RegisterTypeConnectionAppearance(
    Type type,                    // the .NET type to style (e.g., typeof(MyCustomData))
    BuiltinConnectorShape shape,  // one of: HollowCircle, SolidSquare, etc.
    Color color                   // base color for the connector (e.g., System.Drawing.Color.Red)
);
  • BuiltinConnectorShape 一个包含内置形状的小型枚举(HollowCircle、HollowRectangle、HollowTriangle、HollowArrow、HollowSquare,以及对应的“Solid”形状)。
  • 用法 尽早注册(例如在插件初始化期间)。之后,Divooka 会使用你选择的形状 + 颜色来渲染所有承载该 CLR 类型的引脚。

4. 预览处理

Divooka 允许节点为其数据提供一个小型预览(例如显示图像、文本片段或图)。有两种不同的扩展点:

  1. Preview Processor(需要 PreviewProcessor 标志)

    void RegisterPreviewProcessor(
        Type type,                     // the data type your plugin wants to handle
        Func<object, object> processor // a function that converts “type” into a built‑in previewable type (e.g., Bitmap, string, List<float>, etc.)
    );
    
    • 何时使用: 如果你的插件的节点输出 Divooka 无法原生渲染的自定义数据类型,请实现一个处理器,把它转换为宿主已经知道如何显示的类型。
    • 示例: 将 MyImageData 实例转换为 System.Drawing.Bitmap。
  2. Custom Preview Handler(需要 CustomPreviewHandler 标志)

    void RegisterCustomPreviewHandler(
        Type instanceType,                 // the .NET type for which you supply full control
        Action<ProcessorNode, object> previewHandler
    );
    
    • 何时使用: 如果你需要一个宿主无法处理的完全自定义的弹出式预览(例如交互式图表或 3D 视图)。
    • 行为: 每当用户请求预览时,Divooka 都会调用你的 previewHandler,并将节点实例以及原始数据对象传给你。你的代码负责显示窗口或控件、渲染内容、挂接关闭事件等。注意:这只能以单独的弹出窗口形式工作,不能内嵌在画布上。

5. 动态类型 / 工具箱注册

如果启用了 DynamicPackages,插件就可以向 Divooka 告知新的节点类型——既包括后端功能(节点做什么),也包括前端工具箱条目(它在界面中出现在哪里)。RegisterPackages 调用统一处理以下几类定义集合:

void RegisterPackages(
    IEnumerable<ToolboxIndexer.AssemblyToolboxDefinition>? assemblyToolboxes,
    IEnumerable<ToolboxIndexer.FrontendToolboxDefinition>? frontendToolboxes,
    IEnumerable<ToolboxIndexer.TypeToolboxDefinition>? typedToolboxes,
    IEnumerable<ToolboxIndexer.SpecificToolboxEndPointDefinition>? specificEndpoints,
    IEnumerable<ToolboxIndexer.ProceduralContextBaseTypeDefinition>? proceduralContextTypes,
    bool autoUnregister = true
);
  • AssemblyToolboxDefinition 定义整个程序集的节点——Divooka 可以在运行时扫描程序集,找到被注释为节点的类,然后自动填充工具箱。
  • FrontendToolboxDefinition 描述你希望如何在 Divooka 的界面中对节点进行分组和标注(例如文件夹图标、类别)。
  • TypeToolboxDefinition 将单个 .NET 类型显式注册为节点——如果你希望精细控制其名称、图标和出现位置,这会很有用。
  • SpecificToolboxEndPointDefinition 让你可以把一个节点放在多个位置,或放在专门的上下文菜单下。
  • ProceduralContextBaseTypeDefinition 如果你的插件提供了一个“过程式上下文”(一种以自定义方式运行一组节点的有状态环境),就在这里注册其基类型,让 Divooka 了解你的运行时执行模型。

注册完成后,Divooka 的界面会在工具箱中显示这些新的节点类型;用户可以像使用内置节点一样,将它们拖放到画布上。

6. 过程式上下文执行运行器

对于引入自定义过程式执行上下文的插件,请使用:

void RegisterProceduralContextExecutionRunner(
    Type baseType,            // the abstract base for all of your procedural contexts
    Type instantiatorType     // a factory/runner class that knows how to execute that context
);
  • 用途 告诉 Divooka:“每当一个以派生自 baseType 的类型为根的节点图需要执行时,就使用这个 instantiatorType 来运行它。”
  • 行为 Divooka 会在运行时实例化你的运行器并把节点图交给它;你的运行器负责遍历节点、按正确的顺序对它们求值并产生输出。

7. 总结

  1. 启动时
    • 查询 GetFeatureSet()。
    • 如果支持菜单,则按条件调用 RegisterMenu/RegisterSubmenu。
    • 如果你有自定义数据类型,调用 RegisterTypeConnectionAppearance 为它们设置独特的引脚形状/颜色。
    • 如果你想添加新节点,请准备好工具箱定义(程序集扫描、类型描述符等)并调用 RegisterPackages。
  2. 插件初始化期间
    • 如果需要为预览转换节点输出,请使用 RegisterPreviewProcessor 注册一个处理器。
    • 如果需要完全控制预览(例如在浮动窗口中显示 3D 模型),请使用 RegisterCustomPreviewHandler 注册一个处理程序。
    • 如果你的节点需要特殊的执行环境,请注册一个 ProceduralContextExecutionRunner。
  3. 关闭(或卸载)时
    • 如果你设置了 autoUnregister = true,那么当 Divooka 卸载插件时,大多数注册内容(菜单、包、预览处理程序)都会被自动清理。否则,请显式调用 UnregisterMenu、UnregisterSubMenu 以及所需的任何自定义清理操作。

插件作者检查清单

  • 功能检测

    var features = host.GetFeatureSet();
    if ((features & PluginFeature.Menus) != 0)
        RegisterMenus();
    if ((features & PluginFeature.DynamicPackages) != 0)
        RegisterPackages(...);
    // etc.
    
  • 菜单

    • 使用 RegisterMenu/RegisterSubmenu 添加单击即执行的操作。
    • 始终提供唯一的键(例如 "MyPlugin/Tools/DoSomething")。
  • 节点外观

    • 使用 RegisterTypeConnectionAppearance(typeof(MyType), BuiltinConnectorShape.SolidCircle, Color.Blue) 为 MyType 的节点设置蓝色圆形引脚。
  • 预览支持

    • 如果你的节点输出自定义数据类型,请实现 Func<object, object>,将其转换为现有的可预览对象(字符串、Bitmap、数组等)。
    • 如果你需要弹出式编辑器,或者需要 Divooka 无法原生预览的内容,请实现自定义预览处理程序。
  • 动态类型(工具箱)

    • 在 RegisterPackages 中提供一个或多个工具箱定义。
    • 如果只想扫描整个程序集,请使用 AssemblyToolboxDefinition。如果想严格控制单个节点的元数据,请使用 TypeToolboxDefinition。
  • 执行

    • 如果你的插件包含必须在专门上下文中运行的节点,请注册一个 ProceduralContextExecutionRunner,将你的图的基类型映射到某个运行器/工厂。

上述各部分就绪后,Divooka 会自动显示你的菜单、渲染你的节点引脚、让用户把你的自定义节点类型拖入图中,并且在用户单击“Run”或请求预览时,在底层调用你的转换器或自定义预览代码。

通过把功能归入这六个方面(功能标志、菜单、节点外观、预览、动态类型注册和执行运行器),你可以确保插件与 Divooka 的宿主环境顺畅集成,并遵循预期的扩展点。

另请参阅

更多 C# 技巧。