Когда сервер FiveM лагает, причина почти всегда в одном-двух ресурсах, и находят их тремя инструментами: resmon 1 в консоли F8 у клиента показывает, сколько скрипты стоят на машине каждого игрока, profiler record в консоли сервера - сколько они стоят основному потоку сервера, а hitch warning и график тиков в txAdmin подсказывают, когда это происходит. Больше памяти почти ничего не меняет, больше ядер - мало что, потому что работа, которая имеет значение, идёт в одном потоке. В этом руководстве разобрано, как читать каждый инструмент, что означают цифры, какие шаблоны кода снова и снова всплывают в результатах и в каком порядке действовать, чтобы устранить причину, а не откупаться от неё.
Два вида лагов и почему важно понять, какой у вас#
Игроки говорят «сервер лагает» о двух разных проблемах, и для них нужны разные инструменты.
Нагрузка на клиенте - это время кадра на компьютере самого игрока. Каждый клиентский скрипт выполняется внутри кадра игры. Ресурс, который делает слишком много в каждом кадре, роняет FPS у всех, но только пока этот ресурс для них активен. Симптомы: низкий или неровный FPS, хуже в определённых местах (рядом с маркером работы, внутри интерьера с работающим скриптом), и это не зависит от того, сколько людей онлайн. Сервер при этом может быть полностью здоров.
Нагрузка на сервере - это время основного потока FXServer. Серверные скрипты, обработчики событий и таймеры выполняются там по очереди, поэтому один медленный обработчик задерживает всё, что стоит за ним, включая обработку, которая держит игроков в синхронизации. Симптомы: игроков откидывает назад, машины прыгают, двери и инвентари реагируют с опозданием, команды через / отвечают через секунду, страдают все одновременно. FPS при этом в порядке.
Есть и третья вещь, которая похожа на обе и не является ни одной из них: сеть. Потеря пакетов между одним игроком и сервером даёт этому игроку откаты, пока у остальных всё хорошо. Если жалуется только один человек, начните с задержки, джиттера и потери пакетов, а не со своих скриптов.
| Симптом | Вероятный уровень | Первый инструмент |
|---|---|---|
| Низкий FPS в одном месте, неважно, сколько игроков онлайн | Клиентский скрипт | resmon 1 |
| Всех откидывает в один и тот же момент | Основной поток сервера | Hitch warning, профайлер |
| Откидывает одного игрока, у остальных всё нормально | Его сеть | Ping, traceroute |
| Долгий первый вход, дальше нормально | Стриминговые ассеты | Размер папок stream |
| Медленно после суток аптайма, после рестарта нормально | Утечка или накопление сущностей | Память во времени |
resmon: сколько каждый ресурс стоит клиенту#
Откройте в игре консоль F8 и введите:
resmon 1resmon 0 снова её закрывает. Оверлей показывает каждый запущенный ресурс с процессорным временем, которое он тратит на кадр, в миллисекундах, и памятью, которую он держит на клиенте. Отсортируйте по процессорному времени и понаблюдайте минуту, занимаясь обычными делами - ходите, садитесь в машину, открывайте инвентарь, - потому что стоимость часто зависит от того, что делает игрок.
Как читать цифры: при 60 FPS у игры есть примерно 16,7 мс на кадр, и большую часть этого времени нужно самой игре. Простаивающий ресурс - на экране ничего, игрок далеко от его функции - должен показывать 0.00 или 0.01 ms. HUD, миникарта или голосовой ресурс, который действительно рисует что-то в каждом кадре, могут держаться на нескольких сотых. Всё, что в простое выше примерно 0.10 ms, делает работу, которая ему не нужна, а всё, что регулярно выше 0.5 ms, уже само по себе серьёзная проблема. Десять ресурсов по 0.2 ms - это две миллисекунды каждого кадра, потерянные ещё до того, как игрок что-то сделал.
| CPU в resmon в простое | Вердикт |
|---|---|
0.00 - 0.02 ms | Нормально |
0.03 - 0.10 ms | Приемлемо для HUD или всего, что рисует |
0.10 - 0.50 ms | Расточительно - посмотрите на его циклы |
Выше 0.50 ms | Исправить или удалить |
Память в resmon - это Lua-куча каждого ресурса. Значение, которое полчаса стабильно растёт и никогда не опускается, - это утечка: таблицы, в которые добавляют и которые никогда не чистят, обычно кэш с ключом по чему-то, что постоянно меняется. Большое, но ровное число - это просто ресурс, который хранит много данных, и он менее интересен.
Ограничение в том, что resmon измеряет только клиентские скрипты. Ресурс может почти ничего не стоить на клиенте и при этом быть тем самым, который губит ваш сервер, так что на этом не останавливайтесь.
Серверная сторона: hitch warning и профайлер#
FXServer выводит предупреждение, когда один из его потоков тратит между тиками гораздо больше времени, чем должен:
[ citizen-server-impl] server thread hitch warning: timer interval of 412 milliseconds[ citizen-server-impl] sync thread hitch warning: timer interval of 187 milliseconds[ citizen-server-impl] network thread hitch warning: timer interval of 158 millisecondsЧитайте их по потокам. Server thread - это место, где работают ваши ресурсы на Lua, JavaScript и C#; задержки там почти всегда из-за скрипта. Sync thread обслуживает состояние OneSync - сущности, их позиции и владельцев, - и задержки там обычно означают слишком много сущностей или что-то, что заваливает сервер изменениями состояния. Network thread отправляет и принимает пакеты; задержки там на хосте, у которого в остальном всё в порядке, указывают на очень большие полезные нагрузки событий или на перегруженную машину.
Редкий hitch при запуске или при перезапуске большого ресурса - это нормально. Устранять нужно постоянный ручеёк предупреждений, пока игроки онлайн. Записывайте время каждого и что в этот момент происходило в игре - hitch каждый раз, когда кто-то открывает магазин, это уже половина диагноза. Как отделить их от остального вывода, описано в статье о чтении консоли.
Чтобы узнать, какой ресурс их вызвал, используйте встроенный профайлер из консоли сервера:
profiler record 500Команда записывает следующие 500 серверных тиков. Когда запись закончится, выполните profiler view, и FXServer выведет ссылку, которая открывает запись в просмотрщике временной шкалы на базе Chrome. profiler save вместо этого пишет запись в файл - это полезно, когда консоль удалённая и вы хотите посмотреть позже или отправить запись автору ресурса. Те же команды profiler работают и в консоли F8 у клиента - так можно копнуть глубже, чем позволяет resmon, на отдельном клиенте.
На временной шкале каждый тик - это блок, и внутри него видно, какой ресурс и какая функция потратили время. Ищите широкие полосы: обработчик одного ресурса занимает десятки миллисекунд, а всё остальное - доли миллисекунды. Записывайте, пока проблема происходит: профиль тихого сервера в 4 утра ничего не доказывает. Если лаги приходят всплесками, записывайте больше тиков, чтобы всплеск попал в окно.
График производительности в txAdmin#
txAdmin ведёт непрерывную гистограмму времени тиков для потоков FXServer и рисует её на своей панели. В последних версиях можно переключаться между основным потоком, sync и network. Каждый столбец - это отрезок времени; цвета показывают, какая доля тиков попала в каждый диапазон длительности.
Здоровый сервер весь день проводит почти все тики в самом быстром диапазоне. Ищите хвост: небольшую, но постоянную долю медленных тиков, которая появляется в определённые часы или после определённого аптайма. Именно такую форму игроки ощущают как периодические подтормаживания, а не постоянный лаг, и она без всякого профайлера говорит вам две полезные вещи: когда это происходит (сопоставьте с числом игроков и с тем, что творилось в игре) и становится ли хуже с ростом аптайма - а это указывает на утечку или накопление сущностей, а не на один дорогой обработчик.
txAdmin также перезапускает сервер, у которого основной поток полностью перестал отвечать. Если это повторяется, следующий шаг - профайлер; увеличение таймаута зависания лишь удлиняет простой. Общая настройка txAdmin описана в статье о настройке сервера FiveM с txAdmin.
Шаблоны кода, которые всплывают каждый раз#
Если профилировать достаточно серверов FiveM, оказывается, что одна и та же горстка ошибок отвечает за большую часть потраченного времени.
Циклы, которые без причины работают в каждом кадре
Wait(0) означает «выполнить снова в следующем кадре». Это правильно для цикла, который что-то рисует в каждом кадре, и неправильно почти для всего остального. Типичный случай - маркер или точка взаимодействия, которую проверяют в каждом кадре, пока игрок находится на другом конце карты:
-- Тратит CPU в каждом кадре, в любой точке картыCreateThread(function() while true do Wait(0) local coords = GetEntityCoords(PlayerPedId()) if #(coords - shopCoords) < 2.0 then DrawText3D(shopCoords, "[E] Open shop") end endend)Исправление - спать пропорционально расстоянию и переходить на каждый кадр только тогда, когда игрок достаточно близко, чтобы это имело значение:
CreateThread(function() while true do local sleep = 1000 local coords = GetEntityCoords(PlayerPedId()) local dist = #(coords - shopCoords) if dist < 20.0 then sleep = 0 if dist < 2.0 then DrawText3D(shopCoords, "[E] Open shop") end end Wait(sleep) endend)Одно это изменение переводит ресурс с постоянных 0.1-0.3 ms на 0.00 для всех, кто не стоит рядом с магазином. Библиотеки вроде ox_lib дают помощники для точек и зон, которые делают это за вас; использовать их лучше, чем вручную писать один и тот же цикл в сорока ресурсах.
Проверки расстояния медленным способом
#(a - b) для двух значений vector3 - чистое вычисление на Lua, и оно очень дешёвое. GetDistanceBetweenCoords - это вызов native с накладными расходами на маршалинг, и старые ресурсы вызывают его сотни раз за кадр. Замена - механическая правка с измеримым результатом.
Серверные обработчики, которые делают слишком много
На сервере аналогичные ошибки - цикл по всем игрокам с запросом к базе данных внутри, цепочка SetTimeout, которая срабатывает чаще, чем меняется состояние, которое она отражает, и обработчики, которые делают json.encode большой таблицы при каждом вызове. oxmysql асинхронный, поэтому медленный запрос сам по себе поток не заморозит, а вот обработчик, отправляющий пятьдесят запросов на одно событие, заморозит - и очередь колбэков, которые возвращаются позже, тоже попадает в основной поток. Если задать mysql_slow_query_warning, oxmysql будет выводить каждый запрос дольше порога; про сторону базы данных - в статье о базах данных FiveM и oxmysql.
Слишком много рассылок
TriggerClientEvent('name', -1, data) отправляет событие каждому игроку. Делать так раз в секунду с большой таблицей - полным списком работы, деньгами каждого игрока - значит тратить время network thread и время клиента на каждой машине. Отправляйте только то, что изменилось, только тем игрокам, которым это нужно, а для значений, которые многие клиенты читают, но немногие пишут, предпочитайте state bags.
Порядок действий, который находит причину за вечер#
- Подтвердите уровень. Спросите трёх игроков, упал ли у них FPS или их откидывало. Проверьте консоль на hitch warning в те моменты, о которых они говорят.
- Клиентская сторона: попросите одного игрока запустить
resmon 1и сделать скриншот, пока он стоит без дела в городе, и ещё раз - в том месте, где лагает. Запишите каждый ресурс выше0.1 ms. - Серверная сторона: выполните
profiler record 500в часы пик, пока лаг есть, затемprofiler view. Запишите самые широкие ресурсы. - Делите пополам то, что не можете прочитать. Если профиль указывает на ядро фреймворка, в которое обращаются десятки ресурсов, остановите половину необязательных ресурсов (
stop nameв консоли), понаблюдайте за hitch warning и графиком txAdmin пятнадцать минут, затем верните их. Повторите на той половине, которая оказалась важна. - Исправьте или замените. Большинство исправлений - это шаблоны циклов, описанные выше. Ресурсы под escrow, которые вы не можете редактировать, возвращаются автору с приложенным профилем или заменяются.
- Измерьте заново. Те же условия, те же команды. Если цифра не сдвинулась, изменение ничего не дало, что бы ни обещал пост на форуме.
Сделайте это до смены тарифа. На сервере, где основной поток упирается в CPU, больше памяти не даёт вообще ничего, а большая доля CPU помогает, только если график CPU в консоли показывает, что вы действительно упираетесь в лимит. Подробнее этот аргумент разобран в статье CPU или RAM для игровых серверов.
Что добавляет сама машина#
Обычно причина в коде, но не всегда, и полезно знать, как выглядит аппаратная сторона, чтобы подтвердить её или исключить.
| Ресурс | На что влияет | Признак, что упираетесь именно в него |
|---|---|---|
| Доля CPU | Время тика в каждом потоке | График CPU в консоли ровно стоит на лимите тарифа |
| Память | Ни на что, пока не кончится | График памяти с ростом аптайма ползёт к лимиту |
| Диск | Запуск, перезапуск ресурсов, стриминг | Медленная загрузка, медленные первые входы |
| Сеть | Входы и синхронизация | Задержки network thread при низком CPU |
- Доля CPU. Работа сервера определяется основным потоком, поэтому частота важнее числа ядер. Второе ядро всё же помогает: sync и network thread, раздача ассетов и клиент базы данных работают в других местах. Если в пик график CPU стоит на потолке тарифа, тики растягиваются, а следом идут hitch warning.
- Память. Память зависит от числа ресурсов и того, что они хранят, а не от числа игроков. Она редко бывает причиной лагов; когда она заканчивается, сервер останавливается, а это уже другая проблема.
- Сущности. С OneSync каждая машина, пед и объект, которые отслеживает сервер, стоят времени синхронизации. Фоновое население, брошенные машины, которые никто не удаляет, и пропы, заспавненные скриптами и так и не убранные, - всё это копится. Настройки населения и очистка описаны в статье OneSync и слоты игроков.
- Стриминговые ассеты. Большие паки машин и карт стоят места на диске, трафика и памяти клиента, но не CPU сервера. Медленный первый вход - это проблема загрузки; ей посвящена статья о MLO, картах и стриминговых ассетах.
На RE:NODE консоль рисует память, CPU и диск относительно лимитов тарифа, так что вы видите, во что упираетесь на самом деле, прежде чем что-то решать. CPU жёстко ограничен купленной долей: сервер, прижатый к 100%, работает медленнее, но за это его никогда не приостанавливают. Если память достигает лимита, контейнер останавливается и перезапускается начисто, а не уходит в swap, - дальше txAdmin и ваши плановые рестарты продолжают работу.
Аптайм, утечки и плановые рестарты#
FXServer и фреймворки на нём деградируют при долгом аптайме. Lua-кучи растут, число сущностей ползёт вверх, кэши заполняются. Сервер, который хорошо работает первые шесть часов и подтормаживает ко второму вечеру, показывает накопление, а не один дорогой обработчик, и профайлер покажет много ресурсов, каждый немного медленнее, а не одну широкую полосу.
Честные решения - найти ресурс, чья память растёт в resmon или в его собственных логах, и пока вы ищете, перезапускать сервер по расписанию. Рестарт каждые шесть-двенадцать часов с объявлением в игре за несколько минут - обычная практика на ролевых серверах; планировщик txAdmin делает и объявление, и рестарт, а о том, как выбрать час, написано в статье о расписаниях рестартов, которые помогают. Перед каждым плановым рестартом также самое время сделать backup базы данных, потому что персонажи и деньги хранятся там.
Рестарт - не исправление утечки. Это потолок того, насколько плохо утечка может стать. Продолжайте искать ресурс.
Устранение неполадок#
resmon не показывает ничего дорогого, но игроков всё равно откидывает. Дело в сервере или сети. Проверьте hitch warning в моменты жалоб и запустите профайлер.
Профайлер показывает, что один ресурс фреймворка занимает большую часть времени. Обычно фреймворк делает работу от имени других ресурсов - колбэки, использование предметов, поиск данных игрока. Посмотрите на уровень ниже на временной шкале, какие обработчики в него обращаются, или поделите пополам зависящие от него ресурсы.
Задержки каждые несколько минут, как по часам. Что-то работает по таймеру: автосохранение, которое пишет всех игроков сразу, цикл выплат зарплаты, задача сохранения машин. Распределите работу по игрокам, а не делайте всё в одном тике.
Лаги только в первые минуты после рестарта. Ресурсы загружают данные, все одновременно перезаходят и скачивают ассеты. Это проходит; если нет - какой-то ресурс делает большую синхронную загрузку при старте.
Лаги усиливаются с каждым днём до рестарта. Утечка или накопление сущностей. Следите за памятью в resmon и за sync thread по мере роста аптайма.
Один ресурс дорогой и под escrow. Редактировать его нельзя. Отправьте автору профиль и скриншот resmon. Если исправления не будет, заменить ресурс дешевле, чем строить сервер вокруг него.
FAQ#
Какое значение resmon считается хорошим для ресурса FiveM?
В простое - от 0.00 до 0.02 ms. Ресурсы, которые рисуют в каждом кадре, например HUD, вполне могут держаться немного выше. Ресурс, который в простое выше 0.1 ms, тратит время кадра впустую, а тот, что регулярно превышает 0.5 ms, - проблема сам по себе.
Показывает ли resmon производительность сервера?
Нет. resmon измеряет клиентские скрипты на той машине, где вы его запускаете. Для сервера используйте hitch warning в консоли, profiler record и profiler view, а также график тиков в txAdmin.
Исправит ли лаги FiveM тариф побольше?
Только если консоль показывает, что CPU в пик упирается в лимит тарифа. Большинство лагов FiveM - это один-два ресурса, блокирующих основной поток, и больше памяти или более высокий лимит оставляют это ровно как есть. Сначала профилируйте, потом решайте.
Что означает «server thread hitch warning»?
Основной поток сервера, в котором работают ресурсы, потратил между тиками гораздо больше времени, чем обычно, - число показывает, сколько именно. Несколько таких при запуске безвредны; постоянный поток, пока люди играют, означает, что какой-то ресурс блокирует поток, и его нужно найти профайлером.
Почему сервер становится медленнее, чем дольше он работает?
Обычно растёт память в ресурсе с утечкой или копятся сущности без очистки. Плановые рестарты ограничивают ущерб, но причина остаётся; чтобы её найти, следите за памятью каждого ресурса во времени.
Всегда ли Wait(0) - это плохо?
Нет. Он правилен для всего, что должно происходить в каждом кадре, например для отрисовки текста или маркера, на который смотрит игрок. Он неправилен в цикле, которому достаточно реагировать несколько раз в секунду, - а это описывает большинство циклов, где его используют.




Комментарии
Полностью анонимно: без аккаунта, без почты, без cookie. Мы храним имя, которое вы ввели, текст и время - больше ничего. Количество ссылок ограничено, разметка не отображается.