RE:NODE

Миграция с QBCore на Qbox: мост, инвентарь и база данных

Автор Команда RE:NODE
14 мин чтения


Перенос сервера с QBCore на Qbox - это в основном работа с базой данных и инвентарём, а не со скриптами. qbx_core объявляет provide 'qb-core' и содержит мост, который отвечает на старые вызовы exports['qb-core']:GetCoreObject(), поэтому большинство ресурсов qb-* для работ, магазинов и HUD запускаются без правок. Не переносится qb-inventory: Qbox работает только с ox_inventory и отказывается запускаться, пока работает qb-inventory, поэтому описания предметов, инвентарь каждого игрока, а также ваши stash, багажники и бардачки нужно конвертировать. Кроме того, qbx_core.sql меняет таблицу players и добавляет таблицу player_groups для multijob. Сначала сделайте всё это на копии сервера и базы, не трогайте старую папку resources/ и дамп базы - и сам переход займёт вечер, а не неделю.

Какой фреймворк выбрать и чем они отличаются, разобрано в статье FiveM frameworks: ESX vs QBCore vs Qbox. Здесь предполагается, что вы уже определились и у вас работает сервер на QBCore с реальными данными игроков, которые вы не хотите потерять.

Что на самом деле меняется между QBCore и Qbox#

Qbox - это продолжение QBCore, а не переписанный с нуля фреймворк, поэтому миграция вообще возможна. У объекта игрока по-прежнему есть PlayerData, citizenid, charinfo, money, job и gang. Таблица players сохранила имя и большинство столбцов. Изменилось всё, что находится вокруг ядра.

ОбластьQBCoreQbox
Ресурс ядраqb-coreqbx_core (также отвечает как qb-core)
Инвентарьqb-inventoryox_inventory, и ничего другого
Список предметовqb-core/shared/items.luaox_inventory/data/items.lua
Работы и бандыshared/jobs.lua, gangs.luaте же файлы, числовые ключи рангов
Multijobвнешний ресурсвстроен, таблица player_groups
Мультиперсонажqb-multicharacterвстроен в qbx_core
Target, меню, вводqb-target, qb-menu, qb-inputox_target и ox_lib

Основная часть вашей работы прячется в последних трёх строках. Qbox также требует, чтобы ox_lib и oxmysql были запущены первыми, чтобы был включён OneSync и использовалась свежая сборка FXServer. В манифесте текущей версии qbx_core в зависимостях указан /server:10731, а значит, на более старом артефакте он не запустится. Это число меняется от релиза к релизу, поэтому смотрите fxmanifest.lua в скачанной версии, а если ваш сервер отстаёт, читайте FiveM server updates and artifacts.

Как работает мост qb-core и где он заканчивается#

Мост - это набор файлов в qbx_core/bridge/qb/, которые собирают старый объект QBCore из собственных функций Qbox. Поскольку в манифесте qbx_core объявлено provide 'qb-core', ресурс, у которого qb-core указан в зависимостях, считается удовлетворённым, а exports['qb-core']:GetCoreObject() возвращает нечто, похожее на объект QBCore. Мост включён по умолчанию и управляется одной convar:

server.cfg
setr qbx:enableBridge "true"

Что продолжает работать через мост:

  • QBCore.Functions.GetPlayer(source) и методы игрока, которые от неё отходят: AddMoney, RemoveMoney, SetJob, SetMetaData и остальные.
  • Обычные серверные и клиентные события, которые слушает ресурс работы, например QBCore:Client:OnPlayerLoaded.
  • QBCore.Shared.Jobs, Gangs и Vehicles, которые читаются из собственных файлов Qbox.
  • Старые экспорты drawtext, exports['qb-core']:DrawText() и HideText(), теперь привязанные к текстовому UI из ox_lib.
  • Функции предметов у игрока, такие как Player.Functions.AddItem и RemoveItem: мост передаёт их в ox_inventory и переводит данные между двумя форматами предметов.

