【游戏服务器设计案例篇】感悟
1)记录index,而不是具体的内容
原因:当策划配置发生了改变时,比如:本来让玩家领取银币,但是配置程了金币,这时如果是index,那么发放奖励时就没问题。
2)刷新:
在线玩家,需要及时的刷新。
凌晨5点的处理,自然过渡这种,考虑+1min。
用户登陆时的处理。
3)对于无法区分的周循环活动,不要用时间戳来标识,而是单独的期数
而是可以搞一个单独的表,记录的自增的long类型的id,一期一期的这样子就可以清晰的区分开来。
每一期活动都是相同的sn,这样子很难区分本期还是下一期时登录。
4)将重复出现的数据操作封装为对象
原因:比如有一张表A,我有些操作是修改字段1,有些是修改字段2,甚至里面的bean都不存在还要初始化,那十分麻烦。因此可以定义一个统一的xxxColumn,加上readonly,那么就把所有的数据初始化和修改及其查询封装到里面了。
其实这是java封装的体现呀。
5)既然2个定时器无法搞,不知道以谁为主,那就以一个定时器呀。
目前我的做法:使用了2个。
6)用一个字段表示多个意思
用tag标识唯一的一场比赛:哪个门派、几强、哪一期。
排行榜:用score+时间戳编码为一个字段。
64位的long:前32位表示场景id,后32位表示分线。
一个code:模块号+命令号,如:17001
7)警惕全服邮件功能
假如榜单上有1W人,给榜单上的人都发送一份奖励,最开始 写的是:for循环1W次,都发送一个邮件。
其实这个看起来没问题,实际问题很大,因为可能8000人已经是流失用户,给他们发送邮件,其实是需要修改很多人的数据,但是他们可能已经不再登录 了,这样子会 占用数据库资源 ,23:00时特别卡。
正确做法:在线人直接发送。 离线人,则统一Record一下,所有没有领的人,而且记录保留3条。 上线后,统一检查发送。
而且:注意这个for循环很大,不要直接for发送,而是每个玩家都在各自的线程发送。
8)很多玩法允许一定的数据冗余
玩法1要求a,b,c,d字段
玩法2比如:多一个助战次数,那么其实是不应该直接设计1个数据结构的。 可以在通用的结构中冗余一个字段。
9)数据的刷新时机,如:每日登录、活动开启、玩家升级
方式1:在Column的构造函数中每次获取数据时进行checkRefresh();
有可能是升级后开启了功能模块,此时可以监听升级事件,从而进行处理。
方式2:在玩家登录和功能开启时,进行checkRefresh(); // 街霸的处理方式
10)XXXBeanColumn负责数据的初始化
xxxQuery则负责数据的初始化,判断没初始化,则进行初始化。
xxxDoSomething则负责修改数据,当判断没初始化则直接返回。
11)副本目标的设计
goalSn关联一张表,可以配置是刷怪还是什么,然后再配置具体的内容。
12)定时器的使用
逻辑服:走Schedule开启start和end线程监听任务的完成。
场景服:走统一的场景tick,在进入场景时,传入时间戳,然后再tick中检测,并且超时后通知逻辑服更新数据。
13)面向接口编程
如:我有各种逻辑的副本,但是我设置好一个SModLogic后,根据type,让不同的副本去实现这个接口,this.content = new xxxContent(); 最终就可以根据接口的调用周期去实现。
14)LogicRole、SceneRole、db数据存储
LogicRole可以标记上玩家所在的场景信息
SceneRole是记录玩家所在的场景对象。游戏功能做成Module挂载在玩家身上。
db数据用于真正的缓存玩家的落地数据
15)在xxx时间后,可以领奖的问题(类似的还有:称号过期问题)
方法1:”客户端手动领取“,服务器进行校验,这样子很少有问题,很靠谱(推荐)。
方法2:“时间戳法“: 告诉客户端可以领取,客户端到了时间后,主动发起一次领取请求(虽然客户端和服务器时间不一致,但是基本上不会出什么大错,可能不会发,但是起码不会多发。但是最终发现,很多情况下甚至差1ms导致领取不了,最终做法是:领取时,时间戳做更新只是用于客户端显示,服务器只校验领取次数有没有达到上限,没有则发奖,并更新时间,让客户端 做新的倒计时(实际做法)。
同事建议:如果不能领取,则告诉领取的时间戳,客户端再次修正时间。
方法3:“场景tick法“:由于mmo玩家都是在场景中(除了切场景过程中),因此使用组件的HumanModule的tick中挂载定时器ticker不断进行检测(别人这么做的,没错,但是每个人身上永久挂了一个对象)。
16)2次活动bug排查
问题1:2个时间戳之间有很多期活动,周6和周7选择,周1领取。 最终发现可能会出现2次活动一起开。 解决办法:在end时间戳取为周1.
问题2:在周六的4点到5点之间不能进场景。 但是发现周6下午14:00打发现时间为负数。解决办法:如果是大于end,则判断为新的一期。
17)跨天的处理
对于23:59:59s结算的设计,定时器是不准确的,有可能还没跨天。这种实际处理是:非登录情况下+1min,登录情况下,则是不做处理。
18)继承的思考
副本类型:Content
玩法类型:SModLogic。
怪物类型,继承后,增加单位类型,从而让客户端知道是哪个类型的怪。而不是通过id区分。
19)组件的设计:循环引用
Scene:各种组件,人身上的各种组件。
UnitObject身上的各种组件。
其实最终又通过构造函数在构造时相互引用Scene,这样子每个类之间都可以相互访问。
20)优化的点
1.谁与争锋结算
现在做法:
由get+逻辑计算+奖励发放--》select+逻辑计算+奖励发放标记记录在杂项表中。
离线的用户,存到list中,切最多保留3条记录。
解决的问题:
玩家可能已经流失,很久不上线,但是背包却满了,导致服务器内存浪费。
2.弹幕系统
用一个队列,最多保留最近3条。 自己的能直接看到。防止新服玩家过多,直接服务器炸了。
3.观战系统
不是立马观看,而是把消息都存起来。 序列化上传到oss中,让观战 的用户切换到单独的场景,慢慢下载。
4.多线程导表
21)3段论
情景1:oss上传,分为:startXxx updateXxx endXxx
情景2:刷怪,startXxxx . .
情景3:战斗,startBattle updateBattle endBattle
好处:这样子设计非常的清晰,开始则是记录一场战斗,update则可以检测下是否有这场战斗 end则移除战斗。
22)context设计模式
xxxContext: 里面包装了各个需要的参数,包装了需要的ChannelHandlerContext,这样子就可以。
23)性能优化
功能未开启时,每次都select,都是执行sql语句导致新服性能压力很大。
修改为:即使没开始,也插入,下一次走缓存。
24)性能优化:票数
活动开启时,延迟到玩家查询数据时,才进行初始化。 同时添加:弹框提示活动开始,引导玩家开始。
25)CSUnionXxx、SCUnionXxx、UnionXxxNotify
cs和sc分别对应请求和相应,属于人为的触发。
notify属于系统自动引起的改变,比如:凌晨5点触发了重置。
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐

所有评论(0)