Framework hub
Move into the QBCore landing page to compare verified scripts, framework fit, and install-ready products built for modern FiveM servers.
Open QBCore hubNutze diesen Guide, um die Framework-Entscheidung einzugrenzen, und wechsle dann in die zentralen Angebotsseiten für verifizierte Scripts, kuratierte Bundles und einen schnelleren Server-Launch.
Framework hub
Move into the QBCore landing page to compare verified scripts, framework fit, and install-ready products built for modern FiveM servers.
Open QBCore hubFramework hub
Use the ESX landing page to compare framework-specific resources, launch guidance, and premium products that fit ESX-first servers.
Open ESX hubPremium catalog
Move from research into the main shop to compare real products, framework labels, screenshots, and production-ready quality signals.
Open premium shopFiveM Frameworks bilden das Rückgrat von Roleplay-Servern. Sie sind nicht nur Code-Bibliotheken — sie sind komplette Systeme, die Spieleridentität, Jobs, Inventar, Berechtigungen,…
Die Wahl eines Frameworks ist die folgenreichste Entscheidung beim Aufbau eines FiveM-Servers. Sie bestimmt, welche Scripts du nutzen kannst, wie deine Entwickler Code schreiben,…
QBox hat sich 2026 fest als natuerlicher Nachfolger von QBCore im FiveM-Roleplay-Oekosystem etabliert.
Dies ist ein FiveM Framework Adapter – für Scripter. Liefere eine einzige Ressource, die auf ESX, QBCore und QBOX läuft, indem du framework-spezifische Aufrufe hinter einem…


