Skip to main content
支持不同 ClassLoader 中的 Fluxon 运行时实例之间共享普通函数和扩展函数。典型场景:Bukkit 服务器中多个插件间共享脚本函数。

原理

使用 System.getProperties() 作为 JVM 全局单例锚点,存储 MethodHandle + 元数据的版本化 Object[] 元组。所有元素类型均来自 java.base,天然跨 ClassLoader 可见。 跨 ClassLoader 函数共享架构

设置共享身份

每个 Fluxon 运行时实例在导出前需要设置身份标识,用于区分函数来源:
FluxonRuntime runtime = FluxonRuntime.getInstance();
runtime.setSharingIdentity("MyPlugin");  // 通常用 plugin.getName()

导出函数

方式 1:直接导出 MethodHandle

MethodHandle mh = MethodHandles.lookup().findStatic(
    MyClass.class, "heal",
    MethodType.methodType(int.class, int.class));
runtime.exportFunction("heal", mh);

方式 2:导出已注册的函数

runtime.registerFunction("greet", signature, ctx -> {
    ctx.setReturnRef("Hello, " + ctx.getRef(0) + "!");
});
runtime.exportRegisteredFunction("greet");

方式 3:ExtensionBuilder 一步注册 + 导出

runtime.registerExtension(Player.class)
    .sharedFunction("heal", signature, ctx -> {
        // 实现
    })
    .sharedAsyncFunction("save", signature, ctx -> {
        // 异步实现
    })
    .sharedSyncFunction("teleport", signature, ctx -> {
        // 主线程实现
    });

方式 4:@Export 注解自动导出

public class PlayerAPI {
    @Export(shared = true)
    public static int heal(int amount) {
        return amount * 2;
    }
}
ExportRegistry.registerClass() 扫描到 shared = true 的方法时,会自动注册到全局共享注册表(前提是 sharingIdentity 已设置)。

导入函数

// 导入指定插件的指定函数
runtime.importSharedFunction("OtherPlugin", "heal");

// 导入指定插件的所有共享函数
runtime.importAllSharedFunctions("OtherPlugin");

// 导入所有插件的所有共享函数
runtime.importAllSharedFunctions();
导入后的函数注册为本地 NativeFunction,可通过 Fluxon.eval()Environment 正常调用。

显式查找(不自动注册)

Function f = env.getSharedFunction("OtherPlugin", "heal");
返回适配后的 NativeFunction 实例,但不注册到本地运行时。适用于一次性调用或条件导入。

插件卸载

在插件 onDisable 中调用,移除所有已导出的共享函数:
runtime.unexportAll();

版本安全

共享条目使用版本化协议(当前 v1)。当遇到更高版本的条目时:
  • 不会在导入时崩溃 — 返回一个 stub 函数
  • 调用时抛出 UnsupportedOperationException,包含版本差异的详细提示
  • 低版本读取高版本条目时,安全忽略末尾多余字段

版本演进规则

  1. entry[0] 永远是版本号,位置不变
  2. 新版本只能在 Object[] 末尾追加字段,不能修改已有字段
  3. 所有元素类型必须来自 java.base(避免 ClassLoader 隔离问题)

性能特征

跨 ClassLoader 调用通过 MethodHandle.invokeWithArguments() + Object[] 装箱,单次调用约 15-30ns 开销。适用于跨插件 API 调用(冷路径)。 对于热路径函数,建议使用源码级共享(本地 parse/compile)。

完整生命周期示例

// Plugin A: onEnable 
FluxonRuntime runtime = FluxonRuntime.getInstance();
runtime.setSharingIdentity("QuestPlugin");

// 注册并导出
runtime.registerFunction("check_quest", signature, ctx -> { ... });
runtime.exportRegisteredFunction("check_quest");

runtime.registerExtension(Player.class)
    .sharedFunction("get_quest_progress", signature, ctx -> { ... });

// Plugin B: onEnable 
FluxonRuntime runtime = FluxonRuntime.getInstance();

// 导入 QuestPlugin 的所有共享函数
runtime.importAllSharedFunctions("QuestPlugin");

// 现在可以在脚本中使用
Fluxon.eval("check_quest('main_story')");

// Plugin A: onDisable 
runtime.unexportAll();  // 清理所有已导出的函数

相关链接