前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >困扰 80% 玩家,GTA 5 祖传 7 年的加载时间问题,现被一玩家缩短了 70%

困扰 80% 玩家,GTA 5 祖传 7 年的加载时间问题,现被一玩家缩短了 70%

作者头像
深度学习与Python
发布于 2021-03-30 08:55:59
发布于 2021-03-30 08:55:59
1.7K04
代码可运行
举报
运行总次数:4
代码可运行

3 月初,游戏玩家 tostercx 宣称发明了一种新方法,可以把《侠盗猎车手 Online》(检查 GTA 5)的加载时间缩短 70%。对玩家群体来说,这无疑是一个好消息。

一直以来,《侠盗猎车手 Online》因慢如蜗牛的加载时间已经“臭名昭著”。社交媒体 Reddit 上有一份针对这个问题的调查,结果发现超过 80% 的玩家都受到加载时间太慢的困扰。关键是,这个问题已经存在了 7 年,而游戏开发商 Rockstar Games 却没有给出任何解决方案。

而 tostercx 决心深究,他发现加载时间慢的问题在于启动《侠盗猎车手 Online》时存在单线程 CPU 瓶颈,并且游戏在费劲地解析 10MB 的 JSON 文件。对此,他认为,解决这款游戏加载时间慢的问题,只需要一名开发人员不到一天的时间。

随后,他写了一篇技术文章发在博客上,引起很大反响,不仅被 Hacker News 置顶,而且网友纷纷转发,甚至获得游戏开发商 Rockstar 10000 美元的奖励。

Rockstar 在官方通告中称,“经过彻底的调查,我们可以确定玩家 tostercx 确实揭示了《侠盗猎车手 Online》PC 版加载时间有待改善的游戏代码问题。作为调查结果,我们做了一些改进,这些改进会在更新中得到应用。”

慢如蜗牛的加载时间

作为《侠盗猎车手 Online》玩家,tostercx 不久前又上线打了几个任务,但是他发现这款游戏的加载时间仍然和 7 年前刚发布的时候一样慢。

于是,他去网上搜索,想了解是否已经有人解决了这个问题。而网上大多数的结果都是一些个人评论和猜测,比如这款游戏太复杂了所以加载很慢,p2p 网络架构太垃圾了所以加载快不了,等等。

当然,还有一些提升加载速度的建议,比如先载入故事模式,然后玩一次单人任务;另外,还有几个可以跳过启动 logo 视频的 mod。

但是,这些办法结合起来只能节省 10-30 秒。而他玩这款游戏,所需的故事模式加载时间是 1 分 10 秒,而在线模式加载时间是 6 分钟。详情如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
故事模式加载时间: ~1m 10s在线模式加载时间: ~6m 左右禁用启动菜单,R* logo 到进入游戏内的时间 (social club 登录时间不算).
老当益壮的 CPU: AMD FX-8350便宜的 SSD: KINGSTON SA400S37120G当然得有内存: 2x Kingston 8192 MB (DDR3-1337) 99U5471不错的 GPU: NVIDIA GeForce GTX 1070

tostercx 在博客写道,“我知道我的机器配置已经过时,但是加载到在线模式竟然需要 6 倍左右的时间?即使用上别人总结的从故事模式切换到在线模式的加载技巧,也没产生什么效果。”

开始调查

Reddit 的一份调查表明,超过 80% 的玩家都受到这个问题的困扰。

只有 20% 的玩家所用的加载时间不到 3 分钟,而他们极有可能是因为配备了高端游戏 PC。根据 tostercx 的基准测试,高端游戏 PC 加载在线模式,只需要大约 2 分钟。

tostercx 发现:即使如此,他们的故事模式还是需要近 1 分钟的加载时间?他们从故事模式切换到在线模式只需一分多钟。

“我知道,他们的硬件配置要好很多,但肯定不会好 5 倍那么厉害。”他说。

继续研究

利用任务管理器之类的强大工具,tostercx 开始研究哪些资源可能成为瓶颈。

游戏花了一点时间加载用于故事模式和在线模式的通用资源,这部分时间与高端 PC 的耗时差不多。然后,游戏在他的计算机上拉满一个核心跑 4 分钟时间,这 4 分钟其他什么事都不干。

正如上图所示,磁盘利用率为 0,网络利用率在几秒钟后也降为 0,GPU 利用率为 0,而内存使用,则完全是一条直线。

tostercx 表示,“它是在挖掘加密货币还是在干什么勾当?我闻到代码的味道了。真的是很糟糕的代码。”

