Zurück zum Blog

Werden Verse Weak Maps in UEFN automatisch bereinigt, wenn ein Player das Spiel verlässt?

Veröffentlicht am 5. Juni 2026
Werden Verse Weak Maps in UEFN automatisch bereinigt, wenn ein Player das Spiel verlässt?

Kurz und knapp

Dieser Artikel räumt mit dem Missverständnis auf, dass Verse in UEFN Einträge aus `weak_map` automatisch bereinigt, sobald ein Player die Fortnite-Insel verlässt. Er erklärt die technischen Hintergründe persistenter Daten in Verse, warnt vor Runtime-Exceptions sowie Memory Leaks und bietet ein praktisches Tutorial zum manuellen Bereinigen temporärer Session-Daten. Abschließend wird gezeigt, wie sich solche komplexen Zustände und Speicher-Limits durch die Integration eines externen Cloud-Backends wie horizOn vereinfachen lassen.

Wenn du deinem KI-Coding-Assistenten vertraust, wenn er dir erzählt, dass Verse Spielerdaten in derselben Millisekunde automatisch aus einer weak_map löscht, in der sie sich von deiner Fortnite-Insel abmelden, bereitest du dich auf einen schweren Runtime-Crash vor. Die Behauptung klingt logisch: Da es sich um eine Weak Reference handelt, sollte die Engine den Key bereinigen, sobald das Player-Objekt von der Garbage Collection erfasst wird. Doch im Unreal Editor for Fortnite (UEFN) ist die Realität weitaus komplexer. Ein falsches Verständnis darüber, wie der Verse Memory Manager die Player-Lifecycles verarbeitet, führt zu unbemerkten State Leaks und Game-breaking Exceptions.

Wenn ein Player das Spiel verlässt, kann das Referenzieren seines veralteten Objekts in einer Map den gefürchteten Fehler ErrRuntime_WeakMapInvalidKey auslösen oder zu inselweiten Abstürzen führen. Das zwingt dich dazu, ein striktes UEFN server crash fix protocol zu implementieren, um deine Server stabil zu halten. Um dies zu vermeiden, müssen Entwickler verstehen, wie Verse Speicher intern verwaltet und wie man zuverlässige Cleanup-Routinen implementiert.

Das Missverständnis: KI-generierte Ratschläge vs. Verse-Realität

Viele Entwickler fragen ihre KI-Assistenten, wie sie mit den Map-Daten eines Players umgehen sollen, wenn dieser ein Match verlässt. Ein häufiger KI-generierter Ratschlag lautet, dass die Engine den Player-Key als „Weak Reference“ behandelt und den Eintrag des Players bei dessen Verlassen automatisch aus der Map entfernt. Das ist grundlegend falsch.

Obwohl weak_map(player, t) in Verse unter der Haube Weak References als Keys verwendet, um harte Referenzzyklen zu vermeiden, die die Garbage Collection blockieren würden, führt sie keine automatische, sofortige Bereinigung der Map-Einträge selbst durch. Der Eintrag – der sowohl den Key-Slot als auch die damit verknüpften Daten enthält – bleibt im Map-Container allokiert.

Wenn dein Code versucht, auf diesen Key zuzugreifen, ihn auszuwerten oder zu verändern, nachdem der Player das Spiel verlassen hat, versucht die Verse-Runtime, ein Null- oder ungültiges Player-Objekt zu dereferenzieren. Anstatt kontrolliert abzubrechen, löst die Runtime einen Absturz aus oder wirft eine nicht abfangbare Exception. Das System erwartet von dir, dass du Lifecycle-Übergänge explizit behandelst, anstatt dich auf ein automatisches Cleanup zu verlassen.

Warum Weak Maps Player-Einträge nicht automatisch bereinigen

Um zu verstehen, warum das passiert, müssen wir uns den Zweck einer weak_map in UEFN ansehen. Anders als in Standard-Programmierumgebungen, in denen Weak Maps als temporäre Memory-Caches dienen, nutzt Verse weak_map(player, t) in erster Linie als Gatekeeper für persistente Spielerdaten.

Session-übergreifende Persistenz

