Habitação e apartamentos em FiveM: shells, instâncias, estoques e chaves
Como funciona um sistema de habitação em FiveM: interiores shell e MLO, instanciação com routing buckets, um estoque por casa, chaves e permissões, e salvamento de propriedade no banco de dados.
O sintoma: dois jogadores entram em dois apartamentos diferentes e se veem, o estoque é compartilhado entre todas as casas, ou a propriedade desaparece quando o servidor reinicia.
Um sistema de habitação é quatro coisas funcionando juntas: um interior, uma maneira de separar jogadores, um lugar para armazenar itens, e um registro de quem pode entrar. Este artigo explica cada parte e como elas se conectam.
Interior Shell ou MLO
Você tem duas maneiras de dar um interior a uma casa:
| Tipo | Como funciona | Bom para |
|---|---|---|
| Shell | Um prop criado em coordenadas, removido ao sair | Apartamentos, muitas casas idênticas |
| MLO | Um interior permanente no mapa | Casas únicas e mansões |
Um shell é um modelo que você carrega, coloca em uma localização fixa (geralmente alto no céu ou longe, sob o mapa), e deleta quando o jogador sai. Como o mesmo shell pode ser criado para qualquer número de casas, shells são como edifícios de apartamentos são feitos. Um MLO fica no mapa, então fica limitado a um lugar. Para instalar um, veja instalando um MLO, e se quebrar o mapa ao seu redor, conflitos de mapa MLO.
Instanciação com routing buckets
Se cada apartamento usa o mesmo shell nas mesmas coordenadas, então todos que entram ficam no mesmo quarto. A solução é dar a cada casa seu próprio routing bucket, uma cópia isolada do mundo no servidor. Jogadores em buckets diferentes não podem se ver ou ver as entidades um do outro. A ideia em uma sentença: a casa número 12 usa bucket 12 (mais um offset para que nunca colida com outros usos).
-- server
local HOUSE_BUCKET_OFFSET = 1000
RegisterNetEvent('housing:enter', function(houseId)
local src = source
if not canEnter(src, houseId) then return end -- ownership or key check
SetPlayerRoutingBucket(src, HOUSE_BUCKET_OFFSET + houseId)
TriggerClientEvent('housing:spawnShell', src, houseId)
end)
RegisterNetEvent('housing:leave', function()
local src = source
SetPlayerRoutingBucket(src, 0)
TriggerClientEvent('housing:removeShell', src)
end)Todos que visitam essa casa devem ser colocados no mesmo bucket, e cada saída deve definir o bucket de volta para 0. Trate também desconexões e reinicializações de script, para que nenhum jogador permaneça em um bucket que ninguém mais possa alcançar. Todos os natives são explicados em routing buckets.
Dica: se você criar o prop shell no cliente, apenas esse jogador o vê. Para um shell que visitantes também vejam, cada visitante o cria para si mesmo, ou o servidor o cria no mesmo bucket.
Um estoque para cada casa
Cada casa precisa de seu próprio armazenamento. Com ox_inventory, registre um estoque por casa, com um id que inclua o id da casa:
-- server
exports.ox_inventory:RegisterStash('house_' .. houseId, 'House storage', 50, 100000, ownerId)Em seguida, abra-o com o mesmo id no cliente:
exports.ox_inventory:openInventory('stash', 'house_' .. houseId)Se o id é o mesmo para cada casa, cada casa compartilha um estoque, que é o sintoma do início deste artigo. Adicione a verificação de permissão no servidor antes do estoque ser aberto, e veja ox_inventory stashes para slots, peso e quem pode abrir um.
Chaves e permissões
A propriedade não é a única maneira de entrar. Planeje para esses casos:
- Proprietário: sempre entra, pode vender a casa e distribuir chaves.
- Detentores de chaves: amigos ou inquilinos que podem entrar e usar o estoque, mas não podem vender.
- Convidados: visitam quando o proprietário está dentro e os deixa entrar (uma campainha).
- Polícia: uma batida ou uma entrada oficial, se seu servidor tiver uma.
Mantenha as chaves como dados e não como um item que você pode soltar: uma lista de identificadores de character por casa. Verifique a lista no servidor cada vez que alguém tenta entrar ou abrir o estoque.
Salvando propriedade no banco de dados
Tudo que deve sobreviver a uma reinicialização vai no banco de dados. Uma estrutura simples é uma tabela para casas e outra para as chaves:
CREATE TABLE IF NOT EXISTS houses (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(64) NOT NULL,
owner VARCHAR(64) DEFAULT NULL,
price INT NOT NULL DEFAULT 0
);
CREATE TABLE IF NOT EXISTS house_keys (
house_id INT NOT NULL,
holder VARCHAR(64) NOT NULL,
PRIMARY KEY (house_id, holder)
);Armazene o identificador de character que seu framework usa para characters (por exemplo o identificador da character e não a licença do jogador), para que a propriedade siga a character. Carregue os dados quando o recurso inicia e mantenha uma cópia em uma tabela de servidor para verificações rápidas, gravando de volta no banco de dados a cada mudança. Queries do Lua estão em oxmysql queries.
Fluxos de dinheiro também importam: a compra, o aluguel e o reembolso da venda devem todos ser tratados no servidor, e funcionam como um sumidouro de dinheiro, como descrito em balanceamento da economia do seu servidor.
Erros comuns
- Não redefinir o bucket ao sair, morrer, desconectar ou reiniciar.
- Mesmo id de estoque para todas as casas.
- Verificar propriedade no cliente apenas.
- Propriedade mantida na memória e perdida na reinicialização.
- Criar o shell mais de uma vez quando o jogador re-entra rapidamente, deixando props duplicadas.
Lista de verificação
| Sintoma | Solução |
|---|---|
| Jogadores em casas diferentes se veem | Dê a cada casa seu próprio routing bucket |
| Jogador preso em um mundo vazio | Redefina o bucket para 0 ao sair, morrer e desconectar |
| Cada casa compartilha um estoque | Use um id de estoque que inclua o id da casa |
| Propriedade perdida na reinicialização | Salve-a no banco de dados |
| Qualquer um pode entrar | Verifique proprietário ou chave no servidor |
| Props shell duplicadas | Delete o shell ao sair e proteja contra spawns duplos |
Respostas rápidas
Qual é a diferença entre um shell e um interior MLO?
Um MLO é um interior real colocado no mapa em coordenadas fixas. Um shell é um prop que você cria sob demanda, geralmente longe do mapa, e remove quando o jogador sai.
Por que os jogadores podem ver as casas uns dos outros?
Cada interior de casa usa as mesmas coordenadas, então jogadores em casas diferentes ficam no mesmo espaço. Coloque cada casa em seu próprio routing bucket para que fiquem separadas.
Onde a propriedade da casa deve ser salva?
No banco de dados, com o id da casa e o identificador de character do proprietário, e uma tabela ou coluna separada para as pessoas que possuem chaves. Nunca mantenha a propriedade apenas na memória.
Scripts sem esse problema
CCTV Security CamerasCâmeras posicionáveis, um tablet com várias telas ao vivo e fotos impressas como prova.Ver script →
Shop CreatorMonte uma loja em menos de um minuto — donos, funcionários, cofres e assaltos inclusos.Ver script →
Item Creator V2Crie itens usáveis com animações, props, efeitos e muito mais — sem escrever código.Ver script →