Forum Discussion

atlantika_ryan's avatar
atlantika_ryan
Start Partner
13 days ago
Solved

Player Persistent Variables not saving from Published World (works in Editor)

Not sure if this is the best place to post this, but we are running into an issue with our player persistent variables not saving data between sessions while a player is in our recently Published world. We have confirmed the following:

  • Saving and loading works fine in the Editor (and has for months), including between sessions.
  • Saving something from an Editor for a player does successfully get loaded by that player in a Published session.
  • Nothing is being saved from the Published world back to our player persistent variables.

Has anyone else encountered an issue like this? Or is there potentially a configuration step we missed when publishing?

  • gausroth's avatar
    gausroth
    12 days ago

    The only way I know of that will allow you to see the logs in a published world is the Debug Console gizmo. Just make sure it is set to Published mode. Then any editor of the world should be able to see the gizmo in published mode.

7 Replies

  • Can you share the code for this?

    Published mode runs faster than the editor. It could be a race condition. If you're doing it when the player leaves the world you only have the one frame to do so, or the player reference will be null.

    • We do perform a save when we onExitWorld, but we also call it at certain points when we grant items. (we are using a playerVar as our persistence layer)

      We have alot going on.  But suffice it to say that we call "SetPlayervars" in the path both during the test, AND on exit.


      The interesting part to me is why does it work fine reading/ writing in editor.. and reading seems fine in the published world. (if you go do something in editor.. then load that same player in the published world on device.. you have your data as expected)   But saving just doesn't seem to happen.

      Is there anywhere we can go to look for error logs or anything for the published world ?





      Here is a  bunch of our code for context:

      export type PlayerVar = {
        version: number;
        name: string;
        visits: number;
        usingUI: boolean;
        inventory: Array<{itemId: string, quantity: number}>;
        equippedBowItemId: string;  //DEPRECATED
        equippedArtifactItemId: string;
        equippedWingsItemId: string;
        playerXP: number;
        equippedPetItemId: string;
        completedObjectiveIDs: string[];
      }
      
      export const allPlayerVarData = new Map<Player, PlayerVar>();
      
      
      const playerVarName = `PlayerVarsObj`;
      const varGroupName: string = "PlayerData_Live"; //group name for variable group
      const varKey: string = `${varGroupName}:${playerVarName}`;
      
      
      preStart() {
          if (DISPLAY_CONSOLE_PLAYER_DATA_MANAGER)
            console.log(`PlayerDataManager: Prestart PlayerDataManager`);
          
          this.connectCodeBlockEvent(this.entity, CodeBlockEvents.OnPlayerEnterWorld, this.playerEnterWorld.bind(this));
           this.connectCodeBlockEvent(this.entity, CodeBlockEvents.OnPlayerExitWorld, this.playerExitWorld.bind(this));   
        }
      
        start(){    
          if (DISPLAY_CONSOLE_PLAYER_DATA_MANAGER)
            console.log("PlayerDataManager: PlayerDataManager started");
          
        }
        
        playerEnterWorld(player: Player) {
          
          let newPlayerVar = this.GetPlayerVars(player);
          
          if (newPlayerVar == null){
            console.error(`PlayerDataManager: newPlayerVar is null after calling GetPlayerVar`);
            return;
          }
      
          newPlayerVar.visits++;
      
          allPlayerVarData.set(player, newPlayerVar);
          
          if (DISPLAY_CONSOLE_PLAYER_DATA_MANAGER)
            console.log(`PlayerDataManager: Player ${player.name} has entered the world. and visited ${newPlayerVar.visits} times.`);  
      
        }
      
        playerExitWorld(player: Player){
      
          const playerVar = allPlayerVarData.get(player);
      
          if (playerVar){
            this.world.persistentStorage.setPlayerVariable(player, varKey, playerVar);
          }
          else {
            if (DISPLAY_CONSOLE_PLAYER_DATA_MANAGER)
              console.log(`PlayerDataManager: playerVar returned undefined on PlayerVarManager: playerExitWorld`);
          }
      
          allPlayerVarData.delete(player);
      
          if (DISPLAY_CONSOLE_PLAYER_DATA_MANAGER)
            console.log(`PlayerDataManager: Player ${player.name} has exited the world.`);
        }
      
      
      
        // A method to get the player variable for external scripts
        public GetPlayerVars(player: Player): PlayerVar | null{
          let prevPlayerVar : PlayerVar | null = null;    
      
          const valueJson : string | null = this.world.persistentStorage.getPlayerVariable(player, varKey);
      
          if (DISPLAY_CONSOLE_PLAYER_DATA_MANAGER)
            console.log(`PlayerDataManager: valueJSON for ${player.name} is: ${valueJson}`);      
      
          prevPlayerVar = valueJson as unknown as PlayerVar;
          
          if (prevPlayerVar == null || prevPlayerVar === undefined || prevPlayerVar.version == null || prevPlayerVar.version === undefined){
              if (DISPLAY_CONSOLE_PLAYER_DATA_MANAGER)
                console.log(`PlayerDataManager: prevPlayerVar is null or undefined, or its version is null or undefined after retrieving from storage and converting`);
          }
      
          const newPlayerVar = initializeOrMigratePlayerVars(player, prevPlayerVar);
          
          return newPlayerVar;
        }
      
        //A method to set ALL the player vars via a "player var" object.
        public SetPlayerVars(player: Player, playerVar: PlayerVar){
          
          if (playerVar){
            this.world.persistentStorage.setPlayerVariable(player, varKey, playerVar);      
            this.sendLocalBroadcastEvent(Events.onPlayerDataChanged, { player: player});
          }
          else {
            console.log(`PlayerDataManager: playerVar returned undefined on PlayerVarManager: playerExitWorld`);
          }
        }
      
      
      
      
      
      
      
      
      
      
      //This method will check an existing playerVar object, and migrate it to the latest version if needed
      //If no previous object is found, it will initialize a new one
      function initializeOrMigratePlayerVars(player: Player, prevPlayerVar: PlayerVar | null): PlayerVar {
        
        //Init a new player variable
        const newPlayerVar: PlayerVar = {
          version: playerVariableVersion,
          name: player.name.get(), //v1
          visits: 1, //v1
          usingUI: true, //v2
          inventory: PlayerDataManager.convertInventoryItemEntries(STARTING_INVENTORY), //v3
          equippedBowItemId: "" , //v4 //DEPRECATED
          equippedWingsItemId: "" , //v4
          equippedPetItemId: "" , //v4
          equippedArtifactItemId: "" , //v5
          playerXP: 0, //v6,
          completedObjectiveIDs: [] //v7
        }  
      
        if (DISPLAY_CONSOLE_PLAYER_DATA_MANAGER){
          console.log(`PlayerDataManager: initializeOrMigratePlayerVars PrevPlayerVar Object: ${JSON.stringify(prevPlayerVar)}`);
        }
      
      
        // Check if the player has a previous variable
        // If so migrate the data
        if (prevPlayerVar != null && prevPlayerVar !== undefined && prevPlayerVar.version != null){
          
          if (prevPlayerVar.version < playerVariableVersion){
            //PERFORM MIGRATION
            
            // Carry over existing data from 1st version
            newPlayerVar.visits = prevPlayerVar.visits;
            newPlayerVar.name = prevPlayerVar.name;
      
            // Migrate to version 2
            if (prevPlayerVar.version === 1){
              newPlayerVar.usingUI = false; // New field in version 2, default to false
            }
            else{
              newPlayerVar.usingUI = prevPlayerVar.usingUI;
            }
      
            // Migrate to version 3
            if (prevPlayerVar.version < 3){
              if (DISPLAY_CONSOLE_PLAYER_DATA_MANAGER){
                console.log(`PlayerDataManager: Migrating player inventory to version 3 for player ${player.name}`);
              }
              newPlayerVar.inventory = PlayerDataManager.convertInventoryItemEntries(STARTING_INVENTORY);
            }
            else{
              if (DISPLAY_CONSOLE_PLAYER_DATA_MANAGER){
                console.log(`PlayerDataManager: Carrying over existing inventory for player ${player.name}`);
              }
              newPlayerVar.inventory = prevPlayerVar.inventory;
            }
      
            // Migrate to version 4
            if (prevPlayerVar.version < 4){
              if (DISPLAY_CONSOLE_PLAYER_DATA_MANAGER){
                console.log(`PlayerDataManager: Initializing equipped items to empty for player ${player.name}`);
              }
              newPlayerVar.equippedArtifactItemId = "" ;
              newPlayerVar.equippedWingsItemId = "" ;
              newPlayerVar.equippedPetItemId = "" ;
            }
            else{
              // No migration needed, use existing data
              newPlayerVar.equippedArtifactItemId = prevPlayerVar.equippedArtifactItemId;
              newPlayerVar.equippedWingsItemId = prevPlayerVar.equippedWingsItemId;
              newPlayerVar.equippedPetItemId = prevPlayerVar.equippedPetItemId;        
            }
      
      
            // Migrate to version 5
            if (prevPlayerVar.version < 5){
              if (DISPLAY_CONSOLE_PLAYER_DATA_MANAGER){
                console.log(`PlayerDataManager: Initializing equipped artifact item to empty for player ${player.name}`);
              }
              newPlayerVar.equippedArtifactItemId = "" ;
              if (prevPlayerVar.equippedBowItemId != null && prevPlayerVar.equippedBowItemId !== undefined && prevPlayerVar.equippedBowItemId !== ""){
                // Carry over the equipped bow item to the new artifact slot
                newPlayerVar.equippedArtifactItemId = prevPlayerVar.equippedBowItemId;
                if (DISPLAY_CONSOLE_PLAYER_DATA_MANAGER){
                  console.log(`PlayerDataManager: Migrated equipped bow item ${prevPlayerVar.equippedBowItemId} to equipped artifact slot for player ${player.name}`);
                }
              }
            }
            else{
              // No migration needed, use existing data
              newPlayerVar.equippedArtifactItemId = prevPlayerVar.equippedArtifactItemId;        
            }
      
            // Migrate to version 6
            if (prevPlayerVar.version < 6){
              if (DISPLAY_CONSOLE_PLAYER_DATA_MANAGER){
                console.log(`PlayerDataManager: playerXP for player ${player.name}`);
              }
              newPlayerVar.playerXP = 0;        
            }
            else{
              // No migration needed, use existing data
              newPlayerVar.playerXP = prevPlayerVar.playerXP;        
            }
      
            // Migrate to version 7
            if (prevPlayerVar.version < 7){
              if (DISPLAY_CONSOLE_PLAYER_DATA_MANAGER){
                console.log(`PlayerDataManager: completedObjectiveIDs for player ${player.name}`);
              }
              newPlayerVar.completedObjectiveIDs = [];
            }
            else{
              // No migration needed, use existing data
              newPlayerVar.completedObjectiveIDs = prevPlayerVar.completedObjectiveIDs;
            }
          }
          else{
            //no migration needed at all.
            return prevPlayerVar;
          }
          
        }
      
        // the new player var has been setup and migrated if needed
        //return it!
        return newPlayerVar;
      }
      





      • gausroth's avatar
        gausroth
        Mentor

        The only way I know of that will allow you to see the logs in a published world is the Debug Console gizmo. Just make sure it is set to Published mode. Then any editor of the world should be able to see the gizmo in published mode.

  • Did AI generate some of this? What is going on with this line:

    prevPlayerVar = valueJson as unknown as PlayerVar;


    I don't see anything immediately wrong, but I would make sure my PlayerVar value is correct before I try to save it by printing it to a console.log.

  • Thanks gausroth​ and SeeingBlue​ for your help with this. Gausroth's tip about running the Debug Console gizmo led us to realizing that there was a Player OnExitWorld() call that wasn't happening in the Editor (for whatever reason) which was overwriting the player's progress for a session on the Published world.

    After sorting that out we were able to get things working properly again!

    • gausroth's avatar
      gausroth
      Mentor

      OnExitWorld() never gets called in the editor. It might when you return to the creation menu but not sure.