OnPlayerPropertiesUpdate not automatically called on the client that is calling SetCustomProperties

Hi, I have the same problem as in this post https://forum.photonengine.com/discussion/12872/onplayerpropertiesupdate-not-called. I am using the latest SDK and PhotonLoadBalancing server. I need to call OnPlayerPropertiesUpdate on all players, in this case it is not called on the Local player(that is calling SetCustomProperties()) and create a problem. RoomOptions.BroadcastPropsChangeToAll = true does not help solve this problem.

Comments

  • JohnTube
    JohnTube ✭✭✭✭✭
    Hi @atomlg,

    Thank you for choosing Photon!
    "Broadcast Properties Change" option is not available (RoomOptions.BroadcastPropsChangeToAll). The client who calls SetProperties will not receive PropertiesChange event unless CAS is used. CAS is a workaround to have a consistent behaviour between Photon Cloud and Photon Server.

    Read more in "Important Changes Since V4.0.29.11263".
  • Hello thank for answer, what is it CAS and how can I use it to receive OnPlayerPropertiesUpdate on client who calls it? Maybe I can see example(not with roomPropertiesChange,but with OnPlayerPropertiesUpdate) ?
  • JohnTube
    JohnTube ✭✭✭✭✭
    CAS is when you use Expected Properties in the call.
    here.
  • Thanks for the answer. I added this to my script, but I still cannot figure out how to connect this to the player. I used to
    private ExitGames.Client.Photon.Hashtable playerCustomProperties = new ExitGames.Client.Photon.Hashtable(); 
    playerCustomProperties["Team"] = PunTeams.Team.red;
    PhotonNetwork.LocalPlayer.SetCustomProperties(playerCustomProperties);
    or 
    player.SetCustomProperties(new Hashtable() { { "Team", unTeams.Team.red}
    
    and cheked it in
    public override void OnPlayerPropertiesUpdate(Player targetPlayer, ExitGames.Client.Photon.Hashtable changedProps)
        {
            if (targetPlayer.CustomProperties.ContainsKey("Team"))
            { 
            }
        }
    
    How can I do the same with CAS? Sorry for asking. But I did not find enough information. I tried different combinations using
    private OperationResponse playerOperationResponse = new OperationResponse();
    OperationCode.SetProperties, PhotonNetwork.NetworkingClient.OpSetCustomPropertiesOfActor
    
    Maybe need to use PhotonNetwork.NetworkingClient.LoadBalancingPeer.OpSetCustomPropertiesOfActor?
    But still I can’t understand how this should work correctly. I looked at the documentation and searched for various solutions or examples on the Internet, but did not find any more information.
  • JohnTube
    JohnTube ✭✭✭✭✭
    edited December 2019
    Hi @atomlg,

    In order for CAS to work you need to pass a hashtable of already existing properties with values matching on the server.
    So in this case:
    ExitGames.Client.Photon.Hashtable playerCustomProperties = new ExitGames.Client.Photon.Hashtable(); 
    string key = PunTeams.TeamPlayerProp; // "team"
    playerCustomProperties[key] = PunTeams.Team.red;
    ExitGames.Client.Photon.Hashtable expectedPlayerCustomProperties = null;
    object temp;
    if (player.CustomProperties.TryGetValue(key, out temp))
    {
        expectedPlayerCustomProperties = new Hashtable(1);
        expectedPlayerCustomProperties[key] = temp;
     } 
    player.SetCustomProperties(playerCustomProperties, expectedPlayerCustomProperties);
    

    But in order for this to work, a "Team" custom property needs to be there first.
    So I suggest to always initialize OR reset it to "none" as soon as connected to master (initially or after leaving a room):
    using Photon.Pun;
    using Photon.Pun.UtilityScripts;
    
    
    public override void OnConnectedToMaster()
    {
         PhotonNetwork.LocalPlayer.SetTeam(PunTeams.Team.none); // make use of the extension method direcly
    }
    

    Also make sure the custom property key is correct (case sensitive), so better use the constant + use TryGetValue instead of ContainsKey then get it:
    public override void OnPlayerPropertiesUpdate(Player targetPlayer, ExitGames.Client.Photon.Hashtable changedProps)
        {
            string key = PunTeams.TeamPlayerProp;
            object temp;
            if (changedProps.TryGetValue(key, out temp))
            { 
                 PunTeams.Team team = (PunTeams.Team)temp;
            }
        }
    
  • Hello, thanks you a lot for the reply. I do all the actions on the LocalPlayer ,but it’s still not clear how to relate this to
    private void OnEnable()
    {
        PhotonNetwork.NetworkingClient.OpResponseReceived += NetworkingClientOnOpResponseReceived;
    }
    
    private void OnDisable()
    {
        PhotonNetwork.NetworkingClient.OpResponseReceived -= NetworkingClientOnOpResponseReceived;
    }
    
    private void NetworkingClientOnOpResponseReceived(OperationResponse opResponse)
    {
        if (opResponse.OperationCode == OperationCode.SetProperties &&
            opResponse.ReturnCode == ErrorCode.InvalidOperation)
        {
            // cas failure
        }
    }
    
    I added this to the player’s script that initializes when the scene finishes loading.
    If I remove this part
    && opResponse.ReturnCode == ErrorCode.InvalidOperation, 
    
    This called when a player is initialized on scene.
    if (opResponse.OperationCode == OperationCode.SetProperties)// && opResponse.ReturnCode == ErrorCode.InvalidOperation)
            {
    //Debug.Log("Called");
            }
    }
    
    How can I do a check in NetworkingClientOnOpResponseReceived?
    Like in this code below, in order for the actions to be performed when a certain property is changed, for example "Team" ?
    public override void OnPlayerPropertiesUpdate(Player targetPlayer, ExitGames.Client.Photon.Hashtable changedProps)
        {
            string key = PunTeams.TeamPlayerProp;
            object temp;
            if (changedProps.TryGetValue(key, out temp))
            { 
                 PunTeams.Team team = (PunTeams.Team)temp;
            }
        }
    
    And I have a button in another script that does this
    public void ChooseTeam(string team)
        {
            if (team == "red" && PunTeams.PlayersPerTeam[PunTeams.Team.red].Count < maxPlayersInTeam)
            {
    
                ExitGames.Client.Photon.Hashtable playerCustomProperties = new ExitGames.Client.Photon.Hashtable();
                string key = "Team"; // "team"
                playerCustomProperties[key] = PunTeams.Team.red;
                ExitGames.Client.Photon.Hashtable expectedPlayerCustomProperties = null;
                object temp;
                if (PhotonNetwork.LocalPlayer.CustomProperties.TryGetValue(key, out temp))
                {
                    expectedPlayerCustomProperties = new ExitGames.Client.Photon.Hashtable(1);
                    expectedPlayerCustomProperties[key] = temp;
                }
                PhotonNetwork.LocalPlayer.SetCustomProperties(playerCustomProperties, expectedPlayerCustomProperties);
    
            }
    
    private void NetworkingClientOnOpResponseReceived(OperationResponse opResponse) called every time when I press this button, and only if I click this button 3 times in the script PunTeam called OnPlayerPropertiesUpdate and UpdateTeams.
  • I did this
    private void NetworkingClientOnOpResponseReceived(OperationResponse opResponse)
        {
            string key = "Team";
            object temp;
            if (PhotonNetwork.LocalPlayer.CustomProperties.TryGetValue(key, out temp))
            {
                Debug.LogWarning("Worked?");
            }
        }
    
    And it seems to work, is this the right decision, or is there another way?
  • JohnTube
    JohnTube ✭✭✭✭✭
    Hi @atomlg,

    You do not need PhotonNetwork.NetworkingClient.OpResponseReceived.
    In the documentation page, it's mentioned to show you how to get callback for CAS ERRORS which should not happen in your case.
    So use the normal callback
    OnPlayerPropertiesUpdate
    
    for this also and do not complicate things for yourself.
  • atomlg
    atomlg
    edited December 2019
    Hello, I created this discussion because OnPlayerPropertiesUpdate don't work when a local player calls it, I use PhotonServer LoadBalancing. I did everything that was written above and did it the way you said, but there is still no solution to the problem. In another topic, you said
    Yes, what you have reported (OnPlayerPropertiesUpdate "not automatically called on the client that is calling SetCustomProperties()" but should be triggered for the others) is the actual behaviour. And The client who calls SetProperties will not receive PropertiesChange event unless CAS is used. CAS is a workaround to have a consistent behaviour between Photon Cloud and Photon Server
    But after you said
    You do not need PhotonNetwork.NetworkingClient.OpResponseReceived.
    In the documentation page, it's mentioned to show you how to get callback for CAS ERRORS which should not happen in your case.
    So use the normal callback
    OnPlayerPropertiesUpdate
    for this also and do not complicate things for yourself.
    My team does not change either, it only works if another player connects, which completely does OnPlayerPropertiesUpdate useless. How to call OnPlayerPropertiesUpdate on Local player? How to solve this problem? Because when one player start the game and changes his properties, this should work, without the need to connect other players.
  • JohnTube
    JohnTube ✭✭✭✭✭
    hi @atomlg,

    tl;dr:

    Send an email to developer@photonengine.com to request a new Photon Server SDK version that supports this.
    It will take some time as the team is on holidays.

    ---

    Explanation:

    RoomOptions.BroadcastPropsChangeToAll works only on Photon Cloud currently which runs a newer version that the latest public official release of the Photon Server SDK.
    I wanted to suggest a workaround to make OnPlayerPropertiesUpdate be called on the client who triggers it.
    The workaround is the usage of CAS or ExpectedProperties.
    When I shared the documentation paragraph about CAS, you went ahead and implemented the code snippet there which is meant to be used only to catch failures / errors of SetProperties with CAS.

    So I think no need to pursue workarounds anymore, because it causes confusion, unless you are really blocked and cannot wait.
    For this you could even change PUN code to set the properties locally right away (without waiting for any server event) and trigger the callback.
  • JohnTube
    JohnTube ✭✭✭✭✭
    Hi @atomlg,

    v5 BETA is out.

    Thank you for your patience and trust.