IS there an example of CAS anywhere?

I tried this over on the Unity forums with no luck.

Does anyone have a code example for changing a RoomProperty in Photon using CAS, that includes the error handling?

I have a float[] as a custom property. The various players can change elements of the float array. However, because of concurrency, while I try to change one element, another player may try to change a different element.

Ultimately what I am looking for (psuedocode)

ChangeRoomProperties()
{
ReadRoomProperties() //casting them to an array[]
alter the desired element
recreate Hashtable
room.SetCustomProperties(prop, oldvalueProp);
if error == incorrect oldValueProp
{
ChangeRoomProperties()
}
}

Best Answer

Answers

  • Hi @Spektre,

    Here is how to use CAS for room properties, it's an extension method you can directly use:
    
    using ExitGames.Client.Photon;
    using Photon.Realtime;
    
    public static class RoomExtensions 
    {
        public static bool SetCustomPropertySafe(this Room room, string key, object newValue, WebFlags webFlags = null)
        {
            if (room == null)
            {
                return false;
            }
            if (room.IsOffline)
            {
                return false;
            }
            if (!room.CustomProperties.ContainsKey(key))
            {
                return false;
            }
            Hashtable newProps = new Hashtable(1) {{key, newValue}};
            Hashtable oldProps = new Hashtable(1) {{key, room.CustomProperties[key]}};
            return room.LoadBalancingClient.OpSetCustomPropertiesOfRoom(newProps, oldProps, webFlags);
        }
    }
    Use as follows:

    PhotonNetwork.CurrentRoom.SetCustomPropertySafe(key, newValue);

    You should know that:

    - initializing (i.e. first time creating a new property) using CAS is not supported
    - there is no error callback for when SetProperties fails due to CAS
  • Thanks JohnTube.

    Is a flag set or anything to tell you the attempted CAS write failed?

    Per the docs.

    https://doc.photonengine.com/en-us/pun/current/gameplay/synchronization-and-state

    "SetCustomProperties has an optional expectedValues parameter, which can be used as condition. With expectedValues, the server will only update the properties, if its current key-values match the ones in expectedValues. Updates with outdated expectedValues will be ignored (the clients get an error as a result, others won't notice the failed update).
  • Can you get it from a PunBehavior?


    Error CS0117 'PhotonNetwork' does not contain a definition for 'NetworkingClient'
  • Looking at the documentaiton for PhotonNetwork, I do not see a member named NetworkingClient.
  • This is PUN Classic I am working with.
  • We highly recommend migrating to PUN2 asap especially when starting a new project.
    PUN Classic code:
    using ExitGames.Client.Photon;
    
    public static class RoomExtensions 
    {
        public static bool SetCustomPropertySafe(this Room room, string key, object newValue, bool webForward)
        {
            if (room == null)
            {
                return false;
            }
            if (!room.CustomProperties.ContainsKey(key))
            {
                return false;
            }
            Hashtable newProps = new Hashtable(1) {{key, newValue}};
            Hashtable oldProps = new Hashtable(1) {{key, room.CustomProperties[key]}};
            return PhotonNetwork.networkingPeer.OpSetCustomPropertiesOfRoom(newProps, oldProps, webForward);
        }
    }
    Error response is not exposed in PUN1.
    You need to inject the code in "Assets\Photon Unity Networking\Plugins\PhotonNetwork\NetworkingPeer.cs" line 2015:
    case OperationCode.SetProperties:
                    // this.mListener.setPropertiesReturn(returnCode, debugMsg);
                    break;
    with:
    case OperationCode.SetProperties:
            if (opResponse.ReturnCode == ErrorCode.InvalidOperation)
            {
                // cas failure
            }
           break;
  • TobiasTobias admin
    While the error is not exposed, the essential update is still available: When CAS fails, this is because the "expected properties" did not match the server's values. This case triggers an update event for the properties and so the client gets a property updated callback.

    We should maybe add this callback (it's not in PUN 2 either) or at least update the reference doc.

    If you need the error itself, then please follow JohnTube's suggestion. Is this something the client needs?
  • My use case is as such...

    I have a game that has rounds of play where people may join and leave freely. To be eligible for rankings and such, you must have been present at the start of the round. As such, having scores stored as a Player property are not desirable as they disappear when the player leaves (even though they may be eligible.) The scores, as they get updated, therefore need to be a room property as they persist.

    If a separate property were made for each player though, the room would soon become populated with many no longer present players scores from past rounds.

    Therefore, the method decided was to make the master client note who is present at round start and create an array with the player's ID, and another with the corresponding player's scores. Each player in turn can update their score in the array as needed.

    To do this, the array is read, the player updates their score, if they are a member of the array, and the array is written back to the room properties, generating a OnPhotonCustomRoomPropertiesChanged() to tell everyone to update their scoreboard.

    This method however allows for a concurrency error where 2 players at nearby times, read the previous array and try and modify their value, only to have the last player to write erase the first player's score. We therefore write the scores using the CAS method so the second concurrent player does not overwrite the first's score. Unfortunately, this leaves us without the second player's score getting updated.

    The "CAS callback" is needed for this case. If a CAS write fails, the user must reattempt the write.

  • The documentation reads...

    SetCustomProperties has an optional expectedValues parameter, which can be used as condition. With expectedValues, the server will only update the properties, if its current key-values match the ones in expectedValues. Updates with outdated expectedValues will be ignored (the clients get an error as a result, others won't notice the failed update).


    This reads a bit confusing as it says the "clients get an error as a result, others won't notice the failed update" I assumed "clients" in this context meant "the client or clients who attempted the failed update".

    As such, i was looking for this error so that I could resend the player's score.
  • Tobias said:

    While the error is not exposed, the essential update is still available: When CAS fails, this is because the "expected properties" did not match the server's values. This case triggers an update event for the properties and so the client gets a property updated callback.

    We should maybe add this callback (it's not in PUN 2 either) or at least update the reference doc.

    If you need the error itself, then please follow JohnTube's suggestion. Is this something the client needs?

    Are you stating in the case of a failed CAS update, only the client who attempted the update gets a OnPhotonCustomRoomPropertiesChanged() event raised?

    If so, the client would not know if this was raised in response to a failed update or another client's score actually changing. This would necessitate a scan of the array on every Update to see if the score you think should be there for you , is there for you, Getting an actual error would enable this to only happen on actual failed updates.
Sign In or Register to comment.