AzerothCore 拍卖行系统开发指南:从寄售竞价到结算邮件的完整链路 原创
拍卖行是 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, ...);
五、自定义实战:做一个「公会拍卖行」
结合前面知识点,做一个只有公会成员可见的拍卖行:
- 数据层:加一张
guild_auctionhouse表,结构复制 auctionhouse,多一个guildid字段 - 入口:写一个自定义 NPC,OnGossipHello 时根据玩家公会过滤展示
- 寄售:复用 AuctionHouseHandler 的逻辑,但写入新表
- 结算:起一个定时器,扫描 guild_auctionhouse 到期项,调用相同的 SendAuction* 系列邮件函数
- 隔离:玩家在主拍卖行看不到公会拍卖行的物品,反之亦然——避免互相串货
这个模式的关键是复用结算和邮件逻辑,只重做展示和存储,能省 80% 的代码量。
结语
拍卖行展示了 AzerothCore 处理异步经济系统的经典范式:单一事实表 + 状态机 + 定时结算 + 邮件载体。掌握了这套模式,改保证金、加手续费、做公会专用拍卖行、甚至做跨阵营拍卖行都只是参数和过滤条件的事。下一篇讲怎么用它做玩家之间的跨服交易。