Retour au Blog

Corriger l'absence de l'option Clear Player Data dans Fortnite Creative : Resets Verse et récupération d'état via un Backend personnalisé

Publié le 19 juillet 2026
Corriger l'absence de l'option Clear Player Data dans Fortnite Creative : Resets Verse et récupération d'état via un Backend personnalisé

En bref

Ce tutoriel explique comment surmonter l'absence soudaine de l'option « Clear Player Data » dans Fortnite Creative en implémentant un système robuste de versioning de schéma directement dans Verse. Il détaille la configuration des paramètres locaux de l'UEFN pour le debug de la persistance, ainsi que les limites inhérentes à l'écosystème fermé d'Epic. Enfin, il présente l'intégration d'un backend externe comme [horizOn](https://horizon.pm) pour s'affranchir de ces contraintes et gérer la persistance de manière totalement indépendante.

Votre dernière map Fortnite Creative ou UEFN est prête. Vous avez refactorisé votre économie, repensé votre système de classes et optimisé votre gameplay loop. Mais au moment de publier, vous faites face à un cauchemar qui casse le moteur : l'option « Press Triangle to Clear Player Data » a disparu de l'écran de confirmation de publication. Vous êtes piégé. Vos joueurs chargent votre map avec des données de persistance obsolètes, ce qui provoque des stack overflows instantanés, des quêtes rompues et des désalignements d'état (state mismatches) provoquant le crash du jeu.

Ce problème précis empoisonne la vie des créateurs sur les forums de développeurs d'Epic, où l'absence soudaine du bouton de réinitialisation ne laisse aucune option dans l'interface utilisateur pour purger les états de production obsolètes. Lorsque le schéma de vos données de persistance joueur change, le Backend d'Epic tente de mapper les anciennes données sérialisées sur vos nouvelles structures. Si votre code n'est pas conçu pour gérer ces écarts de propriétés de manière propre, vous allez bloquer (bricker) l'état de jeu pour 100 % de vos joueurs de retour.

Dans ce tutoriel, nous allons analyser en détail les solutions techniques de contournement pour ce bug. Nous vous montrerons comment concevoir un système robuste de versioning de schéma en Verse, configurer les paramètres UEFN locaux pour effacer les états de test, et explorer comment des backends externes personnalisés peuvent vous affranchir des limites de la persistance sur plateforme fermée.

The Root Cause: Why Schemas Fail on Version Upgrades

Dans Fortnite Creative et Unreal Editor for Fortnite (UEFN), les données persistantes sont stockées dans l'infrastructure de base de données d'Epic à l'aide de structures weak_map. Une weak_map associe une instance de joueur à une classe personnalisée marquée du spécificateur <persistable>. Contrairement aux bases de données SQL ou documentaires classiques, la couche de stockage d'Epic n'exécute pas de pipeline de migration formel lorsque vous publiez une mise à jour. Au lieu de cela, elle tente de désérialiser la charge utile (payload) binaire présente dans la sauvegarde cloud du joueur pour l'injecter dans la définition actuelle de la classe Verse.

