搞游戏开发这些年,GM系统是我见过最没存在感又最不能缺的东西。新项目立项时,几乎没有策划会主动提“我们要做个GM后台”,可一旦游戏上线,运营、客服、QA、包括研发自己,第一反应都是找GM工具。没有GM系统,测试发不了道具、运营封不了号、客服处理不了玩家工单,整个项目就像被绑住了手脚。
游戏开发里的GM系统,全称是Game Master System,直译是“游戏管理员系统”,但放到今天的研运环境里,它的真实定位远不止“管理员工具”,而是一整套面向运营、客服、策划、QA的线上数据操作与治理平台。你做的是单机Demo还是上线运营的长线游戏,GM系统的复杂度和设计思路完全是两回事。这篇内容主要面向有联网功能、有服务器、有数据库、需要运营介入的正式项目。
这篇文章我会从整体设计思路入手,讲清楚GM系统到底应该拆成哪些模块、每块解决什么问题,再落到实操层面,给出表结构、接口设计、命令注册机制的参考方案,最后整理我在项目里踩过的坑和排查经验。新入行的同学可以当入门地图,老手可以对照自己项目里有没有“灯下黑”的地方。
1. GM系统的整体设计思路与模块拆解
很多团队一开始就把GM系统理解成“一个能改数据的管理后台”,于是拉着程序员做了个网页,接上数据库,能查字段能改数值,就觉得完事了。这是最大的坑。GM系统不是给开发者的,而是给那些完全不懂代码的人用的,运营、客服、策划、QA,他们需要的是一个能把复杂数据操作封装成“看得懂、点得动、出不了大事”的工具体系。
1.1 先搞清楚GM系统到底服务谁
所有设计都源于用户,GM系统的用户比游戏玩家更复杂,因为他们带着明确的业务诉求。
运营要发活动奖励,可能一次性覆盖全服,也可能按条件筛选出一批玩家定向发放。客服要处理玩家问题,最典型的是“我充值没到账”“我道具被吞了”,他们需要快速查到玩家账号、服务器、角色信息,然后做补偿操作。策划天天调数值,不想每次改个道具都找研发重启游戏,最好能在后台配置掉落、邮件内容、公告。QA在测试阶段要模拟各种极端状态,比如给角色秒升满级、塞满背包、触发跨服战斗,用来验证边界逻辑。
研发自己也是用户,查线上数据、修异常状态、看日志定位问题,都需要GM工具支撑。
这四类人的操作习惯完全不同。运营喜欢批量操作、要审批流程;客服需要极简操作界面,最好点两下就完成;策划要的是灵活配置,可能要支持写配置公式;QA要的是快速、可重复、不干扰线上数据的环境。
设计GM系统的第一原则:先把用户画像列出来,再决定功能边界和交互方式。我见过最痛的设计,就是把所有功能塞进一个页面,不管是谁进去都看到几十个按钮。客服误点批量发放,运营找不到入口,QA根本不敢碰。后面全部返工,重做成“按角色分配菜单”的形态才缓过来。
1.2 核心模块如何拆解
一个标准的GM系统,按业务域可以拆成下面这些模块:
| 模块 | 典型功能 | 主要使用角色 |
|---|---|---|
| 玩家画像 | 查询账号、角色、背包、货币、等级、战力、充值记录、登录记录、封禁记录 | 客服、研发 |
| 资源操作 | 发放/扣除货币、发放/删除道具、设置属性、修改等级经验 | 运营、QA |
| 邮件系统 | 全服邮件、定向邮件、附件发放、邮件撤回 | 运营、策划 |
| 公告管理 | 登录公告、活动公告、滚动公告、停服维护公告 | 运营 |
| 封禁处罚 | 封号、封IP、封设备,支持时效和原因备注 | 客服、运营 |
| 任务调度 | 定时发放、周期活动配置、批量任务执行 | 运营、策划 |
| 权限管理 | 管理员账号、角色权限、审批流、操作日志 | 研发、运营主管 |
| 审计日志 | 操作记录、数据快照、操作回放 | 研发、风控 |
这里要特别强调两个容易被忽略但后面非常关键的模块:任务调度和审计日志。
任务调度决定了你能否“批量、安全、可控”地执行运营操作。发放5000个玩家的补偿奖励,如果靠人工一个一个点,不仅慢而且容易重复或漏发。一个可靠的任务调度模块,应该支持“导入玩家ID列表→选择发放内容→创建任务→定时执行→查看结果报告”的完整闭环。
审计日志则是一条保命线。运营手滑发错补偿额度,客服误封了一个大R的账号,没有完整日志你根本不知道是谁干的、什么时候干的、操作前后数据变成了什么样。这三个信息缺任何一个,排查都靠猜。
1.3 设计原则:少做比多做更明智
聊到设计理念,我踩过最大的坑就是想“一步到位”。结果到后面发现,要么功能不会用,要么漏洞百出,要么研发时间被GM系统吞噬了大半。
第一个原则是最小权限。每个管理员只拥有完成自己工作所必需的功能,运营专员不该有封号权限,客服不该有全局货币发放权限。哪怕内部都是可信人员,权限收窄也能显著降低误操作概率。
第二个原则是操作可追溯。所有操作必须有记录,包括操作人、操作时间、操作参数、操作结果、影响范围。这不是为了追责,而是为了“还能救回来”和“下次别再犯”。
第三个原则是命令幂等。同一个发奖任务重复执行两次,玩家拿到的奖励应该只有一份。这个细节通常要等线上事故出现才被重视,但设计时就应该作为硬性标准。
第四个原则是禁止直连数据库操作。我见过不少项目,研发图省事给GM后台写了一套直接update数据库的接口。初期确实快,后期随着业务复杂,数据一致性问题会像滚雪球一样崩坏。正确的做法是:GM后台不碰数据库,它只向游戏服务器发指令,由游戏服的业务逻辑来执行数据变更。这样做的好处是,所有逻辑复用线上业务校验,不会出现“GM改了背包数据但玩家下线再上线又变回去”的破事。
1.4 从最小可用版本开始
如果你是从零开始搭一个新的联网游戏项目,我不建议一次就把上面18个模块全做完。第一版只做三件事:玩家查询、邮件发放、封禁解封。这三个功能能解决日常80%的运营临时需求。
玩家查询解决“信息不透明”问题,邮件发放解决“补偿和奖励”问题,封禁解决“恶意行为和风控”问题。等这三条链路跑通,再逐步加批量发放、任务调度、公告管理、权限精细化。
我曾经参与过一个项目,启动时就规划了23个GM子系统,结果核心玩法还没做完,GM系统占用了两个开发人力整整三个月。老板看到甘特图直接叫停,砍到只剩查询、发邮件、封禁三个功能,三周上线,后面边运营边迭代,反而稳定得多。GM系统是服务业务的,不是拖累业务的,MVP思维在这里同样适用。
2. 核心细节解析与实操要点
模块撑起了GM系统的骨架,但真正决定好用不好用的,是权限、命令协议、审计这些底层细节。这些细节藏在表面之下,却决定了系统上线后是“帮忙”还是“添乱”。
2.1 权限体系:RBAC和审批流怎么结合
GM系统的权限模型,业界用得最多也最稳的是RBAC(Role-Based Access Control,基于角色的访问控制),不是说它最先进,而是它最容易理解、最方便扩展。管理员的权限挂在角色上,角色挂在权限点上,管理员与具体权限解耦。运营主管可以操作功能A和B,运营专员只能操作功能A,互不影响。
权限点设计上,我习惯按“模块+动作”的粒度做,比如:
player:query查询玩家player:modify修改玩家数据mail:send发送邮件ban:create封禁ban:release解封
这样的权限点看起来粒度比较细,但落地时却能非常灵活。比如给客服角色同时分配player:query和ban:create,给QA只分配player:query和mail:send,运营专员则分配mail:send、player:modify,但player:modify要加限制条件。
只做权限控制还不够,高风险操作还要加审批流。什么是高风险操作?批量发放道具、大额度货币变动、封禁大R账号、执行全局配置更新,这些都属于“点错一步就要命”的操作。
审批流的设计不复杂,但要注意几个细节:
- 审批和操作分离:审批人不能是自己,避免“自己申请自己批”。
- 审批要能加备注和截屏:方便事后复盘,减少扯皮。
- 高价值操作支持双人复核:金额或者影响范围超过阈值的操作,必须由两个不同角色分别确认后才执行。
- 审批超时提醒:运营活动经常时间敏感,审批单挂了三天没人处理,活动就废了。加个自动提醒,超过2小时就钉钉/企微通知。
2.2 GM命令协议设计:后台和游戏服的沟通方式
GM后台本质上是一个指令发射器,游戏服才是真正的执行者。它们之间怎么约定指令的格式,直接决定扩展性和稳定性。
我在项目里一般用这样的命令结构(JSON格式):
{ "cmd_id": "grant_item", "server_id": 1001, "player_id": "uid_20240001", "params": { "item_id": 50001, "count": 10, "reason": "activity_reward_20240601" }, "request_id": "a3f9c2e8b7d64f1a", "operator": "ops_zhangsan", "timestamp": 1717203600 }这里面有两个字段特别重要。
第一个是request_id,这是每条指令的唯一ID,用于幂等控制。游戏服收到指令后先检查这个ID有没有执行过,执行过就直接返回成功,避免因网络重试、前端跳转等原因导致重复发放。第二个是reason,业务原因标记,每次操作都要带。一旦出问题,可以通过原因字段反查是哪场活动、哪个批次、哪个责任人发起的。
命令的回传格式同样要规范:
{ "request_id": "a3f9c2e8b7d64f1a", "code": 0, "message": "success", "data": { "changed_rows": 1, "player_current_items": {"50001": 110} } }code=0代表成功,非0代表失败。失败时message必须输出可读的错误原因,比如“玩家不存在”“玩家背包已满”“道具ID未配置”。这样运营在后台看到的是人话,而不是一长串看不懂的堆栈。
2.3 审计日志:比代码更重要的数据
做审计日志有一个被低估的细节,就是一定要记录“操作前快照”。只记“谁在几点发了10个道具”还远远不够,你得知道发之前玩家背包里本来有多少个,不然补偿出错时无法推算正确值。
我的方案是在命令执行前,由游戏服生成一条完整的快照记录:
{ "log_id": "log_8891", "request_id": "a3f9c2e8b7d64f1a", "cmd_id": "grant_item", "operator": "ops_zhangsan", "target_player": "uid_20240001", "before": {"items": {"50001": 100}, "gold": 50000}, "after": {"items": {"50001": 110}, "gold": 50000}, "params": {"item_id": 50001, "count": 10}, "result": "success", "timestamp": 1717203600, "server_ip": "192.168.1.55" }before和after可以是一个大字段,也可以拆成结构表,依据数据量来定。审计日志的记录时机,我建议在游戏服执行完命令后,由游戏服主动上报到日志中心,而不是由GM后台写库。理由是游戏服才是数据真实性的权威来源,后台写库可能漏掉一些由游戏服内部逻辑触发的数据变更。
审计日志要保留多久?我的经验是至少保留两年,因为涉及付费和异常申诉。存储方案建议接入独立的日志存储系统,比如ClickHouse、ElasticSearch,而不是和业务库混在一起。日志数据量大,写多了会影响线上库性能。
2.4 幂等、并发和异步任务细节
这三个词放在一起,是GM系统事故的高发区。
幂等处理的核心就是前面说的request_id。另外还有一个细节:状态机。一条GM命令从提交到执行,要经历“待执行→执行中→成功/失败”这几个状态。前端轮询时可以准确拿到状态,避免用户重复点击。很多后台没有这个状态机,前端一抖动就发了两条请求,玩家拿了两份奖励,哭都来不及。
并发处理要考虑的是“同一玩家同时被多条指令操作”。比如客服正在给玩家发补偿邮件,运营同时给整个服务器发全服邮件,有一个字段是覆盖还是叠加?邮件系统尤其容易出问题,如果实现时用了“先删后插”的逻辑,两个并发请求可能互相覆盖。我的方案是:所有对同玩家数据的变更操作,在服务端加行锁或者版本号。版本号每次变更自动+1,操作时校验当前版本号是否与提交时一致。
异步任务就更常见了,像“群发10000封邮件”“给5000个玩家发道具”,这种任务不可能同步等结果。标准做法是:GM后台创建任务→任务投递到MQ→多个Worker消费执行→回调结果到任务中心→任务中心汇总报告。执行过程中要支持暂停、重试、跳过失败、看进度。这些都做完,运营才敢放心去点“批量发放”。
3. 实操过程与核心环节实现
前面讲的都是设计理念和关键细节,这章我来还原一套可以直接落地的实现方案。这里以“用一个Web后台给游戏服发GM指令”的常见架构为例,从表结构、接口、命令路由到完整操作流程,一步步走一遍。
3.1 服务端数据模型搭建
先看最基础的管理后台数据表。GM系统自己的库,字段设计我会给出一个相对通用且能直接建表执行的方案,里面同时也考虑到了权限点和审计的需要。
-- 管理员表 CREATE TABLE `admin_user` ( `id` int NOT NULL AUTO_INCREMENT, `username` varchar(64) NOT NULL COMMENT '登录账号', `password_hash` varchar(128) NOT NULL COMMENT '密码哈希', `role_id` int NOT NULL COMMENT '角色ID', `status` tinyint NOT NULL DEFAULT '1' COMMENT '1启用 0停用', `last_login_at` datetime DEFAULT NULL, `created_at` datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `uk_username` (`username`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='GM后台管理员表'; -- 角色表 CREATE TABLE `admin_role` ( `id` int NOT NULL AUTO_INCREMENT, `role_name` varchar(64) NOT NULL, `role_desc` varchar(255) DEFAULT NULL, `permissions` text NOT NULL COMMENT '权限点ID列表,逗号分隔', `created_at` datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='GM后台角色表'; -- 权限点表 CREATE TABLE `permission` ( `id` int NOT NULL AUTO_INCREMENT, `perm_code` varchar(100) NOT NULL COMMENT '权限编码,如 player:query', `perm_name` varchar(100) NOT NULL, `module` varchar(64) NOT NULL COMMENT '所属模块', `risk_level` tinyint NOT NULL DEFAULT '1' COMMENT '1普通 2高危 3极高危', PRIMARY KEY (`id`), UNIQUE KEY `uk_perm_code` (`perm_code`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='GM后台权限点表'; -- 操作日志表 CREATE TABLE `operation_log` ( `id` bigint NOT NULL AUTO_INCREMENT, `admin_user_id` int NOT NULL, `admin_username` varchar(64) NOT NULL, `cmd_id` varchar(64) NOT NULL, `request_id` varchar(64) NOT NULL, `server_id` int NOT NULL, `target_player_id` varchar(64) DEFAULT NULL, `params` json DEFAULT NULL, `before_snapshot` json DEFAULT NULL, `after_snapshot` json DEFAULT NULL, `result_code` int NOT NULL, `result_message` varchar(255) DEFAULT NULL, `operator_ip` varchar(45) DEFAULT NULL, `created_at` datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), KEY `idx_admin_user` (`admin_user_id`), KEY `idx_target_player` (`target_player_id`), KEY `idx_request_id` (`request_id`), KEY `idx_created_at` (`created_at`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='GM操作审计日志表';游戏服内部的命令配置表,建议放在游戏服的库里,用来做命令合法性校验:
CREATE TABLE `gm_command_config` ( `id` int NOT NULL AUTO_INCREMENT, `cmd_id` varchar(64) NOT NULL COMMENT '命令ID', `cmd_name` varchar(100) NOT NULL, `need_audit` tinyint NOT NULL DEFAULT '0' COMMENT '是否需要审批', `audit_level` tinyint NOT NULL DEFAULT '1' COMMENT '审批等级', `status` tinyint NOT NULL DEFAULT '1' COMMENT '1启用 0停用', `created_at` datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `uk_cmd_id` (`cmd_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='GM命令配置表';这里再提供一个server_list表,记录所有游戏服的元信息。跨服、合服、开新服、关服,都需要维护这张表,它是GM后台做命令路由的依据:
CREATE TABLE `server_list` ( `id` int NOT NULL AUTO_INCREMENT, `server_id` int NOT NULL COMMENT '服ID,全局唯一', `server_name` varchar(128) NOT NULL, `server_type` tinyint NOT NULL COMMENT '1正式 2测试', `status` tinyint NOT NULL COMMENT '1运行中 0维护中', `api_addr` varchar(255) NOT NULL COMMENT 'GM接入地址', PRIMARY KEY (`id`), UNIQUE KEY `uk_server_id` (`server_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='游戏服务器列表';注意,server_id是全局唯一且永不复用的。哪怕这个服合服关服了,ID也不能再给新服用,否则历史操作记录会关联到错误对象。
3.2 管理后端和接口层设计
管理后端的业务可以借用现成的Web框架快速开发,比如Java系用Spring Boot、Go系用Gin、Node系用Egg.js,这层没有太多性能压力,重点是业务闭环。
接口层我一般会分成三大类:
一是管理端接口,比如管理员登录、查询玩家、创建任务,走/api/gm/路径。二是命令中继接口,管理端调用后把命令投递到游戏服,走/api/gm_relay/路径。三是游戏服回调接口,游戏服执行完毕返回结果,走/api/gm_callback/路径。
命令中继的核心逻辑很简单,就是根据请求里的server_id,从server_list表查对应的api_addr,然后把标准GM命令包通过HTTP POST发过去。但要考虑三个问题:超时、重试、回调。
游戏服执行一个BT命令或批量操作,可能要好几秒,HTTP请求很容易超时。我的做法是:提交命令接口只负责“投递”,立即返回request_id;游戏服执行完后,异步调用GM后台的回调接口,把执行结果写回任务记录。这样整个链路非常清爽,前端轮询任务状态即可。
重试一定要有上限和退避策略。推荐指数退避:第1次失败等1秒重试,第2次等2秒,第3次等4秒,最多重试5次。超过上限就标记任务失败,转人工排查。
3.3 游戏服如何安全地接收和执行GM命令
游戏服这里建议单独开一个“GM命令处理器”,和游戏主循环隔离。说白了,命令进来不直接改数据,而是塞进一个线程安全的队列,由专门的处理器逐个消费。好处是:GM命令的耗时操作不会阻塞玩家主逻辑;命令可以被暂停、回溯、批量失败处理;状态机和幂等控制都可以做在队列层。
游戏服的命令处理器需要实现几个核心方法:
// 伪代码,以C#为例 public class GmCommandHandler { private readonly ConcurrentQueue<GmRequest> _queue; private readonly HashSet<string> _executedRequestIds; public async Task<GmResponse> HandleAsync(GmRequest request) { // 1. 幂等校验 if (_executedRequestIds.Contains(request.RequestId)) return GmResponse.Success("duplicated request"); // 2. 合法性校验 if (!Validate(request)) return GmResponse.Fail("invalid params"); // 3. 入队执行 _queue.Enqueue(request); bool executed = await TryExecuteAsync(request); // 4. 记录结果 if (executed) _executedRequestIds.Add(request.RequestId); LogOperation(request, executed ? "success" : "failed"); return executed ? GmResponse.Success("ok") : GmResponse.Fail("execute error, see log"); } }角色的游戏内数据,尽量调用现成的业务模块接口,比如用ItemManager.AddItem(playerId, itemId, count, reason),而不是直接操作数据库行。这样可以确保所有奖励都走过正常的入库、背包扩容、红点推送、上线通知等逻辑,而不仅仅是改了一个字段。
离线玩家处理是另一个常见问题。玩家不在线时发邮件功能可以实现,但发道具就不一定了。很多项目会把GM指令延迟到玩家上线时执行。实现方式是在玩家上线时检查GM待执行任务表,或者游戏服启动时就周期性地处理离线指令。无论哪种,设计和实现时都要注意时序:如果GM命令被标为“已执行”,但实际是在玩家上线后才完成,中间玩家又下了线,状态机的流转要能覆盖这种情况。
3.4 完整操作流程实录:以“全服活动礼包发放”为例
我用一个最常见的运营场景串起整个链路:运营要全服发一份活动礼包,包含100钻石和10个强化石。
第一步,运营在GM后台发起“邮件发放”功能,选择服务器为全区全服,填写邮件标题“初夏庆典奖励”、正文、附件道具和数量。GM后台校验运营的权限点mail:send,通过后根据该操作的风险等级自动匹配“是否需要审批”。因为全服发放属于高风险,自动生成一条待审批任务。
第二步,运营主管在审批中心看到这条申请,确认内容无误后批准。系统记录审批人的账号、审批时间、审批备注,并把任务状态改为“待执行”。
第三步,运营专员点击“执行”,GM后台生成request_id(UUID),把GM命令投递到消息队列,再由队列分发到目标服务器的GM接口。
第四步,游戏服收到命令后,先做幂等校验,确认这条request_id从未执行过,再执行邮件逻辑:给所有当前离线玩家生成邮件记录,已在线玩家直接收到红点推送。如果中途某个批次执行失败,任务记录会标记“部分成功”,并可查看具体失败原因,支持重试。
第五步,任务结束时,GM后台汇总展示:应发人数、成功人数、失败名单、失败原因、整体耗时。所有细节都回写审计日志,前后快照对得上,运营可自查,研发可兜底。
这套流程看下来,最核心的理念就是:单条命令不可怕,可怕的是命令之间的无序叠加。审批流、幂等、状态机、异步执行,都是在给无序叠加加防护网。
4. 常见问题与排查技巧实录
再完备的设计,到了真实环境总会出幺蛾子。下面这些问题我基本都遇到过,整理成速查表的方式分享,一是方便对照排查,二是提醒你在设计阶段就把雷排掉。
4.1 命令执行成功但数据没变化
这是最让人抓狂的一类问题。后台显示code=0 success,游戏里背包却没有新增道具。
排查顺序:
- 先确认命令执行的目标玩家ID对不对。跨服合服后,玩家ID可能被重新映射过,后台查到的老ID已经失效。
- 检查命令是否被“离线延迟执行”。玩家不在线时,命令可能进入了待执行队列,看起来成功了,实际要等玩家上线才生效。
- 检查游戏的业务模块有没有拦截。比如玩家背包满、道具配置被删除、邮件附件上限,都会导致实际写入失败,但网络层返回了成功。好的做法是游戏服的GM命令执行逻辑,必须返回真正的业务执行结果,而不能只看“没有抛异常”。
4.2 批量任务执行到一半卡死或超时
批量发放任务最怕执行到一半进程崩溃或超时,然后你也不知道到底哪些人拿到了,哪些没拿到。
我推荐的任务设计是“分片执行+进度检查点”。把10000个玩家拆成100个片区,每处理完一个片区就持久化一次进度。重启后从上次的进度继续往下跑,不重复也不遗漏。配合幂等request_id,即使某一个片区重复执行了,玩家也只会收到一份奖励。
排查超时问题时,先看单个玩家的发放逻辑里有没有网络请求、缓存循环等慢操作。我见过一个项目,给玩家发道具时会同步刷新排行榜,一条命令执行了十几秒,20个并发的发放任务直接拖垮了主库。
4.3 重复发放:罪魁祸首往往是前端重试
GM后台的网络请求和普通Web请求一样,也会遇到超时。运营看到按钮转圈,下意识多点了几次,结果每个请求都实际执行了,玩家瞬间变成暴发户。
这是幂等设计没做到位。前面强调的request_id是后端幂等的基础,但在前端也要做防重。我的建议是:提交按钮一旦点击立即置灰,同时绑定同一个request_id在本次会话中复用。后端再做一层边界判断:同一个request_id在N分钟内只允许执行一次。双重保障之下,基本可以堵住这个洞。
4.4 命令注入和安全边界
GM系统的安全性比其他业务系统更高,因为一旦被攻破,等于拿到了整个游戏世界的神力。
首先要防的是命令注入。在设计命令参数时,必须严格采用白名单校验方式。比如发放道具的item_id只能来自道具配置表,不能是任意数字;数量字段只能是非负整数,上限根据业务设定。让每一个参数都有边界,而不是靠用户自觉“别输入奇怪的值”。
其次是越权防护。GM后台的所有接口都必须校验登录态和权限点,而且服务端校验不能只做一次。每个接口、每个功能点都要单独校验权限,不能只在网关层做统一校验。一旦后台被拆分成微服务,每个子服务都要自己校验一次,这是最容易被忽略的地方。
最后是提权命令的保护。如果GM系统支持执行任意数据库脚本或直接运行命令,这种能力必须做“金库模式”:必须由有极高权限的人,在特定的操作终端上,进行多因素认证后才可以操作。不要图方便做一个“万能命令”入口,我真的见过运营后台里塞了个执行任意SQL的文本框,还配上了一个“执行”按钮,这已经不是工具,是自杀按钮。
4.5 误操作后的数据救回
即使所有防护都到位,人还是会犯错。选错批量范围、填错数值、审批不仔细,这种事每年总有几次。与其期望“绝不犯错”,不如提前想好“万一错了怎么办”。
我的做法是一个“数据回收容器”设计。发放和扣除不直接覆盖数据,而是通过“增量变更日志”实现。比如扣除了100钻石,不是直接把玩家的钻石字段改成原值减100,而是新增一条“-100”的变更记录,并更新当前余额。如果需要撤销这条GM操作,只要删除或反写这条变更记录,数据即可回滚到操作前状态。
当然这需要游戏业务的地基打得足够好,虚拟资产走“流水账”模式,而不是只存一个最终值。如果现有架构没有这个能力,退而求其次的方案是:每次执行高风险GM命令前,先自动生成目标数据的全量快照,放在独立备份表里。真出问题时,用快照恢复定向数据。注意是“定向数据恢复”,不是整库回滚。整库回滚一个人操作,可能导致其他玩家同时期产生的新数据全部丢失。
4.6 环境隔离和灰度问题
GM系统在测试环境可以随便造,但线上必须慎之又慎。我经历过一次“在测试服点错按钮给正式服发货”的现场。原因就是GM后台连接的是测试环境地址,但测试服和正式服用的是同一套数据库。从那以后,我每次搭GM后台都会检查两件事:一是正式环境和测试环境必须物理隔离,库、缓存、消息队列都分得干干净净;二是GM后台要有一个非常显眼的“当前环境标识”,绿色测试、红色正式,做成固定不消失的顶栏。
如果要上线一个改动比较大的GM功能,建议把一大波游戏服按权重灰度放开。先让1%的服务器接入新GM系统,跑两天观察日志,再逐渐扩大到10%、50%、100%。
5. 这套系统还能怎么扩展
GM系统设计到这里,已经可以支撑一个中小规模项目的日常运营了。但如果你的项目想走得更远,下面这几个方向是可以继续延伸的。
一个是数据看板化。GM系统的操作顺手之后,可以把玩家关键数据、游戏经济系统指标、运营活动效果集中展示在一张看板上。运营不用再导出Excel,开发也不用被拉着“帮我跑个SQL看看数据”,各取所需。
一个是自动化风控。GM系统积累了海量操作日志后,可以做简单的规则引擎,比如同IP短时间多次创建角色、异常充值和退款行为、批量小号异常登录。规则触发后由GM系统自动执行预先配置的动作,比如标记账户、临时禁言、限制登录,等人工复核。这不是要做成完整的风控系统,但要利用现有数据降低最基础的恶意行为。
还有是数据驱动的策划工具。策划调整掉落概率、商业化配置、活动数值时,可以通过GM系统做线上AB测试。用一个小流量分组把玩家拆成A/B组,分别下发不同配置,实时看数据反馈再决定全量放量。这套能力会大大缩短迭代周期。
以上这些扩展,都不是GM系统的核心功能,但都是以GM系统的数据基础和操作能力为依托生长出来的。也就是说,一个设计良好、数据干净的GM系统,不只是运营工具,它还是游戏数据资产的重要来源。
我自己做GM系统最大的体会是,别把它当成后台网页来做,要当成一条生产线来做。从运营提出需求到数据最终落库,中间经过的每一个环节都需要有标准、有记录、有保护。代码写得快不算本事,写得慢但稳、出事能救、扩展能接,才是真正能支撑一个项目走完生命周期的功夫。最后再分享一个小技巧:每次发版GM系统新功能前,自己以运营的身份把核心流程整个点一遍,从登录到审批到执行到查日志,这种“过家家”式的测试,比看一百遍代码Review更能发现问题。