Voltar ao Blog

Os weak maps do Verse são limpos automaticamente quando um jogador sai no UEFN?

Publicado em 5 de junho de 2026
Os weak maps do Verse são limpos automaticamente quando um jogador sai no UEFN?

Em resumo

Este artigo explica por que os `weak_map` do Verse no UEFN não realizam a limpeza automática de entradas de jogadores após a desconexão, refutando um equívoco comum gerado por IAs. O comportamento ocorre porque esses maps são projetados para persistir dados na nuvem da Epic entre sessões de jogo, o que pode gerar memory leaks, dados obsoletos e crashes caso chaves inválidas sejam referenciadas. É apresentado um tutorial passo a passo para gerenciar estados de sessão de forma segura por meio de reconstrução manual de maps, evitando falhas em runtime. Por fim, discute-se como contornar as limitações de memória do Verse delegando o gerenciamento de sessões a uma solução de backend robusta como a horizOn.

Se você confia no seu assistente de programação com IA quando ele diz que o Verse expurga automaticamente os dados de um jogador de um weak_map no milissegundo em que ele se desconecta da sua ilha do Fortnite, você está se preparando para um travamento grave em runtime. A alegação parece lógica: por ser uma referência fraca (weak reference), a engine deveria limpar a chave assim que o objeto do jogador passar pelo Garbage Collection. Mas no Unreal Editor for Fortnite (UEFN), a realidade é muito mais complexa, e não entender como o gerenciador de memória do Verse processa o ciclo de vida dos jogadores leva a vazamentos de estado silenciosos e exceções catastróficas.

Quando um jogador sai, referenciar seu objeto obsoleto em um map pode acionar o temido ErrRuntime_WeakMapInvalidKey ou resultar em travamentos em toda a ilha, exigindo que você implemente um protocolo rigoroso de crashes de servidor no UEFN para manter seus servidores estáveis. Para evitar isso, os desenvolvedores precisam aprender como o Verse gerencia a memória internamente e como implementar rotinas autoritativas de limpeza.

O Equívoco: Conselhos Gerados por IA vs. A Realidade do Verse

Muitos desenvolvedores perguntam aos seus assistentes de IA como lidar com os dados do map de um jogador quando ele sai de uma partida. Um conselho comum gerado por IA é que a engine trata a chave do jogador como uma "weak reference" e elimina automaticamente a entrada do jogador do map após a sua saída. Isso é fundamentalmente incorreto.

Embora o weak_map(player, t) do Verse utilize referências fracas como chaves sob o capô para evitar ciclos de referências fortes que bloqueariam o Garbage Collection, ele não realiza a limpeza automática e imediata das próprias entradas do map. A entrada — contendo tanto o slot da chave quanto seus dados associados — permanece alocada no container do map.

Se o seu código tentar acessar, avaliar ou modificar essa chave depois que o jogador tiver saído, o runtime do Verse tentará desreferenciar um objeto de jogador nulo ou inválido. Em vez de falhar graciosamente, o runtime causa um crash ou lança uma exceção impossível de capturar. O sistema espera que você lide com as transições de ciclo de vida explicitamente, em vez de depender da limpeza automática.

Por Que os Weak Maps Não Limpam Automaticamente as Entradas dos Jogadores

Para entender por que isso acontece, devemos analisar o propósito de um weak_map no UEFN. Ao contrário dos ambientes de programação padrão, onde weak maps são caches de memória temporários, o Verse usa weak_map(player, t) principalmente como o guardião de dados persistentes do jogador.

Persistência Entre Sessões de Jogo

Quando você usa um weak_map(player, t) declarado no escopo do módulo, a engine vincula os valores ao banco de dados em nuvem persistente da Epic. Se um jogador sai da partida e retorna três dias depois, a engine associa o ID do jogador com a chave persistente do map para restaurar seu progresso.

Se a engine limpasse automaticamente a entrada de um jogador do map no momento em que ele saísse do jogo, o map perderia todos os dados persistentes. Níveis, moedas personalizadas e itens desbloqueados seriam resetados para zero toda vez que um jogador se desconectasse ou sofresse uma queda de conexão. Assim, o banco de dados é arquitetado para manter essas entradas intactas justamente porque elas foram feitas para sobreviver a desconexões.

O Ciclo de Vida Escopado de Objetos do Jogador

