FiveM склады и хранение: Скрипт Shared Stashes
Разработайте общие склады и хранилища в FiveM. Тайники банд, служебные шкафчики, ограничения по весу и лучшие скрипты хранения для ox_inventory и других.
Agency Scripts
Основатель и ведущий разработчик Agency Scripts
Системы хранения на ролевых серверах
Каждому ролевому серверу нужна надёжная система хранения, выходящая за рамки базового инвентаря игрока. Личные инвентари имеют ограниченную вместимость, а игроки накапливают предметы через работу, крафт, торговлю и лут, которые хотят сохранить, но не могут носить постоянно. Системы складов и хранилищ решают эту задачу, предоставляя физические локации в игровом мире, где игроки могут класть и брать предметы. Эти системы служат нескольким игровым целям: личные шкафчики дают игрокам дополнительное место, склады организации позволяют бандам и бизнесам объединять ресурсы, а арендные хранилища создают рынок недвижимости, приносящий регулярный доход экономике сервера. Техническая реализация должна обрабатывать одновременный доступ, ограничения по весу и слотам, права доступа и интеграцию с существующей системой инвентаря сервера.
Схема базы данных для хранения
Проектируйте базу данных хранения с поддержкой нескольких типов хранилищ с гибким владением и контролем доступа. Каждое хранилище должно иметь уникальный идентификатор, ссылку на владельца, которая может быть player citizenid или идентификатором организации, ограничения по вместимости, определяемые весом и количеством слотов, а также фактические данные инвентаря, сериализованные в JSON. Включите таблицу системы аренды, которая отслеживает статус оплаты арендованных единиц с автоматической блокировкой при просрочке платежа. Таблица контроля доступа позволяет владельцам предоставлять другим игрокам разрешение на доступ к их хранилищам:
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)
);
Настройка расположения склада
Определите расположение складов по карте с учётом ролевого контекста. Промышленные зоны возле порта подходят для больших коммерческих складов. Жилые районы подходят для личных ячеек хранения. Подземные или скрытые локации служат тайниками банд. Каждое место требует настройки физической позиции, типа доступных складских единиц, цен на аренду и интерьера, который загружается при входе игрока. Используйте внутренние прокси GTA или кастомные MLO для интерьеров складов, чтобы игроки физически заходили в здание, а не просто открывали меню на улице:
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},
},
},
}
Интеграция инвентаря
Ваша система склада должна бесшовно интегрироваться с любой системой инвентаря, которую использует ваш сервер, будь то ox_inventory, qb-inventory или кастомное решение. Интерфейс хранения должен повторять знакомый интерфейс инвентаря, чтобы игроки интуитивно понимали, как перемещать предметы между личным инвентарём и складским блоком. Реализуйте перенос предметов перетаскиванием с выбором количества для стекаемых предметов. При открытии складского блока загружайте сохранённые предметы из базы данных и отображайте их рядом с личным инвентарём игрока. Все перемещения предметов должны проверяться на сервере, чтобы предотвратить эксплойты дублирования. Проверьте, что исходный инвентарь действительно содержит перемещаемый предмет, что в целевом инвентаре достаточно места по весу и слотам, и что предмет не помечен как не подлежащий торговле или привязанный к игроку. Используйте транзакции базы данных для перемещений предметов, чтобы удаление из одного инвентаря и добавление в другой происходило атомарно:
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)
Система оплаты аренды
Арендные хранилища требуют системы регулярных платежей, которая автоматически списывает средства с арендаторов и обрабатывает просроченные счета. Запускайте серверную задачу по расписанию, которая регулярно проверяет все арендные единицы, обрабатывает платежи по сроку и блокирует просроченные. При наступлении срока оплаты пытайтесь списать сумму аренды с банковского счёта арендатора. Если средств недостаточно, помечайте аренду как просроченную и отправляйте игроку уведомление. Предоставьте просрочившим арендаторам льготный период в несколько дней перед блокировкой их единицы. Заблокированная единица не позволяет арендатору получить доступ к хранящимся предметам до погашения задолженности. Если аренда остаётся неоплаченной дольше установленного срока, содержимое может быть выставлено на аукцион или очищено, создавая интересный дополнительный игровой процесс. Реализуйте журнал истории платежей, чтобы арендаторы могли просматривать свои начисления и даты оплат через интерфейс управления хранилищем.
Организация и общие хранилища
Организации, такие как банды, бизнесы и государственные департаменты, нуждаются в общих хранилищах, к которым несколько участников могут получить доступ с соответствующими уровнями разрешений. Лидер организации имеет полный контроль и может добавлять или удалять участников из списка доступа, устанавливать индивидуальные уровни разрешений и просматривать журналы аудита всех внесённых или изъятых предметов. Обычные участники могут иметь доступ только для внесения, что полезно для членов банды, вносящих материалы в общий пул. Доверенные участники получают полный доступ для внесения и изъятия. Реализуйте полный журнал аудита, который фиксирует каждое перемещение предметов в хранилище и из него, включая игрока, совершившего действие, предмет и количество, а также временную метку. Этот журнал помогает лидерам организаций отслеживать, кто вносит материалы, а кто может их воровать из общего пула. Журнал должен быть доступен через UI хранилища с возможностью фильтрации по игроку, типу предмета и диапазону дат.
Улучшения и уровни вместимости
Разрешить игрокам улучшать вместимость хранилища через систему апгрейдов, которая стимулирует прогресс. Начинать с небольших хранилищ с ограничением по весу и слотам, затем предлагать уровни улучшений, увеличивающие эти лимиты за цену. Каждый уровень улучшения должен стоить дороже предыдущего, создавая значительный денежный слив для экономики сервера. Улучшения могут увеличивать максимальный вес, добавлять дополнительные слоты или открывать специальные функции, такие как температурный контроль для скоропортящихся предметов или усиленную защиту, делающую хранилище устойчивым к взлому. Отображать текущий уровень и доступные улучшения в интерфейсе управления хранилищем с четким указанием цены и преимуществ каждого уровня. Рассмотреть возможность привязки некоторых уровней улучшений к достижениям или игровому времени, а не только к денежной стоимости, вознаграждая преданных игроков премиальными преимуществами, которые нельзя просто купить за деньги.
Механика безопасности и взлома
Единицы хранения не должны быть полностью защищены от преступной деятельности. Реализуйте механику взлома, которая позволяет игрокам пытаться получить доступ к единицам хранения, которыми они не владеют, создавая риск для хранимых ценностей и генерируя сценарии ограблений. Процесс взлома должен требовать специальных инструментов, таких как отмычки или электронные обходные устройства, занимать значительное время с видимыми и слышимыми индикаторами, которые предупреждают nearby игроков, и вызывать уведомление владельцу хранения и правоохранительным органам. Единицы хранения высокого уровня и с улучшенной защитой должны взламываться сложнее, требуя лучших инструментов и большего времени. Если взлом успешен, преступник получает временный доступ к инвентарю хранения и может забрать предметы, но система должна фиксировать взлом и предоставлять владельцу доказательства, такие как частичная информация об идентификаторе, которую полиция может расследовать. Это создаёт полный игровой цикл вокруг рейдов на склады, расследований и последствий, обогащающий криминальную и правоохранительную динамику сервера.