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
内部流程:
- 检查商店是否存在
- 校验玩家权限(
malkuth.shop.<shopId>或malkuth.shop.*) - 执行商店启用条件检查(Kether 脚本 + 限时判断)
- 触发
PlayerOpenShopEvent(可被取消) - 根据商店模式分发到对应的会话处理
返回值:
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.yml 下 goods 的 key(例如 1、vip_pack),不是 goods/*.yml 的全局商品 ID。
动态定价
getEffectivePrice - 获取当前有效价格
获取商品当前有效价格(含动态定价计算)。
fun getEffectivePrice(shopId: String, goodsId: String, player: Player? = null): Double
返回值: 当前有效价格,商品不存在时返回 -1.0。
注意: 参数 goodsId 与 getRemainingPurchaseLimit 一样,按商店内 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 外,源码还公开了 MalkuthItem、ItemSource 与 BuildSourceItem 作为物品源辅助入口,适合在二开时解析 source:itemId、注册自定义来源或读取当前已挂接的物品源。
MalkuthItem.getLoadedSources - 获取当前已加载的物品源
val sources = MalkuthItem.getLoadedSources()
当前注册逻辑会尝试挂载 33 个固定物品源 + 1 个条件物品源(mod:,仅 Mod 服务器加载);其中 minecraft 与 repository 为内置来源,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 当前可用值
和商店配置联动的背包提供者当前只有 vanilla、legendwarehouse、soulringx;未知或不可用提供者都会自动回退到 vanilla。
API 事件系统
Malkuth 提供了完整的事件系统,允许其他插件监听和干预商城操作。所有事件均继承自 BukkitProxyEvent,其中前置事件通常可取消,后置事件与系统事件通常不可取消。
需要特别注意:PlayerBuyFromPlayerEvent / PlayerBuyFromPlayerPostEvent 与 PlayerSellEvent 并不只服务于“玩家商店”,全球市场也会复用这组事件。
购买相关事件
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 当前还会额外提供 buyMode(currency / 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当前是开放字符串字段;源码中常见值包括buy、buy_items、buy_hybrid、gift、player_buy、global_market_buy,UI 侧还兼容sell、recycle、auction等类型。
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"
)
}