В документации Qbox фреймворк описан как примерно на 99% совместимый со скриптами QB, и для объекта ядра это справедливо. Мост заканчивается там, где код выходит за пределы ядра:

  • Всё, что напрямую обращается к `qb-inventory`. На exports['qb-inventory']:..., событие inventory:server:OpenInventory и события stash из qb-inventory никто не отвечает. Собственные функции инвентаря в мосте проверяют, что qb-inventory не запущен, и падают с ошибкой qb-inventory is not compatible with qbx_core. use ox_inventory instead.
  • `Player.Functions.SetInventory` явно не поддерживается и вызывает ошибку. Скрипты, которые перезаписывают инвентарь целиком, нужно переделать на схему «очистить, затем добавить».
  • Ресурсы, которые пишут SQL в таблицы QBCore, смысл которых изменился: старые таблицы stash, столбец job, который теперь перекрывает multijob, строки player_vehicles, которые гаражный ресурс ожидает в определённом виде.
  • Внешние ресурсы multijob и multigang. В Qbox есть собственные, а в документации предупреждается, что непроверенные могут повредить данные. Удалите их до первого запуска.
  • Экспорты `qb-target`. ox_target обеспечивает совместимость с qtarget, но не с qb-target. Ресурсу, который вызывает exports['qb-target']:AddBoxZone, нужен либо запущенный qb-target, либо перевод на ox_target.

Мост - это инструмент миграции, а не постоянный дом. В исходниках его функции помечены как устаревшие, а новый код должен обращаться к exports.qbx_core и ox_lib напрямую. Ресурсы можно переводить по одному, потому что вызовы через мост и нативные вызовы работают бок о бок.

Проведите аудит списка ресурсов, прежде чем что-либо трогать#

Откройте resources/ и разложите все папки в три стопки. Это час работы с текстовым файлом, и от него зависит, сколько времени займёт всё остальное.

  1. Работает без изменений. Ресурсы, которые используют только объект ядра, деньги игрока, работы и события. Большинство HUD, простые работы, магазины, продающие через AddItem, эмоции, погода, голос.
  2. Есть замена для Qbox или ox. Обычно это лучше, чем править код. Qbox поддерживает версии qbx_ для часто используемых ресурсов QB.
  3. Нужно править или убирать. Всё, что обращается к qb-inventory, активно использует qb-menu или qb-input либо пишет собственный SQL в таблицы инвентаря и транспорта.

Замены, которые встречаются почти на каждом сервере:

Ресурс QBCoreАналог в QboxПримечание
qb-inventoryox_inventoryОбязательно, данные нужно конвертировать
qb-targetox_targetЗоны нужно переписать
qb-menu, qb-inputox_libКонтекстные меню и диалоги ввода
qb-doorlockox_doorlockДвери вводятся заново
qb-multicharacterqbx_coreВстроен
qb-garagesqbx_garagesИспользует qbx_vehicles
qb-vehiclekeysqbx_vehiclekeys
qb-policejobqbx_police
qb-ambulancejobqbx_ambulancejobРаботает в паре с qbx_medical

Интеграция ox_inventory с Qbox также проверяет, что запущен qbx_vehicles, так что добавьте его, даже если оставляете собственный гаражный ресурс. С платными скриптами не делайте предположений, а загляните на страницу автора: многие заявляют о поддержке Qbox через переключатель в конфиге, а скрипт с escrow, который вы не можете редактировать, либо поддерживает Qbox, либо нет. Что в таких скриптах можно менять, а что нельзя, описано в FiveM escrow and asset licensing.

Самый быстрый способ собрать третью стопку - поиск. Запустите это из папки resources/ на копии сервера и прочитайте каждое совпадение:

bash
$ grep -rn --include=*.lua -e "qb-inventory" -e "qb-target" -e "SetInventory" .$ grep -rn --include=*.lua -e "stashitems" -e "trunkitems" -e "gloveboxitems" .$ grep -rn --include=*.lua -e "QBCore.Shared.Items\[" .

Последний запрос важен, потому что некоторые ресурсы добавляют предметы во время работы, записывая их в QBCore.Shared.Items. В Qbox предметы принадлежат ox_inventory, и такие добавления на лету ничего полезного не делают.

Сначала соберите тестовый сервер#

