Konwersja FiveM Scripts – ESX, QBCore, QBOX (ramka…

Konwersja FiveM Scripts – ESX, QBCore, QBOX (ramka…

To prosty przewodnik po konwersji skryptów FiveM, który pokazuje dokładnie, jak to zrobić, bez zbędnych zbędnych elementów i w którym najpierw piszemy kod. Konwertuj FiveM na Scripts między konfiguracjami ESX, QBCore, QBOX (qbx_core) i niezależnymi od frameworka (standalone). Otrzymasz mapowania API trójstronne, kod adaptera, kroki migracji SQL (mysql-async → oxmysql), szczegóły dotyczące QBOX, listy kontrolne testów i wskazówki dotyczące wzmacniania produkcji.


TL;DR — Migracja w 10 krokach

  1. Migawka i inscenizacja: Utwórz kopię zapasową bazy danych/plików. Uruchom lokalny serwer testowy z docelową strukturą.
  2. Zidentyfikuj zależności: inwentarz, cel, menu/UI, wywołania zwrotne, warstwa bazy danych, rdzeń dostęp.
  3. Wersje pinów i kolejność: oxmysql → ox_lib → framework (es_extended / qb-core / qbx_core) → twoje zasoby.
  4. Przeczytaj ścieżkę kodu: Znajdź wszystkich graczy, pieniądze, zadania/obowiązki, ekwipunek, wywołania/wydarzenia i wywołania bazy danych.
  5. Interfejsy API map: Użyj poniższych tabel Rosetta, aby nanieść mapę ESX ↔ QBCore ↔ QBOX.
  6. Połącz to: Dodaj most.lua adapter, który automatycznie wykrywa strukturę i normalizuje wywołania.
  7. Konwertuj wywołania zwrotne: Wywołania zwrotne ESX/QB → QBOX/ox_lib lib.callback.* gdy jest to potrzebne.
  8. Migracja bazy danych: Przenieś identyfikatory, konta, elementy, pojazdy; przełącz na oxmysql *.await styl.
  9. Wydarzenia Harden: Uprawomocnić source, granice, grupy/zadania, własność; nigdy nie ufaj klientowi.
  10. Kontrola jakości i wydanie: Uruchom listy kontrolne, tagi, dziennik zmian i plan wycofania.

Czy jesteś programistą i szukasz rozwiązania w zakresie adapterów? Sprawdź nasz darmowy skrypt adaptera tutaj


1) Frameworki 101: ESX vs QBCore vs QBOX vs Standalone

Przegląd architektoniczny

KategoriaESXQBCoreQBOX
Architektura / Dostęp do rdzeniaexports['es_extended']:getSharedObject()ESXexports['qb-core']:GetCoreObject()QBCoreBrak globalnego obiektu rdzeniowego; użyj exports.qbx_core:* dla graczy, grup i powiadomień
Obiekt graczaESX.GetPlayerFromId(src)xPlayerQBCore.Functions.GetPlayer(src)Playerexports.qbx_core:GetPlayer(src)Player z PlayerData
IdentyfikatoryHistorycznie identifier (licencja/Steam)citizenid jako klucz podstawowycitizenid jako klucz podstawowy
Pieniądze ObsługiwaniexPlayer.addMoney() / `addAccountMoney('bank'‘‘'black_money')``Player.Functions.AddMoney('gotówka'‘
Praca i obowiązkixPlayer.job.name, .job.gradePlayer.PlayerData.job.name, .grade.level, OnDutyPomocnicy w pracy/grupie za pośrednictwem exports.qbx_core:* (kontrole grupowe, osoby ustalające obowiązki)
SpisDomyślnie ESX / ox_inventory / qb-inventory (zalecony: ox_inventory)qb-inventory / ox_inventoryox_inventory zalecony
Wywołania zwrotneESX.RegisterServerCallback / ESX.TriggerServerCallbackQBCore.Functions.CreateCallback / TriggerCallbackWoleć ox_lib wywołania zwrotne (lib.callback.register/await) lub eksportuje QBOX
Warstwa bazy danychStary: mysql-async → Teraz: oxmysql (MySQL.query.await, MySQL.update.await)oxmysqloxmysql
UI – PowiadomieniaESX PowiadomPowiadomienie QBQBOX powiadomić pomocników lub lib.notify
UI – Menuesx_menu_default / ox_lib:registerContextqb-menu / ox_lib:registerContextox_lib:registerContext / lib.inputDialog
UI – System docelowyesx_target / ox_targetqb-target / ox_targetox_target zalecony

Czym różni się QBOX

  • Nazwa zasobu i eksporty: qbx_core (NIE GetCoreObject).
  • Pochyla się na ox_lib (oddzwonienia, powiadomienia) i oxmysql domyślnie.
  • Wbudowane funkcje dla wielu postaci/kolejek/grup; pomocnicy wyrażający swoje zdanie w zakresie zadań/obowiązków.

Ramy FiveM: QBCore kontra ESX
Converting FiveM Scripts – ESX, QBCore, QBOX (Frame... 3

2) Preflight: środowisko i narzędzia

  • Serwer: aktualny Serwer FX artefakty, Lua 5.4, CFX ensure zamówienie
  • Zamówienie: ensure oxmysqlensure ox_libensure frameworkensure yourResource
  • Redaktor: Kod VS + Lua LS, rysik; opcjonalnie ripgrep do szybkiego skanowania wzorów
  • Konfiguracja testowa: czysta migawka bazy danych; szczegółowe rejestrowanie; mały zestaw danych zaszczepionych
  • Wzory wyszukiwania użyjesz:
    • ESX. xPlayer. QBCore. PlayerData qbx_core exports.ox_inventory mysql%-async MySQL.

3) Kamień z Rosetty: ESX ↔ QBCore ↔ QBOX (mapowanie trójstronne)

Łatwe w użyciu narzędzie do kopiowania i wklejania. Użyj go, aby zastąpić połączenia telefoniczne lub podłączyć adapter.

Rdzeń i gracz

PojęcieESXQBCoreQBOX
Zdobądź rdzeńexports['es_extended']:getSharedObject()exports['qb-core']:GetCoreObject()(n/a — użyj eksportów)
Pobierz gracza (źródło)ESX.GetPlayerFromId(src)QBCore.Functions.GetPlayer(src)exports.qbx_core:GetPlayer(src)
Uzyskaj za pomocą citizenid(zapytanie niestandardowe)QBCore.Functions.GetPlayerByCitizenId(id)exports.qbx_core:GetPlayerByCitizenId(id) (jeśli występuje)

Identyfikatory

PojęcieESXQBCoreQBOX
Podstawowyidentifier (licencja/Steam)citizenidcitizenid
Przejście dla pieszychtabela id_map(identifier, citizenid)To samoTo samo

Pieniądze

DziałanieESXQBCoreQBOX
Dodaj gotówkęxPlayer.addMoney(a)Player.Functions.AddMoney('cash', a)Player.PlayerData.money.cash += a (poprzez adapter + zapisz)
Dodaj bankxPlayer.addAccountMoney('bank', a)Player.Functions.AddMoney('bank', a)Player.PlayerData.money.bank += a (adapter + zapisz)
Dodaj brudnexPlayer.addAccountMoney('black_money', a)element lub dodatkowe konto (zdefiniowane na serwerze)mapuj do portfela przedmiotów/alternatywnego (np., crypto) (wybór adaptera)

Praca, Obowiązek, Grupy

PojęcieESXQBCoreQBOX
Przeczytaj pracęxPlayer.job.name, .gradePlayer.PlayerData.job.name, .grade.levelPlayer.PlayerData.job.* + pomocnicy grupowi
Na służbie(różnie)Player.PlayerData.job.ondutySetJobDuty/pomocnicy grupowi poprzez eksporty
Kontrola grupowa(dodatek)(dodatek)exports.qbx_core:HasGroup(src, group) (przykład)

Spis

DziałanieESXQBCoreQBOX
Dodaj elementxPlayer.addInventoryItem(n, c)Player.Functions.AddItem(n, c, ...)woleć ox_inventory eksport przez adapter
ox_inventoryexports.ox_inventory:*exports.ox_inventory:*exports.ox_inventory:*

Wywołania zwrotne

PojęcieESXQBCoreQBOX
Rejestr serweraESX.RegisterServerCallbackQBCore.Functions.CreateCallbacklib.callback.register (ox_lib)
Wyzwalacz klientaESX.TriggerServerCallbackQBCore.Functions.TriggerCallbacklib.callback.await

Warstwa bazy danych

PojęcieESX/QB (starsza wersja)oxmysql (cel)
AportowaćMySQL.Async.fetchAll(sql, params, cb)local rows = MySQL.query.await(sql, {p1, ...})
AktualizacjaMySQL.Async.execute(sql, params, cb)local aff = MySQL.update.await(sql, {p1, ...})

UI / Powiadom / Cel

PojęcieESXQBCoreQBOX
NotyfikowaćESX.ShowNotification(msg)QBCore.Functions.Notify(msg, type)exports.qbx_core:Notify(...) Lub lib.notify({...})
Menu(różnie)qb-menuox_lib:registerContext / wejścia
Cel(dodatek)qb-targetox_target

Wskazówka: przychylność ox_lib, ox_inventory, ox_target zachować neutralność w różnych ramach.


4) Samouczek A — ESX → QBCore (praktyczny)

Bramka: Konwertuj prosty zasób sklepu z ESX na QBCore.

Krok 1 — Zależności

  • Zapewnić oxmysql, ox_lib, qb-core są rozpoczęte.
  • Jeśli skrypt używa inwentarza ESX, przeprowadź migrację do ox_inventory (zalecane) lub zmapuj do inwentarza QB.

Krok 2 — Wykryj strukturę i dodaj most

Tworzyć bridge.lua aby znormalizować graczy/pieniądze/ekwipunek:

-- bridge.lua (ESX/QB/QBOX adapter)
local M = {}
local fw

CreateThread(function()
 if GetResourceState('qbx_core') == 'started' then
 fw = 'qbx'
 elseif GetResourceState('qb-core') == 'started' then
 fw = 'qb'
 elseif GetResourceState('es_extended') == 'started' then
 fw = 'esx'
 else
 fw = 'none'
 end
end)

function M.getFramework()
 return fw
end

function M.getPlayer(src)
 if fw == 'qbx' then
 return exports.qbx_core:GetPlayer(src)
 elseif fw == 'qb' then
 return exports['qb-core']:GetCoreObject().Functions.GetPlayer(src)
 elseif fw == 'esx' then
 return exports['es_extended']:getSharedObject().GetPlayerFromId(src)
 end
end

local function saveQbox(src)
 if fw == 'qbx' and exports.qbx_core and exports.qbx_core.Save then
 exports.qbx_core:Save(src)
 end
end

function M.addMoney(src, account, amount)
 local p = M.getPlayer(src)
 if not p or type(amount) ~= 'number' then return false end
 if fw == 'qb' then
 return p.Functions.AddMoney(account, amount)
 elseif fw == 'esx' then
 if account == 'cash' then
 return p.addMoney(amount)
 elseif account == 'bank' then
 return p.addAccountMoney('bank', amount)
 elseif account == 'black' or account == 'black_money' then
 return p.addAccountMoney('black_money', amount)
 end
 elseif fw == 'qbx' then
 local money = p.PlayerData and p.PlayerData.money or {}
 account = account == 'black' and 'crypto' or account -- example mapping
 money[account] = (money[account] or 0) + amount
 saveQbox(src)
 return true
 end
 return false
end

function M.removeMoney(src, account, amount)
 return M.addMoney(src, account, -math.abs(amount))
end

function M.addItem(src, name, count, meta)
 if GetResourceState('ox_inventory') == 'started' then
 return exports.ox_inventory:AddItem(src, name, count, meta)
 elseif fw == 'qb' then
 return exports['qb-inventory']:AddItem(src, name, count, false, meta)
 elseif fw == 'esx' then
 local p = M.getPlayer(src)
 return p and p.addInventoryItem(name, count)
 elseif fw == 'qbx' then
 return exports.ox_inventory:AddItem(src, name, count, meta)
 end
end

function M.notify(src, msg, ntype)
 if fw == 'qb' then
 TriggerClientEvent('QBCore:Notify', src, msg, ntype or 'primary')
 elseif fw == 'esx' then
 TriggerClientEvent('esx:showNotification', src, msg)
 elseif fw == 'qbx' then
 if exports.qbx_core and exports.qbx_core.Notify then
 exports.qbx_core:Notify(src, msg, ntype or 'info')
 else
 lib.notify(src, { title = 'Notice', description = msg })
 end
 else
 print(('notify(%s): %s'):format(src, msg))
 end
end

return M

Krok 3 — Konwersja wywołań zwrotnych

  • ESX → QB: wymień ESX.RegisterServerCallback z QBCore.Functions.CreateCallback.
  • Klient używa QBCore.Functions.TriggerCallback.
-- server.lua (callback)
local bridge = require 'bridge'

local function getPrice(item)
 return 100
end

if GetResourceState('qb-core') == 'started' then
 local QBCore = exports['qb-core']:GetCoreObject()
 QBCore.Functions.CreateCallback('shop:getPrice', function(source, cb, item)
 cb(getPrice(item))
 end)
else
 -- fallback for QBOX/ESX via ox_lib
 lib.callback.register('shop:getPrice', function(source, item)
 return getPrice(item)
 end)
end
-- client.lua (callback)
if GetResourceState('qb-core') == 'started' then
 local QBCore = exports['qb-core']:GetCoreObject()
 QBCore.Functions.TriggerCallback('shop:getPrice', function(price)
 print('price', price)
 end, 'bread')
else
 local price = lib.callback.await('shop:getPrice', false, 'bread')
 print('price', price)
end

Krok 4 — Ścieżki pieniężne i zapasy

  • Zastępować xPlayer.addAccountMoney z Player.Functions.AddMoney (dla QB) lub adapter.
  • Standaryzacja wszystkich zmian stanu magazynowego poprzez adapter.

Krok 5 — Wydarzenia (bezpieczeństwo po stronie serwera)

RegisterNetEvent('shop:buy', function(item, amount)
 local src = source
 if type(item) ~= 'string' or type(amount) ~= 'number' then return end
 if amount < 1 or amount > 10 then return end

 local p = bridge.getPlayer(src)
 if not p then return end

 local price = 100 * amount
 if not bridge.removeMoney(src, 'cash', price) then return end
 bridge.addItem(src, item, amount)
 bridge.notify(src, ('Bought %dx %s'):format(amount, item), 'success')
end)

Krok 6 — Migracja bazy danych (identyfikatory, konta)

  • Utwórz przejście dla pieszych id_map(identifier, citizenid).
  • Zaludniać citizenid dla QB z ESX users tabeli za pomocą wybranej reguły (istniejąca kolumna lub wygenerowana).
-- Example: create crosswalk and backfill (customize to your schema)
CREATE TABLE IF NOT EXISTS id_map (
 identifier VARCHAR(64) PRIMARY KEY,
 citizenid VARCHAR(64) NOT NULL UNIQUE
);

-- Suppose you generated new citizenids and stored them earlier
INSERT IGNORE INTO id_map(identifier, citizenid)
SELECT u.identifier, u.citizenid FROM users u WHERE u.citizenid IS NOT NULL;

mysql‑async → oxmysql

-- before (mysql-async)
MySQL.Async.fetchAll('SELECT * FROM users WHERE identifier = @id', {['@id'] = identifier}, function(rows) ... end)

-- after (oxmysql)
local rows = MySQL.query.await('SELECT * FROM users WHERE identifier = ?', { identifier })

Krok 7 — Kontrola jakości

  • Twórz przedmioty, kupuj/sprzedawaj, upewniaj się, że saldo zmienia się prawidłowo.
  • Sprawdź, czy wywołania zwrotne zwracają wartość pod obciążeniem.
  • Sprawdź, czy metadane inwentarza są zachowane.

5) Samouczek B — QBCore → ESX (praktyczny)

Kluczowe ostrzeżenia:

  • Zastosowania ESX konta Do bank/black_money. Brudne pieniądze Port QB przedmiot (jeśli używane) na konto ESX lub zachowaj jako przedmiot.
  • Schematy stanowisk różnią się (stopień i poziom). Dokładnie je zaplanuj.

Przykład: Mapowanie pieniędzy za pomocą adaptera

-- in bridge.lua, when fw == 'esx'
function M.qbToEsxMoney(account)
 if account == 'cash' then return 'cash' end
 if account == 'bank' then return 'bank' end
 if account == 'black' or account == 'black_money' then return 'black_money' end
 return account
end

Przykład: Konwersja wywołania zwrotnego (serwer)

if GetResourceState('es_extended') == 'started' then
 local ESX = exports['es_extended']:getSharedObject()
 ESX.RegisterServerCallback('garage:getVehicles', function(source, cb)
 local src = source
 local p = bridge.getPlayer(src)
 local rows = MySQL.query.await('SELECT * FROM owned_vehicles WHERE owner = ?', { p.identifier })
 cb(rows)
 end)
end

Lista kontrolna testów

  • Stanowiska pracy/awanse są kontynuowane.
  • Salda bankowe/brudne zmieniają się zgodnie z oczekiwaniami.
  • Potwierdzono własność pojazdu i format tablic rejestracyjnych.

6) Samouczek C — QBCore ↔ QBOX (praktyczny)

QBCore → QBOX

  • Zastępować QBCore.Functions.* z exports.qbx_core:* Lub ox_lib wywołania zwrotne.
  • Odtwarzacz: QBCore.Functions.GetPlayer(src)exports.qbx_core:GetPlayer(src).
  • Notyfikować: QBCore.Functions.Notifyexports.qbx_core:Notify Lub lib.notify.
  • Obowiązki/grupy: użyj eksportów QBOX (SetJobDuty, HasGroup, itp.).

QBOX → QBCore

  • Zastępować exports.qbx_core:* z QBCore.Functions.* odpowiedników lub adaptera.
  • W razie potrzeby ponownie wprowadź wywołania zwrotne QB i odpowiedniki menu/celów.

Fragmenty przed/po

-- Before (QB notify)
QBCore.Functions.Notify('Hello', 'success')

-- After (QBOX)
exports.qbx_core:Notify(source, 'Hello', 'success')
-- or
lib.notify(source, { title = 'Hello', description = 'Welcome', type = 'success' })
-- Before (QB get player)
local Player = QBCore.Functions.GetPlayer(src)

-- After (QBOX)
local Player = exports.qbx_core:GetPlayer(src)

7) Samouczek D — Framework → Standalone z adapterami

Napisz swoje skrypty raz i uruchamiaj je wszędzie:

  • Utwórz most udostępnianie stabilnego API: getPlayer, getIdentifier, add/removeMoney, add/removeItem, notify, hasGroup, onDuty, callbacks obwoluta.
  • Wykryj strukturę w czasie wykonywania (qbx_coreqb-corees_extendednone).
  • Używać ox_lib do wywołań zwrotnych i powiadomień, gdy nie istnieje bezpośredni pomocnik struktury.

Minimalny samodzielny zapasowy

-- when fw == 'none'
function M.getPlayer(src)
 -- implement a minimal table or reject actions gracefully
 return { id = src }
end

function M.notify(src, msg)
 print(('notify(%s): %s'):format(src, msg))
end

8) Wydajność i bezpieczeństwo (wzmocnienie produkcji)

Walidacja po stronie serwera (nigdy nie ufaj klientowi)

  • Uprawomocnić typy I miedza na każdym wydarzeniu.
  • Sprawdzać własność, czasy odnowienia, dystans (jeśli dotyczy), praca/grupa.
  • Upewnij się, że saldo środków pieniężnych nie stanie się ujemne i ogranicz kwoty.
  • Porównywać cena po stronie serwera; nie akceptuj sum podanych przez klienta.

Struktura wydarzenia

  • Użyj jednego zdarzenie serwerowe na akcję; nie udostępniaj klientom surowych funkcji dotyczących zapasów/pieniędzy.
  • Woleć wywołania zwrotne dla przepływów żądanie/odpowiedź.

DB i wydajność

  • Przełącz na oxmysql oczekuj na API, zapisuj wsadowo, unikaj zapytań per-tick.
  • Indeksuj często wyszukiwane kolumny (citizenid, identifier, plate).
  • Zachowaj w pamięci podręcznej konfiguracje/listy cen; wyeksportuj je raz do klientów, a następnie aktualizuj w przypadku zmiany.
  • Używać GlobalState oszczędnie; unikaj aktualizacji w pętli.

9) Typowe pułapki i podręcznik debugowania

  • Zarozumiały GetCoreObject istnieje na QBOX → nie; użyj exports.qbx_core:*.
  • Po migracji do QBOX połączenia zwrotne nigdy nie powracają → Wywołania zwrotne ESX/QB nie są zarejestrowane; przełącz na lib.callback.register/await.
  • Semantyka brudnych pieniędzy różnią się → zdecyduj, czy chcesz wybrać przedmiot, konto czy alternatywny portfel i ustandaryzuj go w swoim adapterze.
  • Założenia dotyczące mieszanych zapasów → znormalizować na ox_inventory.
  • Problemy z zamówieniem początkowymoxmysqlox_lib → framework → twoje zasoby.
  • Dryf metadanych pojazdu → upewnij się, że kolumny JSON i formaty tablic pasują do docelowej struktury.

10) Najlepsze praktyki fxmanifest.lua

fx_version 'cerulean'
game 'gta5'

lua54 'yes'

shared_scripts {
 '@ox_lib/init.lua',
 'bridge.lua',
 'config.lua'
}
client_scripts {
 'client/*.lua'
}
server_scripts {
 '@oxmysql/lib/MySQL.lua',
 'server/*.lua'
}

escrow_ignore {
 'bridge.lua', 'config.lua'
}

-- dependencies (pick what you use): ox_lib, oxmysql, ox_inventory, qb-core OR qbx_core OR es_extended
  • Ogłosić Lua 5.4 (lua54 'yes').
  • Trzymać escrow_ignore minimalne; nigdy nie próbuj ominąć depozytu.

11) Migracja danych: fragmenty kodu SQL (przykłady)

Dostosuj nazwy tabel/kolumn do swojego schematu. Zawsze najpierw uruchamiaj kopię.

Identyfikatory: ESX → QB/QBOX

-- Add citizenid to ESX users if missing
ALTER TABLE users ADD COLUMN IF NOT EXISTS citizenid VARCHAR(64);

-- Populate using a deterministic generator or an existing column
UPDATE users SET citizenid = LOWER(SUBSTRING(MD5(CONCAT(identifier,'-QB')),1,10))
WHERE citizenid IS NULL;

-- Create crosswalk
CREATE TABLE IF NOT EXISTS id_map (
 identifier VARCHAR(64) PRIMARY KEY,
 citizenid VARCHAR(64) NOT NULL UNIQUE
);
INSERT IGNORE INTO id_map(identifier, citizenid)
SELECT identifier, citizenid FROM users WHERE citizenid IS NOT NULL;

Konta/Pieniądze

-- Example: convert ESX bank balances to QB money table (if your QB schema stores it separately)
INSERT INTO player_money (citizenid, account, amount)
SELECT m.citizenid, 'bank', a.money
FROM (
 SELECT u.citizenid, SUM(CASE WHEN account = 'bank' THEN money ELSE 0 END) AS money
 FROM user_accounts ua
 JOIN users u ON u.identifier = ua.identifier
 GROUP BY u.citizenid
) a
JOIN users m ON m.citizenid = a.citizenid
ON DUPLICATE KEY UPDATE amount = VALUES(amount);

Pojazdy

-- Normalize plates to target format (example: 8 chars upper)
UPDATE owned_vehicles SET plate = UPPER(LEFT(plate,8));

-- Ensure JSON metadata columns are valid
UPDATE owned_vehicles SET mods = JSON_MERGE_PATCH('{}', mods) WHERE JSON_VALID(mods) = 0;

12) Adaptery, które można ponownie wykorzystać (wersja startowa)

Wrzuć to do bridge.lua i rozszerzaj zgodnie ze swoimi potrzebami. Automatycznie wykrywa ESX/QB/QBOX i udostępnia stabilne API.

local M = {}
local fw = 'none'

CreateThread(function()
 if GetResourceState('qbx_core') == 'started' then fw = 'qbx'
 elseif GetResourceState('qb-core') == 'started' then fw = 'qb'
 elseif GetResourceState('es_extended') == 'started' then fw = 'esx' end
end)

function M.framework() return fw end

function M.player(src)
 if fw == 'qbx' then return exports.qbx_core:GetPlayer(src)
 elseif fw == 'qb' then return exports['qb-core']:GetCoreObject().Functions.GetPlayer(src)
 elseif fw == 'esx' then return exports['es_extended']:getSharedObject().GetPlayerFromId(src) end
end

function M.identifier(src)
 local p = M.player(src); if not p then return nil end
 if fw == 'qb' or fw == 'qbx' then
 return p.PlayerData and p.PlayerData.citizenid
 elseif fw == 'esx' then
 return p.identifier
 end
end

function M.hasGroup(src, group)
 if fw == 'qbx' and exports.qbx_core and exports.qbx_core.HasGroup then
 return exports.qbx_core:HasGroup(src, group)
 end
 return false
end

function M.notify(src, msg, ntype)
 if fw == 'qb' then
 TriggerClientEvent('QBCore:Notify', src, msg, ntype or 'primary')
 elseif fw == 'esx' then
 TriggerClientEvent('esx:showNotification', src, msg)
 elseif fw == 'qbx' then
 if exports.qbx_core and exports.qbx_core.Notify then
 exports.qbx_core:Notify(src, msg, ntype or 'info')
 else
 lib.notify(src, { description = msg })
 end
 end
end

return M

13) Ostateczne listy kontrolne (skopiuj i wyślij)

A) Odkrywanie i wstępny lot

  • Utwórz migawkę bazy danych/plików; utwórz serwer przejściowy
  • Przypnij artefakty i wersje zasobów; zdefiniuj kolejność początkową
  • Wymieniono zależności ekwipunku/celu/menu
  • Wybierz strategię identyfikacji (przejście dla pieszych z identyfikatorem obywatela)

B) Audyt kodu

  • Grep dla ESX/QB/QBOX/ox_*/mysql‑async
  • Wypisz wszystkie wezwania dotyczące pieniędzy/zapasów/pracy/obowiązków
  • Wyświetlanie listy wywołań zwrotnych i zdarzeń serwera/klienta

C) Mapowanie i projektowanie

  • Wybierz powierzchnię adaptera (gracz, identyfikatory, pieniądze, przedmioty, powiadomienia, wywołania zwrotne)
  • Wybierz strategię dotyczącą brudnych pieniędzy (konto, przedmiot, alternatywny portfel)
  • Preferuj ox_lib/ox_inventory/ox_target

D) Migracja danych

  • Zbuduj przejście dla pieszych dla obywateli
  • Konwersja kont/pieniędzy
  • Normalizacja metadanych i tablic rejestracyjnych pojazdów
  • Przełącz się na wywołania oczekujące Oxmysql

E) Testowanie QA

  • Bezpieczeństwo zdarzeń: typy/granice/własność/czasy odnowienia
  • Wywołania zwrotne: zwracanie wartości pod obciążeniem
  • Zachowano metadane inwentarza
  • Logika zadań/obowiązków/grupy pasuje do projektu

F) Wydanie i wycofanie

  • Wydanie tagu; dziennik zmian
  • Zachowaj migawkę przed migracją przez 7–14 dni
  • Monitoruj błędy/opóźnienia; indeksuj gorące zapytania

14) FAQ (ze schematem JSON‑LD)

P: Czy mogę przenieść skrypty QBCore do QBOX bez konieczności całkowitego przepisania?
A: Z adapterami i ox_lib wywołania zwrotne, większość skryptów wymaga jedynie ponownego mapowania wywołań.

P: Co mam zrobić z czarnymi/brudnymi pieniędzmi?
A: Standaryzacja w adapterze: ESX → black_money Konto; QB → przedmiot lub konto dodatkowe; QBOX → przedmiot lub portfel alternatywny (np. kryptowaluty). Stosuj jedną strategię w całym projekcie.

P: Dlaczego po przejściu na wersję QBOX połączenia zwrotne zostają zawieszone?
A: Wywołania zwrotne ESX/QB nie będą działać na QBOX. Użyj lib.callback.register/await.

P: Jaki jest najlepszy sposób na migrację mysql‑async?
A: Zastąp przez oxmysql oczekuj na API; usuń piramidy wywołań zwrotnych.

P: Jak przekonwertować qb‑target na ox_target?
A: Zastąp wywołania API addBoxZone/Entity w stosunku jeden do jednego; ładunki zdarzeń pozostaną podobne. Zachowaj stabilne nazwy celów.

{ “@context”: “https://schema.org”, “@type”: “FAQPage”, “mainEntity”: [ { “@type”: “Question”, “name”: “Czy mogę przenieść skrypty QBCore do QBOX bez pełnego przepisywania?”, “acceptedAnswer”: {“@type”: “Answer”, “text”: “Dzięki adapterom i wywołaniom zwrotnym ox_lib większość skryptów wymaga tylko ponownego mapowania wywołań.”} }, { “@type”: “Question”, “name”: “Co zrobić z brudnymi pieniędzmi?”, “acceptedAnswer”: {“@type”: “Answer”, “text”: “Ujednolić w adapterze: ESX → konto black_money; QB → przedmiot lub dodatkowe konto; QBOX → przedmiot lub alternatywny portfel (np. krypto).”} }, { “@type”: “Question”, “name”: “Dlaczego wywołania zwrotne wiszą po przeniesieniu do QBOX?”, “acceptedAnswer”: {“@type”: “Answer”, “text”: “Wywołania zwrotne ESX/QB nie będą działać na QBOX. Użyj lib.callback.register/await.”} }, { “@type”: “Question”, “name”: “Najlepszy sposób na migrację mysql-async?”, “acceptedAnswer”: {“@type”: “Answer”, “text”: “Zastąp API oxmysql await; usuń piramidy wywołań zwrotnych.”} }, { “@type”: “Question”, “name”: “Jak przekonwertować qb-target na ox_target?”, “acceptedAnswer”: {“@type”: “Answer”, “text”: “Zamień wywołania API addBoxZone/Entity jeden na jeden; ładunki zdarzeń pozostają podobne. Zachowaj stabilne nazwy celów.”} } ] }

15) Wizualizacje (sugestie)

  • Schemat blokowy (Odkryj → Mapa → Adaptacja → Migracja bazy danych → Testowanie → Wydanie)
  • Schemat adapterów: Skrypt → Most → Struktura (ESX/QB/QBOX)
  • Tabela Rosetta: dołączone powyżej (eksportuj jako plik CSV do pobrania)
[Discover] → [Map APIs] → [Write Adapter] → [Migrate DB] → [Harden] → [QA] → [Release]

16) Prawne i etyczne

  • Szanuj licencje (MIT/GPL/NC). Nie ominąć depozyt lub zaciemnianie.
  • Konwertuj tylko te skrypty, które własny lub uzyskać pozwolenie na adaptację.

17) Materiały do pobrania i szybki przewodnik

Możesz również użyć tego konwertera (nie testowałem):

https://github.com/sledgehamm3r/ESX-QBCore-Converter


28) Następne kroki

  • Upuść most.lua do swojego zasobu i zacznij konwertować najpierw te skrypty, które mają największą wartość.
  • Standaryzować na ox_lib + ox_inventory + ox_target + oxmysql pozostać niezależnym od ram.
  • Korzystaj z list kontrolnych podczas przeglądu i wydania.

Przeczytaj także Migracja SQL i identyfikatorów: steam/license → citizenid i konta → pieniądze (ESX → QBCore/QBOX)