FiveM: конфликт ресурсов и скриптов — как найти и устранить
Поставил новый скрипт — и вместо радости получил спам ошибок в консоли, команду, которая выполняет не то, что должна, или сервер, который вообще не поднимается после ensure. Знакомая ситуация для любого, кто хоть раз набирал resources/ до отказа. Хорошая новость: конфликт ресурсов почти всегда диагностируется по одной и той же схеме, без гадания — разберём, откуда берутся такие конфликты и как вычислить виновника, даже если у тебя установлено полсотни скриптов.
Содержание
- Как понять, что дело именно в конфликте, а не в баге одного скрипта
- Дублирующиеся команды и экспорты
- Дублирующиеся и пересекающиеся события
- Ключевая проблема: ESX и QBCore, запущенные одновременно
- Порядок `ensure` в server.cfg — почему он не косметика
- Метод бинарного поиска: как найти виновника среди полусотни ресурсов
- Что проверить в манифесте ресурса перед установкой
Как понять, что дело именно в конфликте, а не в баге одного скрипта
Первый сигнал — проблема появилась не сразу, а после установки нового ресурса или после смены версии одного из старых. Если сервер работал нормально месяц, а после ensure new_script в консоли посыпались ошибки в совершенно другом, ранее рабочем ресурсе — это почти наверняка конфликт, а не самостоятельный баг.
Второй сигнал — команда или функция срабатывает не так, как ожидалось, но без явной ошибки в консоли. Например, ты набираешь /car и вместо спавна машины из одного скрипта получаешь поведение другого — это классика при дублирующихся RegisterCommand.
Третий сигнал — ошибки вида attempt to call a nil value, attempt to index a nil value (global 'ESX') или QBCore is not defined в скриптах, которые раньше работали. Это почти всегда говорит о проблеме с порядком загрузки или о том, что фреймворк, под который писан ресурс, на сервере вообще не запущен (или запущен не тот).
Смотреть стоит в двух местах: серверная консоль (или вкладка Console в txAdmin) — там видны ошибки Lua и предупреждения о ресурсах; и клиентский F8-консоль вместе с resmon — там ловятся клиентские ошибки скриптов и падение FPS от конкретного ресурса. Если ты ещё не разбирался с базовой установкой — вот тут описан подъём FiveM-сервера с нуля, включая структуру resources/ и server.cfg.
Дублирующиеся команды и экспорты
Самый частый и самый тихий конфликт — два ресурса регистрируют одну и ту же чат-команду через RegisterCommand('give', ...). FiveM не всегда явно ругается на это в консоли — часто просто побеждает тот обработчик, что зарегистрировался последним по порядку загрузки, а первый молча перестаёт срабатывать. Разработчик первого скрипта может решить, что у него сломалась логика, хотя на деле команду просто перехватил другой ресурс.
Найти дубликаты можно быстро, не перебирая ресурсы вручную — грепом по всей папке resources/:
grep -rn "RegisterCommand(" resources/ --include="*.lua" | grep -oP "RegisterCommand\('\K[^']+" | sort | uniq -d
Команда выведет только те названия команд, которые встречаются больше одного раза — это и есть кандидаты на конфликт. Аналогично можно проверить экспорты внутри одного ресурса, если в нём несколько файлов:
grep -rn "exports(" resources/my_resource/ | grep -oP "exports\('\K[^']+"
Важный нюанс: экспорты в FiveM намespaced по имени ресурса (exports['resource_a']:GetSomething()), поэтому два *разных* ресурса с одинаковым именем функции внутри exports друг другу не мешают — конфликт возникает, только если внутри одного и того же ресурса функция с одинаковым именем регистрируется дважды (второй вызов exports() перезатирает первый).
Поднять сервер FiveM (GTA V) за пару минут
Готовый образ MAATRIX GAMES: NVMe, AMD EPYC, DDoS-защита, панель управления. Локации UK, US, RU. Оплата картой РФ, СБП или криптой.
Создать серверДублирующиеся и пересекающиеся события
С событиями (AddEventHandler, RegisterNetEvent) ситуация тоньше, чем с командами: FiveM позволяет вешать на одно и то же имя события сколько угодно обработчиков, и по умолчанию сработают все они по очереди — это нормальная и рабочая механика, а не баг. Проблема начинается, когда два ресурса вешаются на одно и то же имя события, но ожидают разную структуру данных в аргументах.
Типичный сценарий: кастомный скрипт инвентаря шлёт TriggerEvent('inventory:itemUsed', itemName), а другой ресурс уже слушает событие с точно таким же именем, но из другого, несовместимого инвентаря, и ждёт таблицу с другими полями. Оба обработчика отработают, но один из них упадёт с ошибкой обращения к несуществующему полю.
Ищутся такие пересечения тем же способом, что и команды:
grep -rn "RegisterNetEvent(" resources/ --include="*.lua" | grep -oP "RegisterNetEvent\('\K[^']+" | sort | uniq -d
Если находишь совпадение между двумя явно не связанными ресурсами (не форк одного и того же скрипта, не части одного пакета) — это повод открыть оба файла и свериться, что за данные каждый из них реально передаёт и ожидает.
Ключевая проблема: ESX и QBCore, запущенные одновременно
Отдельная и самая болезненная категория конфликтов — попытка держать на одном сервере одновременно es_extended (ESX) и qb-core (QBCore). Это два самостоятельных фреймворка, каждый со своим объектом игрока, своей структурой инвентаря и своим набором глобальных функций (ESX.GetPlayerFromId() против QBCore.Functions.GetPlayer()). Подробное сравнение того, чем они отличаются архитектурно, разбирали в статье про ESX и QBCore — здесь же про то, что бывает, если попытаться запустить оба сразу.
Такое иногда происходит не специально, а по ошибке: скачал «универсальный» пак ресурсов с форума, в котором часть скриптов ESX, часть — QBCore, и ensure их все скопом. Оба core пытаются занять одни и те же роли — регистрируют похожие по смыслу, но несовместимые команды и события, каждый пытается управлять инвентарём и деньгами игрока по-своему. Результат — постоянные ошибки в консоли у ресурсов, написанных под «не тот» фреймворк, и рассинхрон данных игрока.
Держать оба ядра запущенными одновременно без специальной прослойки не стоит — выбирай один core под фактический набор ресурсов, которые собираешься использовать. Существуют совместимые слои и bridge-ресурсы, которые позволяют части community-скриптов работать поверх произвольного core, но это отдельная, более сложная настройка, а не то, что стоит городить, если цель — просто быстро запустить сервер.
Порядок `ensure` в server.cfg — почему он не косметика
Многие резко забивают на порядок строк ensure в server.cfg, потому что на первый взгляд кажется — какая разница, все ресурсы всё равно запустятся. Разница есть, и она критична для ресурсов с зависимостями. Если скрипт вызывает exports['es_extended']:GetSharedObject() при своей загрузке, а es_extended в этот момент ещё не поднят — вызов упадёт с ошибкой, потому что ресурса с таким именем ещё не существует в памяти сервера.
Часть современных ресурсов решает это через fx_manifest.lua, объявляя зависимость явно:
fx_version 'cerulean'
game 'gta5'
dependency 'oxmysql'
dependency 'es_extended'
server_scripts { 'server.lua' }
Директива dependency заставляет FiveM подгрузить нужный ресурс раньше, даже если в server.cfg он записан позже. Но так делают не все авторы, особенно в старых или самописных ресурсах — поэтому базовое правило безопаснее соблюдать руками:
# базовые зависимости — всегда в начале
ensure oxmysql
ensure ox_lib
# фреймворк
ensure es_extended
# ресурсы, зависящие от фреймворка
ensure esx_multicharacter
ensure esx_menu_default
ensure esx_society
# остальные пользовательские скрипты
ensure my_custom_garage
Общий принцип: сначала база данных (oxmysql/mysql-async), затем общие библиотеки (ox_lib), затем фреймворк, затем всё, что от него зависит, и только потом самостоятельные скрипты без внешних зависимостей. Если после ensure какого-то ресурса в консоли вылетает ошибка про nil value при обращении к функциям фреймворка — в первую очередь проверь именно порядок строк, а не сам код ресурса.
Метод бинарного поиска: как найти виновника среди полусотни ресурсов
Когда конфликт есть, но непонятно, между какими именно ресурсами — не нужно проверять их по одному. Быстрее и надёжнее закомментировать примерно половину строк ensure в server.cfg, перезапустить сервер и посмотреть, воспроизводится ли проблема.
Порядок действий:
- Сделай копию рабочего
server.cfgперед экспериментами —cp server.cfg server.cfg.bak. - Раздели список
ensureпополам. Первую половину закомментируй символом#в начале строки. - Перезапусти сервер и проверь, повторяется ли ошибка.
- Если ошибка исчезла — виновник в закомментированной половине. Если осталась — виновник во второй половине, которая осталась активной.
- Повтори деление пополам уже внутри виновной половины, пока не останется 1-2 ресурса.
Для ускорения итераций необязательно перезапускать весь сервер целиком: пока сервер поднят, можно включать и выключать ресурсы прямо из консоли без даунтайма остальных игроков —
stop resource_name
start resource_name
или, если менял код внутри ресурса, refresh (перечитать список ресурсов) и затем ensure resource_name заново. Это особенно удобно, когда тестируешь конфликт на живом сервере с онлайном и не хочешь дёргать всех разом полным рестартом.
Если конфликт клиентский (просадка FPS, визуальные баги, ошибки в F8-консоли), тот же принцип бинарного поиска работает и там — только смотреть нужно клиентский resmon (открывается по F3 в дебаг-режиме или через консольную команду) и клиентскую консоль F8, а не серверный лог. Список активных ресурсов на сервере в любой момент можно получить командой resources в серверной консоли — она покажет статус каждого (started, stopped, starting), что тоже помогает быстро понять, не завис ли какой-то ресурс на старте из-за ожидания зависимости.
Что проверить в манифесте ресурса перед установкой
Часть конфликтов можно предотвратить ещё до ensure, если пробежаться по fx_manifest.lua нового ресурса. Смотри на три вещи: во-первых, fx_version и game 'gta5' — устаревший или нестандартный манифест может вести себя непредсказуемо на актуальном артефакте сервера. Во-вторых, секции dependency, если они есть — это прямая подсказка, что ресурс ожидает конкретный фреймворк или библиотеку, и без них лучше не ставить. В-третьих, открой client_scripts/server_scripts и глазами пробеги имена функций — если видишь сплошь ESX. или QBCore., а у тебя на сервере другой core, ресурс без адаптации не заработает вообще, вне зависимости от порядка загрузки.
Если ставишь ресурс с GitHub без внятного README — не поленись открыть основной серверный файл и поискать явные упоминания фреймворка или зависимостей прямо в коде, прежде чем тратить время на ensure и разбор ошибок постфактум. Более подробно о самом процессе установки кастомных скриптов и типичных ошибках на этом этапе — в статье про установку кастомных скриптов на FiveM.
Поднять сервер FiveM (GTA V) за пару минут
Готовый образ MAATRIX GAMES: NVMe, AMD EPYC, DDoS-защита, панель управления. Локации UK, US, RU. Оплата картой РФ, СБП или криптой.
Создать серверЧастые вопросы
Сервер не падает, но в консоли постоянно скроллится один и тот же warning — это конфликт?
Не обязательно критично, но игнорировать не стоит: часто это как раз повторяющийся вызов сломанного обработчика события или устаревший API, который скоро уберут из артефакта. Найди источник по тексту warning через grep по resources/ и обнови или замени ресурс.
Можно ли одновременно держать esx_вариант и «универсальные» ресурсы под оба фреймворка?
Технически некоторые современные ресурсы пишутся с поддержкой обоих core через условные проверки внутри кода, но это исключение, а не правило — по умолчанию считай, что ресурс работает только под тот фреймворк, под который написан явно.
После обновления одного ресурса всё сломалось — как понять, в нём ли дело?
Временно откати ресурс до предыдущей версии (если сохранил бэкап папки) и сравни поведение. Если проблема исчезла — смотри changelog новой версии на предмет изменившихся имён событий или экспортов, это частая причина после мажорных обновлений.
Обязательно ли использовать dependency в fx_manifest для собственных ресурсов?
Не обязательно, но настоятельно рекомендуется, если ресурс дёргает функции другого ресурса при старте — это избавляет от необходимости вручную следить за порядком строк в server.cfg и защищает от поломки при случайной перестановке ensure другим админом.
Есть ли способ автоматически проверять конфликты перед запуском сервера?
Готового встроенного линтера под все виды конфликтов в FiveM нет, но связку из grep-проверок на дублирующиеся команды/события (см. выше) можно оформить в простой shell-скрипт и гонять перед каждым добавлением новых ресурсов — это займёт пару секунд и снимет львиную долю ручной диагностики.