公开 API · AuroraUI 单例入口

AuroraUI 公开 API

从 v3 起,所有第三方交互通过 AuroraUI 单例 完成。 没有 Bukkit ServicesManager、没有 AuroraUIApi 接口、没有继承 AuroraUIApplicationSession。 一切是 DSL 声明式 + Lambda 事件。

Maven 依赖 auroraui-api Kotlin 1.4+ SAM 必须在主线程调用

设计理念

单例入口

AuroraUI object 是唯一公开入口。没有注册服务,没有接口查询。

DSL 声明

用 AuroraUI.declare(id, owner) { ... } 一次性写完应用配置与所有事件。

Lambda 事件

不再抽象类继承,每个回调是一个 lambda;不需要的就不写。

AuroraUIRealm

事件 receiver 自带上下文(player / surface / mode),不再需要显式传 context。

类型安全

所有返回值是 Kotlin data class / enum,不再有 Object 强转。

向后兼容

旧 AuroraUIApi / AuroraUIApplication / AuroraUIApplicationContext 在 LegacyCompat.kt 保留为 @Deprecated 桥接。

快速上手

依赖(pom.xml):

<dependency>
  <groupId>aurora.studio.aurora.ui</groupId>
  <artifactId>auroraui-api</artifactId>
  <version>LATEST</version>
  <scope>provided</scope>
</dependency>

最小可用示例 — 一个"商店"锚点,带画布、权限、点击事件:

import aurora.studio.aurora.ui.api.*
import org.bukkit.plugin.java.JavaPlugin

class MyShopPlugin : JavaPlugin() {
    override fun onEnable() {
        AuroraUI.declare("myshop:shop", this) {
            permission = "myshop.use"
            captureScroll = true
            tickEvery = 2

            onActivate { evt ->
                // this == AuroraUIRealm,直接访问上下文
                server.broadcast("${player.name} 进入商店")
                cursor(true)
            }

            onScroll { evt ->
                // 滚轮翻页
            }

            onClose { evt ->
                // evt.reason ∈ AuroraUICloseReason
            }
        }
    }
}

通过命名空间路由打开锚点(或从 HTML 菜单用 open-app: myshop:shop):

AuroraUI.route("myshop:shop", this, AuroraUIRoute { player, args ->
    AuroraUI.show(player, "myshop:shop", args, AuroraUIShowKind.ANCHOR)
})

AuroraUI.declare(id, owner) { ... }

声明一个锚点(独立于菜单的、由第三方插件托管的 UI 会话)。DSL 块里可写以下属性与回调:

DSL 项类型说明
permissionString打开前检查的 Bukkit 权限节点(空字符串 = 无要求)
captureScrollBoolean是否在锚点活跃时接管滚轮(默认 false)
inheritCanvasBoolean是否继承上一个会话的画布平面(默认 true)
tickEveryIntonTick 触发间隔(tick),1–1200;默认 20
canvas { ... }AuroraUICanvas DSL逻辑画布:width / height / pixelsPerBlock / distance
onActivate { realm, evt -> ... }Lambda点击命中区时触发
onPointerMove { realm, evt -> ... }Lambda指针位置变化时触发
onPointerModeChange { realm, evt -> ... }Lambdatouch ↔ mouse 切换
onScroll { realm, evt -> ... }Lambda滚轮事件
onSurfaceChange { realm, evt -> ... }Lambda画布平面变化(打开、传送、重生)
onTick { realm, evt -> ... }Lambda周期性 tick
onClose { realm, evt -> ... }Lambda锚点关闭,evt.reason 说明原因

AuroraUI.declare 返回 AuroraUIAnchorHandle:

val handle: AuroraUIAnchorHandle = AuroraUI.declare("myshop:shop", this) { ... }
handle.alive          // 是否已注册
handle.remove()       // 手动注销(插件禁用时自动移除)
// handle.close() 等价于 remove()

AuroraUI.route / AuroraUI.dispatch

轻量命名空间路由 —— 菜单 open-app: myshop:shop args... 的入口。

// 注册
AuroraUI.route("myshop:shop", this, AuroraUIRoute { player, arguments ->
    AuroraUI.show(player, "myshop:shop", arguments, AuroraUIShowKind.ANCHOR)
})
// 也可以直接打开菜单
AuroraUI.route("myshop:quick-help", this, AuroraUIRoute { player, args ->
    AuroraUI.show(player, "help", args, AuroraUIShowKind.MENU)
})

// 查询/移除
AuroraUI.namespaces()      // Set<String>
// 插件禁用时自动移除其所有 route

AuroraUI.show / AuroraUI.hide / AuroraUI.back

方法签名说明
AuroraUI.show(Player, String, List<String>, AuroraUIShowKind): Boolean打开菜单 MENU 或锚点 ANCHOR
AuroraUI.hide(Player): Unit关闭当前会话
AuroraUI.back(Player): Boolean从历史栈返回上一级
AuroraUI.animate(Player, String): Boolean播放动画轨道
AuroraUI.stop(Player, String): Boolean停止动画轨道
AuroraUI.anchors(Player): Set<String>该玩家正在播放的轨道
AuroraUI.anchors(): Set<String>所有已注册锚点 ID

