Sistemas de armazenamento em servidores Roleplay
Todo servidor de roleplay precisa de um sistema de armazenamento robusto além do inventário básico do jogador. Os inventários pessoais têm capacidade limitada e os jogadores acumulam itens por meio de trabalhos, elaboração, comércio e saques que desejam manter, mas não podem carregar o tempo todo. Os sistemas de armazenamento e armazenamento resolvem isso fornecendo locais físicos no mundo do jogo onde os jogadores podem depositar e recuperar itens. Esses sistemas servem a vários propósitos de jogo: armários de armazenamento pessoal oferecem espaço extra aos jogadores individuais, armazéns organizacionais permitem que gangues e empresas reúnam seus recursos e unidades de armazenamento para aluguel criam um mercado imobiliário que gera receitas recorrentes para a economia do servidor. A implementação técnica deve lidar com acesso simultâneo, limites de peso e slots, permissões de acesso e integração com a estrutura de inventário existente do seu servidor.
Esquema de banco de dados para armazenamento
Projete seu banco de dados de armazenamento para suportar vários tipos de armazenamento com propriedade flexível e controle de acesso. Cada unidade de armazenamento precisa de um identificador exclusivo, uma referência de proprietário que pode ser um cidadão do jogador ou um identificador de organização, limites de capacidade definidos por peso e contagem de slots e os dados reais do inventário serializados como JSON. Inclui uma tabela do sistema de aluguel que acompanha o status do pagamento das unidades alugadas, com bloqueio automático quando o aluguel está vencido. A tabela de controle de acesso permite que os proprietários concedam permissão a outros jogadores para acessar suas unidades de armazenamento:
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)
);Configuração de localização de armazém
Defina locais de armazenamento ao redor do mapa com atenção ao contexto da dramatização. As áreas industriais próximas ao porto funcionam bem para grandes armazéns comerciais. Bairros residenciais são adequados para armários de armazenamento pessoal. Locais subterrâneos ou escondidos servem como esconderijos de gangues. Cada local precisa de configuração para sua posição física, o tipo de unidades de armazenamento disponíveis, o preço das unidades de aluguel e o revestimento interno que carrega quando um jogador entra. Use os proxies internos do GTA ou MLOs personalizados para os interiores do armazém para que os jogadores entrem fisicamente em um prédio em vez de apenas abrir um menu na rua:
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},
},
},
}Integração de estoque
Seu sistema de warehouse deve integrar-se perfeitamente a qualquer estrutura de inventário usada pelo servidor, seja ox_inventory, qb-inventory ou uma solução personalizada. A IU de armazenamento deve refletir a interface familiar do inventário para que os jogadores entendam instintivamente como mover itens entre seu inventário pessoal e a unidade de armazenamento. Implemente a transferência de itens do tipo arrastar e soltar com seleção de quantidade para itens empilháveis. Quando um jogador abre uma unidade de armazenamento, carregue os itens armazenados do banco de dados e apresente-os junto com o inventário pessoal do jogador. Todos os movimentos de itens devem ser validados no servidor para evitar explorações de duplicação. Verifique se o inventário de origem realmente contém o item que está sendo movido, se o destino tem capacidade suficiente tanto em peso quanto em slots e se o item não está marcado como não negociável ou vinculado ao jogador. Use transações de banco de dados para transferências de itens para que a remoção de um inventário e a adição a outro aconteçam de forma atômica:
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)Sistema de pagamento de aluguel
As unidades de armazenamento para aluguel exigem um sistema de pagamento recorrente que cobra automaticamente dos inquilinos e administra contas vencidas. Execute uma tarefa agendada no servidor que verifica todas as unidades de aluguel em intervalos regulares, processa pagamentos de unidades vencidas e bloqueia unidades vencidas. Quando o pagamento for devido, tente deduzir o valor do aluguel da conta bancária do inquilino. Se a conta bancária não tiver fundos suficientes, marque o aluguel como vencido e envie uma notificação ao jogador. Dê aos inquilinos atrasados um período de carência de alguns dias antes de bloquear sua unidade. Uma unidade bloqueada impede que o inquilino acesse seus itens armazenados até que o saldo devedor seja pago. Se o aluguel não for pago além de um limite mais longo, o conteúdo poderá ser leiloado ou liberado, criando uma jogabilidade secundária interessante. Implemente um registro do histórico de pagamentos para que os inquilinos possam revisar suas cobranças de aluguel e datas de pagamento por meio da interface de gerenciamento de armazenamento.
Organização e armazenamento compartilhado
Organizações como gangues, empresas e departamentos governamentais precisam de armazenamento compartilhado que vários membros possam acessar com níveis de permissão apropriados. O líder da organização tem controle total e pode adicionar ou remover membros da lista de acesso, definir níveis de permissão individuais e visualizar registros de auditoria de todos os itens depositados ou retirados. Os membros regulares podem ter acesso apenas de depósito, onde podem adicionar itens, mas não podem removê-los, o que é útil para membros de gangues que contribuem com materiais para um pool compartilhado. Membros confiáveis têm acesso total para depósitos e saques. Implemente um registro de auditoria abrangente que registre cada movimento de entrada e saída de itens do armazenamento compartilhado, incluindo o jogador que executou a ação, o item e a quantidade, e o carimbo de data/hora. Essa trilha de auditoria ajuda os líderes da organização a rastrear quem está contribuindo e quem pode estar roubando do pool compartilhado. O registro deve poder ser visualizado por meio da IU de armazenamento com opções de filtragem por jogador, tipo de item e intervalo de datas.
Atualizações e níveis de capacidade
Permita que os jogadores atualizem sua capacidade de armazenamento por meio de um sistema de atualização que oferece um incentivo à progressão. Comece com pequenas unidades de armazenamento com peso e capacidade de slot limitados e, em seguida, ofereça níveis de atualização que aumentem esses limites por um preço. Cada nível de atualização deve custar progressivamente mais, criando uma fonte de recursos significativa para a economia do servidor. As atualizações podem aumentar o peso máximo, adicionar slots adicionais ou desbloquear recursos especiais, como armazenamento com temperatura controlada para itens perecíveis ou segurança reforçada que torna a unidade resistente a arrombamentos. Exiba o nível atual e as atualizações disponíveis na interface de gerenciamento de armazenamento, com preços claros e os benefícios que cada nível oferece. Considere vincular alguns níveis de atualização a conquistas ou marcos de tempo de jogo, em vez de mero custo monetário, recompensando jogadores dedicados com benefícios de armazenamento premium que eles não podem simplesmente comprar com dinheiro.
Segurança e Mecânica de Arrombamento
As unidades de armazenamento não devem estar completamente protegidas de atividades criminosas. Implemente uma mecânica de arrombamento que permita aos jogadores tentar acessar unidades de armazenamento que não possuem, criando risco para objetos de valor armazenados e gerando cenários de assalto. O processo de invasão deve exigir ferramentas específicas, como lockpicks ou dispositivos de desvio eletrônico, levar um tempo significativo com indicadores visíveis e sonoros que alertam os jogadores próximos e acionam uma notificação ao proprietário do armazenamento e às autoridades. Unidades de armazenamento de nível superior e aquelas com atualizações de segurança deverão ser cada vez mais difíceis de invadir, exigindo ferramentas melhores e mais tempo. Se uma invasão for bem sucedida, o criminoso obtém acesso temporário ao inventário de armazenamento e pode levar itens, mas o sistema deve registar a invasão e fornecer ao proprietário provas, tais como informações de identificação parcial, que podem ser investigadas pela polícia. Isso cria um ciclo de jogo completo em torno de invasões, investigações e consequências em armazéns que enriquece a dinâmica criminal e de aplicação da lei do servidor.
