合服是老服的续命术:两个服务器的人气合并,活跃度翻倍。合服的工程核心是数据迁移——几十万角色跨库搬家,ID 的冲突、名字的撞车、行会与邮件的归属,每一样都要有个说法。
---合服迁移:ID 重映射与重名处理
function Merge.run(srcDb, dstDb)
local idMap = {}
for _, role in ipairs(srcDb.query("SELECT * FROM roles")) do
local newId = Merge.nextId()
idMap[role.id] = newId
-- 重名检测:加服名后缀
if Merge.nameExists(dstDb, role.name) then
role.name = role.name .. "@" .. Merge.srcTag()
sendmail(role.name, 8009, "合服改名通知",
"您的名字与另一位玩家重复,已自动调整", "")
end
dstDb.exec("INSERT INTO roles SET ?", role)
end
Merge.saveIdMap(idMap)
end
迁移的第一件事是 ID 的重映射:源服与目标服的角色 ID 空间必然重叠,源 ID 到新 ID 的映射表是后续一切数据(背包、邮件、行会成员)改写罗盘。重名的处理加服名后缀并邮件告知——两个「血饮狂刀」在同一个服里必须有各自的姓名,自动改名加通知是折中的公平。迁移的事务按角色为单位:一个角色的全部数据(角色行、背包、变量)要么全迁要么全不迁,中断重跑不产生半人。
---背包与邮件的 ID 改写
function Merge.remap(idMap)
-- 背包的归属改写
for oldId, newId in pairs(idMap) do
dstDb.exec("UPDATE bags SET uid = ? WHERE uid = ?",
newId, oldId)
end
-- 邮件的收发双方改写
dstDb.exec("UPDATE mails SET toId = ? WHERE toId = ?",
function(row) return idMap[row.toId] or row.toId end)
-- 行会成员的归属改写
Merge.remapGuildMembers(idMap)
end
角色本体迁完后按映射表改写关联表:背包的归属、邮件的收发双方、行会的成员名单,改写的顺序按依赖图走(先角色后关联)。行会的重名同样处理后缀,两个「傲视天下」合并后一山容二虎靠名字的区分。金币与装备的合服通胀是运营的对价:合服奖励的发放与商城的回收活动同期上线,两份家底合并的通胀由活动消化。合服的公告提前两周:名字的变更规则、数据的合并范围、开服的时刻,知情权是减客诉的第一道闸。
迁移的对账三件套:角色数、物品数、邮件数的源目对比,差额逐条追;合服后的登录率与留存监控,迁移的质量由玩家的回归投票。
ID 映射曾经漏了邮件的发件方,系统邮件的归属错乱,映射的覆盖面以表清单驱动。重名的后缀曾经超了名字长度上限,超长名字的改写失败回滚整批,长度约束前置校验。行会的圣物格在两个仓库合并时超容,超容的部分转邮件附件,合服的容量预案写进流程。
合服的演练在测试环境全量走两遍:生产数据的脱敏副本跑真实迁移脚本。合服的窗口放凌晨低峰,迁移的耗时有上限预估,超时的熔断回滚。合服后的数据修复通道开满两周,玩家反馈的资产问题优先处理。
本文由 996 技术组基于 996 引擎官方知识库与浮生梦老师课程体系整理。团队长期从事传奇类引擎 Lua 后端逻辑、客户端界面与商业版本交付,内容以官方知识库与真实项目为出处,按版本持续修订。
全站技术干货持续更新:996 引擎 / Lua 实战帖,语法、参数与示例一篇讲透。进入文章地图 · 查看全部 →
学员常见误区 Lua函数可返回多个值,学员用固定变量数接收时如果变量少于返回值,多余返回值被静默丢弃;如果变量多于返回值,多…
设计初衷 行会建筑的死穴是一次全解锁:会员没有逐步建设的过程感。梯度设计让每栋建筑都有前置条件和资源门槛。 数值模型 建筑分…
设计初衷 婚姻系统的属性加成是社交玩法的经济锚点:加成太弱没人结婚,太强则"为了属性被迫结婚"扭曲了社交本质。婚姻边界的设计…
设计初衷 宝箱类玩法的信任危机都源于同一句话:"概率是不是骗人的。"期望公示把概率从事后争议变成事前契约:奖池概率表全量公示…
设计初衷 流拍物(拍卖未成交的退回物品)堆积在卖家背包里成为死资产:低价值物流拍后无人问津,高价值物流拍后卖家不愿降价重拍。…
业务场景 沙巴克战功榜每周结算,玩家提交战功前不知道"再打多少能进前 10、前 10 的奖励是什么"。名次预览:输入自己的战…