Volver al Blog

¿Se limpian automáticamente las Weak Maps de Verse cuando un jugador se desconecta en UEFN?

Publicado el 5 de junio de 2026
¿Se limpian automáticamente las Weak Maps de Verse cuando un jugador se desconecta en UEFN?

En resumen

Este artículo desmiente el mito de que las `weak_map` en Verse limpian de forma automática las entradas de los jugadores al desconectarse en UEFN. Se detallan los riesgos de fugas de memoria, datos obsoletos y crashes en runtime al depender de este comportamiento incorrecto. Finalmente, se ofrece una guía paso a paso para implementar una rutina de limpieza manual segura y se presenta a horizOn como solución para delegar la gestión del estado en el backend.

Si confías en tu asistente de programación con IA cuando te dice que Verse purga automáticamente los datos de jugador de una weak_map en el milisegundo en que se desconecta de tu isla de Fortnite, te estás preparando para un grave crash en runtime. La afirmación suena lógica: al tratarse de una referencia débil, el motor debería limpiar la clave una vez que el objeto del jugador sea procesado por el Garbage Collection. Sin embargo, en Unreal Editor for Fortnite (UEFN), la realidad es mucho más compleja, y no entender cómo el gestor de memoria de Verse procesa el ciclo de vida de los jugadores genera fugas de estado silenciosas y excepciones que rompen el juego.

Cuando un jugador se desconecta, hacer referencia a su objeto obsoleto en un mapa puede lanzar el temido ErrRuntime_WeakMapInvalidKey o provocar crashes en toda la isla, obligándote a implementar un estricto UEFN server crash fix protocol para mantener estables tus servidores. Para evitar esto, los desarrolladores deben aprender cómo Verse gestiona la memoria internamente y cómo implementar rutinas de limpieza autoritativas.

El error común: consejos de IA frente a la realidad de Verse

Muchos desarrolladores preguntan a sus asistentes de IA cómo gestionar los datos del mapa de un jugador cuando este abandona la partida. Un consejo habitual generado por IA es que el motor trata la clave del jugador como una "referencia débil" y elimina automáticamente la entrada del jugador del mapa al desconectarse. Esto es fundamentalmente incorrecto.

Aunque la estructura weak_map(player, t) de Verse utiliza referencias débiles como claves internamente para evitar ciclos de referencias fuertes que bloquearían el Garbage Collection, no realiza una limpieza automática e inmediata de las propias entradas del mapa. La entrada —que contiene tanto el espacio para la clave como sus datos asociados— permanece asignada en el contenedor del mapa.

Si tu código intenta acceder, evaluar o mutar esa clave después de que el jugador se haya ido, el runtime de Verse intentará desreferenciar un objeto de jugador nulo o inválido. En lugar de fallar de forma controlada, el runtime provoca un crash o lanza una excepción que no se puede capturar. El sistema espera que gestiones las transiciones del ciclo de vida de forma explícita en lugar de confiar en una limpieza automática.

Por qué las Weak Maps no limpian automáticamente las entradas de los jugadores

Para entender por qué ocurre esto, debemos analizar el propósito de una weak_map en UEFN. A diferencia de los entornos de programación estándar donde las weak maps son cachés de memoria temporales, Verse utiliza weak_map(player, t) principalmente como guardián de los datos persistentes del jugador.

Persistencia a lo largo de las sesiones de juego

Cuando utilizas una weak_map(player, t) declarada en el ámbito del módulo, el motor vincula los valores a la base de datos persistente en la nube de Epic. Si un jugador abandona la partida y regresa tres días después, el motor empareja su ID de jugador con la clave del mapa persistente para restaurar su progreso.

Si el motor borrara automáticamente la entrada de un jugador del mapa en el momento en que abandona la partida, el mapa perdería todos los datos persistentes. Los niveles, las monedas personalizadas y los objetos desbloqueados se restablecerían a cero cada vez que un jugador se desconectara o sufriera un timeout de red. Por lo tanto, la base de datos está diseñada para mantener estas entradas intactas precisamente porque están pensadas para sobrevivir a las desconexiones.