Quando um jogador sai de uma partida, o objeto de sua sessão ativa no playspace é destruído. A referência física do player mantida pelo seu código Verse torna-se um handle morto (dead handle).

Como a chave dentro do map agora aponta para um objeto inválido e inativo, consultar o map com essa referência morta falhará. A engine não faz uma varredura ativa para remover chaves mortas do map em tempo real. Em vez disso, ela as deixa inertes, e é por isso que o gerenciamento manual é obrigatório para evitar o acúmulo de referências obsoletas.

As Consequências: Memory Leaks, Dados Obsoletos e Crashes de Servidor

A falta de limpeza das entradas de jogadores resulta em três problemas distintos que degradam o desempenho do jogo e a estabilidade do servidor em partidas longas.

  • Stale Data Leakage: Se um jogador sai e outro entra, o novo jogador pode herdar os dados de sessão do jogador antigo se a engine reutilizar slots internos de jogadores. Isso leva a bugs de estado, como novos jogadores nascendo com o inventário cheio ou estatísticas de partida incorretas.
  • Acúmulo de Memória (Memory Accumulation): Embora um único booleano ou inteiro ocupe um espaço insignificante, armazenar estruturas complexas para até 50 jogadores em um lobby de alta capacidade pode aumentar o uso de memória. Ao longo de uma sessão de servidor de 4 horas, esse acúmulo pode degradar os tick rates do servidor.
  • Look-up Failures: Tentar consultar o status de um jogador inativo ou chamar funções em uma referência de jogador morta dispara crashes imediatos em runtime.

Atingindo os Limites de Cloud Save da Epic

O UEFN impõe limites rígidos para dados persistentes. Você está restrito a um máximo de 4 weak_maps persistentes por ilha, e o tamanho do registro individual de cada jogador não pode exceder 256 KB de dados.

Se você usar um weak_map persistente para armazenar estados temporários de sessão, estará desperdiçando este espaço valioso do banco de dados. Cada atualização grava no banco de dados da Epic, correndo o risco de sofrer uma penalidade de write-throttling (limitação de escrita) ou exceder o limite de 256 KB, o que aciona um erro de runtime ao tentar gravar mais dados.

Tutorial Passo a Passo: Gerenciando Estados de Sessão de Jogadores com Segurança

Para gerenciar os estados dos jogadores sem correr o risco de memory leaks ou inchaço do banco de dados, você precisa separar seus dados de sessão temporários (transientes) dos seus dados persistentes na nuvem. Os dados transientes devem ser armazenados em maps padrão não persistentes, que você deve limpar manualmente nas desconexões dos jogadores.

Passo 1: Defina a Struct de Estado da Sessão

Comece definindo uma struct não persistente que contenha todas as variáveis que seu jogador precisa durante uma única rodada ou partida. Não marque esta classe ou struct como <persistable>.

# Define a estrutura de dados temporários para rastreamento de gameplay ativo
player_session_state := struct:
    IsMoneyBagFull : logic = false
    CurrentGold : int = 0
    SpawnTime : float = 0.0

Passo 2: Estabeleça o Dispositivo Gerenciador

Crie um creative device que funcione como o coordenador. Ele manterá um map mutável e não persistente dos jogadores ativos. Como os maps padrão no Verse são imutáveis, declaramos a variável do map como var para que possamos sobrescrevê-la quando os jogadores entrarem ou saírem.

using { /Fortnite.com/Devices }
using { /Fortnite.com/Playspaces }
using { /Verse.org/Simulation }

# Dispositivo que gerencia eventos de ciclo de vida do jogador e mapeamento de estado de sessão
state_manager_device := class(creative_device):

    # Map não persistente para rastrear sessões de jogadores ativos
    var SessionStates : [player]player_session_state = map{}

Passo 3: Inscreva-se nos Eventos de Playspace

Na função OnBegin, inscreva-se nos eventos de conexão do playspace. Isso garante que você execute o código de inicialização quando um jogador entrar e o código de limpeza quando ele sair.

    OnBegin<override>()<suspends>:void=
        GetPlayspace().PlayerAddedEvent().Subscribe(OnPlayerAdded)
        GetPlayspace().PlayerRemovedEvent().Subscribe(OnPlayerRemoved)
        
        # Inicializa quaisquer jogadores já na sessão (útil para hot-reloading do UEFN)
        for (Player : GetPlayspace().GetPlayers()):
            OnPlayerAdded(Player)