Wenn du eine auf Modulebene deklarierte weak_map(player, t) verwendest, verknüpft die Engine die Werte mit der persistenten Cloud-Datenbank von Epic. Wenn ein Player das Match verlässt und drei Tage später zurückkehrt, gleicht die Engine seine Player-ID mit dem persistenten Map-Key ab, um seinen Fortschritt wiederherzustellen.

Würde die Engine den Eintrag eines Players in dem Moment automatisch aus der Map löschen, in dem er das Spiel verlässt, würde die Map alle persistenten Daten verlieren. Level, benutzerdefinierte Währungen und freigeschaltete Items würden bei jedem Disconnect oder Netzwerk-Timeout eines Players auf Null zurückgesetzt. Daher ist die Datenbank so konzipiert, dass diese Einträge intakt bleiben, eben weil sie Disconnects überstehen sollen.

Die Scoped Lifetime von Player-Objekten

Wenn ein Player ein Match verlässt, wird sein aktives Session-Objekt im Playspace zerstört. Die physische player-Referenz, die dein Verse-Code hält, wird zu einem ungültigen Handle.

Da der Key in der Map nun auf ein ungültiges, inaktives Objekt verweist, schlägt das Abfragen der Map mit dieser toten Referenz fehl. Die Engine scannt und entfernt tote Keys nicht aktiv in Echtzeit aus der Map. Stattdessen verbleiben sie dort inaktiv, weshalb ein manuelles Management zwingend erforderlich ist, um die Anhäufung veralteter Referenzen zu verhindern.

Die Folgen: Memory Leaks, veraltete Daten und Server-Crashes

Das Versäumnis, Player-Einträge zu bereinigen, führt zu drei spezifischen Problemen, die die Performance des Spiels und die Serverstabilität bei langen Matches beeinträchtigen.

  • Stale Data Leakage: Wenn ein Player das Spiel verlässt und ein anderer beitritt, könnte der neue Player die Session-Daten des alten Players erben, falls die Engine interne Player-Slots wiederverwendet. Dies führt zu State-Bugs, wie z. B. dass neue Player mit vollem Inventar oder falschen Match-Statistiken spawnen.
  • Memory-Anhäufung: Während ein einzelner Boolean oder Integer eine vernachlässigbare Menge an Speicher benötigt, kann das Speichern komplexer Strukturen für bis zu 50 Player in einer Lobby mit hoher Kapazität den Speicherbedarf erhöhen. Über eine 4-stündige Server-Session hinweg kann diese Anhäufung die Server-Tickrate beeinträchtigen.
  • Lookup-Fehler: Der Versuch, den Status eines inaktiven Players abzufragen oder Funktionen auf einer toten Player-Referenz aufzurufen, löst sofortige Runtime-Crashes aus.

Erreichen der Epic Cloud-Save-Limits

UEFN setzt der Persistierung von Daten enge Grenzen. Du bist auf maximal 4 persistente weak_maps pro Insel beschränkt, und die Datengröße für den einzelnen Datensatz eines Players darf 256 KB nicht überschreiten.

Wenn du eine persistente weak_map verwendest, um temporäre Session-States zu speichern, verschwendest du diesen wertvollen Speicherplatz in der Datenbank. Jedes Update schreibt in die Epic-Datenbank, was das Risiko eines Write-Throttlings birgt oder das 256-KB-Limit überschreitet, was beim Versuch, weitere Daten zu schreiben, zu einem Runtime-Fehler führt.

Schritt-für-Schritt-Tutorial: Player-Session-States sicher verwalten

Um Player-States ohne das Risiko von Memory Leaks oder Datenbank-Bloat zu verwalten, musst du deine temporären Session-Daten von deinen persistenten Cloud-Daten trennen. Temporäre Daten sollten in Standard-Maps (nicht persistent) gespeichert werden, die du bei Disconnects von Playern manuell bereinigen musst.

Schritt 1: Definiere dein Session-State-Struct

Definiere zunächst ein nicht-persistierbares Struct, das alle Variablen enthält, die dein Player während einer einzelnen Runde oder eines Matches benötigt. Markiere diese Klasse oder dieses Struct nicht als <persistable>.

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

Schritt 2: Richte das Manager Device ein

