Async Turnbased. Error Converting State to type 'Photon.Hive.Plugin.SerializableGameState

Hello,

I am using Pun 2 - Free & PlayFab CloudScript to make an async turn-based game.

Everything has worked smoothly so far except for loading a room with the serialized (JSON.stringified) state.
When loading a new room with the return {"State":"", "ResultCode":0}, the game starts fine and transitions from my Main Menu into the Main Scene. Perfect!
I am also able to successfully save the room state on /PathClose to my web server after the EmptyRoomTTL of 12 seconds has passed. I also have set the PlayerTTL = -1 according to the documentation.

The errors occurs when a Player attempts to ReJoin a room with the return {"State":data.State, "ResultCode":0} where data.State is the serialized state fetched from a web server.

I receive this error in the OnJoinRoomFailed callback. Error converting value data.State to type 'Photon.Hive.Plugin.SerializableGameState'. I am stripping out the DebugInfo property that is passed into the /PathClose webhook before posting to my web server but the error occurs even if I just save the whole State.

I can't quite figure out where to go from here!

Does anybody have any advice or tips to push me in the right direction? Thanks for your time:)

Best Answers

  • WickedWorlok
    edited April 2019 Answer ✓
    Hello @JohnTube

    I was able to successfully get the previous room state to load without any errors by replacing return {"State":data.state, "ResultCode":0}; with return {"State":JSON.parse(data.state), "ResultCode":0};.

    The state was being stored in the DB as a string. I assumed in the return object that it needed to return this string however parsing it back into an object did the trick.

    I now have more questions of course but will try to solve through them using the documentation and forums. You might hear from me again😅. Thanks for your time and help!

