Skip to content

API 与集成 ​

第三方插件通过 emaki-dungeon-api (当前版本 1.0.1) 访问 EmakiDungeon. API Jar 只作编译依赖, 对应 runtime 产物为 EmakiDungeon-1.0.2.jar

Maven 坐标 ​

xml
<repositories>
  <repository>
    <id>jiuwu-releases</id>
    <url>https://repo.crypticlib.com/repository/maven-public/</url>
  </repository>
</repositories>

<dependency>
  <groupId>emaki.jiuwu.craft</groupId>
  <artifactId>emaki-dungeon-api</artifactId>
  <version>1.0.1</version>
  <scope>provided</scope>
</dependency>
kotlin
repositories { maven("https://repo.crypticlib.com/repository/maven-public/") }
dependencies { compileOnly("emaki.jiuwu.craft:emaki-dungeon-api:1.0.1") }

仅编译依赖

runtime 已内嵌未 relocate 的 API 类. 不要安装, bundle, shade 或 relocate API Jar; 重复 ClassLoader 类型会破坏事件与 bridge

EmakiDungeonApi 门面 ​

所有访问从静态门面 emaki.jiuwu.craft.dungeon.api.EmakiDungeonApi 开始, 共五个领域门面, 访问器永不返回 null:

java
// 可用性与身份元数据; EmakiDungeon 缺席时返回 notInstalled
var status = EmakiDungeonApi.status();

var dungeons = EmakiDungeonApi.dungeonApi();   // 进入与实例查询
var party    = EmakiDungeonApi.partyApi();     // 队伍
var instance = EmakiDungeonApi.instanceApi();  // 实例强制结束
var script   = EmakiDungeonApi.scriptApi();    // 流程脚本与触发器
var editor   = EmakiDungeonApi.editorApi();    // 打开编辑器
  • install / uninstall 与 Bridge 接口标记为内部/不可扩展, 第三方不要调用或实现
  • 在使用点解析访问器, 不要缓存到字段: reload 会替换底层 bridge
  • 插件缺席时每个门面返回空实现, 调用得到 Result.err(ErrorCodes.NOT_INSTALLED, ...), 用 status().usable() 判断桥是否可用

五个领域门面 ​

DungeonApi 进入与实例 ​

方法返回说明
enter(Player, String dungeonId)Result<Object>以程序化方式发起进入 (与 /edg enter 同链路, 同样的权限与限制判定)
listInstances()List<InstanceView>当前全部运行中实例的只读视图
getInstance(UUID instanceId)Optional<InstanceView>指定实例视图, 不存在为空

PartyApi 队伍 ​

方法返回说明
create(UUID player)Result<PartyId>为玩家创建队伍
invite(UUID player, UUID target)Result<InviteToken>发起邀请 (无队伍时按邀请模式提交加入)
kick(UUID member)Result<Void>踢出队员 (需队长身份语义由调用方保证)
confirmReady(UUID player)Result<Void>程序化确认准备, 等价 /edg ready

InstanceApi 实例运维 ​

方法返回说明
forceStop(UUID instanceId)Result<Void>强制结束实例, 等价 /edg admin stop 的程序化入口

ScriptApi 流程脚本 ​

方法返回说明
loadFlowScript(String dungeonId, String source)Result<ScriptId>载入一段 GraalJS 流程脚本源码
registerTrigger(String triggerId, String event, ScriptTriggerHandler handler)Result<Void>注册外部触发器处理器; triggerId 不可与已有触发器重复

EditorApi 编辑器 ​

方法返回说明
openEditor(Player, String dungeonId)Result<EditorSession>为玩家打开该副本的编辑会话; 模板单会话互斥等规则与命令路径一致, 调用方自行把好授权关

Result<T> 与错误码 ​

所有可失败方法返回 Result<T> record, 组成为 code / messageKey / args / value:

java
Result<PartyId> result = EmakiDungeonApi.partyApi().create(player.getUniqueId());
if (result.isSuccess()) {
    PartyId id = result.value();   // 失败时 value() 为 null
} else {
    // result.code() 是稳定错误码, result.messageKey() 是可映射到自己语言文件的消息键
}

ErrorCodes 常量按来源分组:

分组错误码
进入限制DENY_COOLDOWN, DENY_DAILY_LIMIT, DENY_WEEKLY_LIMIT, DENY_MONTHLY_LIMIT, DENY_WINDOW, DENY_GLOBAL_FULL, DENY_DUNGEON_FULL, DENY_PARTY_INVALID, DENY_NOT_LEADER, DENY_PERMISSION
队伍PARTY_ALREADY_EXISTS, PARTY_FULL, INVITE_PENDING, TARGET_OFFLINE, MEMBER_NOT_FOUND, NOT_LEADER
挑战与实例CHALLENGE_NOT_ACTIVE, ALREADY_READY, WINDOW_CLOSED, INSTANCE_NOT_FOUND, ALREADY_DISPOSING, DUNGEON_NOT_FOUND
脚本ENGINE_UNAVAILABLE, SCRIPT_COMPILE_ERROR, TRIGGER_DUPLICATE, SCRIPT_NOT_LOADED
桥缺席NOT_INSTALLED

事件 ​

公共事件位于 emaki.jiuwu.craft.dungeon.api.event 包. DungeonEvent 基类携带 getInstanceId() 与 getDungeonId(); DungeonChallengeStartEvent 与 DungeonChallengeCancelEvent 直接携带 dungeonId (挑战阶段还没有实例)

事件触发时机载荷可取消
DungeonChallengeStartEvent挑战发起, 难度与分配模式选定, 进入准备窗口时dungeonId, windowSeconds, difficulty, dropMode是, 取消即中止挑战
DungeonChallengeCancelEvent挑战作废时dungeonId, reason否
DungeonEnterEvent玩家进入副本实例时player (UUID), partyId + 基类字段否
DungeonExitEvent玩家离开副本实例时player (String), reason否
DungeonRoomClearEvent房间被清空时roomId否
DungeonGateOpenEvent门禁满足条件开启时gateId, condition否
DungeonChoiceEvent选项即将弹出选择提示时optionId, selectMode, 候选集是, 取消则不发选择提示
DungeonOptionConfirmEvent选项定案后optionId, selected否
DungeonSealPathEvent落选候选项的封路区生效时optionId, sealedCases否
DungeonCompleteEvent副本通关时score, durationMs否
DungeonFailEvent副本失败时reason (枚举), detail否
DungeonEndingEvent结局落定进入结算时endingId, optionCase否
java
@EventHandler
public void onDungeonComplete(DungeonCompleteEvent event) {
    // event.getInstanceId() / getDungeonId() 来自基类
    log(event.getDungeonId() + " 通关: " + event.getScore() + " 分, 用时 "
        + event.getDurationMs() + "ms");
}
  • 除标注"是"的两个外, 其余事件没有取消通道, 只做信息监听
  • 可取消的两个事件在各自流程点先派发再判定: 取消会直接改变插件后续行为, 适合做权限外的自定义拦截

线程与生命周期 ​

  • 触碰玩家背包, 世界或实体前, 自行确认当前线程符合 Bukkit/Paper 的 owner 线程要求; 无法确认时调度回主线程 (Folia 上对应 region 线程)
  • EmakiDungeon 未安装时, 首次加载 API 类存在链接失败风险, 建议把相关调用隔离并捕获 LinkageError | RuntimeException
  • 事件在插件内部流程点同步派发, 监听器内不要长时间阻塞, 也不要在监听器里直接修改副本世界状态而不考虑流程推进