{"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":"patrones-adaptadores","status":"publish","type":"post","link":"https:\/\/rpcrate.com\/es\/adapter-patterns\/","title":{"rendered":"Crea un peque\u00f1o adaptador de framework para 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 adaptador de framework le da a tu recurso una interfaz peque\u00f1a mientras mantiene los detalles de ESX, QBCore y Qbox en un solo lugar. Comienza con una operaci\u00f3n de solo lectura, verif\u00edcala en cada framework compatible y a\u00f1ade operaciones de dinero o inventario solo cuando su comportamiento de fallo est\u00e9 definido.<\/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\/es\/wp-json\/wp\/v2\/posts\/193012","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/rpcrate.com\/es\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/rpcrate.com\/es\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/rpcrate.com\/es\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/rpcrate.com\/es\/wp-json\/wp\/v2\/comments?post=193012"}],"version-history":[{"count":1,"href":"https:\/\/rpcrate.com\/es\/wp-json\/wp\/v2\/posts\/193012\/revisions"}],"predecessor-version":[{"id":209816,"href":"https:\/\/rpcrate.com\/es\/wp-json\/wp\/v2\/posts\/193012\/revisions\/209816"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/rpcrate.com\/es\/wp-json\/wp\/v2\/media\/193013"}],"wp:attachment":[{"href":"https:\/\/rpcrate.com\/es\/wp-json\/wp\/v2\/media?parent=193012"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/rpcrate.com\/es\/wp-json\/wp\/v2\/categories?post=193012"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/rpcrate.com\/es\/wp-json\/wp\/v2\/tags?post=193012"}],"curies":[{"name":"gracias","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}