• 产品手册
  • 常见问题Q&A
  • 不同步相关

游戏不同步相关

一.游戏不同步的原因

导致游戏逻辑不同步的原因为:

将【本地值】作为参数,传递到【全局操作】中

使用【本地值】进行逻辑判断后,进行【全局操作】,

以上两种操作,导致不同客户端执行不同的逻辑,最终导致不同客户端上【全局值】不同,即游戏逻辑不同步。

1.【本地值】

【界面,特效,镜头,声音等】客户端之间并不同步的数据。

编辑器提供的获取这些值的函数 ECA 获取的是本地玩家的值,在不同客户端获取时得到的值不同。

如【获取本地玩家】,【获取本地控件坐标】,【获取镜头焦点】,【获取滑动条当前值】

任意值与【本地值】进行逻辑运算后得到的值等价于【本地值】

2.【本地操作】

动作类 ECA中,修改数据【本地值】,不会影响游戏逻辑同步的操作.

如【界面】【特效】【镜头】【声音】【氛围】【调试】分类的动作 ECA

3.【全局值】

【单位,技能,物品等】在不同客户端之间需要保持相同的值。

如获取【单位,技能,物品等相关值】的函数 ECA

4.【全局操作】

动作类 ECA中,修改【全局值】,会影响游戏逻辑同步的操作。

如【单位】【技能】【物品】,【投射物】分类的动作 ECA 等

5.可以参考学院文档多人联机同步机制进行学习

二.本地多开问题定位

1.本地配置不同步日志环境

a.打开【通用设置-调试】,进行本地多开相关配置

Async1

打开本地多开同步检测后,会在本地测试出现不同步后弹窗提示,并提供不同步日志文件以供用户定位不同步问题

b.还可以借助 Lua 文件配置更详细的不同步日志。

Async2

c.打开地图路径下 Script 文件夹下的 main.lua 文件,在 lua 文件中配置不同步日志相关 API,例如:

Async3

详细接口介绍见下文【不同步日志 Lua 配置 API】,可根据自己项目状况考虑具体性能影响选择合适的配置。

d.配置完毕后,本地多开运行游戏。如遇游戏逻辑不同步,弹窗提示并在本地生成不同步日志

Async4

Async5

2.定位不同步问题

1.打开下载的不同步日志文件夹,使用外部文本对比工具进行对比(推荐使用 BeyondCompare)

不同步日志文件中包含的是出现不同步情况的帧信息,通过对比玩家日志差异,可以大致定位到问题所在。

3.举例说明

a.地图中在 main.lua 文件中进行配置

GameAPI.api_set_enable_detail_snapshot(true);
GameAPI.api_set_enable_timer_snapshot(true);

b.本地多开测试游戏,遇到不同步状况,编辑器弹窗提示不同步并提供不同步日志以供定位问题。

c.打开不同步日志,查看逐帧日志,了解不同步帧运行情况,辅助定位问题

Async6

d.将多名玩家日志成对拖放到 beyondcompare 中进行对比,对差异部分附近信息重点分析

e.下面对比截图是玩家 2 比玩家 1 多了一个 timer,各字段含义见图

Async7

通过对不同步信息附近的帧信息进行理解,可大致定位问题为某个客户端上多了一个循环计时器,在项目中进行查找可能的问题所在。

Async8

三.线上游戏不同步定位

1.本地配置不同步日志环境

a.打开地图路径下 Script 文件夹下的 main.lua 文件

Async9

b.在 lua 文件中配置不同步日志相关 API,例如:

Async10

详细接口介绍见下文【不同步日志 Lua 配置 API】,可根据自己项目状况考虑具体性能影响选择合适的配置。

c.线上游戏发生不同步时,根据配置 API 玩家客户端会自动上传日志,后续作者可在编辑器中下载不同步日志来定位问题

2.查询下载不同步日志

a.打开编辑器登录 KK 账号

Async11

Async12

b.打开任意地图,点击【菜单栏】【调试】【查看不同步日志】查询下载线上游戏的不同步日志

Async13

c.查找需要定位问题的对局,下载不同步日志,不同步日志包含对局中所有玩家的对战信息。

Async14

3.定位不同步问题

打开下载的不同步日志文件夹,使用外部文本对比工具进行对比(推荐使用 BeyondCompare) 不同步日志文件中包含的是出现不同步情况的帧信息,通过对比玩家日志差异,可以大致定位到问题所在。 详细定位方法,与上文本地多开案例相同

四.不同步日志 Lua 配置 API

