FiveM-Lager und Aufbewahrung: Script für gemeinsame Lager
Richte gemeinsame Lager in FiveM ein, etwa für Gangs oder Jobs. Mit Gewichtslimits und Scripts für ox_inventory und mehr.
Agency Scripts
Gründer und leitender Entwickler bei Agency Scripts
Lagersysteme auf Roleplay-Servern
Jeder Roleplay-Server braucht ein robustes Lagersystem, das über das grundlegende Spielerinventar hinausgeht. Persönliche Inventare haben eine begrenzte Kapazität und Spieler sammeln durch Jobs, Crafting, Handel und Beute Gegenstände an, die sie behalten, aber nicht jederzeit mit sich tragen können. Lager- und Aufbewahrungssysteme lösen dieses Problem, indem sie physische Orte in der Spielwelt bieten, an denen Spieler Gegenstände ablegen und abholen können. Diese Systeme erfüllen verschiedene Gameplay-Zwecke: Persönliche Lager geben einzelnen Spielern zusätzlichen Platz, Lagerhäuser von Organisationen ermöglichen es Gangs und Unternehmen, Ressourcen gemeinsam zu verwalten, und mietbare Lagerräume schaffen einen Immobilienmarkt, der dem Server wiederkehrende Einnahmen bringt. Bei der technischen Umsetzung müssen gleichzeitige Zugriffe, Gewichts- und Slotlimits, Zugriffsberechtigungen und die Integration in das bestehende Inventar-Framework des Servers berücksichtigt werden.
Datenbankschema für Lager
Entwirf deine Lagerdatenbank so, dass sie mehrere Lagertypen mit flexiblen Besitz- und Zugriffsrechten unterstützt. Jede Lagereinheit benötigt eine eindeutige Kennung, einen Verweis auf den Besitzer, der entweder eine Spieler-citizenid oder eine Organisationskennung sein kann, Kapazitätsgrenzen für Gewicht und Slot-Anzahl sowie die eigentlichen, als JSON serialisierten Inventardaten. Füge eine Tabelle für Mietverhältnisse hinzu, die den Zahlungsstatus gemieteter Einheiten erfasst und diese automatisch sperrt, wenn die Miete überfällig ist. Über die Zugriffskontrolltabelle können Besitzer anderen Spielern den Zugriff auf ihre Lagereinheiten erlauben:
CREATE TABLE IF NOT EXISTS storage_units (
id INT AUTO_INCREMENT PRIMARY KEY,
unit_id VARCHAR(50) UNIQUE NOT NULL,
unit_type ENUM('personal', 'organization', 'rental', 'property') DEFAULT 'personal',
owner_id VARCHAR(50) NOT NULL,
label VARCHAR(100) DEFAULT NULL,
max_weight INT DEFAULT 100000,
max_slots INT DEFAULT 50,
items LONGTEXT DEFAULT '[]',
location VARCHAR(50) NOT NULL,
is_locked BOOLEAN DEFAULT FALSE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
INDEX idx_owner (owner_id),
INDEX idx_location (location),
INDEX idx_type (unit_type)
);
CREATE TABLE IF NOT EXISTS storage_access (
unit_id VARCHAR(50) NOT NULL,
citizenid VARCHAR(50) NOT NULL,
permission ENUM('view', 'deposit', 'full') DEFAULT 'deposit',
granted_by VARCHAR(50) NOT NULL,
granted_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (unit_id, citizenid)
);
CREATE TABLE IF NOT EXISTS storage_rentals (
unit_id VARCHAR(50) PRIMARY KEY,
tenant_id VARCHAR(50) NOT NULL,
rent_amount INT NOT NULL,
rent_interval ENUM('daily', 'weekly') DEFAULT 'weekly',
last_paid TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
next_due TIMESTAMP NOT NULL,
is_overdue BOOLEAN DEFAULT FALSE,
INDEX idx_tenant (tenant_id),
INDEX idx_due (next_due)
);
Konfiguration der Lagerstandorte
Lege Lagerstandorte auf der Karte fest und berücksichtige dabei den Roleplay-Kontext. Industriegebiete in der Nähe des Hafens eignen sich gut für große gewerbliche Lager. Wohngebiete sind passend für persönliche Schließfächer. Unterirdische oder versteckte Orte eignen sich als Lagerhäuser für Gangs. Für jeden Standort musst du die physische Position, die verfügbaren Lagereinheit-Typen, die Mietpreise und das Interior-Shell konfigurieren, das beim Betreten geladen wird. Nutze GTA-Interior-Proxys oder benutzerdefinierte MLOs für die Lager-Interiors, damit Spieler tatsächlich ein Gebäude betreten, statt einfach nur mitten auf der Straße ein Menü zu öffnen:
Config.StorageLocations = {
['industrial_1'] = {
label = 'Elysian Island Storage',
entrance = vector3(-96.67, -1601.77, 29.41),
blipSprite = 473,
blipColor = 2,
units = {
{id = 'ind1_small_1', type = 'rental', size = 'small', maxWeight = 50000, maxSlots = 30, rent = 500, interval = 'weekly'},
{id = 'ind1_small_2', type = 'rental', size = 'small', maxWeight = 50000, maxSlots = 30, rent = 500, interval = 'weekly'},
{id = 'ind1_medium_1', type = 'rental', size = 'medium', maxWeight = 150000, maxSlots = 75, rent = 1500, interval = 'weekly'},
{id = 'ind1_large_1', type = 'rental', size = 'large', maxWeight = 300000, maxSlots = 150, rent = 3500, interval = 'weekly'},
},
},
['gang_southside'] = {
label = 'Southside Stash',
entrance = vector3(93.41, -1961.08, 20.82),
blipSprite = 0, -- No blip for hidden locations
restrictedTo = {'ballas', 'gsf'}, -- Gang-only access
units = {
{id = 'gang_ss_main', type = 'organization', size = 'large', maxWeight = 500000, maxSlots = 200},
},
},
}
Inventarintegration
Dein Lagersystem muss sich nahtlos in das Inventar-Framework deines Servers integrieren, egal ob du ox_inventory, qb-inventory oder eine eigene Lösung verwendest. Die Lager-UI sollte der vertrauten Inventaroberfläche entsprechen, damit Spieler sofort verstehen, wie sie Gegenstände zwischen ihrem persönlichen Inventar und der Lagereinheit verschieben. Implementiere eine Drag-and-drop-Übertragung von Gegenständen mit Mengenauswahl für stapelbare Gegenstände. Wenn ein Spieler eine Lagereinheit öffnet, lade die gespeicherten Gegenstände aus der Datenbank und zeige sie zusammen mit dem persönlichen Inventar des Spielers an. Alle Gegenstandsbewegungen müssen serverseitig validiert werden, um Duplizierungs-Exploits zu verhindern. Prüfe, ob das Quellinventar den zu verschiebenden Gegenstand tatsächlich enthält, ob das Ziel über ausreichend Kapazität bei Gewicht und Slots verfügt und ob der Gegenstand nicht als unverkäuflich oder an den Spieler gebunden markiert ist. Verwende Datenbanktransaktionen für Gegenstandsübertragungen, damit das Entfernen aus einem Inventar und das Hinzufügen zum anderen atomar erfolgt:
RegisterNetEvent('storage:server:moveItem', function(unitId, direction, itemName, amount, fromSlot, toSlot)
local src = source
local Player = QBCore.Functions.GetPlayer(src)
if not Player then return end
-- Validate access
if not HasStorageAccess(Player.PlayerData.citizenid, unitId, 'full') then
TriggerClientEvent('QBCore:Notify', src, 'Access denied', 'error')
return
end
-- Load storage unit
local unit = GetStorageUnit(unitId)
if not unit or unit.is_locked then
TriggerClientEvent('QBCore:Notify', src, 'Storage is locked', 'error')
return
end
amount = math.floor(tonumber(amount) or 0)
if amount <= 0 then return end
if direction == 'to_storage' then
-- Player -> Storage
local playerItem = Player.Functions.GetItemBySlot(fromSlot)
if not playerItem or playerItem.name ~= itemName then return end
if playerItem.amount < amount then return end
local itemWeight = QBCore.Shared.Items[itemName].weight * amount
local currentWeight = CalculateStorageWeight(unit.items)
if currentWeight + itemWeight > unit.max_weight then
TriggerClientEvent('QBCore:Notify', src, 'Storage is full (weight)', 'error')
return
end
-- Atomic transfer
Player.Functions.RemoveItem(itemName, amount, fromSlot)
AddItemToStorage(unitId, itemName, amount, playerItem.info, toSlot)
elseif direction == 'from_storage' then
-- Storage -> Player
local storageItem = GetStorageItemBySlot(unit.items, fromSlot)
if not storageItem or storageItem.name ~= itemName then return end
if storageItem.amount < amount then return end
if not Player.Functions.AddItem(itemName, amount, toSlot, storageItem.info) then
TriggerClientEvent('QBCore:Notify', src, 'Inventory full', 'error')
return
end
RemoveItemFromStorage(unitId, itemName, amount, fromSlot)
end
-- Refresh UI for all viewers
RefreshStorageViewers(unitId)
end)
Mietzahlungssystem
Für gemietete Lagereinheiten ist ein wiederkehrendes Zahlungssystem erforderlich, das Mietern automatisch Gebühren berechnet und überfällige Konten verwaltet. Führe serverseitig einen geplanten Task aus, der regelmäßig alle Mieteinheiten prüft, fällige Mietzahlungen verarbeitet und überfällige Einheiten sperrt. Wenn eine Zahlung fällig ist, versuche, den Mietbetrag vom Bankkonto des Mieters abzubuchen. Wenn das Bankkonto nicht ausreichend gedeckt ist, markiere die Miete als überfällig und benachrichtige den Spieler. Gib Mietern mit Zahlungsrückstand einige Tage Kulanzzeit, bevor du ihre Einheit sperrst. Eine gesperrte Einheit verhindert, dass der Mieter auf seine eingelagerten Gegenstände zugreift, bis der offene Betrag bezahlt wurde. Bleibt die Miete über einen längeren Zeitraum unbezahlt, könnten die Inhalte versteigert oder entfernt werden, was für interessantes zusätzliches Gameplay sorgt. Implementiere ein Zahlungsverlaufsprotokoll, damit Mieter ihre Mietgebühren und Zahlungstermine über die Lagerverwaltung einsehen können.
Organisationen und gemeinsam genutzte Lager
Organisationen wie Gangs, Unternehmen und Behörden benötigen gemeinsam genutzte Lager, auf die mehrere Mitglieder mit passenden Berechtigungsstufen zugreifen können. Die Leitung der Organisation hat die volle Kontrolle und kann Mitglieder zur Zugriffsliste hinzufügen oder daraus entfernen, individuelle Berechtigungsstufen festlegen und Prüfprotokolle zu allen eingelagerten oder entnommenen Gegenständen einsehen. Normale Mitglieder können beispielsweise nur Gegenstände einlagern, ohne sie entnehmen zu können. Das ist praktisch, wenn Gangmitglieder Materialien zu einem gemeinsamen Vorrat beisteuern. Vertrauenswürdige Mitglieder erhalten vollen Zugriff zum Einlagern und Entnehmen. Implementiere ein umfassendes Prüfprotokoll, das jede Bewegung von Gegenständen in und aus dem gemeinsam genutzten Lager erfasst, einschließlich des ausführenden Spielers, des Gegenstands, der Menge und des Zeitstempels. So können Organisationsleitungen nachvollziehen, wer etwas beiträgt und wer möglicherweise aus dem gemeinsamen Vorrat stiehlt. Das Protokoll sollte über die Lager-UI mit Filteroptionen nach Spieler, Gegenstandstyp und Zeitraum einsehbar sein.
Kapazitäts-Upgrades und Stufen
Ermögliche es Spielern, ihre Lagerkapazität über ein Upgrade-System zu erhöhen, das einen Anreiz für den Spielfortschritt bietet. Beginne mit kleinen Lagereinheiten mit begrenzter Gewichts- und Slot-Kapazität und biete dann Upgrade-Stufen an, die diese Grenzen gegen Bezahlung erhöhen. Jede Upgrade-Stufe sollte zunehmend mehr kosten und so der Serverwirtschaft effektiv Geld entziehen. Upgrades können das maximale Gewicht erhöhen, zusätzliche Slots hinzufügen oder besondere Funktionen freischalten, etwa temperaturgeregelte Lagerung für verderbliche Gegenstände oder verstärkte Sicherheit, die die Einheit widerstandsfähiger gegen Einbrüche macht. Zeige die aktuelle Stufe und verfügbare Upgrades in der Lagerverwaltung an, mit klaren Preisen und den Vorteilen jeder Stufe. Du kannst manche Upgrade-Stufen an Erfolge oder Spielzeit-Meilensteine statt nur an Geldkosten knüpfen und so engagierte Spieler mit exklusiven Lagervorteilen belohnen, die sie nicht einfach kaufen können.
Sicherheit und Einbruchsmechaniken
Lagereinheiten sollten vor kriminellen Aktivitäten nicht vollständig sicher sein. Implementiere eine Einbruchsmechanik, mit der Spieler versuchen können, auf fremde Lagereinheiten zuzugreifen. So geraten eingelagerte Wertgegenstände in Gefahr und es entstehen Raubüberfall-Szenarien. Für einen Einbruch sollten bestimmte Werkzeuge wie Dietriche oder elektronische Überbrückungsgeräte erforderlich sein. Der Vorgang sollte eine erhebliche Zeit dauern und sichtbare sowie hörbare Hinweise erzeugen, die Spieler in der Nähe alarmieren, außerdem sollten der Besitzer des Lagers und die Strafverfolgungsbehörden benachrichtigt werden. Lagereinheiten höherer Stufen und mit Sicherheits-Upgrades sollten zunehmend schwerer aufzubrechen sein und bessere Werkzeuge sowie mehr Zeit erfordern. Gelingt ein Einbruch, erhält der Täter vorübergehend Zugriff auf das Lagerinventar und kann Gegenstände mitnehmen. Das System sollte den Einbruch jedoch protokollieren und dem Besitzer Beweismaterial liefern, etwa teilweise Informationen zur Kennung, die die Polizei untersuchen kann. So entsteht ein vollständiger Gameplay-Loop aus Lagerhausüberfällen, Ermittlungen und Konsequenzen, der die Dynamik zwischen Kriminellen und Strafverfolgung auf dem Server bereichert.