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).

lua
-- 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:

lua
-- server
exports.ox_inventory:RegisterStash('house_' .. houseId, 'House storage', 50, 100000, ownerId)

Em seguida, abra-o com o mesmo id no cliente:

lua
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:

sql
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 →

Continue lendo