Systèmes de stockage dans les serveurs Roleplay
Chaque serveur de jeu de rôle a besoin d'un système de stockage robuste au-delà de l'inventaire de base des joueurs. Les inventaires personnels ont une capacité limitée et les joueurs accumulent des objets via des travaux, de l'artisanat, des échanges et des pillages qu'ils souhaitent conserver mais ne peuvent pas transporter à tout moment. Les systèmes d'entrepôt et de stockage résolvent ce problème en fournissant des emplacements physiques dans le monde du jeu où les joueurs peuvent déposer et récupérer des objets. Ces systèmes répondent à plusieurs objectifs de jeu : les casiers de stockage personnels offrent aux joueurs individuels un espace supplémentaire, les entrepôts d'organisation permettent aux gangs et aux entreprises de mettre en commun leurs ressources, et les unités de stockage en location créent un marché immobilier qui génère des revenus récurrents pour l'économie des serveurs. La mise en œuvre technique doit gérer l'accès simultané, les limites de poids et d'emplacements, les autorisations d'accès et l'intégration avec le cadre d'inventaire existant de ton serveur.
Schéma de base de données pour le stockage
Concevez ton base de données de stockage pour prendre en charge plusieurs types de stockage avec une propriété et un contrôle d'accès flexibles. Chaque unité de stockage nécessite un identifiant unique, une référence de propriétaire qui peut être un identifiant de citoyen de joueur ou un identifiant d'organisation, des limites de capacité définies par le poids et le nombre d'emplacements, ainsi que les données d'inventaire réelles sérialisées sous le nom JSON. Incluez un tableau du système de location qui suit l'état de paiement des unités louées, avec un verrouillage automatique en cas de retard de loyer. La table de contrôle d'accès permet aux propriétaires d'accorder à d'autres joueurs la permission d'accéder à leurs unités de stockage :
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)
);
Configuration de l'emplacement de l'entrepôt
Définissez les emplacements des entrepôts sur la carte en prêtant attention au contexte du jeu de rôle. Les zones industrielles proches du port conviennent bien aux grands entrepôts commerciaux. Les quartiers résidentiels conviennent aux casiers de stockage personnels. Les endroits souterrains ou cachés servent de cachettes aux gangs. Chaque emplacement doit être configuré pour sa position physique, le type d'unités de stockage disponibles, le prix des unités de location et la coque intérieure qui se charge lorsqu'un joueur entre. Utilisez les proxys intérieurs de GTA ou les MLO personnalisés pour les intérieurs de l'entrepôt afin que les joueurs entrent physiquement dans un bâtiment plutôt que de simplement ouvrir un menu dans la rue :
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},
},
},
}
Intégration de l'inventaire
Ton système d'entrepôt doit s'intégrer de manière transparente à tout cadre d'inventaire utilisé par ton serveur, qu'il s'agisse de ox_inventory, de qb-inventory ou d'une solution personnalisée. L'interface utilisateur de stockage doit refléter l'interface d'inventaire familière afin que les joueurs comprennent instinctivement comment déplacer des objets entre leur inventaire personnel et l'unité de stockage. Implémentez le transfert d'articles par glisser-déposer avec sélection de la quantité pour les articles empilables. Lorsqu'un joueur ouvre une unité de stockage, chargez les objets stockés depuis la base de données et présentez-les à côté de l'inventaire personnel du joueur. Tous les mouvements d'articles doivent être validés côté serveur pour éviter les exploits de duplication. Vérifiez que l'inventaire source contient réellement l'objet déplacé, que la destination a une capacité suffisante en termes de poids et d'emplacements, et que l'objet n'est pas signalé comme non échangeable ou lié au joueur. Utilisez les transactions de base de données pour les transferts d'articles afin que la suppression d'un inventaire et l'ajout à un autre se produisent de manière atomique :
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)
Système de paiement de loyer
Les unités de stockage en location nécessitent un système de paiement récurrent qui facture automatiquement les locataires et gère les comptes en souffrance. Exécutez une tâche planifiée côté serveur qui vérifie toutes les unités locatives à intervalles réguliers, traite les paiements des unités en souffrance et verrouille les unités en retard. Lorsqu'un paiement est dû, essayez de déduire le montant du loyer du compte bancaire du locataire. Si le compte bancaire n'est pas suffisamment approvisionné, marquez la location comme en retard et envoyez une notification au joueur. Accordez aux locataires en retard un délai de grâce de quelques jours avant de verrouiller leur logement. Une unité verrouillée empêche le locataire d'accéder à ses articles stockés jusqu'à ce que le solde impayé soit payé. Si la location reste impayée au-delà d’un seuil plus long, le contenu pourrait être vendu aux enchères ou effacé, créant ainsi un gameplay secondaire intéressant. Mettez en place un journal d'historique des paiements afin que les locataires puissent consulter leurs frais de location et leurs dates de paiement via l'interface de gestion du stockage.
Organisation et stockage partagé
Les organisations telles que les gangs, les entreprises et les services gouvernementaux ont besoin d'un stockage partagé auquel plusieurs membres peuvent accéder avec les niveaux d'autorisation appropriés. Le responsable de l'organisation a un contrôle total et peut ajouter ou supprimer des membres de la liste d'accès, définir des niveaux d'autorisation individuels et consulter les journaux d'audit de tous les éléments déposés ou retirés. Les membres réguliers peuvent avoir un accès au dépôt uniquement où ils peuvent ajouter des éléments mais ne peuvent pas les supprimer, ce qui est utile pour les membres de gangs qui contribuent à un pool partagé. Les membres de confiance bénéficient d’un accès complet aux dépôts et aux retraits. Mettez en œuvre un journal d'audit complet qui enregistre chaque mouvement d'élément entrant et sortant du stockage partagé, y compris le joueur qui a effectué l'action, l'élément et la quantité, ainsi que l'horodatage. Cette piste d'audit aide les dirigeants de l'organisation à déterminer qui contribue et qui pourrait voler le pool partagé. Le journal doit être visible via l'interface utilisateur de stockage avec des options de filtrage par joueur, type d'élément et plage de dates.
Mises à niveau et niveaux de capacité
Permettez aux joueurs d’améliorer leur capacité de stockage grâce à un système de mise à niveau qui offre une incitation à la progression. Commencez avec de petites unités de stockage dont le poids et la capacité d'emplacement sont limités, puis proposez des niveaux de mise à niveau qui augmentent ces limites moyennant un certain prix. Chaque niveau de mise à niveau devrait coûter progressivement plus cher, créant ainsi un puits d’argent significatif pour l’économie des serveurs. Les mises à niveau peuvent augmenter le poids maximum, ajouter des emplacements supplémentaires ou débloquer des fonctionnalités spéciales telles que le stockage à température contrôlée pour les objets périssables ou une sécurité renforcée qui rend l'unité résistante aux effractions. Affichez le niveau actuel et les mises à niveau disponibles dans l'interface de gestion du stockage, avec des tarifs clairs et les avantages offerts par chaque niveau. Envisagez de lier certains niveaux de mise à niveau aux réalisations ou aux jalons de jeu plutôt qu'au coût monétaire pur, en récompensant les joueurs dévoués avec des avantages de stockage premium qu'ils ne peuvent pas simplement acheter avec de l'argent.
Mécaniques de sécurité et d'effraction
Les unités de stockage ne doivent pas être totalement à l’abri des activités criminelles. Implémentez un mécanisme d'effraction qui permet aux joueurs de tenter d'accéder à des unités de stockage qui ne leur appartiennent pas, créant ainsi un risque pour les objets de valeur stockés et générant des scénarios de braquage. Le processus d'effraction devrait nécessiter des outils spécifiques tels que des crochets de serrure ou des dispositifs de contournement électronique, prendre un temps considérable avec des indicateurs visibles et sonores qui alertent les joueurs à proximité et déclencher une notification au propriétaire du stockage et aux forces de l'ordre. Les unités de stockage de niveau supérieur et celles dotées de mises à niveau de sécurité devraient être de plus en plus difficiles à pénétrer, nécessitant de meilleurs outils et plus de temps. Si une effraction réussit, le criminel obtient un accès temporaire à l'inventaire de stockage et peut prendre des objets, mais le système doit enregistrer l'effraction et fournir au propriétaire des preuves telles que des informations d'identification partielles qui peuvent faire l'objet d'une enquête par la police. Cela crée une boucle de jeu complète autour des raids dans les entrepôts, des enquêtes et des conséquences qui enrichissent la dynamique criminelle et policière du serveur.
