FiveM Depo ve Depolama: Ortak Sandık Scripti Geliştirme
FiveM için paylaşımlı depolar ve depolama alanları tasarlayın. Çete depoları, meslek dolapları, ağırlık sınırları ve ox_inventory ile daha fazlası için en iyi depolama scriptleri.
Agency Scripts
Agency Scripts Kurucusu ve Baş Geliştiricisi
Roleplay Sunucularında Depolama Sistemleri
Her roleplay sunucusunun, temel oyuncu envanterinin ötesinde sağlam bir depolama sistemine ihtiyacı vardır. Kişisel envanterler sınırlı kapasiteye sahiptir ve oyuncular meslekler, crafting, ticaret ve loot yoluyla saklamak istedikleri ancak her zaman taşıyamadıkları eşyaları biriktirirler. Depo ve saklama sistemleri, oyun dünyasında oyuncuların eşya bırakıp alabilecekleri fiziksel konumlar sağlayarak bunu çözer. Bu sistemler birden fazla oynanış amacına hizmet eder: Kişisel saklama dolapları bireysel oyunculara ekstra alan sağlar, organizasyon depoları çetelerin ve işletmelerin kaynaklarını birleştirmesine olanak tanır ve kiralık depo birimleri, sunucu ekonomisi için düzenli gelir sağlayan bir mülk piyasası yaratır. Teknik uygulamanın eşzamanlı erişimi, ağırlık ve slot sınırlarını, erişim izinlerini ve sunucunuzun mevcut envanter framework'ü ile entegrasyonunu yönetmesi gerekir.
Depolama İçin Veri Tabanı Şeması
Depolama veritabanınızı, esnek sahiplik ve erişim kontrolü ile birden fazla depolama türünü destekleyecek şekilde tasarlayın. Her depolama biriminin benzersiz bir tanımlayıcıya, bir oyuncu citizenid veya kuruluş tanımlayıcısı olabilen sahip referansına, ağırlık ve slot sayısı ile tanımlanan kapasite sınırlarına ve JSON olarak serileştirilmiş gerçek envanter verilerine ihtiyacı vardır. Kira geciktiğinde otomatik kilitleme özelliğine sahip, kiralanan birimlerin ödeme durumunu izleyen bir kiralama sistemi tablosu ekleyin. Erişim kontrol tablosu, sahiplerin diğer oyunculara depolama birimlerine erişim izni vermesini sağlar:
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)
);
Depo Konumu Yapılandırması
Rol yapma bağlamına dikkat ederek harita genelinde depo konumları tanımlayın. Limanın yakınındaki sanayi bölgeleri büyük ticari depolar için oldukça uygundur. Yerleşim bölgeleri kişisel depolama kilitli dolaplarına uygundur. Yeraltı veya gizli konumlar çete zula evleri olarak hizmet verir. Her konum için fiziksel pozisyon, mevcut depolama birimlerinin türü, kiralık birimlerin fiyatlandırması ve bir oyuncu girdiğinde yüklenen iç mekan kabuğu (interior shell) yapılandırılmalıdır. Depo iç mekanları için GTA'nın interior proxy'lerini veya özel MLO'larını kullanın, böylece oyuncular sokakta sadece bir menü açmak yerine fiziksel olarak binanın içine girer:
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},
},
},
}
Envanter Entegrasyonu
Depo sisteminiz, ox_inventory, qb-inventory veya özel bir çözüm olsun, sunucunuzun kullandığı envanter framework'ü ile sorunsuz bir şekilde entegre olmalıdır. Oyuncuların eşyaları kişisel envanterleri ile depolama birimi arasında nasıl taşıyacaklarını içgüdüsel olarak anlayabilmeleri için depolama kullanıcı arayüzü, alışılagelmiş envanter arayüzünü yansıtmalıdır. İstiflenebilir eşyalar için miktar seçimiyle birlikte sürükle ve bırak eşya aktarımı uygulayın. Bir oyuncu depolama birimini açtığında, saklanan eşyaları veritabanından yükleyin ve bunları oyuncunun kişisel envanterinin yanında sunun. Eşya kopyalama (duplication) açıklarını önlemek için tüm eşya hareketleri sunucu tarafında doğrulanmalıdır. Kaynak envanterin taşınan eşyayı gerçekten içerdiğini, hedefin hem ağırlık hem de yuva (slot) açısından yeterli kapasiteye sahip olduğunu ve eşyanın takas edilemez veya oyuncuya bağlı olarak işaretlenmediğini kontrol edin. Bir envanterden çıkarma ve diğerine ekleme işleminin atomik olarak gerçekleşmesi için eşya transferlerinde veritabanı işlemlerini (transaction) kullanın:
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)
Kira Ödeme Sistemi
Kiralık depo birimleri, kiracılardan otomatik olarak ücret alan ve gecikmiş hesapları yöneten yinelenen bir ödeme sistemi gerektirir. Düzenli aralıklarla tüm kiralık birimleri kontrol eden, vadesi gelmiş birimlerin ödemelerini işleyen ve vadesi geçmiş birimleri kilitleyen sunucu taraflı planlanmış bir görev çalıştırın. Bir ödemenin vadesi geldiğinde, kira tutarını kiracının banka hesabından düşmeyi deneyin. Banka hesabında yetersiz bakiye varsa, kiralamayı vadesi geçmiş olarak işaretleyin ve oyuncuya bir bildirim gönderin. Birimlerini kilitlemeden önce vadesi geçmiş kiracılara birkaç günlük bir ek süre tanıyın. Kilitli bir birim, kiracının ödenmemiş bakiye ödenene kadar depolanan eşyalarına erişmesini engeller. Kiralama daha uzun bir eşiğin ötesinde ödenmemiş kalırsa, içerik açık artırmaya çıkarılabilir veya temizlenebilir; bu da ilginç bir ikincil oynanış yaratır. Kiracıların kiralama ücretlerini ve ödeme tarihlerini depo yönetim arayüzü üzerinden inceleyebilmeleri için bir ödeme geçmişi günlüğü uygulayın.
Organizasyon ve Ortak Depo
Çeteler, işletmeler ve devlet daireleri gibi organizasyonlar, birden fazla üyenin uygun yetki seviyeleriyle erişebileceği ortak depolamaya ihtiyaç duyar. Organizasyon lideri tam kontrole sahiptir; erişim listesine üye ekleyip çıkarabilir, bireysel yetki seviyeleri belirleyebilir ve yatırılan veya çekilen tüm eşyaların denetim günlüklerini görüntüleyebilir. Standart üyeler yalnızca eşya ekleyip çıkaramayacakları salt yatırma erişimine sahip olabilir; bu da çete üyelerinin ortak havuza malzeme aktarması için oldukça kullanışlıdır. Güvenilir üyeler ise hem yatırma hem de çekme için tam erişim elde eder. İşlemi gerçekleştiren oyuncu, eşya, miktar ve zaman damgası dahil olmak üzere ortak depoya giren ve çıkan her eşya hareketini kaydeden kapsamlı bir denetim günlüğü uygulayın. Bu denetim geçmişi, organizasyon liderlerinin kimin katkıda bulunduğunu ve kimin ortak havuzdan hırsızlık yapıyor olabileceğini takip etmesine yardımcı olur. Günlük, oyuncuya, eşya türüne ve tarih aralığına göre filtreleme seçenekleriyle depo arayüzü üzerinden görüntülenebilmelidir.
Kapasite Yükseltmeleri ve Seviyeler
İlerleme teşviki sunan bir yükseltme sistemiyle oyuncuların depolama kapasitelerini geliştirmelerine izin verin. Sınırlı ağırlık ve slot kapasitesine sahip küçük depolama birimleriyle başlayın, ardından bu limitleri belirli bir ücret karşılığında artıran yükseltme kademeleri sunun. Her yükseltme kademesi kademeli olarak daha pahalı olmalı ve sunucu ekonomisi için anlamlı bir para eritme mekanizması oluşturmalıdır. Yükseltmeler maksimum ağırlığı artırabilir, ek slotlar ekleyebilir veya bozulabilir eşyalar için sıcaklık kontrollü depolama ya da birimi hırsızlığa karşı dirençli kılan güçlendirilmiş güvenlik gibi özel özelliklerin kilidini açabilir. Depolama yönetimi arayüzünde mevcut kademeyi ve kullanılabilir yükseltmeleri, net fiyatlandırma ve her kademenin sunduğu avantajlarla gösterin. Bazı yükseltme kademelerini yalnızca parasal maliyet yerine başarılara veya oynama süresi kilometre taşlarına bağlamayı değerlendirin; böylece sadık oyuncuları sadece parayla satın alamayacakları ayrıcalıklı depolama avantajlarıyla ödüllendirebilirsiniz.
Güvenlik ve Hırsızlık Mekanikleri
Depo birimleri yasa dışı faaliyetlere karşı tamamen güvenli olmamalıdır. Oyuncuların sahip olmadıkları depolara erişmeyi denemelerine olanak tanıyan, saklanan değerli eşyalar için risk oluşturan ve soygun senaryoları üreten bir kilit kırma mekaniği uygulayın. Kilit kırma süreci, maymuncuk veya elektronik baypas cihazları gibi belirli aletler gerektirmeli, yakındaki oyuncuları uyaran görsel ve işitsel göstergelerle belirgin bir zaman almalı, depo sahibine ve emniyet güçlerine bildirim tetiklemelidir. Üst düzey depo birimlerinin ve güvenlik yükseltmelerine sahip olanların kilitlerini kırmak daha iyi aletler ve daha fazla zaman gerektirerek giderek zorlaşmalıdır. Kilit kırma başarılı olursa suçlu depo envanterine geçici erişim sağlar ve eşyaları alabilir, ancak sistem kilit kırmayı kaydetmeli ve sahibine polis tarafından soruşturulabilecek kısmi kimlik bilgileri gibi kanıtlar sağlamalıdır. Bu durum depo baskınları, soruşturma ve sonuçlar etrafında sunucunun suç ve emniyet dinamiklerini zenginleştiren eksiksiz bir oynanış döngüsü oluşturur.