[真机Bug]解决服务器强制定位玩家位置后NPC加载超时

2024-06-15 04:52

本文主要是介绍[真机Bug]解决服务器强制定位玩家位置后NPC加载超时,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

问题复现

image.png
图1 玩家在指定任务交付地点发现NPC未出现
image.png
这是一个剧情演出的任务,在播放TV结束后,角色会被强制位置到一个指定地点。而NPC的加载来源于 【场景分块加载】和【玩家的视野区(本质玩家位置)】.

  • 场景分块加载是资源优化的一种手段。对于大型地图(类似河阳城主城),由于玩家途经的位置有限。没有必要把所有GameObject放在一个scene当中。而是将一个scene按绝对位置切分为8块、16块甚至更多。保证渲染质量。
  • 视野区是一种加载物体优化的一种手段。可以对照视锥体剔除的概念理解,对于系统判断在离玩家坐标比较远的位置的物体可以不加载,如果已加载。可以让其失活进入对象池。最大程度减少hierarchy列表中可见物体的数量,从而保证渲染质量。

问题排查

首先对于这个问题,我优先想到的是检查场景分块加载的逻辑。因为最终定的位置离初始任务位置比较远,这部分的场景是后续分块加载的。那么有一种可能性是场景在TV快要结束的时候才被加载,人物加载是后于场景加载的。那么人物加载可能会延期。
于是我在场景分块加载的管理器中打了日志,标识场景id 和时间。发现,其实在播TV的前3s场景已经完成加载了,而TV总共有10s,在剩余7s的时间之内应该正常加载完成npc吧。让我们来看看加载NPC的细节。

using ViewObjIdMap = std::map<int32,bool> // obj id - 是否可见