问题根因

在 tostercx 看来,虽然自己的机器很老(老旧的 AMD CPU 有 8 个核心),制造时间也有很久,但是这可能无法解释所有的加载时间差异。

让他感到奇怪的是,游戏只占用了 CPU 资源。他以为会有大量的磁盘读取过程来加载资源,或很多网络请求负载尝试在 p2p 网络中协商会话。

“但是现在这样?这可能是一个 bug。”

Profiling

Profiler(分析器)是查找 CPU 瓶颈的好工具。这里只有一个问题——大多数 Profiler 都需要检测源代码,才能对程序运行过程中所发生的事情有一个完整的了解。

而 tostercx 没有源代码,也不需要微秒级的读数——其瓶颈长达 4 分钟。

然后,了解一下堆栈采样:对闭源应用程序来说,Profiler 只有一个选项。转储正在运行的进程的堆栈和当前指令指针的位置,以按设置的时间间隔构建一个调用树。然后将它们加起来以获取当前状况的统计信息。

据他了解,只有一个 Profiler 可以在 Windows 上执行这些操作,而且它已经十多年没有更新了。它就是 Luke Stackwalker!

一般来说,Luke 会将相同的函数归为一组,但由于他没有调试符号,因此不得不盯着附近的地址来猜测它是否在同一位置。那么我们看到了什么?不是一个瓶颈,而是两个!

继续深入

tostercx 借用了其朋友的行业标准级反汇编程序的完全合法副本,然后把 GTA 拆开来看个究竟。

这根本不对头。大多数著名游戏都带有针对逆向工程的内置保护措施,可以防止盗版、作弊和修改。这倒不是说这类措施一定有用。这里似乎存在某种混淆 / 加密,已经用乱码代替了大多数指令。

但是,这不是关键,“我们只需要在执行我们要看的部分时转储游戏的内存即可”。在运行之前,必须对指令进行混淆处理,他使用了 Process Dump。

问题一:这是……strlen?!

反汇编现在不太混乱的转储会发现,其中一个地址的一个标签被拉出到了某个地方!这是 strlen?在调用堆栈中,下一个标记为 vscan_fn,此后标记结束。tostercx 认为它就是 sscanf。

它正在解析某些内容。解析什么?反汇编太花时间了,因此他决定使用 x64dbg 从正在运行的进程中转储一些样本。后来经过一些调试步骤,他发现它是……JSON!他们正在解析 JSON,一个带有约 63k 项的条目,体积高达 10MB 的 JSON。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
...,{ "key": "WP_WCT_TINT_21_t2_v9_n2", "price": 45000, "statName": "CHAR_KIT_FM_PURCHASE20", "storageType": "BITFIELD", "bitShift": 7, "bitSize": 1, "category": ["CATEGORY_WEAPON_MOD"]},...

它是什么?根据一些参考资料,它似乎是“在线商店目录”的数据。它可能包含了你可以在 GTA Online 中购买的所有物品和升级的列表。

问题二:使用哈希数组吗?

原来第二名 = 个罪犯和第一个是紧挨着的。就像在这段丑陋的反编译内容中看到的那样,它们甚至都在相同的 if 语句中被调用:

第二个问题?解析项目后,它立即存储在一个数组(或一个内联的 C++ 列表?不确定)中。每个条目如下所示:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
struct { uint64_t
hash; item_t
item;} entry;

但是在存储之前?它会逐一检查整个数组,对比项目的哈希值以查看它是否在列表中。条目总共有约 63k,也就是说需要(n^2+n)/2=(63000^2+63000)/2=1984531500。可是大多数操作都没用。tostercx 表示,“既然你有唯一的哈希,为什么不使用哈希图呢?”

他在反编译时将其命名为 hashmap,但它显然 not_a_hashmap。这还没完。加载 JSON 之前,hash-array-list-thing 是空的。而且 JSON 中的所有项目都是唯一的!他们甚至不需要检查它是否在列表中!它们甚至有一个直接插入项目的函数!就用它就行了!有没有搞错!?

解决方案

tostercx 在找到问题后,写了一个 .dll,将其注入 GTA 中,并 hook 上一些函数,搞定加载时间慢的问题。

JSON 问题比较棘手,他无法替换它们的解析器。用不依赖 strlen 的 sscanf 替换 sscanf 更为现实。但是有一种更简单的方法。

  • hook strlen
  • 等待一个长字符串
  • “缓存”它的起始和长度
  • 如果在字符串范围内再次调用它,则返回缓存的值