Не мигрируйте рабочий сервер на месте. Соберите второй сервер, дайте ему копию базы данных и копию resources/ и сначала выполните все шаги ниже на нём. Общий метод - что копировать, что должно отличаться, насколько маленьким он может быть - описан в running a test server beside production. Для этой миграции в частности:

  • Всегда отдельная база данных. Тестовый сервер должен указывать своим mysql_connection_string на копию, а не на боевую схему. Шаги SQL ниже изменяют таблицы, и случайный запуск их на живых данных - единственная ошибка, которая превращает вечер в выходные.
  • Свежий дамп боевой базы для наполнения. Используйте mysqldump с --single-transaction, чтобы не блокировать игроков на время выполнения, как описано в mysqldump backup and restore.
  • Закройте его от публики. Поставьте низкое значение sv_maxclients, добавьте проверку вроде пароля через ваш whitelist в Discord или просто не публикуйте его в списке, и дайте ему отдельное имя хоста, чтобы никто не подключился не к тому серверу.
  • Заходите настоящим персонажем. Тест на новом персонаже ничего не доказывает про конвертацию. Выберите несколько персонажей с полным инвентарём, оружием, машиной с предметами в багажнике и рангом работы выше нуля и проверьте каждого после конвертации.

На RE:NODE каждый сервер - это отдельный контейнер со своим слотом базы данных, поэтому копия тестового сервера не видит боевую базу, пока вы не дадите ей эти учётные данные. Импортируйте дамп через «Open in phpMyAdmin» на вкладке базы данных тестового сервера или из командной строки, используя сгенерированные хост и пользователя.

Какие изменения в базе данных вносит Qbox#

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

bash
$ mysqldump --single-transaction --routines -h DB_HOST -u DB_USER -p qbcore \    > qbcore-before-qbox.sql

Затем импортируйте qbx_core.sql из релиза qbx_core, который вы устанавливаете. На существующей базе QBCore он делает три важных вещи. Он добавляет в players столбец last_logged_out (а в текущих версиях ещё и userId). Он меняет players так, чтобы таблица и её ключевые столбцы использовали сортировку utf8mb4_unicode_ci. И он создаёт player_groups, где хранятся все работы и банды персонажа, с внешним ключом по citizenid на players.

Именно внешний ключ - то место, где застревает большинство миграций. Для внешнего ключа у обоих столбцов должна быть одна и та же сортировка, а многие базы QBCore созданы с utf8mb4_general_ci. Если импорт падает с ошибкой внешнего ключа, проверьте и исправьте сортировку:

sql
SELECT TABLE_NAME, COLUMN_NAME, COLLATION_NAMEFROM information_schema.COLUMNSWHERE TABLE_SCHEMA = 'qbcore' AND COLUMN_NAME = 'citizenid';ALTER TABLE players  MODIFY citizenid VARCHAR(50) NOT NULL COLLATE utf8mb4_unicode_ci;

Выполните такой же MODIFY для любой другой таблицы, у которой столбец citizenid по-прежнему показывает utf8mb4_general_ci, сохранив у каждого столбца прежний тип и допустимость NULL, а затем импортируйте qbx_core.sql ещё раз. Почему сортировки вообще конфликтуют, описано в MySQL utf8mb4 and collations.

Дальше - работы и банды. Qbox ожидает, что ключи рангов в shared/jobs.lua и shared/gangs.lua будут числами, тогда как в QBCore это были строки:

qbx_core/shared/jobs.lua
police = {    label = 'LSPD',    defaultDuty = true,    grades = {        [0] = { name = 'Recruit', payment = 50 },      -- в QBCore было ['0']        [1] = { name = 'Officer', payment = 75 },        [4] = { name = 'Chief', isboss = true, payment = 150 },    },},

Перенесите свои работы и банды до первого запуска, а затем заполните новую таблицу. При запущенном сервере введите это в консоли txAdmin или в живой консоли:

code
convertjobs

Команда копирует текущую работу и банду каждого персонажа из таблицы players в player_groups, запускается только из консоли сервера, и её безопасно запускать дважды. Поведение multijob после этого определяют две convar: qbx:max_jobs_per_player и qbx:max_gangs_per_player, обе по умолчанию равны 1.

player_vehicles не затрагивается: qbx_vehicles создаёт её, только если такой таблицы ещё нет. При первом запуске ox_inventory добавляет в неё столбцы trunk и glovebox, а в players - столбец inventory, если его нет.