Answers

  • The colleague who can answer this for sure, is currently on vacation.
    Back next week. Please bear with us until he's back.
  • Everyone needs a vacation! Thanks for the quick reply Tobias and looking forward to hearing from your colleague.
  • JohnTube
    JohnTube ✭✭✭✭✭
    hi @WickedWorlok,

    Thank you for choosing Photon!

    The error is probably due to your CloudScript code.
    It looks like you are returning "data.State" as string to Photon instead of its actual value.
    So Photon receives "State": "data.State".

    Feel free to copy paste the code snippet here for PathCreate and PathClose handlers if you can't fix this.
  • Hello @JohnTube,

    NOTE: The stringified object returned from my AWS Gateway has a lowercase s (state). I'm aware in the question I used an uppercase (State). Sorry for any confusion! But this is not the problem to be clear. In the code examples you will see data.state which is correct in my case.

    Since data.state is such a huge object I did not want to paste it here. data.state in this case is actually the serialized room state. I am sure of this because I am logging the values to Slack within the handlers. I also made a simple CloudScript function that simulates getting a rooms state from my AWS API. I am attaching a link to a picture of what the response looks like after running that CloudScript function.

    Link: https://drive.google.com/file/d/1SeT6a0SnvObPrjZtrIcoRstvJPhunkH-/view?usp=sharing


    Here is my /RoomCreated (PathCreate) handler:
    // Triggered automatically when a Photon room is first created
    handlers.RoomCreated = function (args) {
        log.debug("Room Created - Game: " + args.GameId + " MaxPlayers: " + args.CreateOptions.MaxPlayers);
      logToMyAPI(args, "Room Created");
      switch (args.Type) {
        // Player loads room state from AWS API Gateway
        case 'Load':
            try {
              const url = `${AWS_BASE_URL}/${args.GameId}`;
             // justCreated is true if the GameId could not be found in the DB
              const {data, justCreated} = JSON.parse(http.request(url, 'GET', null, contentType, null));
              // logToMyAPI({res, justCreated:res.justCreated, type:typeof res.justCreated, resType:typeof res}, 'Room Created Response Log')
              logToMyAPI({data, justCreated}, 'Room Created Response Log');
              if (justCreated && args.CreateIfNotExists) {
                logToMyAPI('Creating new game with empty state');
              	return {"State":"", "ResultCode":0};
              }
              else {
                const msg = 'Loading game from previous state'
                logToMyAPI({state:data.state}, 'Loaded State')
              	 return {"State":data.state, "ResultCode":0}; 
              }         
            }
          catch (err) {
          	logToMyAPI(err, 'Room Created Error')
            return { "Message" : "Failed to create room", "ResultCode" : 1 } 
          }
    	  break;
      default:
          logToMyAPI('Room Created Type was different than Load')
      	return { ResultCode : 0, Message: 'Room Created Type was different than load' };
      }
    };
    Here is my RoomClosed (PathClose) handler:
    // Triggered automatically when a Photon room closes
    // Note: currentPlayerId is undefined in this function
    handlers.RoomClosed = function (args) {
        log.debug("Room Closed - Game: " + args.GameId);
      var {State} = args;
      var {DebugInfo, ...TheState} = State; // I am stripping out DebugInfo before posting to my AWS API
      var {DebugInfo, Binary, ...logState} = State;
      switch (args.Type) {
        case 'Save':
          try {
            var url = `${AWS_BASE_URL}/${args.GameId}`;
            var response = http.request(url, 'POST', JSON.stringify(TheState), "application/json", null);
            
            var Message = 'Room Closed and Saved!';
            logToMyAPI({response, logState, Message}, 'Room Closed')        
            return { ResultCode : 0, Message };
          }
          catch (err) {
          	logToMyAPI({err, msg:'Error in Room Close.  Failed to update group data'}, 'Room Closed');
            return {ResultCode:1, Message:'Error in Room Close'};
          }
          break;
        default:
          	logToMyAPI('Room Closed but not Saved:(')
    		return { ResultCode : 0, Message: 'Room Closed but not Saved:(' };
      }   
    };
  • To give more context, this is the error I receive within Unity in the OnJoinRoomFailed callback

    To shorten the message, I cut out some of the Binary.
    OnJoinRoomFailed with code: 32752 and message: Failed to load state from https://6B5F9.playfablogic.com/webhook/1/prod/TIY67MX4IBBTCAXZ7HFGXKH4F913T7ESE4NQH9A5PSXC8BJ638/RoomCreated? : Error converting value "{"ActorCounter":1,"ActorList":[{"ActorNr":1,"UserId":"B91D87C244B9C062","Nickname":"Wicked Worlok","Binary":"RGIAAAEBRAAAAAFi/3MADVdpY2tlZCBXb3Jsb2s=","DeactivationTime":"2019-04-17T01:32:22.8579333+00:00","DEBUG_BINARY":{"1":{"255":"Wicked Worlok"}}}],"Binary":{"18":"RAAAAAhi+nkAAHNzAAZjdXJTY25zAAYzX0dhbWVzAA9UVVJOX0lOREVYX1BST1BpAAAAAHMAEFRVUk5fU1RSSU5HX1BST1BzAAUxMDAxO3MACkxFVkVMX1BST1BpAAAAAXMAD0dST1VQX0ZPT0RfUFJPUGkAAABhcwAOSU5WRU5UT1JZX1BST1B5ABRzAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHMAE05VTV9QTEFZRVJTX0FUX0VYSVRpAAAAAA==","19":"RGl6AAEAAAAAAHl6AANpAAAAAWLKaAAEYgZpo7vJoGIHaQAAA+liAWNWAAxAoAAAQOAAAAAAjjYgdpAAAEFWIBY1YADD+AAABAQAAAAAAAAGIAcwAIQ2hlcnJpZXM="},"CheckUserOnJoin":true,"CustomProperties":{},"DeleteCacheOnLeave":false,"EmptyRoomTTL":12000,"IsOpen":true,"IsVisible":true,"LobbyId":"","LobbyType":0,"LobbyProperties":[],"MaxPlayers":4,"PlayerTTL":-1,"SuppressRoomEvents":false,"Slice":0,"ExcludedActors":[],"PublishUserId":true,"ExpectedUsers":[],"RoomFlags":41}" to type 'Photon.Hive.Plugin.SerializableGameState'. Path 'State', line 1, position 10513.
    UnityEngine.Debug:LogFormat(String, Object[])
    MainMenu:OnJoinRoomFailed(Int16, String) (at Assets/scripts/MainMenu.cs:91)
    Photon.Realtime.MatchMakingCallbacksContainer:OnJoinRoomFailed(Int16, String) (at Assets/Photon/PhotonRealtime/Code/LoadBalancingClient.cs:3377)
    Photon.Realtime.LoadBalancingClient:CallbackRoomEnterFailed(OperationResponse) (at Assets/Photon/PhotonRealtime/Code/LoadBalancingClient.cs:1858)
    Photon.Realtime.LoadBalancingClient:OnOperationResponse(OperationResponse) (at Assets/Photon/PhotonRealtime/Code/LoadBalancingClient.cs:1992)
    ExitGames.Client.Photon.PeerBase:DeserializeMessageAndCallback(StreamBuffer) (at C:/Dev/photon-sdk-dotnet/PhotonDotnet/PeerBase.cs:616)
    ExitGames.Client.Photon.EnetPeer:DispatchIncomingCommands() (at C:/Dev/photon-sdk-dotnet/PhotonDotnet/EnetPeer.cs:545)
    ExitGames.Client.Photon.PhotonPeer:DispatchIncomingCommands() (at C:/Dev/photon-sdk-dotnet/PhotonDotnet/PhotonPeer.cs:1473)
    Photon.Pun.PhotonHandler:FixedUpdate() (at Assets/Photon/PhotonUnityNetworking/Code/PhotonHandler.cs:130)
  • WickedWorlok
    edited April 2019 Answer ✓
    Hello @JohnTube

    I was able to successfully get the previous room state to load without any errors by replacing return {"State":data.state, "ResultCode":0}; with return {"State":JSON.parse(data.state), "ResultCode":0};.

    The state was being stored in the DB as a string. I assumed in the return object that it needed to return this string however parsing it back into an object did the trick.

    I now have more questions of course but will try to solve through them using the documentation and forums. You might hear from me again😅. Thanks for your time and help!