Passo 4: Implemente a Lógica de Registro e Limpeza

Quando um jogador entra, preencha o map com o estado de sessão padrão dele. Quando ele sai, você deve expurgar sua entrada do map. Como o Verse não possui uma função Map.Remove() integrada, você deve reconstruir o map, filtrando o jogador que está saindo. Isso evita que referências obsoletas permaneçam na memória.

    # Acionado quando um jogador se conecta ao servidor
    OnPlayerAdded(Player: player):void=
        if (not SessionStates[Player]):
            InitialState := player_session_state{IsMoneyBagFull := false, CurrentGold := 0, SpawnTime := GetEngineTime()}
            if (set SessionStates[Player] = InitialState):
                Print("Initialized gameplay state for joining player.")

    # Acionado quando um jogador desconecta ou sai do jogo
    OnPlayerRemoved(Player: player):void=
        Print("Player disconnected. Initiating map cleanup.")
        RemovePlayerSession(Player)

    # Expruga a entrada do jogador reconstruindo o map
    RemovePlayerSession(PlayerToRemove: player):void=
        var CleanedStates : [player]player_session_state = map{}
        for (ActivePlayer -> State : SessionStates):
            # Copia todos os jogadores, exceto o que saiu
            if (ActivePlayer <> PlayerToRemove):
                if (set CleanedStates[ActivePlayer] = State):
                    # Entrada migrada com sucesso para o map limpo
        
        set SessionStates = CleanedStates
        Print("Successfully removed player session entry from memory.")

Ao reconstruir o map na remoção do jogador, você deleta completamente a chave de referência. O Garbage Collector pode então reivindicar os recursos do jogador sem deixar entradas obsoletas no seu game loop.

Se você quiser rastrear telemetria personalizada durante essas transições de ciclo de vida, também deve ficar atento a limites como o limite de 32 caracteres para nomes de eventos de analytics no Verse ao relatar a duração de sessões ou estatísticas de moedas para backends externos.

Boas Práticas para Gerenciamento de Estado no Verse

Para garantir que seus servidores do UEFN permaneçam estáveis e com boa performance, siga estas diretrizes para gerenciar dados de jogadores:

  1. Diferencie Dados de Sessão vs. Dados Persistentes: Nunca armazene variáveis de curta duração (como vida atual na partida, pontuação da rodada ou posições temporárias) em um weak_map persistente. Mantenha os estados transientes em um map mutável padrão encapsulado dentro de uma classe gerenciadora.
  2. Verifique a Atividade do Jogador com IsActive: Antes de recuperar ou modificar os dados de um jogador em qualquer map, verifique se ele ainda está presente no playspace usando a consulta IsActive[]. Se IsActive[] retornar falso, aborte a busca e acione um evento de limpeza.
  3. Monitore o Tamanho dos Dados com FitsInPlayerMap: Ao escrever em um weak_map persistente, chame FitsInPlayerMap() para confirmar se a atualização não excederá o limite de 256 KB, evitando exceções em runtime.
  4. Consolide Seus Maps: Não crie maps separados para cada variável. Defina uma única classe contendo todas as variáveis do jogador e mapeie o jogador para essa classe. Isso minimiza a contagem de maps e respeita o limite de quatro weak maps persistentes por ilha.

Delegando a Complexidade para um Backend em Nuvem Confiável

Gerenciar ciclos de vida de sessão de jogadores, limites de banco de dados e lógica de limpeza manual no Verse pode se tornar complexo rapidamente. Se você precisa construir progressão cross-session, inventários sincronizados globalmente ou matchmaking regional, gerenciar esses estados manualmente exige a configuração de webhooks, escalonamento de bancos de dados externos e tratamento de sincronização servidor a servidor.

Com o horizOn, esses desafios de backend são tratados de forma automática. Ao integrar o SDK do horizOn no servidor do seu jogo, você pode delegar o gerenciamento de sessões de jogadores a um banco de dados em nuvem dedicado. Quando um jogador se desconecta, o horizOn aciona a limpeza automática da sessão, atualiza os bancos de dados globais e sincroniza os registros de inventário entre as instâncias do servidor, sem atingir os limites de memória de 256 KB do Verse ou arriscar crashes em runtime.

Pronto para escalar seu backend no UEFN? Experimente o horizOn gratuitamente ou confira a documentação da API.


Fonte: When using weak maps, does a player's entry in the map automatically get removed on them leaving the game?