Перевод qb-inventory на ox_inventory#

Это самая тонкая часть, и именно здесь руководства противоречат друг другу сильнее всего, потому что в 2024 году инструменты изменились. Переносятся три разные вещи: описания предметов, инвентари игроков и всё остальное (stash, багажники, бардачки).

Описания предметов

Вставьте вашу таблицу предметов QBCore в qbx_core/shared/items.lua. При следующем запуске мост конвертирует в формат ox_inventory каждый предмет, который ox_inventory ещё не знает, и записывает их в qbx_core/converted.lua, выводя в консоль предупреждение с количеством добавленных. Он пропускает имена, похожие на оружие, обвесы и патроны, потому что у ox_inventory есть собственные описания оружия. Проверьте файл, скопируйте записи в ox_inventory/data/items.lua, снова очистите shared/items.lua и перезапустите сервер. Конвертированная запись выглядит так:

ox_inventory/data/items.lua
['sandwich'] = {    label = 'Sandwich',    weight = 200,    stack = true,    close = true,    description = 'Nice bread for your stomach',    client = {        status = { hunger = 200000 },        image = 'sandwich.png',    },},

Используемые предметы по-прежнему работают через CreateUseableItem в мосте, но значения голода и жажды из вашего ресурса с едой сами не применятся, поэтому проверьте каждый предмет еды в игре. Скопируйте картинки предметов из qb-inventory/html/images в ox_inventory/web/images, иначе все конвертированные предметы будут отображаться пустым квадратом.

Инвентари игроков

И QBCore, и ox_inventory хранят инвентарь игрока как JSON в players.inventory, но поля различаются:

json
{"name": "water_bottle", "amount": 3, "info": {}, "slot": 1, "type": "item"}{"name": "water_bottle", "count": 3, "metadata": {}, "slot": 1}

Первая строка - QBCore, вторая - ox_inventory. Когда ox_inventory загружает неконвертированного персонажа, он останавливается с сообщением Inventory for player.X contains invalid data. Ensure you have converted inventories to the correct format. В текущих релизах ox_inventory есть консольная команда convertinventory, но её единственные варианты - esx и esxproperty; если запустить её без аргумента, она выведет этот список. Конвертер QBCore был удалён в августе 2024 года, а v2.41.0 - последний релиз, в котором есть convertinventory qb. Он переименовывал amount в count и info в metadata, превращал наличные QBCore в предмет money и выдавал оружию серийный номер и прочность на основе прежнего quality.

Практических путей два: один раз запустить этот старый релиз на тестовом сервере только ради конвертации, а потом заменить его актуальным ox_inventory, либо написать то же сопоставление полей самостоятельно на SQL или Lua. В любом случае сравните несколько персонажей до и после. После конвертации наличные хранятся в двух местах, которые ox_inventory синхронизирует: в предмете money и в PlayerData.money.cash.

Stash, багажники и бардачки

Именно здесь данные теряются чаще всего. Старые версии qb-inventory хранили их в stashitems, trunkitems и gloveboxitems; старый конвертер переносил багажники и бардачки в player_vehicles, но stash не обрабатывал вовсе. Текущий qb-inventory (2.x) хранит всё в одной таблице inventories с ключом - строкой identifier, и ни один официальный конвертер её не читает. В Qbox ox_inventory хранит stash в собственной таблице ox_inventory, а багажники и бардачки - в player_vehicles, с ключом по id транспорта.

Поэтому решите, что стоит сохранять. Личные и рабочие stash обычно стоит, и для них нужен небольшой скрипт, который читает каждую строку inventories, конвертирует предметы, как описано выше, и вставляет их под именем stash, которое используют ваши определения stash в ox_inventory. Многие серверы решают, что содержимое багажников и бардачков не стоит скрипта, и объявляют, что они будут очищены. Это законный выбор, но делайте его осознанно и предупредите игроков до переключения, а не после.

День перехода по порядку#

