{"id":193012,"date":"2025-08-16T17:26:39","date_gmt":"2025-08-16T15:26:39","guid":{"rendered":"https:\/\/fivemx.com\/?p=193012"},"modified":"2026-07-03T04:10:26","modified_gmt":"2026-07-03T02:10:26","slug":"modeles-dadaptateur","status":"publish","type":"post","link":"https:\/\/rpcrate.com\/fr\/adapter-patterns\/","title":{"rendered":"Construire un petit adaptateur de framework FiveM"},"content":{"rendered":"<p>A framework adapter gives your resource a small interface while keeping ESX, QBCore and Qbox details in one place. Start with a read-only operation, verify it on each supported framework, and add money or inventory operations only when their failure behavior is defined.<\/p>\n<p>The configuration and code examples here target FiveM for GTA V Legacy. Enhanced has different runtime and compatibility rules; check <a href=\"https:\/\/docs.fivem.net\/docs\/developers\/legacy-vs-enhanced\/\">Cfx.re\u2019s Legacy-to-Enhanced changes<\/a> before applying them to an Enhanced server.<\/p>\n<h2 id=\"define-the-contract-first\">Define the contract first<\/h2>\n<p>The example below returns the logged-in character identifier, or <code data-no-translation=\"\" translate=\"no\">nil<\/code> if no character is loaded. It runs on the server only. It does not convert existing identifiers or make the three frameworks share a database. Use one selected framework per deployment.<\/p>\n<h2 id=\"create-the-resource\">Create the resource<\/h2>\n<p>Create <code data-no-translation=\"\" translate=\"no\">resources\/[local]\/character_adapter\/<\/code> with the following three files. This example selects QBCore. For ESX or Qbox, change both the manifest dependency and the <code data-no-translation=\"\" translate=\"no\">framework<\/code> value. Do not enable all three frameworks to test the adapter.<\/p>\n<p><code data-no-translation=\"\" translate=\"no\">fxmanifest.lua<\/code>:<\/p>\n<pre data-no-translation=\"\" translate=\"no\"><code data-no-translation=\"\" translate=\"no\">fx_version 'cerulean'\ngame 'gta5'\n\ndependency 'qb-core'\nserver_scripts { 'adapter.lua', 'server.lua' }<\/code><\/pre>\n<p><code data-no-translation=\"\" translate=\"no\">adapter.lua<\/code>:<\/p>\n<pre data-no-translation=\"\" translate=\"no\"><code data-no-translation=\"\" translate=\"no\">local framework = 'qbcore' -- 'esx', 'qbcore' or 'qbox'\nCharacterAdapter = {}\n\nif framework == 'esx' then\n    local ESX = exports['es_extended']:getSharedObject()\n    function CharacterAdapter.identifier(src)\n        local player = ESX.GetPlayerFromId(src)\n        return player and player.identifier or nil\n    end\nelseif framework == 'qbcore' then\n    local QBCore = exports['qb-core']:GetCoreObject()\n    function CharacterAdapter.identifier(src)\n        local player = QBCore.Functions.GetPlayer(src)\n        return player and player.PlayerData.citizenid or nil\n    end\nelseif framework == 'qbox' then\n    function CharacterAdapter.identifier(src)\n        local player = exports.qbx_core:GetPlayer(src)\n        return player and player.PlayerData.citizenid or nil\n    end\nelse\n    error('Select a supported framework in adapter.lua')\nend<\/code><\/pre>\n<p><code data-no-translation=\"\" translate=\"no\">server.lua<\/code>:<\/p>\n<pre data-no-translation=\"\" translate=\"no\"><code data-no-translation=\"\" translate=\"no\">RegisterCommand('adapter_check', function(src, args)\n    if src ~= 0 then return end -- server console only\n    local target = tonumber(args[1])\n    if not target or target &lt; 1 or target % 1 ~= 0 then\n        print('Usage: adapter_check &lt;player server ID&gt;')\n        return\n    end\n    local identifier = CharacterAdapter.identifier(target)\n    print(identifier and 'Character resolved.' or 'No loaded character.')\nend, false)<\/code><\/pre>\n<h2 id=\"run-and-check\">Run and check<\/h2>\n<p>Add <code data-no-translation=\"\" translate=\"no\">ensure character_adapter<\/code> after the selected framework in your active server configuration. With a logged-in test character whose server ID is 12, run <code data-no-translation=\"\" translate=\"no\">adapter_check 12<\/code> in the FXServer console. Expect <code data-no-translation=\"\" translate=\"no\">Character resolved.<\/code>; repeat after that character disconnects and expect <code data-no-translation=\"\" translate=\"no\">No loaded character.<\/code>. The command deliberately does not print identifiers.<\/p>\n<p>Repeat on separate ESX and Qbox test installations after selecting their dependency and adapter branch. A Lua syntax check or mocked export test alone cannot confirm a live framework integration.<\/p>\n<h2 id=\"extend-without-hiding-failures\">Extend without hiding failures<\/h2>\n<p>For each new operation, document arguments, return values and errors. Inventory additions must distinguish full inventory from unknown items. Money operations must use supported framework methods and preserve their success result. Never return success unconditionally or mutate <code data-no-translation=\"\" translate=\"no\">PlayerData.money<\/code> directly.<\/p>\n<p>Keep lifecycle handlers specific to the installed framework. <code data-no-translation=\"\" translate=\"no\">playerJoining<\/code> is not proof that a character has loaded. Use local handlers for local events; do not make internal grant or job-update handlers network-accessible for convenience.<\/p>\n<h2 id=\"test-the-boundary\">Test the boundary<\/h2>\n<p>Check missing players, resource start order, character switching and reconnects. For write operations, also test denied access, duplicate requests, insufficient funds and partial failures. Keep identifiers opaque and retain any migration crosswalk as recovery evidence.<\/p>\n<h2 id=\"reference-documentation\">Reference documentation<\/h2>\n<p><a href=\"https:\/\/docs.qbox.re\/resources\/qbx_core\/exports\/server\">Source: docs.qbox.re<\/a> \u00b7 <a href=\"https:\/\/qbcore.org\/docs\/qb-core\/server-function-reference\">Source: qbcore.org<\/a> \u00b7 <a href=\"https:\/\/github.com\/esx-framework\/esx_core\">Source: esx-framework\/esx_core<\/a> \u00b7 <a href=\"https:\/\/docs.fivem.net\/docs\/scripting-manual\/working-with-events\/listening-for-events\/\">Cfx.re \u2014 listening for events<\/a><\/p>\n<p><a href=\"https:\/\/rpcrate.com\/simultaneous-use-esx-qbcore\/\">Simultaneous Use of ESX and QBCore: Why It\u2019s Not Feasible<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Un adaptateur de framework donne \u00e0 votre ressource une petite interface tout en gardant les d\u00e9tails ESX, QBCore et Qbox au m\u00eame endroit. Commencez par une op\u00e9ration en lecture seule, v\u00e9rifiez-la sur chaque framework pris en charge, et n'ajoutez des op\u00e9rations d'argent ou d'inventaire que lorsque leur comportement en cas d'\u00e9chec est d\u00e9fini.<\/p>","protected":false},"author":1,"featured_media":193013,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[2882],"tags":[2858,2950,2859,2940],"class_list":["post-193012","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-framework-conversion","tag-esx","tag-framework","tag-qbcore","tag-qbox"],"_links":{"self":[{"href":"https:\/\/rpcrate.com\/fr\/wp-json\/wp\/v2\/posts\/193012","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/rpcrate.com\/fr\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/rpcrate.com\/fr\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/rpcrate.com\/fr\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/rpcrate.com\/fr\/wp-json\/wp\/v2\/comments?post=193012"}],"version-history":[{"count":1,"href":"https:\/\/rpcrate.com\/fr\/wp-json\/wp\/v2\/posts\/193012\/revisions"}],"predecessor-version":[{"id":209816,"href":"https:\/\/rpcrate.com\/fr\/wp-json\/wp\/v2\/posts\/193012\/revisions\/209816"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/rpcrate.com\/fr\/wp-json\/wp\/v2\/media\/193013"}],"wp:attachment":[{"href":"https:\/\/rpcrate.com\/fr\/wp-json\/wp\/v2\/media?parent=193012"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/rpcrate.com\/fr\/wp-json\/wp\/v2\/categories?post=193012"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/rpcrate.com\/fr\/wp-json\/wp\/v2\/tags?post=193012"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}