Comment migrer ESX → QBCore correctement
Vous souhaitez une migration propre d'ESX vers QBCore sans perte de données ni panne des systèmes centraux. Suivez ce plan. Vous obtiendrez des identifiants stables, des requêtes oxmysql et du code optimisé par ox_lib.
Objectif : déplacer votre serveur d'ESX vers QBCore avec un temps d'arrêt minimal.
Aperçu
Prérequis
- Outils
- GIT et une branche séparée pour la migration.
- MariaDB ou MySQL 8 avec sauvegardes complètes activées.
- Un serveur de préparation qui reflète la production.
- Artefacts du serveur
- Serveur FX mis à jour vers la même version que la production.
- QBCore cadre de base et ressources par défaut.
- Les bibliothèques que vous utiliserez
oxmysqlpour la base de données.ox_libpour les rappels, les assistants d'interface utilisateur et les wrappers d'utilitaires.
Étape 1. Élaborez un plan et un point de retour en arrière
- Geler les modifications de production. Interrompre les nouvelles installations de scripts et les écritures dans la base de données non nécessaires aux tests.
- Sauvegardez l’intégralité de votre vidage de base de données sous forme d’instantané nommé.
- Créez une branche dans votre référentiel de serveur et créez un serveur dédié
migrer-esx-vers-qbcorebifurquer. - Rédigez un dossier d'exécution. Incluez des commandes pour démarrer et arrêter le serveur de test, restaurer la base de données et exécuter des contrôles d'intégrité.
Étape 2. Construire une base QBCore propre
- Déployez une nouvelle base QBCore en phase de test.
- Gardez uniquement les éléments essentiels activés. Désactivez les tâches, les inventaires et les scripts personnalisés jusqu'à la fin de la migration de la base de données.
- Installez et démarrez d’abord ces ressources
QB-Noyauvéhicules qbou vos remplacements préférésoxmysqlox_lib
Étape 3. Remplacez mysql-async par oxmysql
Si des scripts ESX restants utilisent encore MySQL.AsyncConvertissez les appels vers OxMySQL. Utilisez une fonction simple de recherche et de remplacement avec vérification.
Conversions courantes
-- ESX mysql-async MySQL.Async.fetchAll('SELECT * FROM utilisateurs WHERE identifiant = @id', {['@id'] = identifiant}, fonction(lignes) -- ... fin)
-- QBCore oxmysql local rows = MySQL.query.await('SELECT * FROM players WHERE citizenid = ?', { citizenid }) -- rows est une table Lua ; gère directement les vérifications nil et length
-- Exemple scalaire ESX MySQL.Async.fetchScalar('SELECT COUNT(1) FROM owned_vehicles', {}, function(count) -- ... end)
-- oxmysql scalaire local count = MySQL.scalar.await('SELECT COUNT(1) FROM player_vehicles')
-- ESX insert MySQL.Async.execute('INSÉRER DANS LES VALEURS addon_account (@owner, @name, @money)', { ['@owner'] = identifiant, ['@name'] = nom, ['@money'] = argent })
-- oxmysql insert MySQL.prepare.await('INSÉRER DANS player_accounts (citizenid, nom, montant) VALEURS (?, ?, ?)', { citizenid, nom, montant })
Remarques
- Préférer
requête.attendre,scalaire.attendre, etpréparer.attendrepour un flux propre. - Utilisez des instructions préparées pour les opérations d’écriture.
Étape 4 : Mapper les structures de données ESX vers QBCore
Vous déplacerez les identités des joueurs et les entités détenues. Utilisez cette référence pour mapper les tables.
| Table ESX | Colonne clé | Tableau QBCore | Colonne clé | Remarques |
|---|---|---|---|---|
utilisateurs | identifiant | joueurs | citoyenid | Convertir les identifiants et créer citoyenid pour chaque ligne |
véhicules possédés | propriétaire | véhicules_joueurs | citoyenid | Convertir les boîtiers de plaques et les charges utiles JSON |
magasin_de_données | propriétaire | métadonnées du joueur | citoyenid | Si vous stockez du JSON, fusionnez-le avec précaution |
données_du_compte_addon | propriétaire | comptes_joueurs | citoyenid | Associer les noms de compte aux services bancaires ou aux espèces QBCore |
addon_inventory_items | propriétaire | inventaires_des_joueurs | citoyenid | Si vous déménagez à ox_inventaire, migrer séparément |
Vous pouvez conserver des tables personnalisées. Ajustez uniquement les clés étrangères qui référencent des identifiants ESX.
Étape 5. Stabiliser les identifiants
ESX stocke souvent un identifiant CFX comme licence:xxxx ou historique vapeur:xxxx. QBCore utilise citoyenid en tant que clé de lecteur stable et conserve les identifiants d'exécution uniquement pour l'authentification.
Vous serez
- Créer un
citoyenidpour chaque joueur. - Liez les identifiants hérités au nouvel enregistrement.
- Conservez une table de consultation pour le support et les audits.
Amorçage SQL
Exécutez ceci sur une copie de votre base de données ESX pour préparer les tables QBCore.
-- 1) Créez la table des joueurs si elle est manquante. Adaptez-la à votre schéma QBCore. CREATE TABLE IF NOT EXISTS players ( citizenid VARCHAR(11) PRIMARY KEY, license VARCHAR(64) UNIQUE, identifiers JSON NOT NULL, name VARCHAR(64), charinfo JSON NOT NULL, metadata JSON NOT NULL, money JSON NOT NULL, job JSON NOT NULL, position VARCHAR(128) DEFAULT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- 2) L'équivalent d'une fonction d'assistance en SQL utilisant un générateur déterministe serait complexe. -- Au lieu de cela, organisez le mappage dans une table séparée et générez citizenid en Lua. CREATE TABLE IF NOT EXISTS legacy_identifier_map ( license VARCHAR(64) PRIMARY KEY, steam VARCHAR(64) NULL, fivem VARCHAR(64) NULL, discord VARCHAR(64) NULL, xbl VARCHAR(64) NULL, liveid VARCHAR(64) NULL, citizenid VARCHAR(11) UNIQUE ); -- 3) Amorcer le mappage à partir des utilisateurs ESX INSERT INTO legacy_identifier_map (license) SELECT DISTINCT REPLACE(identifier, 'identifier:', '') FROM users WHERE identifier LIKE 'license:%' OR identifier LIKE 'steam:%';
Générer un citizenid et insérer des joueurs dans Lua
Exécutez une fois en mode staging. Sauvegardez d'abord.
-- server/migrate_identifiers.lua local QBCore = exports['qb-core']:GetCoreObject() local function generateCitizenId() local charset = {} pour c = 65, 90 faire table.insert(charset, string.char(c)) fin pour n = 48, 57 faire table.insert(charset, string.char(n)) fin math.randomseed(GetGameTimer()) local id = {} pour i = 1, 11 faire id[i] = charset[math.random(1, #charset)] fin renvoyer table.concat(id) fin local rows = MySQL.query.await('SELECT license FROM legacy_identifier_map WHERE citizenid IS NULL') pour _, r dans ipairs(rows) faire local citizenid = generateCitizenId() MySQL.prepare.await('UPDATE legacy_identifier_map SET citizenid = ? WHERE license = ?', { citizenid, r.license }) end -- Construire des joueurs à partir d'utilisateurs ESX local users = MySQL.query.await([[SELECT u.identifier, u.firstname, u.lastname, u.dateofbirth, u.sex, u.height FROM users u]]) for _, u in ipairs(users) do local license = u.identifier local map = MySQL.single.await('SELECT citizenid FROM legacy_identifier_map WHERE license = ?', { license }) if map and map.citizenid then local name = string.format('%s %s', u.firstname or 'John', u.lastname or 'Doe') local charinfo = json.encode({ firstname = u.firstname, lastname = u.lastname, birthdate = u.dateofbirth, gender = u.sex, height = u.height }) local metadata = json.encode({ faim = 100, soif = 100 }) argent local = json.encode({ cash = 0, banque = 0, crypto = 0 }) emploi local = json.encode({ name = 'chômeur', label = 'Chômeur', grade = { name = '0', niveau = 0 }}) MySQL.prepare.await('INSERT IGNORE INTO players (citizenid, licence, identifiers, nom, charinfo, métadonnées, argent, travail) VALEURS (?, ?, ?, ?, ?, ?, ?, ?, ?)', { map.citizenid, licence, json.encode({ license = licence }), nom, charinfo, métadonnées, argent, travail }) end end print('Migration des identifiants terminée')
Déplacer les véhicules possédés
INSÉRER IGNORER DANS player_vehicles (citizenid, plaque, véhicule, garage, état) SÉLECTIONNER m.citizenid, UPPER(JSON_UNQUOTE(JSON_EXTRACT(v.vehicle, '$.plate'))), v.vehicle, 'legion', 1 DE owned_vehicles v JOIN legacy_identifier_map m ON m.license = v.owner;
Valider des échantillons aléatoires dans le jeu. Vérifier les formats de plaques et les garages.
Étape 6. Porter le code ESX vers QBCore avec ox_lib
Remplacez l'API d'exécution ESX par des équivalents QBCore. Utilisez ox_lib pour les rappels et les notifications.
Objet joueur
-- ESX local xPlayer = ESX.GetPlayerFromId(src) xPlayer.addMoney(100)
-- Lecteur local QBCore = QBCore.Functions.GetPlayer(src) Player.Functions.AddMoney('cash', 100)
Emplois
-- Vérification de la tâche ESX si xPlayer.getJob().name == 'police' alors -- ... fin
-- Vérification du travail QBCore travail local = Player.PlayerData.job si job et job.name == 'police' alors -- ... fin
Rappels et interface utilisateur
-- Rappel du serveur ESX ESX.RegisterServerCallback('resource:getData', function(source, cb) cb({ ok = true }) end)
-- ox_lib rappel lib.callback.register('resource:getData', function(source) return { ok = true } fin)
-- Notification lib.notify(source, { title = 'Emploi', description = 'Promotion accordée', type = 'succès' })
Commandes
-- ESX RegisterCommand('pay', function(src, args) local amount = tonumber(args[1]) ou 0 xPlayer.removeMoney(amount) end)
-- QBCore avec les autorisations QBCore.Commands.Add('pay', 'Payer en espèces', {{name = 'amount', help = 'Amount'}}, false, function(src, args) local amount = tonumber(args[1]) ou 0 local Player = QBCore.Functions.GetPlayer(src) Player.Functions.RemoveMoney('cash', amount) end)
Étape 7. Inventaire et articles
Si vous déménagez de es_extended stocks à qb-inventaire ou ox_inventaire, traitez cela comme une sous-migration distincte.
- Geler les ajouts d'articles.
- Exporter la liste principale des articles.
- Mapper les noms des éléments un à un.
- Migrez les inventaires des joueurs par lots. Validez la taille et le poids des piles.
Exemple de mappage d'éléments CSV
esx_name,qb_name,notes pain,pain, eau,eau, crochetage,crochetage,
Étape 8. Test et déploiement
- Tests unitaires
- Recherche d'identifiants de test pour un ensemble aléatoire de joueurs.
- Testez les transferts d’argent, les changements d’emploi et la possession d’un véhicule.
- Tests de gameplay
- Créez des joueurs avec d'anciens identifiants ESX et confirmez le mappage automatique.
- Exécutez une mission de police, un vol de magasin et un achat de véhicule.
- Tests de performance
- Utiliser
résmonpour surveiller le CPU et la mémoire. - Confirmez que le nombre de requêtes DB a chuté après la conversion oxmysql.
- Utiliser
- Plan de déploiement
- Déplacer la base de données intermédiaire vers la production pendant une fenêtre de maintenance.
- Annoncez un temps d’arrêt de 60 minutes.
- Surveillez les journaux pour les identifiants manquants et les erreurs de clé étrangère.
Dépannage
- Citoyens en double
- Cause : Exécution de la migration deux fois.
- Correction. Appliquer des clés uniques sur
citoyenidet utiliserINSÉRER IGNORERpendant le semis.
- Véhicules disparus
- Cause. La clé du propriétaire ne correspond pas entre
owned_vehicles.owneretlegacy_identifier_map.license. - Correction. Normaliser les valeurs du propriétaire et relancer l'insertion du véhicule pour les plaques concernées.
- Cause. La clé du propriétaire ne correspond pas entre
- Les joueurs apparaissent sans inventaire
- Cause. La migration de l'inventaire a été ignorée.
- Correction. Reconstruisez la cartographie de l'inventaire et réimportez.
- Les scripts échouent avec
MySQL.Asyncnon trouvé- Cause. Le script dépend toujours de mysql-async.
- Correction. Remplacez les appels par oxmysql et supprimez mysql-async du serveur.
Liste de contrôle de basculement
- Sauvegarder la base de données de production avec un horodatage.
- Arrêtez le serveur et verrouillez les connexions des joueurs.
- Restaurez le vidage final en production.
- Déployer la version QBCore avec
QB-Noyau,oxmysql,ox_libpremier dans l'ordre d'assurance. - Exécutez le script d’amorçage de l’identifiant une fois.
- Activez les scripts convertis uniquement lorsque leurs requêtes sont sur oxmysql.
- Rouvrez le serveur et surveillez les journaux pendant 30 minutes.
- Postez un plan de restauration si des erreurs critiques apparaissent.
Annexe A. Exemple de fxmanifest pour l'assistant de migration
fx_version 'cerulean' jeu 'gta5' lua54 'oui' server_scripts { '@oxmysql/lib/MySQL.lua', '@ox_lib/init.lua', 'server/migrate_identifiers.lua' }
Annexe B. Aides JSON sécurisées
local function safeDecode(jsonStr, fallback) if type(jsonStr) ~= 'string' or jsonStr == '' then return fallback end local ok, result = pcall(json.decode, jsonStr) if not ok then return fallback end return result end
Ce que vous avez accompli
- Enregistrements de joueurs stables indexés par
citoyenid. - Une couche oxmysql propre avec des instructions préparées et des attentes.
- Code ESX porté vers QBCore à l'aide des rappels et des utilitaires ox_lib.
- Un plan versionné que vous pouvez répéter pour les futurs serveurs.
Liens utiles à l'intérieur de votre site
- Centre de conversion de framework. https://fivemx.com/framework-conversion
- Guide MySQL Async vers oxmysql. https://fivemx.com/mysql-async-to-oxmysql
- Migration des identifiants SQL. https://fivemx.com/sql-identifiers-migration
- Modèles d'adaptateur pour les ports de script. https://fivemx.com/adapter-patterns
- Démarrage rapide de l'installation de QBCore. https://fivemx.com/how-to-install-qbcore
- Liste de contrôle de conversion de script. https://fivemx.com/converting-fivem-scripts
- Aperçu de QBOX avec pile ox. https://fivemx.com/qbox-ox-stack
- Résmon et la performance. https://fivemx.com/how-to-use-resmon-in-fivem-optimize-resources