Когда тестовый сервер прошёл весь процесс и пережил сессию с настоящими тестировщиками, боевое переключение сводится к чек-листу:

  1. Объявите окно обслуживания и закройте сервер через whitelist в txAdmin или остановив его.
  2. Сделайте итоговый mysqldump и резервную копию файлов сервера в панели. Скачайте обе.
  3. Выполните шаги SQL в том порядке, который сработал на тестовом сервере.
  4. Замените resources/ проверенной папкой и перенесите из неё server.cfg, ox.cfg и permissions.cfg.
  5. Запускайте с открытой консолью. ensure oxmysql, ox_lib, qbx_core и ox_inventory должны подняться раньше всего, что от них зависит. На что смотреть, описано в Reading the console.
  6. Выполните convertjobs и ту конвертацию инвентаря, которую вы отрабатывали.
  7. Зайдите тремя персонажами из тех, что вы проверяли на тестовом сервере. Откройте инвентарь, stash и багажник.
  8. Откройте сервер для игроков и первый час следите за консолью и resmon.

План отката#

Запишите план отката до начала, потому что в тот момент, когда он понадобится, вы не сможете ясно мыслить. Миграция обратима, только если обе половины старого сервера остались нетронутыми.

  • Файлы. Храните старую папку resources/ и server.cfg как архив, не перезаписывая их. Возврат назад - это восстановление этой папки.
  • База данных. Восстановите qbcore-before-qbox.sql в базу. Исправление вперёд - это не откат: конвертированный JSON инвентаря, новая сортировка и player_groups сами не отменятся.
  • Крайний срок решения. Выберите момент, после которого вы исправляете вперёд, а не откатываетесь. У игроков, которые сутки играли на Qbox, есть данные, которые восстановление сотрёт. Разумное правило: откатывайтесь свободно в окно обслуживания и после него - только при потере данных.

На RE:NODE резервная копия в панели охватывает файлы, а дамп - базу данных, поэтому делайте обе. Резервную копию можно заблокировать от ротации, чтобы копия перед миграцией пережила ваши запланированные бэкапы, но проверьте восстановление на тестовом сервере, прежде чем на него полагаться.

Устранение неполадок#

Ошибки, которые возникают почти при каждой миграции:

СимптомПричина
qb-inventory is not compatible with qbx_coreqb-inventory всё ещё запущен
contains invalid data при входеИнвентарь всё ещё в формате QBCore
Ошибка внешнего ключа при импорте qbx_core.sqlСортировки citizenid различаются
Ранг работы показывает nilКлючи рангов всё ещё строки
Предметы отображаются пустыми квадратамиКартинки не скопированы
Игроки потеряли работуcleanPlayerGroups включён, а работ не хватает

Ещё две проблемы менее заметны. Ресурс, который молча ничего не делает, обычно обращается к QBCore.Shared.Items, чтобы проверить существование предмета; замените эту проверку на exports.ox_inventory:Items(). А дублирующиеся папки - старый qb-inventory в забытой папке в квадратных скобках - запускают то, что FXServer найдёт первым, поэтому удалите старую папку, а не оставляйте её без ensure.

FAQ#

Можно ли запускать Qbox без моста qb-core?

Да, установите qbx:enableBridge в false, но только когда на сервере ничего больше не обращается к qb-core. Большинство серверов держат его включённым месяцами и переводят ресурсы постепенно.

Потеряю ли я данные персонажей при переходе на Qbox?

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

Можно ли оставить qb-inventory на Qbox?

Нет. qbx_core отказывается работать при запущенном qb-inventory, а ox_inventory - единственный инвентарь, который поддерживает Qbox.

Будут ли мои платные скрипты QBCore работать на Qbox?

Многие работают через мост, и многие авторы выпускают вариант для Qbox. Ломаются те, что напрямую обращаются к qb-inventory или qb-target. Проверьте заметки автора и протестируйте на копии.

Сколько занимает миграция?

Для сервера с умеренным списком ресурсов - выходные на подготовку на тестовом сервере и окно обслуживания в один-два часа. Большие серверы с десятками скриптов, зависящих от инвентаря, требуют больше времени, в основном на переписывание этих скриптов.

Можно ли вернуться на QBCore после перехода?

Только восстановив дамп базы до миграции и старую папку resources/, что сотрёт всё, что игроки сделали после переключения. Определите крайний срок отката до начала.