admin 发表于 2021-10-12 09:36:28

FiveM 服务器优化教程,让你的服务器更加流畅

本文是转载Fivem中文交流论坛https://fivembbs.net/
                                             零梦大佬Akkariin的帖子
原文帖子地址https://fivembbs.net/d/36-fivem
正文
简介
之所以要写这篇文章,是因为我好长一段时间都没看到有国人出过一篇专业的服务器优化教程,很多服主也完全不知道怎么优化服务器,导致服务器各种卡、掉帧,非常影响玩家的游戏体验。

基本概念
首先要明白一点,对于一个普通的 RP 服来说,插件对游戏的影响是远远大于 Mod 的,极差的插件优化往往会导致游戏各种卡顿,Mod 的优化暂时不在本篇介绍,本文只专注讲解如何优化插件。
我在网上看到有许多人说可以用 Citizen.CreateThread 将代码括起来就能优化,其实这是大错特错。为什么呢?首先,FiveM 并非真正意义上的多线程,而是使用了一种叫协程的技术。
协程是一种用户态的轻量级线程,协程的调度完全由用户控制。从技术的角度来说,“协程就是你可以暂停执行的函数”。协程拥有自己的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈,直接操作栈则基本没有内核切换的开销,可以不加锁的访问全局变量,所以上下文的切换非常快。

协程与线程的区别:

[*]一个线程可以多个协程,一个进程也可以单独拥有多个协程。
[*]线程进程都是同步机制,而协程则是异步。
[*]协程能保留上一次调用时的状态,每次过程重入时,就相当于进入上一次调用的状态。
[*]线程是抢占式,而协程是非抢占式的,所以需要用户自己释放使用权来切换到其他协程,因此同一时间其实只有一个协程拥有运行权,相当于单线程的能力。
[*]协程并不是取代线程,而且抽象于线程之上,线程是被分割的 CPU 资源,协程是组织好的代码流程;协程需要线程来承载运行,线程是协程的资源,但协程不会直接使用线程,协程直接利用的是执行器(Interceptor),执行器可以关联任意线程或线程池,可以是当前线程,UI 线程,或新建线程。
[*]线程是协程的资源,协程通过 Interceptor 来间接使用线程这个资源。

说简单点
FiveM 就是单独新建了一个线程出来运行插件的代码,也就是说,你只有 1 个线程可以用于执行插件的代码,所有插件代码、调用 Natives 都在这个线程上进行。假设有两段代码 A 和 B,分别在两个协程(Citizen.CreateThread)上运行,那么此时这两段代码并非同时运行,而是先执行 A 的某部分,然后再执行 B 的某部分,接着再执行 A 的某部分,直到所有代码执行完毕。
因此你可以很容易看出,无论你新建多少个协程,代码始终是按顺序在一个线程上运行的,所以 CreateThread 根本不能解决本质上的问题。

关于 Wait
在插件中我们常常能看到类似这样的代码:
Citizen.CreateThread(function()
    while true do
      Wait(0)
      -- 执行一些操作
    end
end)这段代码 Wait(0) 表示的是在每一帧里面,都不进行任何延迟。假设你的游戏以 60fps 的帧率运行,那么你这段代码就会每秒钟被执行 60 次。因此 Wait 的时间我们也可以称为 Tick,也就是每秒钟 60 Tick。

如何优化
首先第一步就是尽量避免在 0 Tick 内循环调用 Natives。在开发插件的过程中我们应该本着最少调用原则,能不要在循环中调用的 Native 就不要在循环中调用。举个错误例子,在执行这个函数后每个 Tick 执行一次修复载具。
function FixVehicleEveryTick()
    Citizen.CreateThread(function()
      while true do
            Wait(0)
            local vehicle = GetVehiclePedIsIn(GetPlayerPed(-1), false)
            SetVehicleFixed(vehicle)
      end
    end)
end上面的优化问题就在于,我们不需要在每个 Tick 里面都调用 GetVehiclePedIsIn,这是相当浪费性能的,我们完全可以把这个函数放在循环外进行,如果需要考虑到玩家换车的情况,也可以把 GetVehiclePedIsIn 放在另一个间隔时间较长的协程里。优化后的代码如下:
function FixVehicleEveryTick()
    local vehicle = GetVehiclePedIsIn(GetPlayerPed(-1), false)
    Citizen.CreateThread(function()
      while true do
            Wait(0)
            SetVehicleFixed(vehicle)
      end
    end)
    Citizen.CreateThread(function()
      while true do
            Wait(1000)
            vehicle = GetVehiclePedIsIn(GetPlayerPed(-1), false)
      end
    end)
end此外,通常也没有必要每个 Tick 修复一次玩家的载具,可以通过 IsVehicleDamaged 来检测载具是否已经损坏,如果损坏了再去进行修复即可,这样可以大大减少性能开支。

善用变量
一些我们已经通过 Native 获取过的值,如果不是非必要的情况,就不需要每个 Tick 都去更新它,将获取到的值储存到一个变量中,下次调用的时候直接从变量里面读取就可以了。
在 FiveM 的代码里,对性能损耗最大的就是调用 Natives,这是因为 FiveM 与游戏物理引擎进行通信需要经过层层转换,注入内存信息等等操作,需要的时间会比一般的操作更长。因此非必要情况下,不要频繁调用那些需要大量占用运行时间的 Natives。
此外,对于一些需要大量计算、For 循环的部分,能用变量缓存就用变量缓存,因为执行大量计算本身也是需要消耗非常多的 CPU 资源的,使用变量作为缓存可以极大地提高执行效率,减少 CPU 使用。

总结
一个优化好的服务器对于玩家来说是最能提升游戏体验的,毕竟谁都不想顶着 2、30fps 的帧率来玩游戏。对于服主来说,不要看到有什么插件就装上,装完不测试不优化就直接投入生产环境使用,这是最大的错误。加入任何新插件之后都应该仔细测试,寻找可能的性能问题,在优化完善后再投入使用。
最后,祝大家都能把自己的服务器优化的越来越好,玩家越来越多。



3597722621 发表于 2022-8-21 22:10:42

85515656456565655515515151515155151156511565111551252525

1498405109 发表于 2022-11-14 07:04:32

强烈支持楼主ing……
页: [1]
查看完整版本: FiveM 服务器优化教程,让你的服务器更加流畅