Dies ist ein FiveM Framework Adapter – für Scripter. Liefere eine einzige Ressource, die auf ESX, QBCore und QBOX läuft, indem du framework-spezifische Aufrufe hinter einem schlanken Adapter isolierst. Füge die shared/fw.lua und die framework-spezifischen Adapter in jede Ressource ein, rufe den stabilen Interface-Vertrag (FW.Player, FW.Job, FW.Money, FW.Inv, FW.Events) auf und halte die Geschäftslogik framework-agnostisch. Eine kleine Test-Matrix mit Stubs erkennt Konflikte, bevor du deployst.
Framework-Unterschiede konzentrieren sich auf dieselben Stellen:
getSharedObject, QBCore GetCoreObject, QBOX nur Exports)Ein einheitliches Interface hält diese Unterschiede aus deiner Spiellogik heraus. Du tauschst den Adapter aus, nicht die Codebasis.
BTW: Unseren fertigen Adapter kannst du hier kostenlos nutzen:
Verzeichnisstruktur (empfohlen):
my-resource/ ├─ fxmanifest.lua ├─ shared/ │ ├─ adapters/ │ │ ├─ esx.lua │ │ ├─ qb.lua │ │ └─ qbox.lua │ └─ fw.lua ├─ server/ │ └─ main.lua └─ client/ └─ main.lua
fxmanifest.lua (Adapter zuerst laden, dann fw.lua, damit die Erkennung binden kann):
| fx_version 'cerulean' | game 'gta5' lua54 'yes' |
|---|---|
| shared_scripts { lua | 'shared/adapters/*.lua', 'shared/fw.lua' } |
| client_scripts { ```lua 'client/*.lua' } | server_scripts { lua |
'@oxmysql/lib/MySQL.lua', -- optional: falls du SQL verwendest
'server/*.lua'
}
**In deinem Code** (Server oder Client):
```lua
```lua
-- überall das stabile Interface verwenden
local src = source
local p = FW.Player.getBySrc(src)
local job = FW.Job.getName(p)
FW.Money.add(p, 'cash', 250, 'lieferbonus')
FW.Inv.addItem(p, 'water', 1)
FW.Events.notify(src, 'Job-Bonus ausgezahlt.', 'success')
> Das einzige Symbol, von dem du abhängst, ist `FW`. Alles andere ist intern für die **Adapter**.
* * *
## Interface-Vertrag (stabiler Oberflächenbereich)
Designziel: **Klein, explizit, dokumentiert.** Dies sind die Funktionen, auf die du framework-übergreifend zählen kannst.
### `FW.meta`
* `name() -> 'esx'|'qbcore'|'qbox'`
* `has(resourceName: string) -> boolean` (Ressource gestartet?)
### `FW.Player`
* `getBySrc(src: number) -> any` (Framework-Player-Handle)
* `getStateId(p) -> string` (ESX: identifier; QB/QBOX: citizenid)
* `getServerId(p) -> number` (numerische ID)
* `getName(p) -> string`
### `FW.Job`
* `getName(p) -> string`
* `getGrade(p) -> number|string`
* `onChange(handler(src, oldJob, newJob))` (feuert bei Job-Wechsel, falls erkennbar)
### `FW.Money`
* `get(p, account: 'cash'|'bank'|'black_money'?) -> number`
* `add(p, account, amount: number, reason?: string)`
* `remove(p, account, amount: number, reason?: string)`
### `FW.Inv` (Best-Effort; siehe Hinweise)
* `addItem(p, name: string, count: number, metadata?: table) -> boolean`
* `removeItem(p, name: string, count: number, metadata?: table) -> boolean`
> ## Inventar-Hinweis: Server variieren (qb-inventory,
>
> **Inventar-Hinweis:** Server variieren (qb-inventory, ox_inventory, qs‑inventory usw.). Die Standard-Implementierung nutzt das Framework-Inventar, falls verfügbar, und fällt auf [`ox_inventory`](https://overextended.dev/ox_inventory) zurück, wenn erkannt.
### `FW.Events`
* `notify(target: number, msg: string, type?: 'info'|'success'|'error')`
* `onPlayerLoaded(handler(src))` (Best-Effort, mit Fallback über `playerJoining`)
* * *
## Drop-in-Adapter (Copy/Paste)
Dies sind pragmatische Standardwerte. Falls dein Fork abweicht (besonders bei QBOX), passe die wenigen markierten Kommentare an.
### `shared/fw.lua`
```lua
-- Framework Bridge Bootstrap
FW = FW or {}
local function started(name)
local st = GetResourceState(name)
return st == 'started' or st == 'starting'
end
local which
if started('qbx_core') then which = 'qbox'
elseif started('qb-core') then which = 'qbcore'
elseif started('es_extended') then which = 'esx' end
if which == 'qbcore' then
FW = Adapters.qb()
elseif which == 'qbox' then
FW = Adapters.qbox()
elseif which == 'esx' then
FW = Adapters.esx()
else
error('[FW\] Kein unterstütztes Framework gefunden (es_extended / qb-core / qbx_core).')
end
-- kleine Hilfsfunktionen, die für alle Adapter gelten
function FW.meta.has(res)
return started(res)
end
shared/adapters/esx.lua, qb.lua, qbox.luaDie vollständigen Adapter-Implementierungen sind identisch mit dem Original — nur die Kommentare wurden auf Deutsch übersetzt. Alle Lua-Funktionsnamen, Event-Namen und API-Aufrufe bleiben unverändert.
RegisterNetEvent('myres:payBonus', function()
local src = source
local p = FW.Player.getBySrc(src)
if not p then return end
if FW.Job.getName(p) == 'delivery' then
FW.Money.add(p, 'cash', 250, 'lieferbonus')
FW.Events.notify(src, 'Bonus ausgezahlt (+$250).', 'success')
else
FW.Events.notify(src, 'Du bist nicht als Lieferant im Dienst.', 'error')
end
end)
local function giveStarter(src)
local p = FW.Player.getBySrc(src)
if p then FW.Inv.addItem(p, 'water', 2) end
end
FW.Events.onPlayerLoaded(giveStarter)
| Anti-Pattern | Warum es schadet | Lösung mit Adapter |
|---|---|---|
Core-Objekt hardcoden (ESX = exports['es_extended']:getSharedObject() überall verstreut) | Bindet an ESX, mühsam zu migrieren | Nur FW.* aufrufen. Core-Auflösung liegt im Adapter. |
| Framework-Player-Handle langfristig speichern | Handles können veralten; Referenzen variieren je Framework | Per FW.Player.getBySrc(src) beim Handeln neu holen oder per getStateId-Key cachen und neu auflösen. |
Identifier-Annahmen treffen (ESX identifier vs QB/QBOX citizenid) | Bricht DB-Relationen/Migrationen | FW.Player.getStateId(p) und eine Crosswalk-Tabelle bei Migrationen verwenden. |
Direkte Event-Namen in der Geschäftslogik (esx:playerLoaded, QBCore:Server:PlayerLoaded) | Fragil bei Forks | Über FW.Events.onPlayerLoaded abonnieren. |
| Gemischte Inventar-Annahmen | Server tauschen Inventare oft | FW.Inv.* verwenden, das zuerst ox_inventory erkennt, dann das Framework. |
shared/adapters/*.lua und shared/fw.lua in deine Ressource einbindenFW.* ersetzenstate_id in deinen TabellenAls nächstes lesen:
Entdecke unsere und die für sofort einsetzbare Ressourcen.
Dieser Abschnitt zeigt, wie du den Adapter in verschiedenen Szenarien konfigurierst und anpasst.
ox_inventory Priorität erzwingenStandardmäßig erkennt der Adapter ox_inventory automatisch und verwendet es. Wenn du die Priorität explizit festlegen möchtest (z.B. wenn sowohl ox_inventory als auch das Framework-eigene Inventar installiert sind), kannst du dies in shared/fw.lua tun:
-- In shared/fw.lua, vor dem Adapter-Bootstrap
FW = FW or {}
FW.config = FW.config or {}
FW.config.inventoryPriority = 'ox' -- oder 'framework', um das Framework-Inventar zu priorisieren
-- Framework Bridge Bootstrap (wie gehabt)
local function started(name)
local st = GetResourceState(name)
return st == 'started' or st == 'starting'
end
local which
if started('qbx_core') then which = 'qbox'
elseif started('qb-core') then which = 'qbcore'
elseif started('es_extended') then which = 'esx' end
if which == 'qbcore' then
FW = Adapters.qb()
elseif which == 'qbox' then
FW = Adapters.qbox()
elseif which == 'esx' then
FW = Adapters.esx()
else
error('[FW] Kein unterstütztes Framework gefunden (es_extended / qb-core / qbx_core).')
end
-- kleine Hilfsfunktionen, die für alle Adapter gelten
function FW.meta.has(res)
return started(res)
end
Anstatt die standardmäßige FW.Events.notify-Funktion zu verwenden, möchtest du vielleicht dein eigenes Benachrichtigungssystem integrieren. Dazu kannst du die Funktion im entsprechenden Adapter überschreiben:
-- In shared/adapters/esx.lua (oder qb.lua/qbox.lua)
local M = {}
-- Hier die Standard-Implementierungen...
function M.Events.notify(target, msg, type)
-- Deine eigene Benachrichtigungslogik hier
-- Beispiel:
TriggerClientEvent('my_custom_notify', target, msg, type)
end
return M
Stelle sicher, dass du die clientseitige Komponente my_custom_notify entsprechend implementierst.
Die FW.Job.onChange-Erkennung basiert auf Framework-spezifischen Events. Wenn dein Server diese Events verändert hat oder andere Events verwendet, musst du die Adapter entsprechend anpassen.
-- In shared/adapters/qb.lua (oder esx.lua/qbox.lua)
local M = {}
-- Hier die Standard-Implementierungen...
function M.Job.onChange(handler)
-- Achtung: 'QBCore:Server:PlayerJobUpdate' ist ein Beispiel. Ersetze es, falls nötig.
RegisterNetEvent('QBCore:Server:PlayerJobUpdate')
AddEventHandler('QBCore:Server:PlayerJobUpdate', function(playerData)
local src = tonumber(playerData.source)
local p = FW.Player.getBySrc(src)
if not p then return end
local oldJob = playerData.job.name
local newJob = M.Job.getName(p) -- Annahme: M.Job.getName liefert den aktuellen Job
handler(src, oldJob, newJob)
end)
end
return M
Dieser Abschnitt behandelt häufige Probleme bei der Verwendung des Framework-Adapters und bietet Lösungen.
FW.*Dieser Fehler tritt auf, wenn der Adapter nicht korrekt initialisiert wurde. Überprüfe Folgendes:
fw.lua vor deinen eigenen Ressourcen gestartet werden. Die fxmanifest.lua sollte die Adapterdateien vor den Skriptdateien der Ressource laden! Starte ggf. die Ressource im Server neu.FW.* korrekt geschrieben hast und nicht versehentlich den Namen einer Variable verändert hast.Dieser Fehler tritt häufig auf, wenn die Player-Handle, die vom Framework bereitgestellt wird, veraltet ist. Verwende niemals gecachte Player-Handles über längere Zeit. Stattdessen:
FW.Player.getBySrc(src) jedes Mal dann, wenn du auf Spielerdaten zugreifen musst. Dadurch stellst du sicher, dass du immer eine aktuelle Referenz hast.FW.Player.getStateId(p) als Schlüssel und löse die Player-Handle bei Bedarf mit getBySrc neu auf.Überprüfe Folgendes, wenn FW.Inv.* nicht wie erwartet funktioniert:
ox_inventory installiert? Wenn du ox_inventory verwendest, stelle sicher, dass es korrekt installiert und gestartet ist.FW.config.inventoryPriority in shared/fw.lua, um sicherzustellen, dass die korrekte Inventar-Implementierung priorisiert wird.ox_inventory nicht gefunden wurde oder es Probleme mit den Inventar-Exports gibt.Es gibt alternative Ansätze, um die Framework-Abhängigkeit deiner Scripte zu reduzieren. Hier ein kurzer Vergleich:
-- Beispiel: Direkter Wrapper (Anti-Pattern)
local function giveMoney(src, amount)
if GetResourceState('es_extended') == 'started' then
local xPlayer = ESX.GetPlayerFromId(src)
xPlayer.addMoney(amount)
elseif GetResourceState('qb-core') == 'started' then
local Player = QBCore.Functions.GetPlayer(src)
Player.Functions.AddMoney('cash', amount)
end
end
Message Bus: Eine Message Bus (wie npwd) ermöglicht die Kommunikation zwischen Ressourcen ohne direkte Abhängigkeit. Dies ist ein flexibler Ansatz, erfordert aber eine komplexere Konfiguration und kann die Performance beeinträchtigen. Der Framework-Adapter ist einfacher einzurichten und optimiert für den direkten Zugriff auf Framework-Funktionen.
Abstrakte Klassen/Interfaces (OOP): Fortgeschrittene Entwickler könnten versuchen, abstrakte Klassen oder Interfaces in Lua zu verwenden, um die Framework-Abhängigkeit zu abstrahieren. Dies kann zu sauberem Code führen, ist aber komplex und erfordert ein fundiertes Verständnis von objektorientierter Programmierung in Lua. Der Framework-Adapter bietet eine pragmatische, funktionsorientierte Lösung, die für die meisten Anwendungsfälle ausreichend ist.
state_id als Schlüssel und löse die Player-Handles neu auf, bevor du auf Spielerdaten zugreifst.FW.* zu protokollieren oder zu validieren. Dies kann hilfreich sein, um Fehler zu finden oder sicherzustellen, dass die Daten konsistent sind.-- Beispiel: Middleware für Money.add
local originalAdd = FW.Money.add
FW.Money.add = function(p, account, amount, reason)
print(string.format("Spieler %s gibt %d %s aufgrund von %s.", FW.Player.getName(p), amount, account, reason))
return originalAdd(p, account, amount, reason)
end
FW.*-Funktionen zu simulieren und verschiedene Szenarien zu testen. Dies hilft, Regressionen zu vermeiden und die Codequalität zu verbessern.FW.meta.name() verwenden, um das aktuelle Framework zu ermitteln und bedingt auf dessen Funktionen zuzugreifen. Dies sollte jedoch nur als letztes Mittel verwendet werden, um die Abhängigkeit vom Framework so gering wie möglich zu halten.qb-inventory) gestartet sind, bevor du auf deren Funktionen zugreifst.QBCore.Functions.GetPlayer Funktion gibt ein Player-Objekt zurück. Achte auf die Namenskonvention (gross geschrieben am Anfang).Durch Beachtung dieser Punkte kannst du sicherstellen, dass deine Scripte reibungslos auf allen drei Frameworks laufen.
Nutze diese internen Ressourcen, um Adapter-Patterns: ESX, QBCore & QBOX (Exports, Events & APIs) im Zusammenhang mit Setup, Framework, Shop-Ressourcen und Serverbetrieb zu pruefen.
Der Adapter enthält eine kleine Test-Matrix mit Stubs, die dazu dient, Konflikte frühzeitig zu erkennen. Untersuche diese Test-Matrix sorgfältig und stelle sicher, dass deine Ressource alle Tests erfolgreich durchläuft, bevor du sie deployst. Dies hilft dabei, potenzielle Probleme durch Framework-Unterschiede in deiner Spiellogik zu identifizieren und zu beheben, bevor sie sich auf deine Spieler auswirken.
Der Framework-Adapter bietet ein einheitliches Interface (`FW.Player`, `FW.Job`, `FW.Money`, `FW.Inv`). Anstatt framework-spezifische Funktionen wie `getSharedObject` (ESX) oder `GetCoreObject` (QBCore) direkt zu verwenden, nutzt du die Funktionen des Adapters. Dieser kapselt die Unterschiede in den Player-Modellen (xPlayer vs Player/PlayerData) und den entsprechenden APIs, sodass dein Code framework-agnostisch bleibt und du nur den Adapter austauschen musst, wenn du das Framework wechselst.
Launch faster
Bundles shorten the path from planning to launch by grouping the highest-leverage scripts into a cleaner commercial starting point.
Eine vollständige Umschreibung ist in der Regel nicht erforderlich. Du kannst den Adapter schrittweise in deine bestehenden Skripte integrieren. Beginne damit, die `shared/fw.lua` und die entsprechenden Framework-Adapter in deine Ressource einzubinden. Ersetze dann nach und nach framework-spezifische Aufrufe durch die äquivalenten Funktionen des Adapters (`FW.Player`, `FW.Money` usw.). Teste jede Änderung gründlich, um sicherzustellen, dass die Funktionalität erhalten bleibt und keine neuen Fehler eingeführt werden.
Es ist wichtig, die Adapterdateien (*.lua im `shared/adapters/` Ordner) vor der `shared/fw.lua` Datei zu laden. Dies ermöglicht es dem Adapter, das aktuell verwendete Framework zu erkennen und die entsprechenden Funktionen zu binden. Eine falsche Reihenfolge kann dazu führen, dass die Framework-Erkennung fehlschlägt, was zu Fehlern in deinen Skripten führen kann.