跳到主要内容

API 开发文档

Malkuth 提供了完整的公开 API 接口,供其他插件开发者集成和扩展商城功能。

获取 API 实例

import kim.hhhhhy.malkuth.Malkuth

val api = Malkuth.api

所有 API 方法均为线程安全的,可在异步线程中调用查询方法。但涉及 UI 操作的方法(如 openShop)必须在主线程调用。


商店操作

openShop - 打开商店

为玩家打开指定商店。

fun openShop(player: Player, shopId: String, itemPage: Int = 0): Boolean

内部流程:

  1. 检查商店是否存在
  2. 校验玩家权限(malkuth.shop.<shopId>malkuth.shop.*
  3. 执行商店启用条件检查(Kether 脚本 + 限时判断)
  4. 触发 PlayerOpenShopEvent(可被取消)
  5. 根据商店模式分发到对应的会话处理

返回值:

  • true - 商店存在并已尝试打开
  • false - 商店不存在

注意: 返回 true 仅表示商店配置存在,不代表商店成功打开。玩家可能因权限不足、条件不满足或事件被取消而无法实际进入商店。

示例:

api.openShop(player, "weapon_shop")
api.openShop(player, "weapon_shop", itemPage = 2) // 直接打开第 3 页

openPlayerShop - 打开玩家商店

为玩家打开指定玩家的商店。

fun openPlayerShop(player: Player, shopId: String, targetUuid: UUID): Boolean

返回值:

  • true - 商店存在并已尝试打开
  • false - 商店不存在

示例:

api.openPlayerShop(player, "player_shop", targetPlayer.uniqueId)

getShop - 获取商店配置

获取已加载的商店序列化对象。

fun getShop(shopId: String): ShopSerializer?

返回的 ShopSerializer 包含商店的完整配置信息:货币设置、布局、商品列表、图标、模板、模式等。

示例:

val shop = api.getShop("weapon_shop")
if (shop != null) {
println("商店模式: ${shop.mode}")
println("商品数量: ${shop.goods.size}")
}

getShopIds - 获取所有商店 ID

fun getShopIds(): List<String>

返回所有已加载商店的 ID 列表(不可变)。

isShopEnabled - 检查商店可用性

综合检查商店对指定玩家是否可用。

fun isShopEnabled(player: Player, shopId: String): Boolean

检查项包括:商店存在性、权限、启用条件、限时状态。


商品操作

getGoods - 获取商品配置

获取已加载的商品序列化对象。

fun getGoods(goodsId: String): GoodsSerializer?

返回的 GoodsSerializer 包含商品的完整配置信息:显示设置、给予模式、购买动作、标签等。

示例:

val goods = api.getGoods("diamond_sword")
if (goods != null) {
println("给予模式: ${goods.giveSetting.mode}")
println("标签: ${goods.tags}")
}

getGoodsIds - 获取所有商品 ID

fun getGoodsIds(): List<String>

返回所有已加载商品的 ID 列表(不可变)。


货币系统

registerCurrency - 注册自定义货币

注册后,商店配置中可通过 currency: "<id>" 引用该货币。

fun registerCurrency(id: String, currency: ShopSerializer.CustomCurrency)

示例:

api.registerCurrency("my_coin", ShopSerializer.CustomCurrency(
placeholder = "%my_plugin_coins%",
giveAction = "my_plugin give coins {{ amount }}",
takeAction = "my_plugin take coins {{ amount }}",
denyAction = "tell inline \"余额不足\""
))

getCurrencyBalance - 获取玩家余额

获取玩家在指定商店货币体系下的余额。

fun getCurrencyBalance(player: Player, shopId: String): Number

返回值: 余额,商店不存在时返回 -1

hasCurrency - 检查余额是否足够

fun hasCurrency(player: Player, shopId: String, price: Number): Boolean

限购系统

getPlayerPurchaseCount - 查询购买次数

查询玩家在指定商店购买指定商品的累计次数。

fun getPlayerPurchaseCount(uuid: UUID, shopId: String, goodsId: String): Int

查询的是全时段总购买次数,不受商品限购时间窗口影响。数据来源于数据库持久化记录。

示例:

val count = api.getPlayerPurchaseCount(player.uniqueId, "weapon_shop", "diamond_sword")
println("玩家已购买 $count 次")

getRemainingPurchaseLimit - 获取剩余限额

获取玩家对指定商品的剩余购买限额。

fun getRemainingPurchaseLimit(player: Player, shopId: String, goodsId: String): Int

返回值: 剩余可购买数量,无限购时返回 999。
注意: 这里的 goodsId 在当前实现中对应商店 shop.ymlgoods 的 key(例如 1vip_pack),不是 goods/*.yml 的全局商品 ID。


动态定价

getEffectivePrice - 获取当前有效价格

获取商品当前有效价格(含动态定价计算)。

fun getEffectivePrice(shopId: String, goodsId: String, player: Player? = null): Double

返回值: 当前有效价格,商品不存在时返回 -1.0。 注意: 参数 goodsIdgetRemainingPurchaseLimit 一样,按商店内 goods 键解析。传入 player 时可获取含玩家上下文的动态价格。

示例:

val price = api.getEffectivePrice("weapon_shop", "1")
println("当前价格: $price")

交易记录

getTransactions - 查询玩家交易记录

fun getTransactions(uuid: UUID, limit: Int = 50): List<TransactionRecord>

返回交易记录列表,按时间倒序。

getShopTransactions - 查询商店交易记录

fun getShopTransactions(shopId: String, limit: Int = 50): List<TransactionRecord>

收藏夹

getFavorites - 获取收藏列表

fun getFavorites(uuid: UUID): List<FavoriteRecord>

hasFavorite - 检查是否已收藏

fun hasFavorite(uuid: UUID, shopId: String, goodsId: String): Boolean

addFavorite - 添加收藏

fun addFavorite(uuid: UUID, shopId: String, goodsId: String)

removeFavorite - 移除收藏

fun removeFavorite(uuid: UUID, shopId: String, goodsId: String)

邮箱系统

sendMailItem - 发送物品邮件

fun sendMailItem(
receiver: UUID,
item: ItemStack,
amount: Int,
senderName: String,
reason: String,
message: String = ""
)

示例:

api.sendMailItem(
receiver = targetPlayer.uniqueId,
item = ItemStack(Material.DIAMOND),
amount = 10,
senderName = "系统",
reason = "活动奖励",
message = "感谢参与活动!"
)

getUnclaimedMailCount - 查询未领取邮件数

fun getUnclaimedMailCount(uuid: UUID): Int

拍卖行

getActiveAuctions - 获取活跃拍卖

fun getActiveAuctions(): List<AuctionRecord>

getPlayerAuctions - 获取玩家拍卖

fun getPlayerAuctions(uuid: UUID): List<AuctionRecord>

购物车

getCartItemCount - 获取购物车商品数

fun getCartItemCount(uuid: UUID): Int

clearCart - 清空购物车

fun clearCart(uuid: UUID)

通知

sendNotification - 发送通知

向指定玩家发送商城通知消息。

fun sendNotification(uuid: UUID, message: String)

物品仓库

getRepositoryItem - 获取仓库物品

从插件内置物品仓库获取物品。

fun getRepositoryItem(category: String): ItemStack?

示例:

val item = api.getRepositoryItem("my_custom_sword")
if (item != null) {
player.inventory.addItem(item)
}

getRepositoryCategories - 获取所有分类

fun getRepositoryCategories(): List<String>

配置重载

reloadAll - 重载所有配置

fun reloadAll()

重载范围(当前实现):

  • config.yml 主配置(含 player-shop-block.*
  • currency.yml
  • 所有商品配置(goods/*.yml
  • 所有商店配置(shop/*.yml
  • 所有 UI 配置(ui/*.yml
  • 回收规则(recycle/*.yml,通过 RecycleRuleLoader 重新加载)
  • 玩家商店方块配置与绑定缓存(PlayerShopBlockService.reload()
  • 语言文件
  • 重置缓存服务与邮箱后端
  • 刷新商店占位符缓存
  • 清除随机商品结果缓存
  • 清空所有玩家购物车
  • 清理回收统计缓存
  • 关闭所有在线玩家当前打开的界面
  • 跨服同步通知(发布缓存失效广播)
  • 触发 MalkuthReloadEvent

不包含: 物品源仓库数据库同步、数据库连接重建等额外维护动作;这些仍需要你按实际运维流程单独处理


物品源辅助 API

Malkuth.api 外,源码还公开了 MalkuthItemItemSourceBuildSourceItem 作为物品源辅助入口,适合在二开时解析 source:itemId、注册自定义来源或读取当前已挂接的物品源。

MalkuthItem.getLoadedSources - 获取当前已加载的物品源

val sources = MalkuthItem.getLoadedSources()

当前注册逻辑会尝试挂载 33 个固定物品源 + 1 个条件物品源(mod:,仅 Mod 服务器加载);其中 minecraftrepository 为内置来源,baikiruto 也已作为固定来源加入,其余来源会按对应插件是否存在决定是否实际加载。

MalkuthItem.getSource / register - 解析或注册物品源

val source = MalkuthItem.getSource("mm")
MalkuthItem.register(mySource)

自定义物品源需要实现:

interface ItemSource {
val name: String
val alias: List<String>
val pluginName: String
val isLoaded: Boolean
fun build(id: String, player: Player? = null): ItemStack
}

MalkuthItem.parse2ItemStack - 解析 source:itemId

val built = MalkuthItem.parse2ItemStack("mm:legendary_sword", player)
  • 支持 source:itemId 与裸 itemId
  • itemId 默认按 minecraft 解析
  • 未识别的 source 会自动回退到 minecraft
  • 返回的 BuildSourceItem.source 是来源显示名(如 MythicMobs),不是 source key

target_inventory 当前可用值

和商店配置联动的背包提供者当前只有 vanillalegendwarehousesoulringx;未知或不可用提供者都会自动回退到 vanilla


API 事件系统

Malkuth 提供了完整的事件系统,允许其他插件监听和干预商城操作。所有事件均继承自 BukkitProxyEvent,其中前置事件通常可取消,后置事件与系统事件通常不可取消。

需要特别注意:PlayerBuyFromPlayerEvent / PlayerBuyFromPlayerPostEventPlayerSellEvent 并不只服务于“玩家商店”,全球市场也会复用这组事件。

购买相关事件

PlayerPurchaseEvent - 购买前事件

在货币扣除之前触发,可取消。

@EventHandler
fun onPurchase(event: PlayerPurchaseEvent) {
val player = event.player
val shopId = event.shopId
val goodsId = event.goodsId
val amount = event.amount
val price = event.price
val currency = event.currency
val buyMode = event.buyMode
val requirements = event.buyItemRequirements

// 取消购买
if (某些条件) {
event.isCancelled = true
}
}

PlayerPurchaseEvent 当前还会额外提供 buyModecurrency / items / hybrid)与 buyItemRequirements。同时,事件里的 price 表示当前成交单价,不是 amount * price 之后的总价。当你接入物物交换或混合支付商店时,推荐优先读取这两个字段,而不是只根据 currency 推断支付方式。

PlayerPurchasePostEvent - 购买后事件

购买成功后触发,不可取消。

@EventHandler
fun onPurchasePost(event: PlayerPurchasePostEvent) {
// 购买已完成,可记录日志或触发其他逻辑
}

玩家间交易事件

PlayerBuyFromPlayerEvent - 玩家间购买前

从玩家商店或全球市场购买前触发,可取消。

@EventHandler
fun onBuyFromPlayer(event: PlayerBuyFromPlayerEvent) {
val buyer = event.buyer
val seller = event.sellerUuid
val itemDisplayName = event.itemDisplayName
val price = event.price
}

PlayerBuyFromPlayerPostEvent - 玩家间购买后

购买成功后触发,不可取消。

回收相关事件

PlayerRecycleEvent - 回收前事件

物品回收前触发,可取消。

@EventHandler
fun onRecycle(event: PlayerRecycleEvent) {
val player = event.player
val item = event.itemStack
val price = event.price
}

PlayerRecyclePostEvent - 回收后事件

回收成功后触发,不可取消。

拍卖相关事件

PlayerAuctionBidEvent - 拍卖出价前

在出价写入之前触发,可取消。

@EventHandler
fun onAuctionBid(event: PlayerAuctionBidEvent) {
val player = event.player
val auctionId = event.auctionId
val currentPrice = event.currentPrice
val newPrice = event.newPrice
}

PlayerAuctionBidPostEvent - 拍卖出价后

出价成功后触发,不可取消。

赠送相关事件

PlayerGiftEvent - 赠送前事件

赠送商品前触发,可取消。

@EventHandler
fun onGift(event: PlayerGiftEvent) {
val sender = event.player
val receiver = event.targetUuid
val goodsId = event.goodsId
}

PlayerGiftPostEvent - 赠送后事件

赠送成功后触发,不可取消。

出售相关事件

PlayerSellEvent - 玩家上架前

玩家上架商品到玩家商店或全球市场前触发,可取消。当前 goodsId 来自上传物品的 ItemStack.type.name,不是 goods/*.yml 中的商品配置 ID。

PlayerSellPostEvent - 玩家上架后

上架成功后触发,不可取消。

购物车事件

PlayerCartCheckoutEvent - 购物车结算前

在逐个商品购买之前触发,可取消整个结算流程。

@EventHandler
fun onCartCheckout(event: PlayerCartCheckoutEvent) {
val player = event.player
val itemCount = event.itemCount
val totalPrice = event.totalPrice
}

PlayerCartCheckoutPostEvent - 购物车结算后

结算成功后触发,不可取消。

商店操作事件

PlayerOpenShopEvent - 打开商店前

商店打开前触发,可取消。

@EventHandler
fun onOpenShop(event: PlayerOpenShopEvent) {
val player = event.player
val shopId = event.shopId
}

PlayerCloseShopEvent - 关闭商店

商店关闭时触发,不可取消。

系统事件

MalkuthReloadEvent - 重载事件

配置重载完成后触发。

@EventHandler
fun onReload(event: MalkuthReloadEvent) {
// 配置已重载,可刷新缓存
}

ShopRandomRefreshEvent - 商店随机刷新事件

商店随机商品池刷新时触发。

@EventHandler
fun onRandomRefresh(event: ShopRandomRefreshEvent) {
val shopId = event.shopId
// 随机商品已刷新
}

数据结构

TransactionRecord - 交易记录

data class TransactionRecord(
val uuid: UUID,
val seller: String,
val shopId: String,
val goodsId: String,
val amount: Int,
val price: Double,
val timestamp: Long,
val type: String
)
  • goodsId 的含义取决于交易来源:系统商店通常记录配置中的 goods key,玩家商店 / 全球市场更常记录展示名或上传物品语义。
  • type 当前是开放字符串字段;源码中常见值包括 buybuy_itemsbuy_hybridgiftplayer_buyglobal_market_buy,UI 侧还兼容 sellrecycleauction 等类型。

FavoriteRecord - 收藏记录

data class FavoriteRecord(
val uuid: UUID,
val shopId: String,
val goodsId: String,
val timestamp: Long
)

AuctionRecord - 拍卖记录

data class AuctionRecord(
val auctionId: String,
val seller: UUID,
val startPrice: Double,
val currentPrice: Double,
val highestBidder: String,
val endTime: Long,
val shopId: String,
val status: String // "active" | "settled" | "cancelled"
)

完整示例

示例 1:自定义购买限制

@EventHandler
fun onPurchase(event: PlayerPurchaseEvent) {
val player = event.player
val goodsId = event.goodsId

// VIP 玩家才能购买传说物品
if (goodsId.contains("legendary") && !player.hasPermission("vip.member")) {
event.isCancelled = true
player.sendMessage("§c只有 VIP 玩家才能购买传说物品!")
}
}

示例 2:购买后奖励积分

@EventHandler
fun onPurchasePost(event: PlayerPurchasePostEvent) {
val player = event.player
val price = event.price

// 每消费 100 金币奖励 1 积分
val points = (price / 100).toInt()
if (points > 0) {
// 调用你的积分系统 API
MyPointsPlugin.addPoints(player, points)
player.sendMessage("§a获得 $points 积分奖励!")
}
}

示例 3:自定义货币集成

class MyPlugin : JavaPlugin() {
override fun onEnable() {
val malkuthAPI = Malkuth.api

malkuthAPI.registerCurrency("my_points", ShopSerializer.CustomCurrency(
placeholder = "%mypoints_balance%",
giveAction = "mypoints give {{ sender }} {{ &money }}",
takeAction = "mypoints take {{ sender }} {{ &money }}",
denyAction = "tell inline \"§c积分不足!需要 {{ &need-money }},你只有 {{ &money }}\""
))
}
}

示例 4:监控商店交易

@EventHandler
fun onPurchasePost(event: PlayerPurchasePostEvent) {
val player = event.player
val shopId = event.shopId
val goodsId = event.goodsId
val amount = event.amount
val price = event.price

// 记录到自定义日志系统
MyLogger.log(
"玩家 ${player.name} 在商店 $shopId 购买了 $amount$goodsId,花费 $price"
)

// 发送到 Discord Webhook
DiscordWebhook.send(
"**商城交易**\n" +
"玩家: ${player.name}\n" +
"商品: $goodsId x$amount\n" +
"价格: $price"
)
}