如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
size_t strlen_cacher(char
str){ static char
start; static char* end; size_t len; const size_t cap = 20000; // if we have a "cached" string and current pointer is within it if (start && str >= start && str <= end) { // calculate the new strlen len = end - str; // if we're near the end, unload self // we don't want to mess something else up if (len < cap / 2) MH_DisableHook((LPVOID)strlen_addr); // super-fast return! return len; } // count the actual length // we need at least one measurement of the large JSON // or normal strlen for other strings len = builtin_strlen(str); // if it was the really long string // save it's start and end addresses if (len > cap) { start = str; end = str + len; } // slow, boring return return len;}

至于哈希数组问题,其实更简单——完全跳过重复检查并直接插入项目,因为我们知道值是唯一的。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
char fastcall netcat_insert_dedupe_hooked(uint64_t catalog, uint64_t
key, uint64_t
item){ // didn't bother reversing the structure uint64_t not_a_hashmap = catalog + 88; // no idea what this does, but repeat what the original did if (!(*(uint8_t(fastcall*
)(uint64_t
))(
item + 48))(item)) return 0; // insert directly netcat_insert_direct(not_a_hashmap, key, &item); // remove hooks when the last item's hash is hit // and unload the .dll, we are done here :) if (
key == 0x7FFFD6BE) { MH_DisableHook((LPVOID)netcat_insert_dedupe_addr); unload(); } return 1;}

很快,这办法就奏效了,《侠盗猎车手 Online》加载时间缩短 70%。如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Original online mode load time: ~6m flatTime with only duplication check patch: 4m 30sTime with only JSON parser patch: 2m 50sTime with both issues patched: 1m 50s(6
60 - (1
60+50)) / (6*60) = 69.4% load time improvement (nice!)

根据这名玩家的总结:

  • 启动 GTA Online 时存在单线程 CPU 瓶颈
  • 事实证明,GTA 原来在费劲地解析 10MB 的 JSON 文件
  • JSON 解析器本身没做好,并且
  • 解析后,有一个缓慢的重复项目删除流程

附:

玩家 tostercx 的 PoC 完整资料

https://github.com/tostercx/GTAO_Booster_PoC

官方更新

