Экспорты FiveM в Lua: exports('name', fn) и 'No such export'

Как определять и вызывать экспорты в FiveM Lua, экспорты fxmanifest и стили server_exports, и как исправить ошибки 'No such export', вызванные порядком запуска или неправильной стороной.

Скрипт вызывает другой и консоль отвечает:

text
No such export getCount in resource my_inventory

Эта статья показывает, как экспорты определяются и вызываются в Lua, два способа их объявления и обычные причины появления этой ошибки.

Определение экспорта

Экспорт позволяет одному ресурсу предложить функцию другим. Простой способ — функция exports внутри скрипта:

lua
-- my_inventory/server/main.lua
local function getCount(source, item)
    -- ...
    return 5
end

exports('getCount', getCount)

Вы также можете передать функцию встроенным образом:

lua
exports('getCount', function(source, item)
    return 5
end)

Это всё. Нечего не добавляется в fxmanifest.lua, и экспорт существует на той стороне, где запускается скрипт: поместите его в серверный скрипт, и это серверный экспорт, поместите его в клиентский скрипт, и это клиентский экспорт.

Стиль манифеста

Более старые ресурсы объявляют экспорты в fxmanifest.lua и определяют простую глобальную функцию с тем же именем:

lua
-- fxmanifest.lua
fx_version 'cerulean'
game 'gta5'

server_exports { 'getCount' }
exports { 'getOwnedCount' }
lua
-- server script
function getCount(source, item)
    return 5
end
  • exports { … } перечисляет клиентские экспорты.
  • server_exports { … } перечисляет серверные экспорты.

Это все еще работает, и многие ESX и QBCore ресурсы это используют. Два стиля не конфликтуют, поэтому ресурс может их смешивать. Для нового кода exports('name', fn) проще, потому что функция и экспорт живут вместе, и вы не можете забыть строку в манифесте. Помните, что fxmanifest.lua — это текущее имя манифеста: смотрите fxmanifest vs __resource.lua.

Вызов экспорта

Из любого другого ресурса на той же стороне:

lua
local count = exports['my_inventory']:getCount(source, 'water')

Точка и версии двоеточия — это одно и то же, если имя — действительный идентификатор Lua:

lua
local count = exports.my_inventory:getCount(source, 'water')

Используйте форму в скобках, когда имя ресурса имеет тире, такое как exports['qb-core']:GetCoreObject(). Вызов синхронный: он возвращает значение функции и может выжидать, если функция ждет.

Совет: аргументы и возвращаемые значения путешествуют между ресурсами, поэтому таблицы копируются. Изменение таблицы, которую вы получили от экспорта, не меняет данные другого ресурса. Функции внутри возвращаемой таблицы все еще работают, так как они вызывают обратно в владельца.

Почему вы получаете "No such export"

text
No such export getCount in resource my_inventory

Проверьте это, в этом порядке:

  1. Ресурс не запущен. Откройте консоль и запустите ensure my_inventory. Если это не запустится, сначала исправьте это.
  2. Он запустился после вызывающего. Экспорт существует только один раз, когда владелец загрузил его скрипты. Смотрите start order in server.cfg и поместите владельца выше скриптов, которые его используют.
  3. Имя неправильно написано. Имена экспортов чувствительны к регистру: getCount и GetCount разные.
  4. Неправильная сторона. Серверный скрипт не может вызвать клиентский экспорт, и наоборот. Ошибка выглядит одинаково.
  5. Экспорт объявлен, но функция отсутствует. При стиле манифеста server_exports { 'getCount' } нужна глобальная функция, называемая getCount на сервере. Локальная функция не считается.
  6. Владелец не сработал или был остановлен. Если он разбился на ошибку скрипта или вы его остановили, его экспорты исчезают до запуска снова. Прочитайте его вывод при запуске в консоли.

Для реальных версий этой ошибки смотрите No such export GetSharedObject in resource es_extended и QBCore is nil: GetCoreObject.

Сделайте порядок уверенным с зависимостями

Не полагайтесь только на порядок строк ensure. Объявите зависимость в манифесте скрипта, который вызывает экспорт:

lua
dependency 'my_inventory'

FiveM затем запускает my_inventory первым, и отказывается запустить ваш ресурс, если он отсутствует, с более четким сообщением (could not find dependency).

Обработка отсутствующего ресурса с грацией

Если другой ресурс опционален, проверьте его состояние перед вызовом:

lua
if GetResourceState('my_inventory') == 'started' then
    local count = exports.my_inventory:getCount(source, 'water')
end

GetResourceState возвращает строку, такую как started, stopped, starting или missing. Это чистый способ поддержки опциональной интеграции без ошибки консоли.

Вызов ваших собственных экспортов

Ресурс может вызвать свои собственные экспорты тоже, с его собственным именем: exports['my_inventory']:getCount(...). Для функции, которая используется только внутри ресурса, обычная локальная функция быстрее и проще. Экспорты — для границы между ресурсами.

Слово о производительности

Каждый вызов пересекается между двумя ресурсами, поэтому он медленнее обычного вызова функции. Не вызывайте экспорт в плотном цикле за кадр, если вы можете прочитать значение один раз и сохранить его. Вызывание одного несколько раз в секунду нормально.

Контрольный список

Симптом Исправление
No such export x in resource y Запустите y и поместите его перед вызывающим в server.cfg
Работает на сервере, не работает на клиенте Определите экспорт в клиентском скрипте тоже, или вызовите его с сервера
Экспорт стиля манифеста отсутствует Определите глобальную функцию с тем же именем, что и запись манифеста
Регистр имени отличается Совпадайте написание точно
Имя ресурса имеет тире exports['my-resource']:name()
Опциональная интеграция спамит консоль Сначала проверьте GetResourceState('x') == 'started'

Короткие ответы

Как мне вызвать экспорт из другого ресурса?

Используйте exports['resource_name']:exportName(args) или exports.resource_name:exportName(args). Другой ресурс должен быть запущен и должен определить этот экспорт на той же стороне (клиент или сервер).

Нужны ли мне экспорты в fxmanifest.lua?

Не с exports('name', fn): он регистрирует экспорт из самого скрипта. Списки exports {} и server_exports {} в манифесте — это более старый стиль, где глобальная функция выставляется по имени.

Почему экспорт работает на клиенте, но не на сервере?

Клиентские и серверные экспорты отделены. Экспорт, определенный в клиентском скрипте, только существует на клиенте, и один, определенный в серверном скрипте, только на сервере.

Скрипты без этой проблемы

Item Creator V2Создавайте используемые предметы с анимациями, пропами, эффектами и не только — без кода.Смотреть скрипт →Mic PhoneСкладной телефон, который раскладывается в планшет и работает и на настоящем телефоне игрока.Смотреть скрипт →Shop CreatorМагазин меньше чем за минуту — владельцы, сотрудники, сейфы и ограбления в комплекте.Смотреть скрипт →

Читайте также