ox_lib Руководство: Обязательная библиотека FiveM для разработчиков
Изучите ox_lib с нуля. Уведомления, контекстные меню, обратные вызовы, зоны и экспорты, от которых зависят современные скрипты FiveM, с реальными примерами кода.
Agency Scripts
Основатель и ведущий разработчик Agency Scripts
Что такое ox_lib и почему стоит его использовать?
ox_lib , это библиотека утилит с открытым исходным кодом для FiveM, которая стала де-факто стандартом для современного скрипт-девелопмента. Она предоставляет огромную коллекцию готовых UI-компонентов, утилит и инструментов производительности, избавляя от необходимости изобретать велосипед для каждого скрипта. До появления ox_lib разработчикам приходилось создавать собственные системы уведомлений, диалоги ввода, прогресс-бары и контекстные меню с нуля, что часто приводило к несогласованному UI в разных скриптах на одном сервере. ox_lib решает эту проблему, предоставляя единый, отшлифованный набор компонентов, которые выглядят профессионально и работают надежно из коробки. Она поддерживает Lua и JavaScript, работает с любым фреймворком (QBCore, ESX или standalone) и активно поддерживается командой Overextended. Если вы пишете скрипты для FiveM в 2026 году и не используете ox_lib, вы тратите лишнее время на создание того, что уже существует.
Настройка ox_lib в вашем ресурсе
Добавление ox_lib в ваш ресурс требует всего два шага: добавление зависимости в ваш fxmanifest.lua и вызов библиотеки в ваших скриптах. The @ox_lib/init.lua import даёт доступ ко всем общим утилитам, в то время как модульная система позволяет выборочно загружать только нужные функции. Это сохраняет ресурс лёгким, так как неиспользуемые модули никогда не загружаются. Убедитесь, что ox_lib запускается перед вашим ресурсом в server.cfg, разместив ensure ox_lib над вашими пользовательскими ресурсами. Вот минимальная настройка для нового ресурса, использующего ox_lib:
-- fxmanifest.lua
fx_version 'cerulean'
game 'gta5'
name 'my-awesome-script'
version '1.0.0'
-- Required: import ox_lib
shared_scripts {
'@ox_lib/init.lua',
'config.lua',
}
client_scripts {
'client/*.lua',
}
server_scripts {
'server/*.lua',
}
-- Declare ox_lib as a dependency
dependencies {
'ox_lib',
}
-- Enable ox_lib locale system (optional)
lua54 'yes'
Уведомления: Чистые, последовательные оповещения
Уведомления ox_lib заменяют уродливые стандартные сообщения чата и пользовательские NUI всплывающие окна, которые используют большинство скриптов. Они появляются как стильные toast-сообщения с иконками, цветами и автоматическим закрытием. Вы можете задать позицию, длительность, тип (success, error, warning, info) и даже добавить описание под заголовком. Система уведомлений работает только на стороне клиента и невероятно легкая, практически не добавляя нагрузки на скрипт. Уведомления , самая часто используемая функция ox_lib и должны быть вашим стандартным способом общения с игроками в любом скрипте.
-- client.lua: Notification examples
-- Simple notification
lib.notify({
title = 'Vehicle Stored',
description = 'Your vehicle has been stored in the garage.',
type = 'success', -- 'success' | 'error' | 'warning' | 'info'
duration = 5000, -- milliseconds
position = 'top-right', -- 'top' | 'top-right' | 'top-left' | 'bottom' | 'bottom-right' | 'bottom-left'
})
-- Error notification with icon
lib.notify({
title = 'Access Denied',
description = 'You do not have the required key.',
type = 'error',
icon = 'lock',
iconColor = '#ff4444',
})
-- Notification from server side
-- server.lua
RegisterNetEvent('garage:store', function()
local src = source
TriggerClientEvent('ox_lib:notify', src, {
title = 'Garage',
description = 'Vehicle stored successfully.',
type = 'success',
})
end)
Контекстные меню: интерактивные списки опций
Контекстные меню , это прокручиваемые списки опций, по которым игроки могут кликать для запуска действий. Они идеально подходят для меню профессий, интерфейсов магазинов, опций транспорта и любых сценариев, где игроку нужно выбрать из нескольких действий. Каждый пункт меню может иметь иконку, описание, метаданные, отображаемые справа, и вложенные подменю для организации сложных деревьев опций. Меню остаётся открытым, пока игрок явно не закроет его или не выберет пункт без подменю, что делает его идеальным для просмотра категорий предметов. Контекстные меню также могут динамически генерироваться на основе данных сервера, позволяя создавать меню магазинов с актуальным инвентарём из вашей базы данных.
-- client.lua: Context menu examples
-- Simple shop menu
lib.registerContext({
id = 'weapons_shop',
title = 'Ammu-Nation',
options = {
{
title = 'Pistol',
description = 'Standard 9mm handgun',
icon = 'gun',
metadata = {
{label = 'Price', value = '$2,500'},
{label = 'Ammo', value = '12 rounds'},
},
onSelect = function()
TriggerServerEvent('shop:buy', 'weapon_pistol')
end,
},
{
title = 'Body Armor',
description = 'Standard kevlar vest',
icon = 'shield',
metadata = {
{label = 'Price', value = '$5,000'},
{label = 'Protection', value = '50%'},
},
onSelect = function()
TriggerServerEvent('shop:buy', 'armor')
end,
},
{
title = 'Attachments',
description = 'Browse weapon modifications',
icon = 'wrench',
arrow = true, -- Shows arrow indicating submenu
menu = 'attachments_submenu',
},
},
})
lib.showContext('weapons_shop')
Прогресс-бары и прогресс-круги
Прогресс-бары дают визуальную обратную связь во время таймированных действий, таких как взлом замков, крафт, ремонт транспорта или приготовление еды. ox_lib предоставляет как линейный бар, так и круговой индикатор. Во время анимации прогресса можно отключить управление игроком, например движение, бой и посадку в машину, чтобы предотвратить эксплойты. Также можно прикрепить анимацию и объект к игроку, чтобы он визуально выполнял действие, пока заполняется бар. Функция возвращает true если игрок отменил действие (например, перемещением) и false если операция выполнена успешно. Всегда проверяйте это возвращаемое значение, чтобы избежать выдачи предметов или завершения действий, которые игрок прервал.
-- client.lua: Progress bar examples
-- Linear progress bar with animation
local cancelled = lib.progressBar({
duration = 8000,
label = 'Lockpicking door...',
useWhileDead = false,
canCancel = true,
disable = {
car = true,
move = true,
combat = true,
},
anim = {
dict = 'anim@amb@clubhouse@tutorial@bkr_tut_ig3@',
clip = 'machinic_loop_mechandler',
},
prop = {
model = 'prop_lockpick_01',
bone = 57005,
pos = vec3(0.14, 0.0, -0.01),
rot = vec3(0.0, 0.0, 0.0),
},
})
if cancelled then
lib.notify({ title = 'Cancelled', type = 'error' })
else
lib.notify({ title = 'Door Unlocked', type = 'success' })
TriggerServerEvent('lockpick:success', doorId)
end
-- Circular progress (useful for quick actions)
if lib.progressCircle({
duration = 2000,
label = 'Searching...',
position = 'bottom',
useWhileDead = false,
canCancel = true,
disable = { move = true },
}) then
lib.notify({ title = 'Search cancelled', type = 'error' })
else
TriggerServerEvent('search:complete')
end
Диалоги ввода: сбор данных игроков
Диалоги ввода позволяют собирать введённый текст, числа, выбор из выпадающего списка, флажки, выбор цвета, даты и значения слайдера от игроков через чистый модальный интерфейс. Это необходимо для скриптов, которым нужен ввод от игроков, например, установка цены дома, ввод номера автомобиля, название банды или настройка параметров профессии. Каждое поле ввода имеет метку, необязательное описание, обязательный флаг и типоспецифические опции, такие как мин/макс значения для чисел или предопределённые варианты для выпадающих списков. Функция возвращает nil если игрок отменяет диалог и массив значений в порядке полей, если он отправляет данные. Всегда проверяйте возвращаемые данные как на клиенте, так и на сервере, чтобы предотвратить эксплойты.
-- client.lua: Input dialog examples
-- Vehicle listing form
local input = lib.inputDialog('List Vehicle for Sale', {
{ type = 'input', label = 'Title', description = 'Name for the listing', required = true, max = 50 },
{ type = 'number', label = 'Price ($)', description = 'Asking price', required = true, min = 1000, max = 10000000 },
{ type = 'select', label = 'Condition', options = {
{ value = 'new', label = 'Brand New' },
{ value = 'used', label = 'Used - Good' },
{ value = 'damaged', label = 'Damaged' },
}},
{ type = 'textarea', label = 'Description', description = 'Describe your vehicle', max = 500 },
{ type = 'checkbox', label = 'I agree to the marketplace terms' },
})
if not input then return end -- Player cancelled
local title, price, condition, description, agreedTerms = table.unpack(input)
if not agreedTerms then
lib.notify({ title = 'You must agree to the terms', type = 'error' })
return
end
TriggerServerEvent('marketplace:list', {
title = title,
price = price,
condition = condition,
description = description,
})
Зоны: эффективное обнаружение области
Зоны ox_lib заменяют старый, неэффективный метод проверки позиции игрока каждый кадр с помощью GetEntityCoords и вычисления расстояния. Система зон использует оптимизированный алгоритм пространственного обнаружения, который проверяет координаты с настраиваемыми интервалами и вызывает колбэки входа/выхода при пересечении границ зон игроками. Вы можете определить зоны как сферы, коробки или полигоны, что делает их достаточно гибкими для всего , от маленьких точек взаимодействия до больших границ районов. Зоны поддерживают вращение, отладочную отрисовку для разработки и произвольные данные, передаваемые в колбэки. Для любого скрипта, которому нужно обнаруживать, когда игрок находится в определенной области, зоны ox_lib , самое производительное решение.
-- client.lua: Zone examples
-- Sphere zone for a shop entrance
local shopZone = lib.zones.sphere({
coords = vec3(25.7, -1347.3, 29.5),
radius = 3.0,
debug = true, -- Set false in production
onEnter = function(self)
lib.notify({ title = 'Press [E] to open shop', type = 'info' })
lib.showTextUI('[E] Open Shop', { position = 'right-center' })
end,
onExit = function(self)
lib.hideTextUI()
end,
})
-- Box zone with rotation for a parking spot
local parkingZone = lib.zones.box({
coords = vec3(215.3, -810.0, 30.7),
size = vec3(6.0, 3.0, 2.0),
rotation = 70.0,
debug = true,
onEnter = function(self)
lib.showTextUI('[E] Store Vehicle')
end,
onExit = function(self)
lib.hideTextUI()
end,
})
-- Clean up zones when resource stops
AddEventHandler('onResourceStop', function(resource)
if resource == GetCurrentResourceName() then
shopZone:remove()
parkingZone:remove()
end
end)
Кэш: умный доступ к данным
Параметр lib.cache модуль обеспечивает мгновенный доступ к часто необходимым данным игрока без вызова native каждый кадр. Значения, такие как cache.ped, cache.vehicle, cache.seat, cache.weapon , и cache.playerId автоматически обновляются ox_lib через слушатели событий, а не опрос. Это значит, что вы можете безопасно читать cache.vehicle в любом месте вашего кода без опасений за производительность. Кэш также генерирует события при изменении значений, так что вы можете регистрировать обработчики для ox_lib:cache:vehicle для реакции, когда игрок садится или выходит из транспортного средства. В сочетании с зонами и остальной частью ox_lib, система кэша позволяет писать чистый, событийно-ориентированный код вместо циклов опроса кадров, которые тратят ресурсы CPU на проверку условий, которые редко меняются.
-- client.lua: Cache examples
-- Access cached values (no native calls needed)
local myPed = cache.ped
local myVehicle = cache.vehicle -- nil if not in a vehicle
local mySeat = cache.seat -- -1 = driver, 0 = front passenger, etc.
local myWeapon = cache.weapon
-- React to vehicle changes
lib.onCache('vehicle', function(vehicle)
if vehicle then
-- Player entered a vehicle
local plate = GetVehicleNumberPlateText(vehicle)
lib.notify({
title = 'Vehicle',
description = 'Plate: ' .. plate,
type = 'info',
})
else
-- Player exited a vehicle
lib.notify({ title = 'On foot', type = 'info' })
end
end)
-- React to weapon changes
lib.onCache('weapon', function(weapon)
if weapon then
print('Player equipped weapon:', weapon)
end
end)