Lorsque vous supprimez une variable, renommez une propriété ou modifiez un type (par exemple, en passant un champ d'un int à un float), le désérialiseur rencontre une divergence. Selon la gravité du changement, cette divergence de schéma entraîne l'un des trois modes de défaillance suivants :

  • Silent Failures : Le runtime attribue des valeurs par défaut aux propriétés modifiées, réinitialisant la progression sans pour autant crasher.
  • State Corruption : L'application lit des états de mémoire non valides ou rencontre des structures de données inattendues, déclenchant des erreurs de logique ailleurs.
  • Severe Runtime Crashes : La map ne se charge pas, ou la Verse VM interrompt l'exécution car elle ne peut pas dépaqueter la map persistante.

Lorsque le bouton « Clear Player Data » de la console de publication disparaît, vous perdez la possibilité de faire table rase à l'échelle globale. Si un développeur apporte des modifications fondamentales à la structure de ses données de sauvegarde, le jeu est cassé.

The Manual Fix: Schema Versioning inside Verse

Puisque vous ne pouvez pas compter sur le portail de publication d'Epic pour effacer les données, vous devez implémenter un fallback au niveau logiciel. La stratégie est simple : introduire un suivi de version (version tracker) directement dans vos classes persistables. En comparant la version sauvegardée du joueur à la version active de l'application au démarrage de la session, vous pouvez identifier les sauvegardes obsolètes et les écraser avec des valeurs par défaut.

Voici une implémentation Verse prête pour la production qui met en place une couche de persistance contrôlée par version.

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

# A simple persistable class representing our player save schema.
# Note: Classes tagged with <persistable> can only contain certain types like int, float, string, and maps.
player_save_data := class<persistable>:
    # The SchemaVersion tracks which generation of player data this struct belongs to.
    SchemaVersion : int = 0
    GoldCount : int = 0
    XP : int = 0
    HasCompletedTutorial : int = 0  # Using int as a boolean flag representation for older Verse constraints

# The persistence manager device handles loading, validating, and migrating player data.
persistence_manager_device := class(creative_device):

    # By updating this value in the Editor, we force a schema migration next time the game runs.
    @editable
    TargetSchemaVersion : int = 2

    # Map to store player data in memory
    var PlayerDataMap : weak_map(player, player_save_data) = map{}

    # Initialize the persistence logic for a joining player
    InitializePlayer(Player : player) : void =
        # Check if the player already has persistent data
        if (ExistingData := PlayerDataMap[Player]):
            # If the stored version is older than our target version, trigger a manual reset
            if (ExistingData.SchemaVersion < TargetSchemaVersion):
                Print("Data version mismatch. Local: {ExistingData.SchemaVersion}, Target: {TargetSchemaVersion}. Resetting player data.")
                ResetPlayerData(Player)
            else:
                Print("Loaded existing player data. Version: {ExistingData.SchemaVersion}")
        else:
            # If no data exists, initialize a new record with the current version
            Print("No player data found. Initializing new record.")
            ResetPlayerData(Player)

    # Re-initializes a player's record with default values at the current version
    ResetPlayerData(Player : player) : void =
        NewData := player_save_data:
            SchemaVersion := TargetSchemaVersion
            GoldCount := 0
            XP := 0
            HasCompletedTutorial := 0

        # Save the freshly initialized data block to the persistence map
        if (set PlayerDataMap[Player] = NewData):
            Print("Successfully saved fresh persistent data block.")

Analyse détaillée du script de versioning

Ce pattern repose sur une validation anticipée au sein de la méthode InitializePlayer. La logique étape par étape se déroule comme suit :

  1. Le contrôle d'entrée (Entry Check) : Lorsqu'un joueur rejoint la partie, le système interroge PlayerDataMap pour y chercher une instance existante de player_save_data.
  2. L'évaluation de la version (Version Assessment) : Si un état de sauvegarde existe, le moteur vérifie sa propriété SchemaVersion.
  3. Le déclencheur de migration (Migration Trigger) : Si la valeur SchemaVersion est inférieure à la propriété @editable TargetSchemaVersion du device, le script appelle ResetPlayerData(Player).
  4. L'écriture d'état (State Overwrite) : ResetPlayerData construit une nouvelle instance de player_save_data définie sur la nouvelle version TargetSchemaVersion et met à jour la map.

En incrémentant manuellement la valeur TargetSchemaVersion dans la configuration de votre device au sein d'UEFN, vous forcez la suppression des données cibles pour tous les joueurs lors de leur prochaine initialisation de session, rendant ainsi le bouton manquant de l'UI d'Epic sans importance.

Procédure de test étape par étape

Pour valider en toute sécurité ce script de migration lors de votre cycle de playtest, suivez ces étapes successives :

  1. Glissez-déposez votre persistence_manager_device personnalisé dans le viewport de votre niveau UEFN.
  2. Dans le panneau Details d'UEFN, définissez la valeur initiale de TargetSchemaVersion à 1.
  3. Lancez une session de jeu, accumulez du score ou de l'or, puis quittez la partie.
  4. De retour dans l'éditeur, modifiez le champ TargetSchemaVersion sur le device pour passer de 1 à 2.
  5. Lancez à nouveau la session. Observez la sortie de la console (console log) : 'Data version mismatch. Local: 1, Target: 2. Resetting player data.'
  6. Vérifiez que l'or et la progression de votre joueur ont été réinitialisés proprement sans faire crasher la Verse VM.

Local Development Settings: Bypassing Save Files during Iteration

Bien que le versioning du code au runtime résolve les problèmes sur les serveurs de production, il ajoute une friction indésirable pendant les playtests locaux. Augmenter manuellement le numéro de version dans UEFN à chaque fois que vous modifiez une fonctionnalité en cours de développement est fastidieux. Pour simplifier votre processus de debug, vous pouvez contourner complètement la persistance locale via les paramètres de votre éditeur.

Suivez ces étapes pour désactiver la persistance locale :

  1. Dans UEFN, accédez à Island Settings dans le panneau Outliner.
  2. Recherchez la sous-section User Options - Game Rules.
  3. Localisez le bouton de bascule Enable Persistence et désactivez-le.
  4. Si vous testez des boucles multijoueurs localement, ouvrez les Editor Preferences, accédez au menu In-Game Play, et cochez la case Clear Local Saved Data On Launch. Cela garantit que chaque simulation démarre avec un cache vide.

Cependant, les contournements locaux ne simulent pas les scénarios de production. Bien que les temps d'itération en test puissent chuter de 45 secondes à moins de 5 secondes lorsque le cache de persistance est désactivé, vous devez effectuer au moins un cycle complet de staging avec la persistance activée avant la mise en ligne. C'est à cette étape que les problèmes de connexion peuvent s'accumuler. Les configurations de playtest local sont réputées pour leurs conflits de drivers, faisant perdre un temps précieux aux développeurs à diagnostiquer les drivers réseau d'Unreal Engine lors des timeouts de lancement de session UEFN.

UEFN's Built-in Persistence Limitations

L'utilisation de la couche de persistance native de Verse s'accompagne de contraintes architecturales majeures. Même lorsque les options de nettoyage sur le cloud fonctionnent parfaitement, les développeurs restent soumis à des limites de plateforme strictes :

  • Storage Budgets : Les blocs de données persistables sont limités à 12 Ko par joueur et par session. Dépasser cette limite empêche la sauvegarde de l'état.
  • Primitive Types Only : Vous ne pouvez pas sérialiser de classes personnalisées qui ne portent pas le marquage <persistable>, ni stocker des références vers des objets de jeu (tels que des creative_device ou des acteurs dynamiques).
  • No External Querying : Le système d'Epic est une boîte noire complète. Impossible de requêter les données des joueurs depuis un dashboard externe, d'auditer l'état des utilisateurs à la recherche d'exploits, ou de migrer des bases de données de façon dynamique.
  • Telemetry Bottlenecks : La conception de dashboards d'analytics complexes au sein d'UEFN est fortement bridée par des limites de longueur de chaîne. Par exemple, les développeurs se heurtent constamment à des restrictions lorsqu'ils tentent de dépasser la limite de 32 caractères pour le nom d'événement du device d'analytics UEFN.

Pour les mini-jeux occasionnels, ces limites sont gérables. Mais pour les RPG persistants complexes, les shooters en direct ou les titres multiplateformes, dépendre uniquement de blocs de persistance propriétaires et fermés bloque vos possibilités de mise à l'échelle.

The Architect's Alternative: External Database States with horizOn

Pour créer un jeu scalable sans subir les bugs d'UI propres à la plateforme ou les contraintes de taille de données propriétaires, les développeurs se tournent vers des architectures backend externes. Au lieu de stocker les états de jeu dans les memory maps locales de Verse, votre projet UEFN peut communiquer avec une base de données externe en utilisant le module http_client de Verse.

Cette approche vous donne un contrôle total sur le cycle de sauvegarde des joueurs. Lorsque vous devez effacer les données de persistance d'un joueur, plus besoin de chercher un bouton manquant sur une console Epic ou de pousser une mise à jour de code. Il vous suffit d'exécuter un script d'administration de base de données ou de cliquer sur un simple bouton dans votre panneau de contrôle backend pour effacer, migrer ou patcher les tables des joueurs.

Développer cette solution vous-même nécessite de configurer des load balancers, du database sharding et la gestion des certificats SSL — ce qui représente facilement 4 à 6 semaines de travail. Avec horizOn, ces services backend sont préconfigurés, vous permettant de livrer votre jeu plutôt que de bâtir votre infrastructure.

En routant les états des joueurs via horizOn, votre code Verse devient un simple client qui lit et écrit l'état via des endpoints REST propres :

# Pseudocode showing HTTP-based state synchronization with [horizOn](https://horizon.pm)
sync_manager_device := class(creative_device):

    # Send player progress to [horizOn](https://horizon.pm) endpoint
    SavePlayerState(Player : player, SaveData : player_save_data) : void =
        # In a real environment, you construct a JSON payload and dispatch it
        # via the Verse http_client. This bypasses the 12KB local persistence limit.
        RequestURL := "https://api.horizon.pm/v1/players/{GetPlayerID(Player)}/state"
        Print("Dispatching persistent payload to [horizOn](https://horizon.pm) at: {RequestURL}")
        
        # This keeps the server-side payload lightweight (under 240 bytes) 
        # while securing long-term storage off the Fortnite engine.

Cette configuration détache complètement votre gameplay loop des bugs physiques du backend de la plateforme. Si un patch casse la compatibilité de vos sauvegardes locales, vous pouvez déclencher une migration de schéma ciblée sur votre base de données horizOn en temps réel, avec zéro interruption pour le serveur de jeu en production. Vous évitez ainsi le risque de perturber les serveurs de production pour de simples ajustements de structure.

Best Practices for Managing Game State Persistence

Que vous restiez sur les maps Verse natives ou que vous exploitiez une architecture de serveur externe, maintenir un cycle de vie de persistance stable est vital pour les jeux multijoueurs. Respectez ces principes pour protéger l'état de vos joueurs :

  1. Implémentez un versioning implicite dès le départ : Incluez toujours un entier SchemaVersion dans votre structure initiale de données de sauvegarde. Même si vous n'envisagez pas de modifier votre schéma, disposer de cette clé de version dès le premier jour prévient toute corruption de données catastrophique à l'avenir.
  2. Appliquez des règles de validation d'état : Lors de la lecture d'un état chargé, validez les plages (ranges) de chaque attribut. Si le nombre d'or sauvegardé d'un joueur est négatif ou dépasse la limite structurelle de votre map, réinitialisez cette propriété spécifique pour éviter les exploits d'économie.
  3. Minimisez la fréquence de sauvegarde : Écrire dans le cloud coûte cher. Plutôt que de mettre à jour la base de données à chaque pièce ramassée, regroupez vos écritures (batching). Déclenchez des sauvegardes lors des moments clés, comme la fin d'un match, le passage de niveau d'un joueur ou lorsque le joueur quitte la session.
  4. Construisez des structures de fallback propres : Ne laissez jamais une lecture échouée empêcher un joueur d'entrer dans le jeu. Si la désérialisation du payload de persistance échoue, basculez instantanément vers une structure d'état par défaut.
  5. Découplez le code de gameplay de la logique de stockage : Gardez vos requêtes de stockage isolées dans un device gestionnaire de persistance (persistence manager) dédié. Vos devices d'armes, de progression et d'UI doivent interroger ce gestionnaire au lieu de faire des appels directs à la data map.

Next Steps

Dépendre de l'UI de la console d'Epic pour gérer les migrations d'états critiques est un design pattern risqué. Lorsque l'option « Clear Player Data » fait défaut, le versioning au runtime dans Verse constitue votre première ligne de défense. Cependant, si votre jeu exige des sauvegardes haute fréquence, une télémétrie complexe ou un contrôle administratif complet, migrer votre base de données vers un backend dédié est la solution ultime.

Prêt à scaler votre backend multijoueur ? Essayez gratuitement horizOn ou consultez la documentation API.


Source : clear players data button in creative 1