Sistema de Baú
Configuração do baú das casas com suporte a inventários externos
Sistema de Baú
O baú de cada casa pode operar em dois modos, selecionados via CFG['preferences'].chestProvider:
"builtin"(padrão) — UI própria do DS-Homes + persistência emds_homes.chest."external"— delega a abertura para hooks emshared/chest_hooks.lua. Útil para integrar com o inventário próprio do servidor (ox_inventory, qb-inventory, vRP nativo, sistemas customizados).
Em ambos os modos, o DS-Homes continua validando:
- Se o jogador está dentro da casa
- Se é dono OU morador com
canAccessChest - Lock exclusivo (apenas um jogador abre o baú por vez)
Config (preferences.lua)
CFG['preferences'] = {
chestProvider = "builtin", -- "builtin" | "external"
defaultChestSlots = 50, -- passado em ctx.maxSlots para hooks externos
}Modo External
Quando chestProvider = "external", o DS-Homes chama o hook onOpen em shared/chest_hooks.lua. Você é dono de tudo a partir daí: UI, persistência, limites de peso, eventos de close.
Arquivo shared/chest_hooks.lua
CFG = CFG or {}
CFG['chestHooks'] = {
onRegisterStash = function(ctx)
-- Opcional. Chamado lazy antes da primeira onOpen por stashId.
end,
onOpen = function(source, ctx)
-- OBRIGATÓRIO. Abre o inventário do seu sistema.
-- Return { success = bool, message? = string }.
return { success = true }
end,
onDump = function(ctx)
-- Opcional. Usado pelo comando dshomes:chest:export.
-- Return { items = { {item, quantity, metadata?}, ... } } ou nil.
return nil
end,
onClear = function(ctx)
-- Opcional mas RECOMENDADO. Chamado toda vez que a casa é removida
-- (IPTU vencido, venda pro governo, apreensão por inadimplência,
-- deleção via admin). Limpe a stash externa associada ao stashId
-- para evitar dados órfãos no seu inventário.
end,
}Campos do ctx
| Campo | Tipo | Descrição |
|---|---|---|
stashId | string | dshomes:<ownerId>:<homeName> — determinístico, idempotente |
ownerId | number | user_id do dono da casa |
homeName | string | Nome da casa (ou apartamento) |
buildingName | string | Nome do prédio (igual a homeName em casas não-apartamento) |
maxWeight | number | Peso máximo (baseStorage + chest_storage_upgrade) |
maxSlots | number | Slots — vem de CFG['preferences'].defaultChestSlots |
Exemplos de integração
CFG = CFG or {}
CFG['chestHooks'] = {
onRegisterStash = function(ctx)
exports.ox_inventory:RegisterStash(ctx.stashId, ctx.homeName, ctx.maxSlots, ctx.maxWeight, nil)
end,
onOpen = function(source, ctx)
exports.ox_inventory:forceOpenInventory(source, 'stash', ctx.stashId)
return { success = true }
end,
onClear = function(ctx)
exports.ox_inventory:ClearInventory(ctx.stashId)
end,
}CFG = CFG or {}
CFG['chestHooks'] = {
onOpen = function(source, ctx)
TriggerClientEvent('inventory:client:SetCurrentStash', source, ctx.stashId)
TriggerEvent('qb-inventory:server:OpenInventory', 'stash', ctx.stashId, {
maxweight = ctx.maxWeight,
slots = ctx.maxSlots,
})
return { success = true }
end,
}CFG = CFG or {}
CFG['chestHooks'] = {
onOpen = function(source, ctx)
vRP.openChest(source, ctx.stashId, ctx.maxWeight)
return { success = true }
end,
}Dispare um evento cliente que o seu inventário já escuta, passando o payload que ele espera:
CFG = CFG or {}
CFG['chestHooks'] = {
onOpen = function(source, ctx)
TriggerClientEvent("meu_inventario:openStash", source, {
id = ctx.stashId,
maxWeight = ctx.maxWeight,
name = ctx.homeName,
})
return { success = true }
end,
onDump = function(ctx)
local raw = vRP.getSData("STASH:" .. ctx.stashId)
if not raw or raw == "" then
return { items = {}, source = "custom" }
end
local decoded = json.decode(raw) or {}
local items = {}
for item, data in pairs(decoded) do
items[#items + 1] = { item = item, quantity = data.amount }
end
return { items = items, source = "custom" }
end,
onClear = function(ctx)
vRP.setSData("STASH:" .. ctx.stashId, "")
end,
}Se o inventário externo valida internamente se o jogador está dentro da casa (chamando, por exemplo, exports.brasil_homes:isUserInsideHome(source, houseId)), aponte essa chamada para o DS-Homes:
if not exports['ds-homes']:isUserInsideHome(source, houseId) then
return false
endO DS-Homes expõe isUserInsideHome(source, houseId) retornando true se o jogador está dentro da casa informada.
Comando de dump
Admins podem exportar o conteúdo do baú (qualquer modo) via console do servidor:
dshomes:chest:export <ownerId> <homeName>Retorna um JSON com ctx e dump.items. Útil para debug, migração manual ou auditoria.
- No modo
"builtin": dump lido direto deds_homes.chest. - No modo
"external": dump vem do hookonDump(ctx). Se o hook não estiver implementado, o comando reporta "Dump indisponivel".
Troca de provider é feita via config + restart. Não existe migration automática entre modos — dados em ds_homes.chest ficam intactos se você voltar para builtin.
Upgrade de peso
Funciona em ambos os modos. O jogador pode expandir o baú in-game; o valor expandido é somado em ctx.maxWeight e é visível pelos hooks externos.
CFG['preferences'] = {
chestUpgrade = {
pricePerKg = 1500,
maxUpgradeKg = 500,
minUpgradeKg = 10,
},
}Limpeza ao remover a casa
Toda vez que uma casa é deletada — IPTU vencido, venda pro governo, apreensão por inadimplência no financiamento, deleção via admin — o DS-Homes chama CFG['chestHooks'].onClear(ctx) antes de apagar a row em ds_homes.
No modo "builtin", isso é desnecessário: a coluna chest mora dentro da própria row e some junto. No modo "external", sem esse hook os itens da stash externa ficam órfãos no seu inventário — a casa some mas o conteúdo do baú continua acessível pelo stashId, ocupando espaço.
CFG['chestHooks'] = {
onClear = function(ctx)
-- ctx é o mesmo passado em onOpen/onDump
-- Implemente a chamada que zera a stash no seu inventário
end,
}| Inventário | Implementação típica |
|---|---|
ox_inventory | exports.ox_inventory:ClearInventory(ctx.stashId) |
qb-inventory | exports['qb-inventory']:ClearStash(ctx.stashId) |
| vRP/sdata custom | vRP.setSData("STASH:" .. ctx.stashId, "") |
Se você não implementar onClear, o DS-Homes apenas loga um warning e segue com a deleção da casa. Itens permanecem no seu inventário externo até você limpar manualmente.
Onde a remoção acontece
Internamente, o DS-Homes centraliza a remoção em Homes.deleteHome (server/modules/lifecycle.lua), invocado por:
- Loop de IPTU vencido (
server/_index.lua) - Venda pro governo (
server/modules/sell.lua) - Apreensão por inadimplência (
server/modules/financing.lua) - Deleção via admin (
server/modules/admin.lua—deletePlayerHome, purgar config,/delhome)
Em todos esses caminhos, a sequência é: chama onClear → limpa ds_homes_favorites e ds_homes_outfits → DELETE FROM ds_homes → invalida cache.
Lifecycle do lock exclusivo
No modo "external", o lock (chestsInUse) libera quando:
- O jogador sai da casa.
- O jogador desconecta.
Se você quiser que o lock libere assim que o jogador fecha a UI do seu inventário, dispare o evento server:
TriggerEvent('homes:closeChest')Geralmente de dentro do evento de close do seu próprio inventário.