Can not update room properties in iOS (Photon engine)

Vasilis
Vasilis
edited July 2020 in Native
Hi all,

I am trying to create/join a room with some custom properties in my iOS app. I do get an error message, but I do not know how to resolve that error. In the mean time, when I call the method to get the room properties back, the server does return these properties. But, on the web version, the room has no properties.

Here are the logs:
2020-07-22 11:34:07.323040+0300 [16530:146240] PHOTON creating room "demo"
2020-07-22 11:34:08,131756 ERROR   EGUtils.mm                     +[EGUtils(Private) newEGObjectFromNSObject::]                358   unsupported primitive type c for value
2020-07-22 11:34:08,132158 ERROR   EGUtils.mm                     +[EGUtils(Private) newEGObjectFromNSObject::]                358   unsupported primitive type c for value
2020-07-22 11:34:08.232434+0300 [16530:146240] PHOTON game room "demo" has been entered
2020-07-22 11:34:08.233660+0300 [16530:146240] PHOTON regularly sending dummy events now
2020-07-22 11:34:08.234773+0300 [16530:146240] prop {
    Prefix = demo;
    RAName = "Room 1";
    RMaxUsers = "{length = 8, bytes = 0x0400000000000000}";
}

The prop is an NSDictionary, that I get by calling the method getRoomProperties.

Thank you


