Dokumentation
Offizielle Installations- und Konfigurationsanleitung für das Nebula Shop-System — bestehend aus nebula_core, nebula_notify und nebula_shop.
Was ist enthalten?
Das Nebula Shop-System besteht aus drei einzelnen Ressourcen, die zusammenarbeiten. Jede hat eine klar definierte Aufgabe.
Voraussetzungen
Folgende Ressourcen müssen auf deinem Server laufen, bevor die Nebula Scripts gestartet werden.
| Ressource | Zweck | Pflicht |
|---|---|---|
| oxmysql | Datenbankanbindung (wird direkt in fxmanifest eingebunden) | ✅ Ja |
| es_extended | ESX Framework — Geld, Inventar, Player | ESX oder QBCore |
| qb-core | QBCore Framework — Alternative zu ESX | ESX oder QBCore |
| ox_core | ox_core Framework — weitere Alternative | Optional |
| ox_inventory | Inventarsystem (wird bevorzugt erkannt) | Optional |
| qs-inventory | Alternative zu ox_inventory | Optional |
| qb-inventory | QBCore Inventarsystem | Optional |
| codem-inventory | Weiteres unterstütztes Inventarsystem | Optional |
Startreihenfolge
Die Ressourcen haben feste Dependencies. nebula_shop deklariert in seiner fxmanifest.lua explizit beide anderen als Abhängigkeiten.
# Framework (nur das, was du verwendest) ensure oxmysql ensure es_extended # oder qb-core # Optional: ox_inventory, qs-inventory, etc. ensure ox_inventory # Nebula — genau in dieser Reihenfolge! ensure nebula_core ensure nebula_notify ensure nebula_shop
nebula_core
Das Herzstück des Systems. Verwaltet die Datenbank, die dynamische Preis-Engine, Sicherheitsfilter und stellt Exports für alle anderen Nebula-Scripts bereit.
Installation
/resources/[nebula]/nebula_core/ ├── config.lua ├── fxmanifest.lua ├── migration_fix_shopid.sql ├── server/ │ ├── adapters.lua ← Framework/Inventory Auto-Detection │ ├── db.lua ← Database layer │ ├── logger.lua ← Discord Webhook Logging │ ├── main.lua ← Purchase logic & security │ ├── pricing.lua ← Dynamic pricing engine │ └── utils.lua └── shared/ └── api.lua
-- File: nebula_core/migration_fix_shopid.sql -- Run once if you get shop_id errors: ALTER TABLE ns_price_state MODIFY COLUMN shop_id INT NOT NULL DEFAULT 0;
ensure nebula_core
nebula_notify
Das Notification-System für alle Nebula Scripts. Stellt drei Exports bereit: Notify, TextUI und HideTextUI. Wird von nebula_shop automatisch verwendet.
Installation
ensure nebula_notify
Verfügbare Exports
| Export | Parameter | Beschreibung |
|---|---|---|
| Notify | message, type, duration | Zeigt eine Notification. type: 'success' | 'error' | 'info'. duration in ms (Standard: 3500) |
| TextUI | message | Zeigt den Interaktionshinweis beim Nähern an einem Marker |
| HideTextUI | — | Versteckt die TextUI wieder |
-- Send notification exports['nebula_notify']:Notify('Purchase successful!', 'success', 3500) exports['nebula_notify']:Notify('Not enough money.', 'error', 4000) -- TextUI interaction hint exports['nebula_notify']:TextUI('~INPUT_CONTEXT~ Open Shop') exports['nebula_notify']:HideTextUI()
nebula_shop
Verwaltet alle Shops mit Markern, Blips und der NUI-Oberfläche. Beim Serverstart werden alle Shops aus der config.lua automatisch in die Datenbank synchronisiert.
Installation
/resources/[nebula]/nebula_shop/ ├── config.lua ← Define all shops & items here ├── fxmanifest.lua ├── client/ │ └── main.lua ← Markers, blips, proximity detection ├── server/ │ └── main.lua ← Config sync, shop data, purchase handling └── web/ ├── index.html ├── app.js ← Shop NUI interface ├── style.css └── items/ ← Item images (.png/.jpg/.webp)
ensure nebula_shop
Ingame-Steuerung
| Aktion | Standard | Config-Variable |
|---|---|---|
| Shop öffnen | E (Key 38) | NebulaShop.OpenKey |
| Marker anzeigen ab | 15.0 | NebulaShop.DrawDistance |
| Interaktionsradius | 2.0 | NebulaShop.InteractDistance |
nebula_core — config.lua
Steuert die Preis-Engine, Sicherheitseinstellungen und das Discord-Logging.
NebulaCore = {} -- Debug output in server console (set false in production!) NebulaCore.Debug = true NebulaCore.Price = { Enabled = true, -- Enable dynamic pricing? Scope = 'global', -- 'global' = shared price across shops -- 'shop' = each shop has its own price UpdateIntervalSeconds = 60, -- How often prices are recalculated MaxStepPercent = 0.15, -- Max 15% price change per update RandomRange = 0.08, -- ±8% random market fluctuation DemandWeight = 0.35, -- Influence of purchase demand (last hour) DefaultTrend = 1.0 } NebulaCore.Security = { MaxPurchasesPer10s = 50, -- Rate limiting: max purchases in 10 seconds MaxQtyPerTxn = 200, -- Max quantity per single purchase LockTimeoutMs = 500 -- Minimum pause between purchases (ms) } NebulaCore.Logging = { DiscordWebhook = '', -- Discord webhook URL for purchase logs LogPurchases = true, LogSuspicious = true }
| Variable | Typ | Beschreibung |
|---|---|---|
| NebulaCore.Debug | boolean | Preis-Updates und Events in der Serverkonsole ausgeben |
| Price.Scope | string | 'global': ein globaler Preis für alle Shops. 'shop': eigene Preise pro Shop |
| Price.UpdateIntervalSeconds | number | Sekunden zwischen automatischen Preis-Updates |
| Price.MaxStepPercent | number | Maximale Preisänderung pro Update (0.15 = max ±15%) |
| Price.RandomRange | number | Zufällige Marktschwankung auch ohne Käufe (0.08 = ±8%) |
| Price.DemandWeight | number | Wie stark Käufe den Preis erhöhen (0.35 = 35% Einfluss) |
| Security.MaxPurchasesPer10s | number | Rate-Limiting: maximale Käufe innerhalb von 10 Sekunden |
| Security.MaxQtyPerTxn | number | Maximale Stückzahl in einem einzelnen Kauf |
| Logging.DiscordWebhook | string | Discord Webhook URL — leer lassen um Logging zu deaktivieren |
nebula_shop — config.lua
Hier werden alle Shops mit Koordinaten, Markern, Blips und Items definiert. Beim Serverstart werden diese automatisch in die Datenbank synchronisiert — kein manuelles SQL nötig.
NebulaShop = {} NebulaShop.OpenKey = 38 -- E key to open shop NebulaShop.DrawDistance = 15.0 -- Marker visible from this distance NebulaShop.InteractDistance = 2.0 -- Interaction hint at this distance NebulaShop.Shops = { { key = '247_grove', -- Unique key (must be unique!) name = '24/7 (Grove)', type = 'shop', coords = vector3(25.7, -1346.6, 29.5), marker = { enabled = true, type = 2, scale = 0.65 }, blip = { enabled = true, sprite = 52, scale = 0.75, colour = 2, label = '24/7' }, items = { { name = 'bread', label = 'Bread', base_price = 50, min_price = 30, max_price = 120, tax = 0.05 }, { name = 'water', label = 'Water', base_price = 35, min_price = 20, max_price = 100, tax = 0.05 }, } }, -- Black market example (no blip, hidden location) { key = 'black_market_grove', name = 'Black Market', type = 'shop', coords = vector3(318.6, -197.5, 53.5), marker = { enabled = true, type = 2, scale = 0.5 }, blip = { enabled = false }, -- No blip on the map! items = { { name = 'weed_brick', label = 'Weed', base_price = 800, min_price = 500, max_price = 1800 }, } }, }
Item-Felder erklärt
| Feld | Typ | Beschreibung |
|---|---|---|
| name | string | Item-Name — muss exakt mit deiner items.lua übereinstimmen |
| label | string | Anzeigename im Shop UI |
| base_price | number | Ausgangspreis für die Preis-Engine |
| min_price | number | Untere Preisgrenze — Preis fällt nie darunter |
| max_price | number | Obere Preisgrenze — Preis steigt nie darüber |
| tax | number | Steuer-Multiplikator (0.05 = 5% Aufschlag, optional) |
Dynamische Preise
Die Preis-Engine in nebula_core berechnet Preise in Echtzeit basierend auf Nachfrage und Marktschwankungen.
Wie funktioniert die Berechnung?
target = base_price × demandFactor × randomFactorDann wird der Preis sanft in Richtung des Zielpreises verschoben (max. MaxStepPercent pro Update) und innerhalb von min_price und max_price gehalten.
| Faktor | Beschreibung |
|---|---|
| demandFactor | Steigt mit der Anzahl der Käufe in der letzten Stunde. Bei 500+ Käufen: +35% (DemandWeight) |
| randomFactor | Zufällige Schwankung ±RandomRange% — bewegt Preise auch ohne Käufe |
| smoothStep | Verhindert abrupte Preissprünge — max. MaxStepPercent Änderung pro Update |
| Scope: global | Alle Shops teilen denselben Preis für ein Item |
| Scope: shop | Jeder Shop berechnet seinen eigenen Preis unabhängig |
Exports & API
nebula_core stellt Exports bereit, über die andere Ressourcen auf die Datenbank und Preis-Engine zugreifen können.
-- Get current price of an item local price = exports['nebula_core']:getPrice('bread', shopId) -- Ensure/create a shop (returns shopId) local shopId = exports['nebula_core']:dbEnsureShop({ name = 'My Shop', ... }) -- Register/update an item in the database exports['nebula_core']:dbUpsertItem({ name = 'bread', label = 'Bread', base_price = 50, ... }) -- Assign item to a shop exports['nebula_core']:dbUpsertShopItem(shopId, 'bread', { stock_mode = 'infinite' }) -- Get all items of a shop local items = exports['nebula_core']:dbGetShopItems(shopId) -- Calculate price via pricing engine local price = exports['nebula_core']:pricingGetPrice('bread', shopId)