AzerothCore 拍卖行系统开发指南:从寄售竞价到结算邮件的完整链路 原创

温馨提示:
本文最后更新于 2026-10-19,已超过 0 天没有更新。 若文章内的图片失效(无法正常加载),请留言反馈或直接 联系我。

拍卖行是 AzerothCore 里最复杂的异步经济系统:玩家寄售、他人竞价、到期结算、邮件返还——所有步骤都跨会话、跨角色,且任何一步出错都会造成「钱物两空」或「凭空刷物」。这篇从 auctionhouse 表出发,把寄售、竞价、结算、邮件四个阶段串起来,讲清楚怎么安全地定制它。

一、核心表:auctionhouse 的字段语义

AzerothCore 的拍卖行只用一个表:

SELECT id, houseid, itemguid, item_entry, item_count,
       item_owner, buyoutprice, time, buyguid, lastbid, startbid, deposit
FROM auctionhouse LIMIT 1;
字段 说明 易踩坑
houseid 拍卖行编号(阵营/中立) 不是副本 ID,别混
itemguid 物品实例 GUID 关联 item_instance.guid
item_owner 寄售者角色 GUID 结算时回邮的对象
startbid 起拍价 current bid 由 lastbid 决定
buyoutprice 一口价 0 = 无一口价
lastbid 当前最高出价 结算时给 owner 的金额
buyguid 当前最高出价者 到期结算的物品归属
time 到期时间戳 世界服务器每分钟扫一次
deposit 保证金 流拍才返还

关键不变量:物品在寄售期间不在玩家背包里,而是在 item_instance 中被标记为拍卖状态。这就是为什么直接 SQL 删行会造成物品凭空消失——必须先恢复物品状态。

二、寄售流程:从玩家点击到入库

源码入口在 AuctionHouseHandler::HandleAuctionSellItem。简化链路:

1. 校验:物品是否绑定、是否有足够保证金
2. 扣保证金:player->ModifyMoney(-deposit)
3. 移除背包物品,记录 item_guid
4. 写入 auctionhouse 行
5. 设置物品状态为 AUCTION(不在玩家背包显示)

如果你想加「VIP 玩家免保证金」这种定制,Hook 点在第 1 步校验后:

// 在 AuctionHouseHandler.cpp 找到扣费处
uint32 deposit = sAuctionMgr->GetAuctionDeposit(auctionLocation, ...);

// 自定义逻辑
if (player->GetSession()->GetSecurity() >= SEC_VIP) {
    deposit = 0;  // VIP 免保证金
}

三、竞价与一口价:状态切换的两个分支

竞标(Bid)和买断(Buyout)是两个完全不同的路径:

// 竞标
HandleAuctionPlaceBid:
  1. 校验 lastbid + 加价幅度
  2. 退回上一出价者的钱(邮件)
  3. 更新 buyguid 和 lastbid
  4. 扣新出价者的钱

// 一口价
HandleAuctionBuyout:
  1. 校验 buyoutprice > 0
  2. 立即扣款
  3. 立即结算(钱给 owner、物品给买家)
  4. 删除 auctionhouse 行

常见 bug 是「跨服时间差」:客户端时间不准会导致玩家看到的剩余时间和服务器不一致。永远以服务器时间为准,客户端只做展示。

四、到期结算:邮件是核心载体

worldserver 每分钟调用一次 AuctionHouseMgr::Update(),扫所有 time <= now() 的行:

if (auction 有人出价)
    邮寄物品给 buyguid + 邮寄钱给 owner
else
    邮寄物品回 owner + 返还保证金
删除 auctionhouse 行

这就是为什么玩家「拍到东西要等 1 分钟才收到」——结算周期是 60s。改这个值在 worldserver.conf 的 Auction.UpdateInterval。

自定义「手续费比例」通常也在这里加:

// 卖出价 1000G,收 5% 手续费
uint32 fee = lastbid * sWorld->getRate(RATE_AUCTION_CUT) / 100;
uint32 payout = lastbid - fee;
SendAuctionWonMail(...);
SendAuctionSuccessfulMail(owner, payout, ...);

五、自定义实战:做一个「公会拍卖行」

结合前面知识点,做一个只有公会成员可见的拍卖行:

  1. 数据层:加一张 guild_auctionhouse 表,结构复制 auctionhouse,多一个 guildid 字段
  2. 入口:写一个自定义 NPC,OnGossipHello 时根据玩家公会过滤展示
  3. 寄售:复用 AuctionHouseHandler 的逻辑,但写入新表
  4. 结算:起一个定时器,扫描 guild_auctionhouse 到期项,调用相同的 SendAuction* 系列邮件函数
  5. 隔离:玩家在主拍卖行看不到公会拍卖行的物品,反之亦然——避免互相串货

这个模式的关键是复用结算和邮件逻辑,只重做展示和存储,能省 80% 的代码量。

结语

拍卖行展示了 AzerothCore 处理异步经济系统的经典范式:单一事实表 + 状态机 + 定时结算 + 邮件载体。掌握了这套模式,改保证金、加手续费、做公会专用拍卖行、甚至做跨阵营拍卖行都只是参数和过滤条件的事。下一篇讲怎么用它做玩家之间的跨服交易。