联动
Map 模块跨模块联动、公开 Capability、EventBus 主题与数据库表结构
联动
Capability 注册
Map 模块在 startService() 中注册以下 Capability,供其他模块跨模块调用:
MapNavigable(公开)
地图导航能力接口,标注 @PublicCapability + @Stable——AXS 模块内部经 getCapability(MapNavigable.class) 获取,外部 Bukkit 插件经 AxsCapabilities.get(MapNavigable.class) 获取(插件需 depend: [ArcartXSuite] 或 softdepend)。
定位:地图是展示层与事件源。来源(QuestGPS / 外部插件)推送第三方插件注册路径点供玩家浏览;玩家点击"导航"时按优先级路由:全局委托 → useMapNavigation 地图代导航 → 来源 MapNavigationHandler 回调。
| 方法 | 参数 | 说明 |
|---|---|---|
upsertExternalTarget | Player, MapExternalTargetSpec spec, boolean select | 新增/更新单个第三方插件注册路径点;select=true 切到目标世界并选中 |
upsertExternalTargets | Player, List<MapExternalTargetSpec> | 批量推送第三方插件注册路径点,一次视图同步 |
reconcileExternalTargets | Player, String source, List<MapExternalTargetSpec> | 用给定集合原子替换该来源全部目标:单次同步、仍存在的目标选中态不闪断;空列表 = 清空该来源 |
clearExternalTargets | Player, String source, boolean syncView | 按来源清除第三方插件注册路径点(source 为空串 = 全部来源) |
setExternalNavigated | Player, String targetId, boolean navigated | 来源回推导航状态,驱动面板"导航 ↔ 取消"按钮(useMapNavigation 目标由地图自维护,无需调用) |
navigateExternalTarget | Player, String targetId | 由地图内置导航栈开始导航指定第三方插件注册路径点("地图代导航"命令式入口);已在导航返回 true,目标不存在/导航不可用返回 false |
registerNavigationSource | Plugin owner, String source, MapNavigationHandler | 注册来源导航回调;owner 插件禁用时自动注销 |
unregisterNavigationSource | String source | 注销来源导航回调 |
setNavigationDelegate | Plugin owner, MapNavigationDelegate | 注册全局导航委托(全量接管导航点击);全服仅一个,重复注册由后者覆盖;owner 插件禁用时自动注销 |
clearNavigationDelegate | Plugin owner | 注销导航委托(仅当当前委托属于该插件时生效) |
hasNavigationDelegate | — | 当前是否已注册全局导航委托 |
clearTrack | Player | 静默清除地图自身导航——来源开始自身导航时调用,实现导航互斥 |
openMenuFor | Player, String targetId | 打开地图并选中目标;依次匹配第三方插件注册路径点 → 路径点 → 标记点,未命中按世界 ID 兜底打开 |
MapExternalTargetSpec
第三方插件注册路径点规格 record,字段:targetId / source / worldId / title / description / x / y / z / iconId / navigated / sortOrder / useMapNavigation(保留省略 useMapNavigation 的兼容构造,另有省略 iconId/navigated/sortOrder/useMapNavigation 的便捷构造)。
useMapNavigation=true 时,玩家点该目标的"导航"由地图内置导航栈执行(客户端路标 + 路径标记 + 到达判定),不回调来源 handler——来源推送后零代码获得导航能力;导航样式/到达半径/命令钩子走地图 navigation.* 配置。默认 false 走来源回调/全局委托。
MapNavigationHandler
MapNavigationDelegate(全局委托)
setNavigationDelegate(owner, delegate) 注册的委托会先于一切导航处理拦截玩家在地图上的"导航"点击(锚点/路径点/第三方插件注册路径点;标记点 pin 不可导航不经此接口):
MapNavigationRequest record 携带目标完整描述:targetType(anchor/waypoint/external)、targetId、worldId、title、x/y/z、source(自有目标固定 "map")。契约要点:
- 导航执行(信标/路径标记/追踪状态)完全由委托方负责,地图不建客户端路标
- 委托接管第三方插件注册路径点时应另调
setExternalNavigated(player, targetId, true)驱动该目标行按钮切换 - 全服同一时间只有一个委托;重复注册由后者覆盖;
owner插件禁用时自动注销
导航互斥约定
互斥体现在跨来源层面(来源 ↔ 地图自身的锚点/路径点导航不共存;来源内部多目标并行不受此约束):
- 来源开始自身导航 → 调
clearTrack(player)静默清除地图导航(nav_cleared,reason=source_took_over) - 地图开始锚点/路径点导航 → 发布
axs.map.nav_started,来源应订阅并停止自身导航
调用示例(外部插件)
upsertExternalTarget/reconcile/clearExternalTargets/openMenuFor为同步主线程操作;registerNavigationSource/unregisterNavigationSource线程安全。
PlayerDataPurgeable
玩家数据清除能力,支持 /axs purge 统一清理玩家地图数据。
| 方法 | 参数 | 说明 |
|---|---|---|
moduleId | — | 返回模块 ID map |
purgePlayerData | UUID playerUuid | 清除指定玩家的全部地图数据(解锁记录 + 路径点 + 标记点),返回删除行数 |
purgeAllPlayerData | — | 清除所有玩家的全部地图数据,返回删除行数 |
purgePlayerData在事务中删除AXS_map_unlocks、AXS_map_waypoints、AXS_map_pins三表中该玩家的所有记录。失败时返回-1。
DatabaseMigratable
数据库迁移能力,支持 /axs migrate map 跨源数据库迁移。
| 方法 | 参数 | 说明 |
|---|---|---|
moduleId | — | 返回模块 ID map |
migrateDatabase | StorageDescriptor target, boolean overwrite | 将数据从当前存储迁移到目标存储 |
currentDescriptor | — | 返回当前存储描述符 |
迁移时支持从 SQLite 迁移到 MySQL 或反向迁移,
overwrite为true时覆盖目标库已有数据。
第三方插件对接方案
获取接口:AxsCapabilities.get(MapNavigable.class)(需 depend/softdepend: [ArcartXSuite] + compileOnly axs-api)。
第三方插件注册路径点"导航"点击按优先级路由:全局委托 → useMapNavigation 地图代导航 → 来源 handler → 提示「来源未提供导航支持」。按接入用途选择:
| 用途 | 接入方式 | 导航执行方 |
|---|---|---|
| 第三方插件注册路径点由 Map 执行导航 | spec 设 useMapNavigation=true,或调用 navigateExternalTarget | Map 模块导航栈 |
| 第三方插件代理自身注册路径点导航 | registerNavigationSource 注册来源回调 | 第三方插件 |
| 第三方插件代理全部地图标记点导航(含自带锚点/路径点) | setNavigationDelegate | 第三方插件 |
| 仅展示第三方插件注册路径点,不提供导航 | 仅推送第三方插件注册路径点 | 无 |
同一
source可按目标混用:部分目标useMapNavigation=true交由 Map 导航,其余经来源 handler 自行执行。
第三方插件注册路径点由 Map 执行导航(useMapNavigation)
spec 设 useMapNavigation=true 后,点击"导航"由 Map 导航栈全程执行——客户端路标、路径标记、到达判定、finish-commands、按钮态翻转、取消、互斥、跨世界 cross_world_navigate:
- 取消路径同样由 Map 处理:来源插件不接收
onCancelRequest,亦无需调用setExternalNavigated(navigated标志由 Map 维护) upsert/reconcile推送新坐标时自动刷新路标;目标被reconcile/clear移除时其导航态一并回收- 导航样式与行为参数(
navigation.waypoint.*/finish-*等)统一采用 Map 配置,来源插件不可按目标定制 navigateExternalTarget亦可在来源 handler 内按需转交:同一source可混用"Map 执行"与"来源自执行"的目标
第三方插件代理自身注册路径点导航(来源回调)
来源插件注册 MapNavigationHandler:玩家点击"导航"/"取消"回调至来源插件,导航执行由来源插件完成:
互斥义务:来源插件开始导航前调用 map.clearTrack(player) 清除地图导航;订阅 axs.map.nav_started,在 Map 开始导航时停止自身导航。QuestGPS 模块即采用此模式。
第三方插件代理全部地图标记点导航(全局委托)
setNavigationDelegate(this, delegate) 后,锚点/路径点/第三方插件注册路径点的"导航"点击统一回调 onNavigateRequest(标记点 pin 不可导航,不经委托):
- 返回
true表示接管:Map 记录委托导航态并发布nav_started,不创建路标——路标/路径/到达判定均由委托方实现;返回false表示放行,移交下一级处理(可按targetType/source选择性接管) onCancelRequest在取消/互斥/玩家退出时触发,须保证幂等;取消逻辑应以targetType + targetId建立稳定映射——request 坐标为尽力重解(目标已删除时为 0),不可依赖坐标- 委托接管第三方插件注册路径点时需另调
setExternalNavigated更新该行按钮;全局唯一委托,重复注册覆盖,owner插件禁用时自动注销 - 边界:
cross_world_navigate的传送段(含扣费)由 Map 先行执行,成功后移交委托方;传送/解锁/路径点管理不经委托;除onCancelRequest外无其他生命周期回调(无到达事件)
其他入口
- 仅展示:仅推送目标、不注册 handler,点击"导航"提示「来源未提供导航支持」(适合位置公示,可在
description注明) - 事件订阅:经
EventBusCapability订阅下表主题——external_navigate在回调/委托之前发布,各接管路径均可观测 - 打开地图:
openMenuFor(player, targetId)依次匹配第三方插件注册路径点 → 路径点 → 标记点,未命中按世界兜底打开
AXS 模块额外选项:复用导航栈
axs-api 的 NavigationTargetService / NavigationMarkerService / NavigationMarkerConfig / NavigationTarget 是可直接实例化的共享组件(Map 与 QuestGPS 的导航均构建其上)。AXS 模块经 createWaypointBridge() / createAdyeshachNpcBridge() 注入桥接后自行 new 实例,即可获得多目标并行登记 + 单路标单路线、diff 推送、到达滞回 + 命令钩子、NPC/动态 resolver、跨世界提示、标题模板等完整能力。
外部 Bukkit 插件拿不到
WaypointBridgeAPI/AdyeshachNpcBridgeAPI(非@PublicCapability,仅注入 AXS 模块上下文),此路线仅适用于 AXS 模块。
EventBus 主题
Map 模块向 EventBus 发布以下主题,其他模块/外部插件可经 EventBusCapability 订阅:
| 主题 | 触发时机 | Payload |
|---|---|---|
axs.map.nav_started | 玩家开始导航(锚点/路径点,含委托接管与地图代导航的第三方插件注册路径点) | type(anchor/waypoint/external)、id、world_id |
axs.map.nav_cleared | 玩家停止地图导航(含委托态移除) | type(anchor/waypoint/external)、id、world_id、reason(cleared/来源接管 source_took_over) |
axs.map.external_navigate | 面板点击第三方插件注册路径点"导航"(在委托/来源回调之前发布,两种接管路径都能观测) | target_id、source、world_id |
axs.map.external_nav_cancel | 面板点击第三方插件注册路径点"取消" | target_id、source、world_id |
跨服同步
Map 模块的配置文件支持 SyncPolicy,ArcartXMap.yml 中以下段标记为动态段,可在跨服环境下同步配置:
| 同步段 | 说明 |
|---|---|
worlds | 世界定义(动态段,跨服同步;仅内联兼容格式生效) |
anchors | 锚点定义(动态段,跨服同步) |
waypoints | 路径点配置(动态段,跨服同步) |
其他配置段(
ui、join、storage、navigation、share等)为静态段,各服独立加载。注意:实际生效的世界/锚点定义位于worlds/目录的独立文件中,不受ArcartXMap.yml节级同步覆盖——多服部署需自行保持各服worlds/文件一致。
数据库表结构
Map 模块使用三张表存储玩家数据,表名前缀取决于存储模式。
共享模式表名
共享模式下使用本体统一表前缀,表名固定为 AXS_map_unlocks、AXS_map_waypoints、AXS_map_pins。
自建模式表名
自建模式下使用 storage.mysql.table-prefix(默认 axs_map_)作为表名前缀。
表结构
AXS_map_unlocks(锚点解锁记录)
| 列 | SQLite 类型 | MySQL 类型 | 说明 |
|---|---|---|---|
uuid | TEXT NOT NULL | VARCHAR(36) NOT NULL | 玩家 UUID |
anchor_id | TEXT NOT NULL | VARCHAR(128) NOT NULL | 锚点 ID |
unlock_time | INTEGER NOT NULL | BIGINT NOT NULL | 解锁时间戳(毫秒) |
| 主键 | (uuid, anchor_id) | (uuid, anchor_id) | 联合主键 |
AXS_map_waypoints(玩家路径点)
| 列 | SQLite 类型 | MySQL 类型 | 说明 |
|---|---|---|---|
uuid | TEXT NOT NULL | VARCHAR(36) NOT NULL | 玩家 UUID |
waypoint_id | TEXT NOT NULL | VARCHAR(128) NOT NULL | 路径点 ID |
name | TEXT NOT NULL | VARCHAR(128) NOT NULL | 路径点名称 |
description | TEXT NOT NULL | TEXT NOT NULL | 描述 |
world | TEXT NOT NULL | VARCHAR(64) NOT NULL | 所在世界 ID |
x | REAL NOT NULL | DOUBLE NOT NULL | X 坐标 |
y | REAL NOT NULL | DOUBLE NOT NULL | Y 坐标 |
z | REAL NOT NULL | DOUBLE NOT NULL | Z 坐标 |
created_at | INTEGER NOT NULL | BIGINT NOT NULL | 创建时间戳(毫秒) |
updated_at | INTEGER NOT NULL | BIGINT NOT NULL | 更新时间戳(毫秒) |
| 主键 | (uuid, waypoint_id) | (uuid, waypoint_id) | 联合主键 |
AXS_map_pins(玩家标记点)
| 列 | SQLite 类型 | MySQL 类型 | 说明 |
|---|---|---|---|
uuid | TEXT NOT NULL | VARCHAR(36) NOT NULL | 玩家 UUID |
pin_id | TEXT NOT NULL | VARCHAR(128) NOT NULL | 标记点 ID |
name | TEXT NOT NULL | VARCHAR(128) NOT NULL | 名称 |
description | TEXT NOT NULL | TEXT NOT NULL | 描述 |
world | TEXT NOT NULL | VARCHAR(64) NOT NULL | 所在世界 ID |
x | REAL NOT NULL | DOUBLE NOT NULL | X 坐标 |
z | REAL NOT NULL | DOUBLE NOT NULL | Z 坐标 |
created_at | INTEGER NOT NULL | BIGINT NOT NULL | 创建时间戳(毫秒) |
updated_at | INTEGER NOT NULL | BIGINT NOT NULL | 更新时间戳(毫秒) |
| 主键 | (uuid, pin_id) | (uuid, pin_id) | 联合主键 |
MySQL 表使用
ENGINE=InnoDB DEFAULT CHARSET=utf8mb4。SQLite 使用INSERT OR IGNORE/ON CONFLICT语法,MySQL 使用INSERT IGNORE/ON DUPLICATE KEY UPDATE语法实现原子 upsert。
与其他模块联动
QuestGPS
| 联动方式 | 说明 |
|---|---|
| 第三方插件注册路径点注入 | QuestGPS 通过 MapNavigable.reconcileExternalTargets 把进行中任务原子同步为地图第三方插件注册路径点(source = questgps),任务接取/失败/完成/进服时刷新 |
| 坐标跟随 | Chemdah tracker 动态坐标变化 > 0.5 格时经 upsertExternalTarget 更新目标坐标 |
| 导航回调 | QuestGPS 注册 MapNavigationHandler:面板"导航"→ trackQuest/trackTask,"取消"→ 停止追踪(标记保留) |
| 导航互斥 | QuestGPS 起导航调 clearTrack;地图起导航发布 axs.map.nav_started,QuestGPS 订阅后停止自身任务导航 |
| 打开地图 | QuestGPS UI open_map 操作调用 MapNavigable.openMenuFor 打开 Map 菜单并选中目标 |
Map 模块不存在时 QuestGPS 导航功能正常运行,仅跳过地图标记同步。
Chat(点位分享)
| 联动方式 | 说明 |
|---|---|
| 分享卡片 | share_waypoint / share_pin 通过 Chat 模块的 ChatCardSendable 发送 axs_map_share 卡片到聊天栏 |
| 卡片领取 | 卡片按钮触发 claim_share packet 领取点位(不经 actionToken,分享码即凭据) |
| 文本降级 | 卡片不可用时降级为文本广播 + share-claim-button 点击文本 |
ArcartX Waypoint
| 联动方式 | 说明 |
|---|---|
| 路标导航 | 通过 WaypointBridgeAPI 创建/移除路标,在客户端显示方向指引 |
| 样式解析 | 路标样式 ID 优先使用 navigation.waypoint.style-id,回退到 waypoints.default-style-id |
| 初始化 | 模块启动时调用 waypointBridge.initialize("Map 导航") 初始化路标系统 |
ArcartX Waypoint 不存在时锚点/路径点导航不可用(
waypointRuntimeReady返回false),但地图菜单、HUD 与第三方插件注册路径点转发正常使用。
Adyeshach
| 联动方式 | 说明 |
|---|---|
| 导航标记模型 | 通过 AdyeshachNpcBridgeAPI 创建私有实体,使用 ArcartX 模型渲染客户端导航标记 |
| 路径标记 | 沿导航路径放置标记模型,仅在追踪状态变更时发包,客户端独立渲染 |
| Debug 模式 | debug: true 时 Adyeshach 桥接开启调试日志 |
Adyeshach 不存在时模型导航提示自动关闭(
navigation.model-marker.enabled仍可设为true,但运行时检测不可用后跳过),路标导航与贴图导航提示(navigation.texture-marker)不受影响。
Multiverse-Core
| 联动方式 | 说明 |
|---|---|
| 世界显示名 | 世界文件 display-name 留空时,用 Multiverse colourless alias 自动补齐 |
| 小地图世界名 | map_hud.yml 用 Placeholder.parse("%multiverse-core_alias%") 显示当前世界名 |
Vault / 货币系统
| 联动方式 | 说明 |
|---|---|
| 锚点解锁费用 | 通过 CurrencyBridgeAPI 扣除锚点解锁所需的货币 |
| 锚点传送费用 | 通过 CurrencyBridgeAPI 扣除锚点传送所需的货币 |
| 余额检查 | 解锁/传送前检查玩家货币余额是否充足 |
| 回滚机制 | 扣费失败或后续操作失败时自动回滚已扣除的货币 |
Vault 不存在时锚点解锁/传送的货币消耗不可用。配置了货币消耗但桥接不可用时,操作失败并提示「货币桥接不可用」。
物品消耗
| 联动方式 | 说明 |
|---|---|
| 锚点解锁物品 | 通过 ItemMatcherAPI 匹配玩家背包物品,解锁时消耗指定数量 |
| 预检机制 | 解锁前先检查背包是否有足够物品,不足时提示「缺少解锁所需物品」 |
| 回滚机制 | 物品扣除后若后续操作失败,自动恢复原始背包内容 |
物品匹配器配置参考 /docs/guide/item-sources 中的物品匹配器语法。
ArcartX 核心
| 联动方式 | 说明 |
|---|---|
| UI 注册 | 通过 registerModuleUi 注册地图菜单和小地图 HUD 两个 UI |
| Packet 通讯 | 通过 PacketBridgeAPI 向客户端 UI 发送 init/update 包 |
| Packet 安全 | 通过 PacketGuardAPI 对客户端 Packet 进行权限校验(在路由层统一调用) |
| 客户端初始化 | 通过 ClientInitializedHandler 监听客户端初始化完成,触发自动解锁和 HUD 显示 |
| 按键绑定 | 打开地图/切换小地图按键由宿主 config.yml 的 keybinds 节统一注册 |
| 物品源 | 通过 ItemSourceRegistry 获取物品源 |
| 物品匹配 | 通过 ItemMatcherAPI 进行物品匹配 |
| 调度器 | 通过 AxsScheduler 执行延迟任务(如入服 HUD 延迟显示) |
配置版本迁移
Map 模块内置配置迁移脚本,自动将旧版本配置升级到当前版本:
| 迁移 | 操作 |
|---|---|
| 1 → 2 | client 节的 UI 配置字段(menu-ui-id/hud-ui-id/packet-id/register-ui-on-enable/overwrite-ui-files)整体迁移到统一 ui 节,移除 client 节 |
| 2 → 3 | storage.mode 重命名为 storage.shared,值从 sqlite/mysql 映射为 true/false;storage.shared 缺失时设为 true |
| 3 → 4 | 移除全局 default-unlocks 节,自动解锁改为 worlds/<world>.yml 锚点的 unlock.default / unlock.on-permission |
迁移脚本位于
resources/migrations/目录,模块启动时自动检测config-version并按序执行迁移。