《孙子兵法》里写:“静如处子,动如脱兔。”
上篇聊 World 库,它是游戏的"静"——全服共享,上线之后基本不动。Characters 库正好相反,它是游戏的"动":每打一只怪、每完成一个任务、每换一件装备,这个库里就有数据被写入。玩家一退出,数据落盘存档;重新上线,从这里把状态读出来接着玩。
如果说 World 库是游戏世界的"静",Characters 库就是"动"。
角色主表:一切从这里出发
先说characters,这张表是 Characters 库的中心,其他所有表都围着它转。
里面存的都是最基本的信息:名字、种族、职业、等级、当前所在的地图和坐标、在线还是离线、金币数量、角色创建时间、上次登录时间。
有个字段我之前没太注意——online,布尔值,直接记录角色当前是否在线。服务端崩了重启之后,读这个字段就知道哪些角色在线需要恢复状态。这是最朴素的方案,但也最可靠。
character_entry_point记录角色上次进入副本或战场时的入口坐标——万一掉线重连,需要把人拉回正确位置,不是随便扔在原地。
character_homebind存炉石的绑定地点。你绑在哪个主城,死了之后灵魂医者送你去哪张地图,全在这。
背包里的东西:item_instance 与 character_inventory
背包系统是 Characters 库里我最喜欢的部分,设计得很巧妙。
两件事要分开存:物品"是什么"和物品"在哪里/属于谁"。
item_instance存的是每件物品的"实例数据"——它当前的主人是谁、附魔了哪条属性、有没有随机属性(敏爆、力爆之类的)、耐久度还剩多少。这张表在 Characters 库里,因为每件装备装到角色身上之后,就属于这个角色了。
character_inventory存的是"位置关系"——哪件物品在背包的第几格,或者在银行的哪个标签页的第几格。物品换了个位置,只需要改这条记录的 slot 信息,item_instance本身不用动。
这个拆分厉害的地方在哪?同一个物品实例,可以出现在背包里、邮箱附件里、公会银行里、甚至交易窗口里——"属于谁"是它自己的属性,"放在哪"是另一张表的关联关系。不然背包系统没法实现物品在多个容器之间流转。
还有两张有意思的表:item_refund_instance记录两小时退款窗口的信息,买的东西不满意可以在窗口期内退。item_soulbound_trade_data处理灵魂绑定物品在团队分配后两小时内可以交易的设计——团队掉落被分配了但还没绑定,这个窗口期内可以转给队友。
任务:六张表各管各的
character_queststatus系列是最能体现"变更频率差异"的一组设计。
MMO 里的任务有几类:普通任务做一次就永久完成,日常任务每天重置,周常每周重置,节日任务按赛季重置,还有普通任务做完领了奖励的记录。
如果全塞一张表,备份的时候怎么处理?日常任务每天都在变,周常每周变,普通任务做完就不动——混在一起备份策略没法差异化。
所以 Characters 库拆成了六张任务状态表:
character_queststatus:普通任务的当前状态character_queststatus_rewarded:已完成且领取奖励的任务(永久记录)character_queststatus_daily/_weekly/_monthly/_seasonal:按周期重置的任务
重置的意思是清空进度记录,下个周期重新开始。character_queststatus_rewarded单独拎出来因为它永远不会重置,完成了就是完成了,这个角色从此记得这件事。
quest_tracker比较特殊,这是给 GM 和系统用的追踪表——GM 想监控某个任务被多少玩家完成了,就往这张表里写。
公会:银行系统比想象中复杂
公会相关的表有将近十张,可见公会银行(Guild Bank)不是简单的一个物品柜。
公会银行是 WoW 里的共享仓库,公会成员可以把物品存进去、取出来,由会长分配权限。这套权限系统比个人背包复杂得多。
guild存公会基本信息——名字、阵营、创建时间。guild_member记录成员和各自的职位、贡献度。guild_rank定义职位等级和每个等级的权限。
真正复杂的是银行:
guild_bank_tab:标签页配置,几号标签页叫什么名字、放什么图标guild_bank_item:每个标签页里具体放了哪些物品guild_bank_right:每个成员对每个标签页的权限——谁能看、谁能取、谁能存guild_bank_eventlog:所有操作的流水日志,谁在哪天存了什么、取了什么,金币怎么动的,全都记录
guild_eventlog存成员加入、离开、职位变更这类大事记。和银行日志一样,都是审计用的数据。
还有一个有意思的表petition+petition_sign——创建公会需要向系统发起请愿,其他玩家签名支持,签名够了公会才能成立。这是暴雪原来的设计,在正式版里被移除了,但 AzerothCore 把这个流程保留了。
副本:谁进了哪个门
副本系统涉及几张表,逻辑分两层。
第一层是副本实例。玩家组队进入一个副本,服务器创建一个副本实例,instance表记录这个实例的 ID、所属地图、创建时间、重置时间。队伍里的每个人在character_instance里有一条记录,标记这个角色绑定了哪个副本实例。
第二层是副本进度。哪个首领被杀了、哪个还没杀,character_instance里存了这个信息。团队里只要有一个人击杀了首领,全队成员的副本进度都跟着更新。
instance_reset表控制副本的重置周期。普通难度和英雄难度分开计时,raid 和 dungeon 也分开,不同服务器的团本重置时间可能不一样,全在这张表里配。
社交、邮件、日历
三个子系统,零散但各有意思。
社交:character_social记录好友列表和忽略列表,还有备注字段。这个设计很朴素,好友 ID 加备注,直接存一张表里。
邮件:mail是邮件主表——谁发给谁、主题、正文、有没有金币或 COD(货到付款)。mail_items存附件物品。mail_server_template是自动邮件模板,比如等级奖励、节日礼品,通过这个模板定时发邮件给玩家,不需要人工操作。
calendar_events+calendar_invites是游戏内日历。团队副本什么时候开、谁组织的、邀请了谁、谁接受了谁拒绝了——全流程都在这里管理。这套系统和真实世界里约人开会有点像,只是变成了游戏内的一个功能。
竞技场和战场:PvP 的数据
竞技场系统的数据结构比较特殊,因为队伍是临时的、赛季是周期性的。
arena_team存战队信息——队名、等级、积分、所属阵营。arena_team_member存每个成员的个人等级和本周场次。character_arena_stats存角色的竞技场个人统计。
active_arena_season记录当前赛季信息,赛季结束时根据这些数据结算奖励,然后这张表清空等待下个赛季。
战场这边没有那么多独立表,pvpstats_battlegrounds和pvpstats_players存战场统计。battleground_deserters记录逃跑者,方便加 debuff 惩罚。
写完这篇,对 Characters 库的整体印象是两个字:细和实时。
细在于,一个"任务"拆成六张表,"物品"拆成实例和位置,“公会银行"拆成日志、权限、标签页,每个变化维度都独立成表。实时在于,这个库里几乎每一条记录都是"当前状态”,不是配置,是活的。
开篇引了《孙子兵法》的一句话:“静如处子,动如脱兔。” World 库是静,Characters 库是动——前者告诉你游戏"可以有什么",后者告诉你"现在具体是什么"。两个视角合在一起,才是完整的游戏世界。