API描述参数返回值
api_set_enable_detail_snapshot开启/关闭不同步详细日志的总开关,默认关闭。(这个是总开关,关了这个之后别的设置接口都不生效了了,但性能最好)enable:是否开启,类型为 bool,默认为 false
api_set_snapshot_traceback_level设置某些日志的堆栈记录详细等级,默认为 0,0 代表不记录堆栈,1 代表仅记录最近一层堆栈,2 代表完整堆栈(带压缩,数据量小但有一点性能开销),3 代表完整日志(不压缩,数据量稍大),越完整的堆栈记录越便于定位不同步产生点但是性能消耗会增高level:堆栈记录等级,类型为 Int32,默认值为 0
api_set_enable_timer_snapshot开启/关闭 timer 不同步检测日志。默认关闭。开启后可以检测出哪里多创建了 ECA 计时器,但计时器不一致并不一定代表着实际游戏内容不同步(比如计时器回调里只做表现层修改就是安全的)enable:是否开启,类型为 bool,默认值为 false
api_set_enable_detail_snapshot开启/关闭 ECA 不同步检测日志。默认关闭,开销较高。可通过参数过滤掉一些安全的 API 以防止误报,例如创建特效、UI 操作等enable:是否开启,类型为 bool,默认为 falsestring:开启结果
filter_mode:过滤模式。类型为 Int32,默认为 1; 1: 剔除模式,不记录 filter_set 中指定的 api ;0 :包含模式,仅记录 filter_set 中指定的 api
filter_set:过滤集合,类型为 table,默认为"client_only", "client_possible"可传入想要剔除/包含的 API(取决于上个参数)。client_only 和 client_possible 为官方确认安全/较安全的 API 集合,即在使用得当的情况下即使调用次数不一致也不会影响游戏核心逻辑,通常可以将其加入剔除集合中以避免误报
api_set_detail_snapshot_enable_tag设置不同步详细日志级别。越详细越利于定位不同步产生点,但性能消耗会增高tag:用于控制开启哪些日志的 mask。类型为 UInt64。0xFFFFFFFF 全部开启,默认开启 16+32。1 运动器 tick.2 运动器碰撞检测.4 寻路回调.8 寻路坐标更新.16 血量变化.32 坐标瞬变
add_detail_log记录自定义日志,用于定位不同步log:日志内容,类型为 string,bool,值恒定为 true

五.本地逻辑相关 ECA

【异步】玩家 - 获取本地玩家

【异步】游戏 - 获取本地语言环境

【异步】玩家 - 获取玩家鼠标真实 x 坐标

【异步】玩家 - 获取玩家鼠标真实 y 坐标

【异步】镜头 - 获取本地玩家镜头实数属性

【异步】镜头 - 获取本地玩家镜头整数属性

【异步】镜头 - 获取本地玩家镜头焦点

【异步】画面 - 获取屏幕横向分辨率

【异步】画面 - 获取屏幕纵向分辨率

【异步】界面 - 获取本地控件相对坐标的 X

【异步】界面 - 获取本地控件相对坐标的 Y

【异步】界面 - 获取本地控件绝对坐标的 X

【异步】界面 - 获取本地控件绝对坐标的 Y

【异步】界面 - 获取本地控件相对旋转

【异步】界面 - 获取本地控件绝对旋转

【异步】界面 - 获取本地控件相对缩放的 X

【异步】界面 - 获取本地控件相对缩放的 Y

【异步】界面 - 获取本地控件绝对缩放的 X

【异步】界面 - 获取本地控件绝对缩放的 Y

【异步】界面 - 获取控件的真实长度

【异步】界面 - 获得控件宽度

【异步】界面 - 获取控件的真实高度

【异步】界面 - 获得控件高度

【异步】界面 - 获取当前窗体横向尺寸

【异步】界面 - 获取当前窗体纵向尺寸

【异步】滑动条 - 获取滑动条当前值

轮播图 - 获取轮播图当前图片索引

轮播图 - 获取轮播图点击图片索引

复选框 - 获取复选框当前选中状态

标签页 - 获取标签页控件选中页索引

元件 - 获取事件中的元件实例

元件 - 获得玩家的元件实例

元件 - 通过元件路径获取元件实例下的控件

元件 - 获取控件所属的元件实例

元件类型 - 获取事件中的元件类型

场景界面 - 获取场景界面中的控件

界面 - 获取指定命名的子控件

界面 - 获得界面控件的父控件

六.随机数相关 ECA

函数

圆形区域  -  获取区域内随机点

矩形区域  -  获取区域内随机点

运算  -  获取随机角度

运算  -  范围内随机实数

运算  -  范围内随机整数

单位组  -  单位组中随机整数个单位

单位组  -  获取单位组中随机一个单位

单位名字池随机选择一个单位

随机池  -  获取随机池中指定整数的权重概率

随机池  -  获取随机池中指定整数的权重概率

随机池  -  获取整数,是否放回

随机池  -  获取总权重

随机池  -  获取整数数量

随机池  -  获取指定整数的权重

游戏  -  当前随机数种子

多边形区域  -  获取区域内随机点

玩家组  -  获取玩家组中随机玩家

点  -  获取路径的随机点

单位  -  获取单位周围的随机单位

物品组  -  获取物品组中的随机物品

特殊  -  获取服务器随机池掉落执行结果

点  -  获取圆形范围里的随机点

单位组  -  随机移动

单位组  -  随机攻击移动

保底随机结果判断

动作

变量  -  获取随机数组

随机池  -  创建随机池

随机池  -  设置随机池指定整数权重

随机池  -  移除随机池指定整数

游戏  -  设置随机数种子

随机池  -  遍历随机池

随机池  -  增加随机池指定整数权重

随机池  -  复制随机池

逻辑  -  概率执行动作