指针模式

枚举说明
AuroraUIMode.TOUCH默认"十字准星"模式
AuroraUIMode.MOUSE真正的光标模式
AuroraUIPolicy.CHOICE玩家可以自己选择模式
AuroraUIPolicy.FORCE_TOUCH服务器强制 touch
AuroraUIPolicy.FORCE_MOUSE服务器强制 mouse
AuroraUI.modeOf(player)                      // AuroraUIMode
AuroraUI.modeOf(player, AuroraUIMode.MOUSE)       // Boolean(是否成功切换)
AuroraUI.policy()                            // AuroraUIPolicy

// 查询当前画布/指针
AuroraUI.surfaceOf(player)                   // AuroraUISurface?
AuroraUI.pointOf(player)                     // AuroraUIPoint?

DSL 事件 —— 生命周期

onActivate

onActivate { evt ->
    // this@AuroraUIRealm 可用
    val p: Player = player
    val point: AuroraUIPoint? = evt.point
    val button: AuroraUIButton = evt.button      // LEFT / RIGHT / SHIFT_LEFT ...
    val sneaking: Boolean = evt.sneaking
    cursor(true)
    // 打开子锚点:
    showAnchor("myshop:detail", listOf("item=5"))
}

onClose

onClose { evt ->
    val reason: AuroraUICloseReason = evt.reason
    // REQUESTED / BACK / REPLACED / PLAYER_QUIT / PLUGIN_DISABLE / ERROR ...
}

DSL 事件 —— 指针

onPointerMove { evt ->
    val current: AuroraUIPoint? = evt.point
    val prev:    AuroraUIPoint? = evt.previous
}
onPointerModeChange { evt ->
    val prev = evt.previous    // AuroraUIMode
    val now  = evt.current
}
onScroll { evt ->
    val steps: Int = evt.steps  // 正=上, 负=下
}

DSL 事件 —— 画布 & Tick

onSurfaceChange { evt ->
    val surface: AuroraUISurface = evt.surface
    val initial: Boolean    = evt.initial   // true = 首次建立
}
onTick { evt ->
    val tick: Long = evt.tick
}

AuroraUIRealm —— 事件 receiver 的上下文

所有 DSL lambda 的 receiver 都是 AuroraUIRealm,自带 player、画布、指针等能力:

成员类型说明
playerPlayer直接可用的 Bukkit Player
anchorIdString当前锚点 ID
argsList<String>打开时传入的参数
surface()AuroraUISurface?当前画布快照
point()AuroraUIPoint?当前指针位置
mode()AuroraUIMode当前指针模式
aliveBoolean锚点是否还活着(onClose 触发时先变 false)
cursor(interactive)Unit视觉光标样式

从旧 API 迁移

旧 API(AuroraUIApi / AuroraUIApplication / AuroraUIApplicationContext)在 LegacyCompat.kt 保留为 @Deprecated 桥接层,当前 Paper 端仍接受。但所有新代码应直接写 AuroraUI DSL。

旧代码

AuroraUIApi ui = Bukkit.getServicesManager().load(AuroraUIApi.class);

AuroraUIApplicationHandle handle = ui.registerApplication(
    this, "myshop:shop",
    AuroraUIApplicationOptions.builder()
        .permission("myshop.use")
        .captureMouseScroll(true)
        .build(),
    context -> new ShopSession(context)
);

// ShopSession 继承 AuroraUIApplicationSession,重写 onActivate / onClose ...

新代码(等价)

AuroraUI.declare("myshop:shop", this) {
    permission = "myshop.use"
    captureScroll = true

    onActivate { evt ->
        // 原来写在 ShopSession.onActivate 的逻辑
    }
    onClose { evt ->
        // 原来写在 ShopSession.onClose 的逻辑
    }
}

对照表

旧 API新 API
ServicesManager.load(AuroraUIApi.class)直接 AuroraUI 单例(无需获取)
ui.registerApplication(...)AuroraUI.declare(id, owner) { ... }
AuroraUIApplicationOptions.BuilderDSL 里直接写属性:permission = / captureScroll = ...
extends AuroraUIApplicationSessionDSL 里写 onActivate { ... } 等 lambda
AuroraUIApplicationContext(显式传递)隐式 AuroraUIRealm receiver(this.player / this.surface())
ui.registerRoute(owner, id, AuroraUIRoute)AuroraUI.route(id, owner, AuroraUIRoute { player, args -> ... })
ui.open(player, menuId, args)AuroraUI.show(player, menuId, args, AuroraUIShowKind.MENU)
ui.openApplication(player, id, args)AuroraUI.show(player, id, args, AuroraUIShowKind.ANCHOR)
AuroraUICapability.APPLICATION_SESSIONS已废弃,所有能力始终可用
AuroraUIApplicationHandle.unregister()handle.remove() 或让插件禁用时自动移除