https://support.rockstargames.com/articles/360061161574/

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-03-23,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 InfoQ 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
六千字详细图解网络时间协议(NTP),带你领略NTP的魅力!
时间是我们日常生活的重要组成部分,而在数字时代,时间同步也在计算机和网络系统中扮演着至关重要的角色。网络时间协议(Network Time Protocol,NTP)是一种用于确保网络中各个设备保持准确时间的关键协议。随着互联网的普及和数字技术的不断发展,NTP变得愈加重要,影响着多个领域,从通信和安全到金融和医疗。
网络技术联盟站
2023/11/22
16.6K0
【Linux】日历和时间命令
在 Linux 操作系统中,我们经常需要管理系统的日期和时间。无论是调整时钟、查看日历,还是同步硬件时钟,掌握与时间相关的命令是必不可少的技能。本文将深入介绍一些常用的 Linux 时间和日期命令,助您更好地管理系统的时间。
人不走空
2024/02/21
8390
linux中设置NTP时间同步服务
你可以使用以下命令轻松保持系统的日期和时间准确 NTP (Network Time Protocol). 它使你可以通过网络连接同步计算机时钟并使其准确。基本上,客户端从远程服务器请求当前时间,并使用它来设置自己的时钟。 chrony 是 Network Time Protocol (NTP)你可以使用 chrony: 将系统时钟与NTP 服务器同步 。 将系统时钟与参考时钟同步,例如 GPS 接收器。 并将系统时钟与手动时间输入同步。 作为 服务器或对等点向网络中的其他计算机提供时间服务。 NTPv4(R
入门笔记
2022/06/02
6.9K0
再见 NTP,是时候拥抱下一代时间同步服务 Chrony 了
Chrony 是一个多功能的 NTP (Network Time Protocol) 实现,类 Unix 系统上 NTP 客户端和服务器的替代品。它可以通过 NTP 服务或者类似 GPS 时钟接收器的硬件级参考时钟来同步系统时钟,具有更好的时钟准确度,并且对于那些间歇性互联网连接的系统很有帮助。Chrony 是免费开源的,并且支持 GNU/Linux 和 BSD 衍生版(比如:FreeBSD、NetBSD)、macOS 和 Solaris 等。
iMike
2019/11/30
22.5K0
Linux学习命令汇总二——Linux文件系统,日期时间和文件目录管理
#ls -l /root 显示/root 列表长选项 ,(各项参数含义如下)
兔云小新LM
2019/07/22
1.5K0
设置让 Linux 保持精确时间
如何保持正确的时间,如何使用 NTP 和 systemd 让你的计算机在不滥用时间服务器的前提下保持同步。
用户7639835
2021/11/19
3.9K0
05 vsftpd搭建
l restart chronyd #查看客户端同步情况: timedatectl #查看时间同步源: chronyc sources -v
张哥编程
2024/12/07
1510
Linux 系统时间同步方法小结
在 Windwos 中,系统时间的设置很简单,界面操作,通俗易懂,而且设置后,重启,关机都没关系。系统时间会自动保存在 BIOS 时钟里面,启动计算机的时候,系统会自动在 BIOS 里面取硬件时间,以保证时间的不间断。但在 Linux 下,默认情况下,系统时间和硬件时间并不会自动同步。在 Linux 运行过程中,系统时间和硬件时间以异步的方式运行,互不干扰。硬件时间的运行,是靠 BIOS 电池来维持,而系统时间,是用 CPU Tick 来维持的。在系统开机的时候,会自动从 BIOS 中取得硬件时间,设置为系统时间。
jwangkun
2021/12/23
4.7K0
Linux 系统时间同步方法小结
NTP时间同步问题
NTP(网络时间协议)用于同步计算机系统的时间,确保所有设备的时间一致。NTP时间同步问题可能导致系统日志记录不准确、定时任务失败等问题。以下是一些诊断和解决NTP时间同步问题的方法:
是山河呀
2025/02/03
3340
定时任务不准时?解析Linux服务器时间概念与调整策略
在检测海外服务器日志的时候,发现脚本启动时间与定时任务设定的时间不一致,现进行问题排查。
用户3578099
2023/12/11
1.2K0
定时任务不准时?解析Linux服务器时间概念与调整策略
树莓派:光阴的故事
对于电子设备来说,时间都是基础性的功能,也很容易被人忽视。上世纪的“千年虫”问题,就是时间方面设计缺陷造成的。对于网络连接的多设备来说,保持时间同步又是一个新的问题。对于树莓派的众多应用情景来说,时间
Vamei
2018/01/18
1.6K0
树莓派:光阴的故事
如何在Linux服务器上使用Chrony避免时间漂移
你会惊讶于如果 Linux 系统的时间不同步,它可能会遇到多少问题。以下是如何使用 Chrony 来校准 NTP。
云云众生s
2024/08/27
2630
NTP 时间服务器「建议收藏」
NTP 时间服务器 ,为客户机提供标准时间 原理:NTP(Network TimeProtocol,网络时间协议)是用来使计算机时间同步的一种协议。它可以使计算机对其服务器或时钟源做同步化,它可以提供高精准度的时间校正
全栈程序员站长
2022/07/22
9820
linux中管理文件类型和系统时间的5个有用命令
接下来,我们将查看使用ls、grep命令计算给定目录中特定类型文件数量的技巧。命令之间的通信是通过命名管道实现的。
入门笔记
2022/06/02
1.1K0
【LFS 系列】从零开始 DIY Linux 系统:(七)基本系统配置 - 配置系统时间
本节将会讨论如何配置 systemd-timedated 系统服务,包括配置系统时间和时区。
Lucifer三思而后行
2021/10/08
8490
Linux集群系统时间同步
要做到服务器集群的时间同步,集群中各台机器的时区必须相同的,我们在国内就使用中国时区,如果你的机器的时区不是"Asia/Shanghai",需要修改时区
CoderJed
2018/09/13
8.9K0
Linux集群系统时间同步
系统设计中 跨时区问题 解决方案
产品功能设计中,经常会遇到一场活动,分跨不同时区,系统需要显示不同时区的时间,同时希望跨时区的用户可以同一时间开始,同一时间结束。
架构精进之路
2024/04/30
9040
系统设计中 跨时区问题 解决方案
CentOS设置系统时间与网络时间同步
Linux的时间分为System Clock(系统时间)和Real Time Clock (硬件时间,简称RTC)。
程序员纬度
2021/03/02
6.7K0
修改后门ctime | Linux 后门系列
最新版 Linux 中多了一个属性,文件创建时间,在 Centos Stream 中叫做 created time,在 Ubuntu中叫 Birth time
意大利的猫
2023/02/24
5.9K1
修改后门ctime | Linux 后门系列
推荐阅读
相关推荐
六千字详细图解网络时间协议(NTP),带你领略NTP的魅力!
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档