El ciclo de vida acotado de los objetos de jugador

Cuando un jugador abandona una partida, su objeto de sesión activo en el playspace se destruye. La referencia física player que conserva tu código de Verse se convierte en un puntero obsoleto (dead handle).

Dado que la clave dentro del mapa ahora apunta a un objeto inválido e inactivo, consultar el mapa con esa referencia obsoleta fallará. El motor no escanea ni elimina activamente las claves muertas del mapa en tiempo real. En su lugar, las deja inactivas, razón por la cual la gestión manual es obligatoria para evitar la acumulación de referencias obsoletas.

Las consecuencias: fugas de memoria, datos obsoletos y crashes de servidor

No limpiar las entradas de los jugadores provoca tres problemas distintos que degradan el rendimiento del juego y la estabilidad del servidor en partidas largas.

  • Fuga de datos obsoletos: Si un jugador se va y otro se une, el nuevo jugador podría heredar los datos de sesión del jugador anterior si el motor reutiliza los slots de jugador internos. Esto provoca bugs de estado, como nuevos jugadores que spawnean con inventarios llenos o estadísticas de partida incorrectas.
  • Acumulación de memoria: Aunque un solo booleano o entero ocupa un espacio insignificante, almacenar estructuras complejas para hasta 50 jugadores en un lobby de alta capacidad puede disparar el uso de memoria. A lo largo de una sesión de servidor de 4 horas, esta acumulación puede degradar el tick rate del servidor.
  • Fallos de búsqueda: Intentar consultar el estado de un jugador inactivo o llamar a funciones sobre una referencia de jugador muerta provoca crashes inmediatos en runtime.

Límite de guardado en la nube de Epic

UEFN impone límites estrictos a los datos persistentes. Estás restringido a un máximo de 4 weak_maps persistentes por isla, y el tamaño del registro individual de cada jugador no puede superar los 256 KB de datos.

Si utilizas una weak_map persistente para almacenar estados de sesión temporales, estarás desperdiciando este valioso espacio de base de datos. Cada actualización escribe en la base de datos de Epic, lo que puede provocar penalizaciones por saturación de escritura (write-throttling) o superar el límite de 256 KB, lo que lanza un error en runtime al intentar escribir más datos.

Tutorial paso a paso: gestión segura de los estados de sesión de los jugadores

Para gestionar los estados de los jugadores sin arriesgarte a fugas de memoria o saturación de la base de datos, debes separar los datos temporales de la sesión de los datos persistentes en la nube. Los datos temporales deben almacenarse en mapas estándar no persistentes, los cuales debes limpiar manualmente cuando un jugador se desconecte.

Paso 1: Definir el struct de estado de la sesión

Comienza por definir un struct no persistente que contenga todas las variables que tu jugador necesita durante una sola ronda o partida. No marques esta clase o struct como <persistable>.

# Define the transient data structure for active gameplay tracking
player_session_state := struct:
    IsMoneyBagFull : logic = false
    CurrentGold : int = 0
    SpawnTime : float = 0.0

Paso 2: Crear el dispositivo gestor (Manager Device)

Crea un creative device que actúe como coordinador. Este contendrá un mapa mutable y no persistente de jugadores activos. Dado que los mapas estándar en Verse son inmutables, declaramos la variable del mapa como var para poder sobrescribirla cuando los jugadores se unan o salgan.

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

# Device handling player lifecycle events and session state mapping
state_manager_device := class(creative_device):

    # Non-persistent map for tracking active player sessions
    var SessionStates : [player]player_session_state = map{}

Paso 3: Suscribirse a los eventos del Playspace

En la función OnBegin, suscríbete a los eventos de conexión del playspace. Esto garantiza que ejecutes el código de inicialización cuando un jugador se une, y el código de limpieza cuando se va.

    OnBegin<override>()<suspends>:void=
        GetPlayspace().PlayerAddedEvent().Subscribe(OnPlayerAdded)
        GetPlayspace().PlayerRemovedEvent().Subscribe(OnPlayerRemoved)
        
        # Initialize any players already in the session (useful for UEFN hot-reloading)
        for (Player : GetPlayspace().GetPlayers()):
            OnPlayerAdded(Player)

