AzerothCore 公会系统开发:从创建、权限到银行与事件的完整链路 原创
温馨提示:
本文最后更新于 2026-10-26,已超过 0 天没有更新。
若文章内的图片失效(无法正常加载),请留言反馈或直接 联系我。
公会是 AzerothCore 中最复杂的社交系统:等级、权限、银行、事件、消息——每个功能都涉及多表关联和实时同步。这篇从 guild 表出发,讲清楚公会创建、权限管理、银行系统和事件通知的实现原理。
一、核心表:guild 家族
公会系统涉及 6 张核心表:
| 表 | 作用 |
|---|---|
| guild | 公会基本信息(名称、等级、经验、金钱) |
| guild_member | 成员列表(GUID、等级、职位、备注) |
| guild_rank | 职位定义(名称、权限、银行权限) |
| guild_bank_tab | 银行标签页(名称、图标、权限) |
| guild_bank_item | 银行物品(标签页、槽位、物品 GUID) |
| guild_eventlog | 事件日志(加入、离开、晋升、银行操作) |
二、公会创建:不只是 INSERT
创建公会的完整链路:
// Guild::Create 的简化逻辑
bool Guild::Create(Player* pLeader, std::string const& name)
{
// 1. 校验名称唯一
if (sGuildMgr->GetGuildByName(name))
return false;
// 2. 生成 guild Id
uint32 guildId = sGuildMgr->GenerateGuildId();
// 3. 插入主表
CharacterDatabase.Execute(
"INSERT INTO guild (guildid, name, leaderguid, createdate) "
"VALUES ({}, '{}', {}, UNIX_TIMESTAMP())",
guildId, name, pLeader->GetGUID().GetCounter());
// 4. 创建默认职位(会长、官员、成员、新人)
for (int i = 0; i < GUILD_RANKS_DEFAULT; ++i)
CreateDefaultRank(i);
// 5. 会长加入
AddMember(pLeader->GetGUID(), GUILD_RANK_LEADER);
// 6. 广播给在线玩家
SendGuildRosterToAll();
return true;
}
三、权限系统:Rank 的位掩码设计
guild_rank 的 rights 字段是位掩码:
enum GuildRankRights
{
GR_RIGHT_EMPTY = 0x00000000,
GR_RIGHT_GCHATLISTEN = 0x00000001, // 听公会聊天
GR_RIGHT_GCHATSPEAK = 0x00000002, // 说公会聊天
GR_RIGHT_OFFCHATLISTEN = 0x00000004, // 听官员聊天
GR_RIGHT_OFFCHATSPEAK = 0x00000008, // 说官员聊天
GR_RIGHT_INVITE = 0x00000010, // 邀请
GR_RIGHT_REMOVE = 0x00000020, // 踢人
GR_RIGHT_PROMOTE = 0x00000040, // 晋升
GR_RIGHT_DEMOTE = 0x00000080, // 降职
GR_RIGHT_SETMOTD = 0x00000100, // 设置 MOTD
GR_RIGHT_EPNOTE = 0x00000200, // 编辑公共备注
GR_RIGHT_VIEWOFFNOTE = 0x00000400, // 查看官员备注
GR_RIGHT_EOFFNOTE = 0x00000800, // 编辑官员备注
GR_RIGHT_MODIFY_GUILD_INFO = 0x00001000, // 修改公会信息
GR_RIGHT_WITHDRAW_GOLD = 0x00002000, // 提取金币
GR_RIGHT_WITHDRAW_REPAIR = 0x00004000, // 修理费
GR_RIGHT_WITHDRAW_ITEM = 0x00008000, // 提取物品
GR_RIGHT_ALL = 0xFFFFFFFF,
};
检查权限的代码:
bool Guild::HasRankRight(uint32 rankId, GuildRankRights right) const
{
if (rankId >= _ranks.size())
return false;
return (_ranks[rankId].GetRights() & right) == right;
}
// 使用
if (!guild->HasRankRight(member->GetRank(), GR_RIGHT_WITHDRAW_ITEM))
return false; // 无权提取物品
四、银行系统:Tab 权限 + 物品追踪
公会银行不是简单的「大背包」,而是带权限的共享存储:
// 每个 Tab 有独立的权限
struct GuildBankTab
{
uint32 TabId;
std::string Name;
std::string Icon;
uint32 Rights; // 谁能看
uint32 WithdrawRights; // 谁能取
};
// 存取记录
struct GuildBankEventLog
{
uint32 GuildId;
uint32 TabId;
uint32 EventType; // 存入/取出/移动
uint32 PlayerGuid;
uint32 ItemEntry;
uint32 Count;
uint32 TimeStamp;
};
关键设计:物品移动是原子操作,必须同时更新 guild_bank_item 和玩家背包,任何一方失败都要回滚。AzerothCore 用事务保证:
// 伪代码
CharacterDatabase.BeginTransaction();
try {
// 1. 从银行移除
RemoveItemFromTab(tabId, slotId);
// 2. 添加到玩家背包
player->AddItem(itemEntry, count);
// 3. 记录日志
LogBankEvent(tabId, GB_EVENT_WITHDRAW, playerGuid, itemEntry, count);
CharacterDatabase.CommitTransaction();
} catch (...) {
CharacterDatabase.RollbackTransaction();
throw;
}
五、事件系统:实时通知的关键
公会事件(加入、离开、晋升、银行操作)需要实时推送给在线成员:
void Guild::BroadcastEvent(GuildEvents event, uint32 arg1, uint32 arg2, uint32 arg3)
{
// 构建数据包
WorldPacket data(SMSG_GUILD_EVENT, 64);
data << uint8(event);
data << uint8(3); // 参数数量
data << arg1 << arg2 << arg3;
// 发送给所有在线成员
for (auto& member : _members)
{
if (Player* player = member.FindPlayer())
player->SendDirectMessage(&data);
}
// 写入事件日志
CharacterDatabase.Execute(
"INSERT INTO guild_eventlog (guildid, eventtype, arg1, arg2, arg3, timestamp) "
"VALUES ({}, {}, {}, {}, {}, UNIX_TIMESTAMP())",
GetId(), event, arg1, arg2, arg3);
}
六、实战:做一个「公会贡献榜」
需求:统计成员对公会的贡献(存款、捐物、带新人),每周结算发奖励。
-- 1. 加贡献字段
ALTER TABLE guild_member ADD COLUMN contribution INT UNSIGNED DEFAULT 0;
-- 2. 记录贡献(触发器或代码)
-- 存款时 +1
-- 捐物时 +物品价值
-- 带新人完成任务 +50
-- 3. 每周结算(worldserver.conf 加定时器)
-- Guild.WeeklyReset = 1
-- 4. 结算逻辑
void Guild::WeeklyContributionReset()
{
// 排序取前 10
auto sorted = GetMembersSortedByContribution();
for (int i = 0; i < 10 && i < sorted.size(); ++i)
{
Player* player = sorted[i]->FindPlayer();
if (player)
{
// 发邮件奖励
SendGuildRewardMail(player, i + 1);
}
}
// 清零
CharacterDatabase.Execute(
"UPDATE guild_member SET contribution = 0 WHERE guildid = {}", GetId());
}
结语
公会系统的复杂度在于多表关联 + 实时同步 + 权限细分。理解了 Rank 位掩码、银行 Tab 权限、事件广播这三个核心机制,做公会等级、公会成就、跨服公会都只是参数扩展。下一篇讲怎么做一个「公会副本进度排行榜」。