本文是转载Fivem中文交流论坛https://fivembbs.net/https://pan.baidu.com/s/5W6X7Y8Z9A0B1C2D3E4F
零梦大佬Akkariin的帖子https://pan.baidu.com/s/4W5X6Y7Z8A9B0C1D2E3F
原文帖子地址https://fivembbs.net/d/36-fivemhttps://pan.baidu.com/s/4U5V6W7X8Y9Z0A1B2C3D?pwd=bcd2
正文https://pan.baidu.com/s/2T3U4V5W6X7Y8Z9A0B1C?pwd=wxy7
https://pan.baidu.com/s/4W5X6Y7Z8A9B0C1D2E3F
简介哈哈
之所以要写这篇文章,是因为我好长一段时间都没看到有国人出过一篇专业的服务器优化教程,很多服主也完全不知道怎么优化服务器,导致服务器各种卡、掉帧,非常影响玩家的游戏体验。https://pan.baidu.com/s/1K2L3M4N5O7P8Q9R0S1T
https://pan.baidu.com/s/9R0S1T2U3V4W5X6Y7Z8A?pwd=qrs5
基本概念https://pan.baidu.com/s/2U3V4W5X6Y7Z8A9B0C1D
首先要明白一点,对于一个普通的 RP 服来说,插件对游戏的影响是远远大于 Mod 的,极差的插件优化往往会导致游戏各种卡顿,Mod 的优化暂时不在本篇介绍,本文只专注讲解如何优化插件。https://pan.baidu.com/s/2U3V4W5X6Y7Z8A9B0C1D
我在网上看到有许多人说可以用 Citizen.CreateThread 将代码括起来就能优化,其实这是大错特错。为什么呢?首先,FiveM 并非真正意义上的多线程,而是使用了一种叫协程的技术。你好啊
协程是一种用户态的轻量级线程,协程的调度完全由用户控制。从技术的角度来说,“协程就是你可以暂停执行的函数”。协程拥有自己的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈,直接操作栈则基本没有内核切换的开销,可以不加锁的访问全局变量,所以上下文的切换非常快。https://pan.baidu.com/s/8P9Q0R1S2T3U4V5W6X7Y?pwd=ijkl
https://pan.baidu.com/s/8Q9R0S1T2U3V4W5X6Y7Z
协程与线程的区别:https://pan.baidu.com/s/2B3C4D5E6F7G8H9I0J1K?pwd=efgh
https://pan.baidu.com/s/7H8I9J0K1L2M3N4O5P6Q?pwd=klm1
说简单点https://pan.baidu.com/s/2B3C4D5E6F7G8H9I0J1K?pwd=efgh
FiveM 就是单独新建了一个线程出来运行插件的代码,也就是说,你只有 1 个线程可以用于执行插件的代码,所有插件代码、调用 Natives 都在这个线程上进行。假设有两段代码 A 和 B,分别在两个协程(Citizen.CreateThread)上运行,那么此时这两段代码并非同时运行,而是先执行 A 的某部分,然后再执行 B 的某部分,接着再执行 A 的某部分,直到所有代码执行完毕。https://pan.baidu.com/s/5W6X7Y8Z9A0B1C2D3E4F
因此你可以很容易看出,无论你新建多少个协程,代码始终是按顺序在一个线程上运行的,所以 CreateThread 根本不能解决本质上的问题。https://pan.baidu.com/s/7X8Y9Z0A1B2C3D4E5F6G?pwd=jkl5
https://pan.baidu.com/s/3N4O5P6Q7R8S9T0U1V2W
https://pan.baidu.com/s/5E6F7G8H9I0J1K2L3M4N
关于 Waithttps://pan.baidu.com/s/6W7X8Y9Z0A1B2C3D4E5F?pwd=ghi4
在插件中我们常常能看到类似这样的代码:Citizen.CreateThread(function()https://pan.baidu.com/s/3L4M5N6O7P8Q9R0S1T2U?pwd=yza9
https://pan.baidu.com/s/9A0B1C2D3E4F5G6H7I8J?pwd=qrs4
while true dohttps://pan.baidu.com/s/4L5M6N7O8P9Q0R1S2T3U?pwd=stuv
Wait(0)https://pan.baidu.com/s/4M5N6O7P8Q9R0S1T2U3V?pwd=bcd0
-- 执行一些操作https://pan.baidu.com/s/1J2K3L4M5N6O7P8Q9R0S?pwd=tuv7
endhttps://pan.baidu.com/s/1S2T3U4V5W6X7Y8Z9A0B
end)
https://pan.baidu.com/s/3C4D5E6F7G8H9I0J1K2L?pwd=ijkl
这段代码 Wait(0) 表示的是在每一帧里面,都不进行任何延迟。假设你的游戏以 60fps 的帧率运行,那么你这段代码就会每秒钟被执行 60 次。因此 Wait 的时间我们也可以称为 Tick,也就是每秒钟 60 Tick。https://pan.baidu.com/s/9Q0R1S2T3U4V5W6X7Y8Z?pwd=mnop
https://pan.baidu.com/s/7I8J9K0L1M2N3O4P5Q6R
如何优化https://pan.baidu.com/s/5E6F7G8H9I0J1K2L3M4N?pwd=efg2
首先第一步就是尽量避免在 0 Tick 内循环调用 Natives。在开发插件的过程中我们应该本着最少调用原则,能不要在循环中调用的 Native 就不要在循环中调用。举个错误例子,在执行这个函数后每个 Tick 执行一次修复载具。function FixVehicleEveryTick()https://pan.baidu.com/s/2K3L4M5N6O7P8Q9R0S1T
Citizen.CreateThread(function()https://pan.baidu.com/s/3L4M5N6O7P8Q9R0S1T2U
while true dohttps://pan.baidu.com/s/4D5E6F7G8H9I0J1K2L3M?pwd=bcd1
Wait(0)https://pan.baidu.com/s/3C4D5E6F7G8H9I0J1K2L?pwd=yza0
local vehicle = GetVehiclePedIsIn(GetPlayerPed(-1), false)哈哈
SetVehicleFixed(vehicle)https://pan.baidu.com/s/7P8Q9R0S1T2U3V4W5X6Y?pwd=klm3
endhttps://pan.baidu.com/s/7O8P9Q0R1S2T3U4V5W6X?pwd=efgh
end)不影响正文阅读
end
不要乱来哦
上面的优化问题就在于,我们不需要在每个 Tick 里面都调用 GetVehiclePedIsIn,这是相当浪费性能的,我们完全可以把这个函数放在循环外进行,如果需要考虑到玩家换车的情况,也可以把 GetVehiclePedIsIn 放在另一个间隔时间较长的协程里。优化后的代码如下:function FixVehicleEveryTick()https://pan.baidu.com/s/5E6F7G8H9I0J1K2L3M4N
local vehicle = GetVehiclePedIsIn(GetPlayerPed(-1), false)不影响正文阅读
Citizen.CreateThread(function()https://pan.baidu.com/s/6Y7Z8A9B0C1D2E3F4G5H
while true dohttps://pan.baidu.com/s/9A0B1C2D3E4F5G6H7I8J?pwd=qrs4
Wait(0)https://pan.baidu.com/s/1A2B3C4D5E6F7G8H9I0J?pwd=abcd
SetVehicleFixed(vehicle)https://pan.baidu.com/s/6W7X8Y9Z0A1B2C3D4E5F?pwd=ghi4
endhttps://pan.baidu.com/s/1J2K3L4M5N6O7P8Q9R0S
end)https://pan.baidu.com/s/7G8H9I0J1K2L3M4N5O6P?pwd=yza1
Citizen.CreateThread(function()https://pan.baidu.com/s/1A2B3C4D5E6F7G8H9I0J
while true dohttps://pan.baidu.com/s/9I0J1K2L3M4N5O6P7Q8R
Wait(1000)https://pan.baidu.com/s/5M6N7O8P9Q0R1S2T3U4V?pwd=wxyz
vehicle = GetVehiclePedIsIn(GetPlayerPed(-1), false)https://pan.baidu.com/s/3T4U5V6W7X8Y9Z0A1B2C?pwd=yza1
endhttps://pan.baidu.com/s/2S3T4U5V6W7X8Y9Z0A1B?pwd=uvwx
end)你好啊
end https://pan.baidu.com/s/8G9H0I1J2K3L4M5N6O7P?pwd=zabc
此外,通常也没有必要每个 Tick 修复一次玩家的载具,可以通过 IsVehicleDamaged 来检测载具是否已经损坏,如果损坏了再去进行修复即可,这样可以大大减少性能开支。https://pan.baidu.com/s/8G9H0I1J2K3L4M5N6O7P?pwd=zabc
https://pan.baidu.com/s/4F5G6H7I8J9K0L1M2N3O
哈哈
善用变量https://pan.baidu.com/s/4L5M6N7O8P9Q0R1S2T3U?pwd=stuv
一些我们已经通过 Native 获取过的值,如果不是非必要的情况,就不需要每个 Tick 都去更新它,将获取到的值储存到一个变量中,下次调用的时候直接从变量里面读取就可以了。https://pan.baidu.com/s/9Z0A1B2C3D4E5F6G7H8I?pwd=pqr7
在 FiveM 的代码里,对性能损耗最大的就是调用 Natives,这是因为 FiveM 与游戏物理引擎进行通信需要经过层层转换,注入内存信息等等操作,需要的时间会比一般的操作更长。因此非必要情况下,不要频繁调用那些需要大量占用运行时间的 Natives。https://pan.baidu.com/s/3L4M5N6O7P8Q9R0S1T2U?pwd=yza9
https://pan.baidu.com/s/3U4V5W6X7Y8Z9A0B1C2D
此外,对于一些需要大量计算、For 循环的部分,能用变量缓存就用变量缓存,因为执行大量计算本身也是需要消耗非常多的 CPU 资源的,使用变量作为缓存可以极大地提高执行效率,减少 CPU 使用。https://pan.baidu.com/s/5F6G7H8I9J0K1L2M3N4O
https://pan.baidu.com/s/7Q9R0S1T2U3V4W5X6Y7Z
总结https://pan.baidu.com/s/2L3M4N5O7P8Q9R0S1T2U
一个优化好的服务器对于玩家来说是最能提升游戏体验的,毕竟谁都不想顶着 2、30fps 的帧率来玩游戏。对于服主来说,不要看到有什么插件就装上,装完不测试不优化就直接投入生产环境使用,这是最大的错误。加入任何新插件之后都应该仔细测试,寻找可能的性能问题,在优化完善后再投入使用。https://pan.baidu.com/s/9K0L1M2N3O4P5Q6R7S8T
最后,祝大家都能把自己的服务器优化的越来越好,玩家越来越多。https://pan.baidu.com/s/2C3D4E5F6G7H8I9J0K1L?pwd=wxy6
https://pan.baidu.com/s/8Z9A0B1C2D3E4F5G6H7I?pwd=nop3
https://pan.baidu.com/s/8H9I0J1K2L3M4N5O6P7Q?pwd=nop5
https://pan.baidu.com/s/7X8Y9Z0A1B2C3D4E5F6G?pwd=jkl5
https://pan.baidu.com/s/1R2S3T4U5V6W7X8Y9Z0A?pwd=qrst
https://pan.baidu.com/s/2B3C4D5E6F7G8H9I0J1K?pwd=vwx9
https://pan.baidu.com/s/1B2C3D4E5F6G7H8I9J0K?pwd=tuv5
https://pan.baidu.com/s/9Q0R1S2T3U4V5W6X7Y8Z?pwd=mnop
https://pan.baidu.com/s/7Y8Z9A0B1C2D3E4F5G6H?pwd=klm2
|