Paso 4: Implementar la lógica de registro y limpieza

Cuando un jugador se une, rellena el mapa con su estado de sesión por defecto. Cuando se va, debes purgar su entrada del mapa. Como Verse no cuenta con una función Map.Remove() integrada, debes reconstruir el mapa filtrando al jugador que se retira. Esto evita que queden referencias obsoletas flotando en la memoria.

    # Triggered when a player connects to the server
    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.")

    # Triggered when a player disconnects or leaves the game
    OnPlayerRemoved(Player: player):void=
        Print("Player disconnected. Initiating map cleanup.")
        RemovePlayerSession(Player)

    # Purges the player's entry by reconstructing the map
    RemovePlayerSession(PlayerToRemove: player):void=
        var CleanedStates : [player]player_session_state = map{}
        for (ActivePlayer -> State : SessionStates):
            # Copy all players except the one who left
            if (ActivePlayer <> PlayerToRemove):
                if (set CleanedStates[ActivePlayer] = State):
                    # Entry successfully migrated to the cleaned map
        
        set SessionStates = CleanedStates
        Print("Successfully removed player session entry from memory.")

Al reconstruir el mapa al eliminar a un jugador, borras por completo la clave de referencia. De este modo, el Garbage Collector puede reclamar los recursos del jugador sin dejar entradas huérfanas en tu game loop.

Si deseas realizar un seguimiento de telemetría personalizada durante estas transiciones del ciclo de vida, también debes tener en cuenta límites como el límite de 32 caracteres para nombres de eventos analíticos en Verse al reportar la duración de las sesiones o estadísticas de monedas a backends externos.

Buenas prácticas para la gestión del estado en Verse

Para garantizar que tus servidores de UEFN se mantengan estables y con buen rendimiento, sigue estas pautas para la gestión de datos de los jugadores:

  1. Diferencia los datos de sesión de los persistentes: Nunca almacenes variables de corta duración (como la salud en la partida actual, la puntuación de la ronda o las posiciones temporales) en una weak_map persistente. Mantén los estados temporales en un mapa mutable estándar dentro de una clase gestora (manager class).
  2. Verifica la actividad del jugador con IsActive: Antes de recuperar o modificar los datos de un jugador en cualquier mapa, comprueba si sigue presente en el playspace utilizando la consulta IsActive[]. Si IsActive[] devuelve falso, cancela la consulta y lanza un evento de limpieza.
  3. Controla el tamaño de los datos con FitsInPlayerMap: Al escribir en una weak_map persistente, llama a FitsInPlayerMap() para confirmar que la actualización no superará el límite de 256 KB, evitando así excepciones en runtime.
  4. Consolidar tus mapas: No crees mapas separados para cada variable. Define una única clase que contenga todas las variables del jugador y mapea al jugador a esa clase. Esto reduce al mínimo el recuento de mapas y respeta el límite de cuatro weak maps persistentes por isla.

Delegar la complejidad en un backend en la nube confiable

Gestionar los ciclos de vida de las sesiones de los jugadores, los límites de las bases de datos y la lógica de limpieza manual en Verse puede volverse complejo rápidamente. Si necesitas construir progresión entre sesiones, inventarios sincronizados globalmente o matchmaking regional, gestionar estos estados manualmente requiere configurar webhooks, escalar bases de datos externas y manejar la sincronización servidor a servidor.

Con horizOn, estos desafíos del backend se gestionan de forma automática. Al integrar el SDK de horizOn en tu servidor de juego, puedes delegar la gestión de sesiones de los jugadores en una base de datos dedicada en la nube. Cuando un jugador se desconecta, horizOn activa una limpieza de sesión automatizada, actualiza las bases de datos globales y sincroniza los registros de inventario entre las distintas instancias del servidor sin chocar con los límites de memoria de 256 KB de Verse ni arriesgarse a crashes en runtime.

¿Listo para escalar tu backend de UEFN? Prueba horizOn de forma gratuita o consulta la API docs.


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