bool Obj_Player::Tick_ViewNpc()
{Packet::GC_CREATE_NPC_PAK pak;for(auto it = m_viewNPCIdMap.begin() ; it !=m_viewNPCIdMap.end(); it++){if(IsInSight(it->first)){//如果在视野内,插入到创建map里m_viewCurNpcIdMap.insert(std::make_pair(it->first,true));//消息包插入待创建npc数据pak.m_PacketData.add_npcdata();curCount++;//一批最大创建数量(10)if(curCount >= VIEW_NPC_MAX_COUNT){braek;} } }if(curCount > 10){sendPacket(pak);//发GC_CREATE_NPC_PAK创建的包for(auto it = m_viewCurNPCIdMap.begin() ; it !=m_viewCurNPCIdMap.end(); it++){//设置每个NPC位置,发GC_MOVE的包,状态同步}}}

上面是服务器Tick里的部分逻辑,可以看出npc的创建依赖于

  1. m_viewCurNpcIdMap 是否插入了有效id
  2. m_viewCurNpcIdMap的位置,是在第几批创建,我们可以推算,一次创建10,一次tick是1000ms,那么7s正常是可以创建70 个npc实例,那么对于需求来说是完全足够的。这还是挤在单一场景中。
  3. 创建了,但是同步位置出错,导致不可见(已在客户端排除)

所以问题范围缩小到判断m_viewCurNpcIdMap的内容。

于是我在所以insert m_viewCurNpcIdMap的位置断了点。逐帧调试,在字典添加的时候打印一下日志
image.png
发现在14:25:22 ~14:25:29 这里有点奇怪,首先是没有插入别的有效id,然后耽搁了非常多时
发现扫描的id是不变的。
这说明什么呢。说明玩家的位置要么是没有变化,要么是以前已经到达的位置( ps:已经达到过的位置npc已经被标记,不会再次进入map.由于篇幅限制,这里不再展示扫描部分的代码。重点在于理解)

于是我又打印了玩家的位置,发现其在这个时间段,玩家在朝着起点位置移动,移动了一段时间后,才被强制定位到目标位置。
那么就可以解释的通了,正是因为玩家没有第一时间定位,导致玩家的服务器位置没有及时更新,那么依赖于玩家服务器位置的视野扫描在这一时间段扫描的还是旧位置,那么目标NPC便不会在这段时间被纳入字典,也不会创建了。

问题分析

那么为什么会在执行强制位置的时候往回走呢?初步推测这多半跟自动寻路有关系。
先看看强制位置部分的逻辑是怎么做的

void Obj_Char::ForceSetScene(ScenePos val,bool bMoveToPos ,int32 type)
{SetScenePos(val);if(IsSceneValid()){//设置新位置,准备发包给客户端Packet::GC_FORCE_STEPOS_PAK pak;pak.m_packetData.set_serverid(GetId());pak.m_packetData.set_posX(m_ScenePos.m_fX);pak.m_packetData.set_posY(m_ScenePos.m_fY);pak.m_packetData.set_posZ(m_ScenePos.m_fZ);//状态同步GetrScene().BroadCast_InSight_Inclue(pak,GetId());}if(IsPlayer()){Obj_Player& rPlayer = dynamic_cast<Obj_Player&>(*this);//玩家强制设置位置后,手动改变消息包的版本号,//防止接受到的位置之前的CG_MOVE消息包,导致位置异常。rPlayer.IncMovePakVersion();}}

然鹅好像没有发现什么跟寻路有关的东西,于是我又换了一种思路,从获取玩家位置的引用地方查,终于。。
找到了这么一个地方。

if(rRealEndPos!=GetScenePos())
{bool bMove1 = IsMoveing();StopMove(true,false);m_TargetPos = rRealEndPosl;m_fStopRange = 0.01f;m_PathCont.CleanUp();m_PahtCont.PushBack(PathNode(GetScenePos(),rRealEndPos))//---
}

发现如果m_PathCont缓存不为空,并且当前位置没有校验成功,会朝目标位置移动。所以如果在ForceSetScene的时候m_PathCont 不为空,而这一帧的时候客户端的实际位置又和服务器位置不一样(正常,因为服务器这边强改了位置,要通知客户端改本地的locationPos嘛,符合位置校验没有成功的情况,而m_PathCont 数据又没有清空,此时玩家的【服务器位置】会朝原始位置修正。而为什么修正一段时间后,又成功的定位到了目标位置,可以推测这时刻客户端正确收到了GC_FORCE_STEPOS_PAK的消息,修改了自身的位置,此时服务器校验通过,【服务器位置】变化中止]),这也就解释了为什么实际NPC是延迟加载出来 而不是完全加载不出。
image.png
这里方便阅读做一个补充。
(【客户端位置】就是unity工程中玩家的自身坐标它既依赖于服务器位置消息包的数据而变化(寻路、状态同步),又可以通过自身改变从而影响【服务器位置】,比如客户端主动的行为(按键等等))上文说的位置全是【服务器位置】

问题解决

在ForceSetScene 类似的服务器主动大范围修改玩家位置的地方之前,将m_PathCont 位置缓存清空即可。这样保证了下一帧服务器不会触发位置修正。而在客户端正确收到位置消息包从而改变【客户端位置】后,正常向服务器发送CG_MOVE消息的时候再校验。

这篇关于[真机Bug]解决服务器强制定位玩家位置后NPC加载超时的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1062479

相关文章

qt5cored.dll报错怎么解决? 电脑qt5cored.dll文件丢失修复技巧

《qt5cored.dll报错怎么解决?电脑qt5cored.dll文件丢失修复技巧》在进行软件安装或运行程序时,有时会遇到由于找不到qt5core.dll,无法继续执行代码,这个问题可能是由于该文... 遇到qt5cored.dll文件错误时,可能会导致基于 Qt 开发的应用程序无法正常运行或启动。这种错

SpringBoot排查和解决JSON解析错误(400 Bad Request)的方法

《SpringBoot排查和解决JSON解析错误(400BadRequest)的方法》在开发SpringBootRESTfulAPI时,客户端与服务端的数据交互通常使用JSON格式,然而,JSON... 目录问题背景1. 问题描述2. 错误分析解决方案1. 手动重新输入jsON2. 使用工具清理JSON3.

MySQL 设置AUTO_INCREMENT 无效的问题解决

《MySQL设置AUTO_INCREMENT无效的问题解决》本文主要介绍了MySQL设置AUTO_INCREMENT无效的问题解决,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参... 目录快速设置mysql的auto_increment参数一、修改 AUTO_INCREMENT 的值。

关于跨域无效的问题及解决(java后端方案)

《关于跨域无效的问题及解决(java后端方案)》:本文主要介绍关于跨域无效的问题及解决(java后端方案),具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录通用后端跨域方法1、@CrossOrigin 注解2、springboot2.0 实现WebMvcConfig

Go语言中泄漏缓冲区的问题解决

《Go语言中泄漏缓冲区的问题解决》缓冲区是一种常见的数据结构,常被用于在不同的并发单元之间传递数据,然而,若缓冲区使用不当,就可能引发泄漏缓冲区问题,本文就来介绍一下问题的解决,感兴趣的可以了解一下... 目录引言泄漏缓冲区的基本概念代码示例:泄漏缓冲区的产生项目场景:Web 服务器中的请求缓冲场景描述代码

解决JSONField、JsonProperty不生效的问题

《解决JSONField、JsonProperty不生效的问题》:本文主要介绍解决JSONField、JsonProperty不生效的问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑... 目录jsONField、JsonProperty不生效javascript问题排查总结JSONField

github打不开的问题分析及解决

《github打不开的问题分析及解决》:本文主要介绍github打不开的问题分析及解决,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、找到github.com域名解析的ip地址二、找到github.global.ssl.fastly.net网址解析的ip地址三

springboot加载不到nacos配置中心的配置问题处理

《springboot加载不到nacos配置中心的配置问题处理》:本文主要介绍springboot加载不到nacos配置中心的配置问题处理,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑... 目录springboot加载不到nacos配置中心的配置两种可能Spring Boot 版本Nacos

RedisTemplate默认序列化方式显示中文乱码的解决

《RedisTemplate默认序列化方式显示中文乱码的解决》本文主要介绍了SpringDataRedis默认使用JdkSerializationRedisSerializer导致数据乱码,文中通过示... 目录1. 问题原因2. 解决方案3. 配置类示例4. 配置说明5. 使用示例6. 验证存储结果7.

Swagger在java中的运用及常见问题解决

《Swagger在java中的运用及常见问题解决》Swagger插件是一款深受Java开发者喜爱的工具,它在前后端分离的开发模式下发挥着重要作用,:本文主要介绍Swagger在java中的运用及常... 目录前言1. Swagger 的主要功能1.1 交互式 API 文档1.2 客户端 SDK 生成1.3