Skip to content

事件系统

AIOShop 提供了完整的事件系统,让开发者可以监听和干预商店交易。

事件类型

ShopPrePurchaseEvent

购买前置事件,在玩家购买商品之前触发,可以被取消。

触发时机:主线程,进入异步处理链之前

可取消:是

kotlin
class ShopPrePurchaseEvent(
    val player: Player,      // 购买玩家
    val shop: UnifiedShop,   // 商店实例
    val item: ShopItem,      // 商品实例
    val amount: Int,         // 购买数量
    val price: Price         // 单价(已考虑折扣)
) : AIOShopEvent(), Cancellable {

    var cancelReason: String? = null  // 取消原因

    val totalPrice: Double            // 总价格
        get() = price.amount * amount
}

使用示例

kotlin
@EventHandler
fun onPrePurchase(event: ShopPrePurchaseEvent) {
    val player = event.player
    val item = event.item
    val totalPrice = event.totalPrice

    // 区域保护
    if (isRestrictedArea(player.location)) {
        event.isCancelled = true
        event.cancelReason = "此区域禁止交易"
        return
    }

    // 反作弊检测
    if (isAbnormalPurchase(player, item, event.amount)) {
        event.isCancelled = true
        event.cancelReason = "检测到异常交易行为"
        return
    }

    // 自定义权限检查
    if (!hasCustomPermission(player, item)) {
        event.isCancelled = true
        event.cancelReason = "你没有购买此商品的权限"
    }
}

ShopPostPurchaseEvent

购买后置事件,在玩家成功购买商品后触发,不可取消。

触发时机:异步线程,交易完全完成后

可取消:否

kotlin
class ShopPostPurchaseEvent(
    val transaction: Transaction,  // 完整交易记录
    val shop: UnifiedShop,         // 商店实例
    val item: ShopItem             // 商品实例
) : AIOShopEvent() {

    val player: Player?            // 购买玩家(可能为 null)
        get() = Bukkit.getPlayer(transaction.playerId)

    val isAsync: Boolean           // 是否在异步线程
        get() = !Bukkit.isPrimaryThread()

    val amount: Int                // 购买数量
        get() = transaction.amount

    val totalPrice: Double         // 总价格
        get() = transaction.price
}

使用示例

kotlin
@EventHandler
fun onPostPurchase(event: ShopPostPurchaseEvent) {
    val transaction = event.transaction

    // Discord 通知
    sendDiscordWebhook(
        "**${transaction.playerName}** 购买了 **${event.item.displayName}** x${transaction.amount}"
    )

    // 任务系统
    questManager.onPurchase(transaction.playerId, event.item.id, transaction.amount)

    // 经济分析
    economyAnalytics.recordPurchase(
        shopId = event.shop.id,
        itemId = event.item.id,
        price = transaction.price,
        timestamp = transaction.timestamp
    )

    // 数据库日志
    database.insertTransactionLog(transaction)
}

ShopPreSellEvent

出售前置事件,在玩家出售物品之前触发,可以被取消。

触发时机:主线程

可取消:是

kotlin
class ShopPreSellEvent(
    val player: Player,
    val shop: UnifiedShop,
    val item: ShopItem,
    val amount: Int,
    val price: Price
) : AIOShopEvent(), Cancellable {

    var cancelReason: String? = null

    val totalPrice: Double
        get() = price.amount * amount
}

使用示例

kotlin
@EventHandler
fun onPreSell(event: ShopPreSellEvent) {
    // 检查出售冷却
    if (isOnCooldown(event.player, "sell")) {
        event.isCancelled = true
        event.cancelReason = "出售操作冷却中"
        return
    }

    // 自定义出售限制
    val dailySold = getDailySoldAmount(event.player)
    if (dailySold + event.amount > 1000) {
        event.isCancelled = true
        event.cancelReason = "已达到今日出售上限"
    }
}

ShopPostSellEvent

出售后置事件,在玩家成功出售物品后触发,不可取消。

触发时机:异步线程

可取消:否

kotlin
class ShopPostSellEvent(
    val transaction: Transaction,
    val shop: UnifiedShop,
    val item: ShopItem
) : AIOShopEvent() {

    val player: Player?
    val isAsync: Boolean
    val amount: Int
    val totalPrice: Double
}

使用示例

kotlin
@EventHandler
fun onPostSell(event: ShopPostSellEvent) {
    val transaction = event.transaction

    // 更新成就
    achievementManager.onSell(transaction.playerId, transaction.amount)

    // 日志记录
    logger.info("[出售] ${transaction.playerName}: ${event.item.id} x${transaction.amount} = ${transaction.price}")
}

Transaction 对象

交易记录包含完整的交易信息:

kotlin
data class Transaction(
    val id: Long,              // 交易 ID
    val playerId: UUID,        // 玩家 UUID
    val playerName: String,    // 玩家名称
    val shopId: String,        // 商店 ID
    val itemId: String,        // 商品 ID
    val type: TransactionType, // 交易类型(BUY/SELL)
    val amount: Int,           // 交易数量
    val price: Double,         // 总价格
    val currency: String,      // 货币类型
    val timestamp: Long        // 时间戳
)

线程安全注意事项

前置事件(Pre)

  • 在主线程触发
  • 可以安全地操作 Bukkit API
  • 可以取消事件

后置事件(Post)

  • 在异步线程触发
  • 操作 Bukkit API 需要切回主线程
  • 不可取消事件
kotlin
@EventHandler
fun onPostPurchase(event: ShopPostPurchaseEvent) {
    // 异步操作(安全)
    database.saveTransaction(event.transaction)

    // 需要切回主线程的操作
    Bukkit.getScheduler().runTask(plugin) {
        event.player?.sendTitle("购买成功", "感谢惠顾")
    }
}

完整示例插件

kotlin
class MyShopAddon : JavaPlugin(), Listener {

    override fun onEnable() {
        server.pluginManager.registerEvents(this, this)
        logger.info("MyShopAddon 已加载")
    }

    @EventHandler(priority = EventPriority.HIGH)
    fun onPrePurchase(event: ShopPrePurchaseEvent) {
        // VIP 折扣
        if (event.player.hasPermission("vip.discount")) {
            // 通知玩家享受折扣
            event.player.sendMessage("§a[VIP] 您享受 VIP 专属折扣!")
        }
    }

    @EventHandler
    fun onPostPurchase(event: ShopPostPurchaseEvent) {
        // 首次购买奖励
        if (isFirstPurchase(event.transaction.playerId)) {
            Bukkit.getScheduler().runTask(this) {
                event.player?.let { player ->
                    player.sendMessage("§6恭喜完成首次购买!获得 100 金币奖励!")
                    // 给予奖励...
                }
            }
        }
    }

    private fun isFirstPurchase(playerId: UUID): Boolean {
        // 检查数据库...
        return false
    }
}

基于 CC0 1.0 许可发布