Comments

  • Hi @Vasilis.

    That error means that you have passed some data of an unsupported type to Photon for serialization.

    Please show your code for creating/joining a room with custom properties, especially the code that sets the custom properties.
  • Hi @Kaiserludi,

    I do realized that this is what this error means, but, my problem is that I do not know which variable is causing that error. Since, I can not debug the c++ code, is it possible to print the variable that is causing this error?

    Also, I just realized that the Photon SDK is very picky with the type of the variables in the properties of a room/actor.e,g,

    If I use the [NSNumber numberWithInt: ] method to set the value of a property, then, when I receive that property its type will not be an integer, but it could be a double or a long.
    Instead, I should use the [NSValue valueWithBytes: objCType:@encode()] method to set the value and the type.
  • Kaiserludi
    Kaiserludi admin
    edited July 2020
    Hi @Vasilis.
    Vasilis wrote: »
    my problem is that I do not know which variable is causing that error
    That's the reason why a asked you to share the code where you set the properties. I could probably spot unsupported type.
    Alternatively if you can't share that code, you could simply list all the types here, that you are trying to add.

    Photon supports the following types as raiseEvent payload and property values:

    primitive types:
    • nByte (defined as unsigned char)
    • short
    • int
    • int64 (defined as long long)
    • bool (but not BOOL!)
    • float
    • double

    class types:
    • NSString
    • EGArray (all values need to be of the same type, which needs to be of one of the other supported types)
    • NSArray (all values need to be of the other supported types)
    • EGDictionary (all keys must be of the same type, which must be of one of the supported key types and all values need to be of the same type, which needs to be one of the other supported types)
    • NSDictionary (all keys must be of one of the supported key types and all values need to be of one of the other supported types)
    • subclasses of EGCustomType

    the supported key types are:
    • nByte
    • short
    • int
    • int64
    • NSString
    is it possible to print the variable that is causing this error?
    No, but as you can see the error message contains the type encoding of the variable that caused the error:
    unsupported primitive type c for value
    You can look i.e. at https://nshipster.com/type-encodings/, to figure out which type encoding matches which type (or simply check it yourself by printing the result of @encode() for each type that you want to check). As you can see, a small 'c' matches 'char', 'signed char' and 'BOOL', so the variable that caused the error is a variable of one of these types.

    Note for Swift users:
    Apple introduced a major bug in their Swift to Objective C bridging layer in Swift 4 that prevents Swift code from being able to interact correctly with objective C code which expects a C99/C++ bool wrapped in an NSValue and which compares objcType() return value of the NSValue instance with '@encode(bool)'.
    This bug does not affect objective C users or users of Swift 1-3, but it might affect future versions of Swift as well, if Apple should not fix it until then.

    Due to this bug any attempt to pass a boolean value from Swift code to the Photon objective C APIs results in the Photon client receiving an unsupported type.
    Unfortunately the only one who can fix this issue is Apple.

    Also, I just realized that the Photon SDK is very picky with the type of the variables in the properties of a room/actor.e,g,

    If I use the [NSNumber numberWithInt: ] method to set the value of a property, then, when I receive that property its type will not be an integer, but it could be a double or a long.
    Instead, I should use the [NSValue valueWithBytes: objCType:@encode()] method to set the value and the type.
    Actually that is not Photon being picky, but Apples Cocoa framework being buggy. The NSNumber class does not keep proper type information, when it gets archived and unarchived.

    Using NSValue instead of NSNumber is the proper workaround for this. Unfortunately I strongly doubt the Apple will fix this anytime soon, as this bug has been around at least a decade by now, so I am afraid that they might actually keep it in there on purpose to not break any existing apps that might rely on its existence.
  • Hi @Kaiserludi,

    I do not know why, but I no longer see these two errors in the Xcode console. But, on the web version, the custom properties of that room are empty.
    On the iOS, after the room is created - joined, I call the method
    mLoadBalancingClient.CurrentlyJoinedRoom.CustomProperties
    

    I do see the properties of that room. On the web version, the _customProperties field is an empty dictionary. Any idea why I have this issue?
  • Hi @Kaiserludi,

    I started the LoadBalancingClient and enabled all logs. Here is the output when I create a room
    2020-07-28 09:57:12,119711 INFO    EGLoadBalancingClient.mm       -[EGLoadBalancingClient onOperationResponse:]                881   OperationResponse - operationCode: 226, returnCode: 0 {252=[1], 254=1, 248={"Prefix"="GamePrefixXeri", "RAName"="Room 1", 248=1, 250=[], "RMaxUsers"=4, 253=true, 254=true, 255=0}, 191=11}
    2020-07-28 09:57:12,121655 INFO    PhotonEngine.mm                -[PhotonEngine joinOrCreateRoomReturn:::::]                  498   
    2020-07-28 09:57:12,121791 INFO    PhotonEngine.mm                -[PhotonEngine joinOrCreateRoomReturn:::::]                  517   localPlayerNr: 1
    2020-07-28 09:57:12.121937+0300 Games[5053:42907] PHOTON game room "Room GamePrefixXeri 1" has been entered
    2020-07-28 09:57:12.122865+0300 Games[5053:42907] PHOTON regularly sending dummy events now
    2020-07-28 09:57:12.131484+0300 Games[5053:42907] Rooms properties: {
        Prefix = GamePrefixXeri;
        RAName = "Room 1";
        RMaxUsers = "{length = 4, bytes = 0x04000000}";
    }
    2020-07-28 09:57:12,143001 DEBUG   EnetPeer.cpp                   dispatchIncomingCommands()                                   405   
    2020-07-28 09:57:12,143213 DEBUG   PeerBase.cpp                   deserializeOperation()                                       513   
    

    The Room properties is the result of the command:
    mLoadBalancingClient.CurrentlyJoinedRoom.CustomProperties
    

    But, on the web client, I do not see these custom properties
  • Hi @Vasilis.

    When you say "web version", which Photon Client SDK are you referring to?
  • Hi @Vasilis.

    I see that joinOrCreateRoomReturn is called, which implies that you create the room by calling opJoinOrCreateRoom().

    I assume that you attempt to join it that way with the other client as well (assuming that with "web version" you mean the JS Client, that would mean that you use opJoinRoom with the 'createIfNotExists' option set to 'true').

    That means that if the other client can't find the room, it will create it (while opJoinRoom() with the 'createIfNotExists' option set to 'false' would return an error indicating that there is no such room).

    Please check, which regions the two clients are connecting to and please also check if the clients can see each other inside the room or if they are the only client inside.

    I assume that both clients might connect to different regions or use a different appID/appVersion.
    That way the second client would also create a room with the same name, but in a different region/appID/appVersion, because there is no room with that name in that region/appID/appVersion yet (as the first client has created its room in a different region).

    The second room of course would only hold the properties that have been set for that room and not the properties that have been set for the first room, despite both rooms having the same name.

  • Hi @Kaiserludi,

    with web client I mean the JS client (sorry for the misunderstanding).

    Both clients, iOS and JS, are connected on the same region. The iOS client can see the room that the JS client creates, it also gets the properties of the room. The JS client can also see the room that the iOS client creates, but it does not get the properties of that room

  • Hi @Vasilis.
    The JS client can also see the room that the iOS client creates, but it does not get the properties of that room
    You mean that it can see it in the room list in the lobby and join it when setting the 'createIfNotExists' option to 'false'?

    Are you trying to get the properties from within the room or from outside of the room?
    If the latter is the case, then please be aware, that only those properties are visible outside of rooms, for which the keys have been included in EGRoomOptions.PropsListedInLobby.
  • Vasilis
    Vasilis
    edited July 2020
    Hi @Kaiserludi,

    Correct, I am trying to get the room properties without joining that room. I will check if these properties are set in the PropsListedInLobby array on the iOS version.
    I am guessing that the JS client makes all properties available in the lobby. Is this correct?
  • Hi @Kaiserludi ,

    I created a list of keys that I want to be available in the lobby, and now I do see those properties in the JS client.

    Thank you!!
  • Hi @Vasilis
    I am guessing that the JS client makes all properties available in the lobby. Is this correct?
    No. The JavaScript client has a propsListedInLobby option for createRoom() as well. Only the properties that are included there can be seen in the lobby.
    I created a list of keys that I want to be available in the lobby, and now I do see those properties in the JS client.
    Great.

    One further note:
    Listing properties in the lobby causes a lot of traffic when you have a lot of CCU.
    Every player in the lobby needs to receive all those lobby-visible properties for every single room that is visible in the lobby.
    Hence you should make sure that you a) only make those properties available outside of the room that you actually need to be visible outside of the room, but not the rest of the room properties and b) keep the properties that are visible outside of the room as small as possible - use one character strings as keys and also keep the values as small as possible.

    Example:
    You can optimize "Prefix"="GamePrefixXeri", "RAName"="Room 1", 248=1, 250=[], "RMaxUsers"=4
    to "P"=(nByte)1, "N"=(nByte)1

    The receiving client then lookup in a local set of constants that "P" stands for "Prefix" and "N" for RAName, it could also have a list of constants for the possible prefixes with 1 standing for "GamePrefixXeri" and it could also know that "1" is short for "Room 1".

    You don't have to send the max players at all, as they are already one of the built-in room properties (EGLoadBalancingRoom.MaxPlayers in in objective C and roomInfo.maxPlayers in JS), so that sending them yourself as well is redundant.