Erstelle ein Creative Device, das als Koordinator fungiert. Es verwaltet eine veränderliche, nicht-persistente Map der aktiven Player. Da Standard-Maps in Verse unveränderlich (immutable) sind, deklarieren wir die Map-Variable als var, damit wir sie überschreiben können, wenn Player beitreten oder das Spiel verlassen.

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{}

Schritt 3: Playspace-Events abonnieren

Abonniere in der OnBegin-Funktion die Verbindungs-Events des Playspaces. Dadurch wird sichergestellt, dass du Initialisierungscode ausführst, wenn ein Player beitritt, und Cleanup-Code, wenn er das Spiel verlässt.

    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)

Schritt 4: Registrierungs- und Cleanup-Logik implementieren

Wenn ein Player beitritt, befüllst du die Map mit seinem Standard-Session-State. Wenn er das Spiel verlässt, musst du seinen Eintrag aus die Map entfernen. Da Verse keine integrierte Map.Remove()-Funktion besitzt, musst du die Map neu aufbauen und den ausscheidenden Player dabei filtern. Dies verhindert, dass veraltete Referenzen im Speicher verbleiben.

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

Durch den Neuaufbau der Map beim Entfernen des Players löschst du den Referenz-Key vollständig. Die Garbage Collection kann dann die Ressourcen des Players freigeben, ohne veraltete Einträge in deinem Game-Loop zu hinterlassen.

Wenn du während dieser Lifecycle-Übergänge benutzerdefinierte Telemetrie erfassen möchtest, solltest du auch Limits wie das 32-character analytics event name limit in Verse beachten, wenn du Session-Längen oder Währungsstatistiken an externe Backends meldest.

Best Practices für das Verse-State-Management

Um sicherzustellen, dass deine UEFN-Server stabil und performant bleiben, befolge diese Richtlinien zur Verwaltung von Spielerdaten:

  1. Unterscheide Session- vs. persistente Daten: Speichere niemals kurzlebige Variablen (wie die aktuelle Match-Gesundheit, Runden-Scores oder temporäre Positionen) in einer persistenten weak_map. Halte temporäre States in einer veränderbaren Standard-Map, die in einer Manager-Klasse gekapselt ist.
  2. Überprüfe die Aktivität des Players mit IsActive: Bevor du die Daten eines Players in einer Map abrufst oder änderst, prüfe mit der IsActive[]-Abfrage, ob er noch im Playspace anwesend ist. Wenn IsActive[] false zurückgibt, brich den Lookup ab und stoße ein Cleanup-Event an.
  3. Überwache die Datengröße mit FitsInPlayerMap: Wenn du in eine persistente weak_map schreibst, rufe FitsInPlayerMap() auf, um sicherzustellen, dass das Update das 256-KB-Limit nicht überschreitet, und so Runtime-Exceptions zu verhindern.
  4. Konsolidiere deine Maps: Erstelle nicht für jede Variable eine eigene Map. Definiere eine einzige Klasse, die alle Spieler-Variablen enthält, und weise ihr den Player zu. Dies minimiert die Anzahl deiner Maps und respektiert das Insel-Limit von maximal vier persistenten weak_maps.

Komplexität an ein zuverlässiges Cloud-Backend auslagern

Die Verwaltung von Player-Session-Lifecycles, Datenbank-Limits und manueller Cleanup-Logik in Verse kann schnell komplex werden. Wenn du sessionübergreifenden Fortschritt, global synchronisierte Inventare oder regionales Matchmaking aufbauen willst, erfordert die manuelle Verwaltung dieser States das Einrichten von Webhooks, das Skalieren externer Datenbanken und das Handling von Server-zu-Server-Synchronisierung.

Mit horizOn werden diese Backend-Herausforderungen automatisch gelöst. Durch die Integration des horizOn SDKs in deinen Gameserver kannst du das Player-Session-Management in eine dedizierte Cloud-Datenbank auslagern. Wenn ein Player die Verbindung trennt, stößt horizOn ein automatisches Session-Cleanup an, aktualisiert globale Datenbanken und synchronisiert Inventardaten über Serverinstanzen hinweg – ohne die 256-KB-Limits von Verse zu überschreiten oder Runtime-Crashes zu riskieren.

Bereit, dein UEFN-Backend zu skalieren? Teste horizOn kostenlos oder wirf einen Blick in die API docs.


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