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
- Migawka i inscenizacja: Utwórz kopię zapasową bazy danych/plików. Uruchom lokalny serwer testowy z docelową strukturą.
- Zidentyfikuj zależności: inwentarz, cel, menu/UI, wywołania zwrotne, warstwa bazy danych, rdzeń dostęp.
- Wersje pinów i kolejność: oxmysql → ox_lib → framework (es_extended / qb-core / qbx_core) → twoje zasoby.
- Przeczytaj ścieżkę kodu: Znajdź wszystkich graczy, pieniądze, zadania/obowiązki, ekwipunek, wywołania/wydarzenia i wywołania bazy danych.
- Interfejsy API map: Użyj poniższych tabel Rosetta, aby nanieść mapę ESX ↔ QBCore ↔ QBOX.
- Połącz to: Dodaj most.lua adapter, który automatycznie wykrywa strukturę i normalizuje wywołania.
- Konwertuj wywołania zwrotne: Wywołania zwrotne ESX/QB → QBOX/ox_lib
lib.callback.*gdy jest to potrzebne. - Migracja bazy danych: Przenieś identyfikatory, konta, elementy, pojazdy; przełącz na oxmysql
*.awaitstyl. - Wydarzenia Harden: Uprawomocnić
source, granice, grupy/zadania, własność; nigdy nie ufaj klientowi. - 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
| Kategoria | ESX | QBCore | QBOX |
|---|---|---|---|
| Architektura / Dostęp do rdzenia | exports['es_extended']:getSharedObject() → ESX | exports['qb-core']:GetCoreObject() → QBCore | Brak globalnego obiektu rdzeniowego; użyj exports.qbx_core:* dla graczy, grup i powiadomień |
| Obiekt gracza | ESX.GetPlayerFromId(src) → xPlayer | QBCore.Functions.GetPlayer(src) → Player | exports.qbx_core:GetPlayer(src) → Player z PlayerData |
| Identyfikatory | Historycznie identifier (licencja/Steam) | citizenid jako klucz podstawowy | citizenid jako klucz podstawowy |
| Pieniądze Obsługiwanie | xPlayer.addMoney() / `addAccountMoney('bank'‘ | ‘'black_money')` | `Player.Functions.AddMoney('gotówka'‘ |
| Praca i obowiązki | xPlayer.job.name, .job.grade | Player.PlayerData.job.name, .grade.level, OnDuty | Pomocnicy w pracy/grupie za pośrednictwem exports.qbx_core:* (kontrole grupowe, osoby ustalające obowiązki) |
| Spis | Domyślnie ESX / ox_inventory / qb-inventory (zalecony: ox_inventory) | qb-inventory / ox_inventory | ox_inventory zalecony |
| Wywołania zwrotne | ESX.RegisterServerCallback / ESX.TriggerServerCallback | QBCore.Functions.CreateCallback / TriggerCallback | Woleć ox_lib wywołania zwrotne (lib.callback.register/await) lub eksportuje QBOX |
| Warstwa bazy danych | Stary: mysql-async → Teraz: oxmysql (MySQL.query.await, MySQL.update.await) | oxmysql | oxmysql |
| UI – Powiadomienia | ESX Powiadom | Powiadomienie QB | QBOX powiadomić pomocników lub lib.notify |
| UI – Menu | esx_menu_default / ox_lib:registerContext | qb-menu / ox_lib:registerContext | ox_lib:registerContext / lib.inputDialog |
| UI – System docelowy | esx_target / ox_target | qb-target / ox_target | ox_target zalecony |
Czym różni się QBOX
- Nazwa zasobu i eksporty:
qbx_core(NIEGetCoreObject). - 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.

2) Preflight: środowisko i narzędzia
- Serwer: aktualny Serwer FX artefakty, Lua 5.4, CFX
ensurezamówienie - Zamówienie:
ensure oxmysql→ensure ox_lib→ensure framework→ensure 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.PlayerDataqbx_coreexports.ox_inventorymysql%-asyncMySQL.
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ęcie | ESX | QBCore | QBOX |
|---|---|---|---|
| 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ęcie | ESX | QBCore | QBOX |
|---|---|---|---|
| Podstawowy | identifier (licencja/Steam) | citizenid | citizenid |
| Przejście dla pieszych | tabela id_map(identifier, citizenid) | To samo | To samo |
Pieniądze
| Działanie | ESX | QBCore | QBOX |
|---|---|---|---|
| Dodaj gotówkę | xPlayer.addMoney(a) | Player.Functions.AddMoney('cash', a) | Player.PlayerData.money.cash += a (poprzez adapter + zapisz) |
| Dodaj bank | xPlayer.addAccountMoney('bank', a) | Player.Functions.AddMoney('bank', a) | Player.PlayerData.money.bank += a (adapter + zapisz) |
| Dodaj brudne | xPlayer.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ęcie | ESX | QBCore | QBOX |
|---|---|---|---|
| Przeczytaj pracę | xPlayer.job.name, .grade | Player.PlayerData.job.name, .grade.level | Player.PlayerData.job.* + pomocnicy grupowi |
| Na służbie | (różnie) | Player.PlayerData.job.onduty | SetJobDuty/pomocnicy grupowi poprzez eksporty |
| Kontrola grupowa | (dodatek) | (dodatek) | exports.qbx_core:HasGroup(src, group) (przykład) |
Spis
| Działanie | ESX | QBCore | QBOX |
|---|---|---|---|
| Dodaj element | xPlayer.addInventoryItem(n, c) | Player.Functions.AddItem(n, c, ...) | woleć ox_inventory eksport przez adapter |
| ox_inventory | exports.ox_inventory:* | exports.ox_inventory:* | exports.ox_inventory:* |
Wywołania zwrotne
| Pojęcie | ESX | QBCore | QBOX |
|---|---|---|---|
| Rejestr serwera | ESX.RegisterServerCallback | QBCore.Functions.CreateCallback | lib.callback.register (ox_lib) |
| Wyzwalacz klienta | ESX.TriggerServerCallback | QBCore.Functions.TriggerCallback | lib.callback.await |
Warstwa bazy danych
| Pojęcie | ESX/QB (starsza wersja) | oxmysql (cel) |
|---|---|---|
| Aportować | MySQL.Async.fetchAll(sql, params, cb) | local rows = MySQL.query.await(sql, {p1, ...}) |
| Aktualizacja | MySQL.Async.execute(sql, params, cb) | local aff = MySQL.update.await(sql, {p1, ...}) |
UI / Powiadom / Cel
| Pojęcie | ESX | QBCore | QBOX |
|---|---|---|---|
| Notyfikować | ESX.ShowNotification(msg) | QBCore.Functions.Notify(msg, type) | exports.qbx_core:Notify(...) Lub lib.notify({...}) |
| Menu | (różnie) | qb-menu | ox_lib:registerContext / wejścia |
| Cel | (dodatek) | qb-target | ox_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-coresą 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.RegisterServerCallbackzQBCore.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.addAccountMoneyzPlayer.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ć
citizeniddla QB z ESXuserstabeli 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.*zexports.qbx_core:*Lub ox_lib wywołania zwrotne. - Odtwarzacz:
QBCore.Functions.GetPlayer(src)→exports.qbx_core:GetPlayer(src). - Notyfikować:
QBCore.Functions.Notify→exports.qbx_core:NotifyLublib.notify. - Obowiązki/grupy: użyj eksportów QBOX (
SetJobDuty,HasGroup, itp.).
QBOX → QBCore
- Zastępować
exports.qbx_core:*zQBCore.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,callbacksobwoluta. - Wykryj strukturę w czasie wykonywania (
qbx_core→qb-core→es_extended→none). - 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ć
GlobalStateoszczędnie; unikaj aktualizacji w pętli.
9) Typowe pułapki i podręcznik debugowania
- Zarozumiały
GetCoreObjectistnieje na QBOX → nie; użyjexports.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ątkowym →
oxmysql→ox_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_ignoreminimalne; 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.luai 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.
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)
