C++ iOS cloud : custom properties not working

Paresh
edited August 2013 in Native
Hi,

I am using latest iOS cloud SDK 3.2.2. with Cocos2d-x

my turn based game is based on Load balancing example, where I am setting player's custom property in NetworkLogic constructor as mentioned below:

NetworkLogic::NetworkLogic(OutputListener* listener, const wchar_t* appVersion)
: mLoadBalancingClient(*this, L"My app id", appVersion, PLAYER_NAME)
, mLastInput(INPUT_NON)
, mOutputListener(listener)
{
mStateAccessor.setState(STATE_INITIALIZED);
mLoadBalancingClient.setDebugOutputLevel(ExitGames::Common::DebugLevel::INFO);
mLogger.setListener(*this);
mLogger.setDebugOutputLevel(ExitGames::Common::DebugLevel::INFO);

ExitGames::Common::Hashtable ev;
char str[50];
sprintf(str, "%d",12345);
ev.put(1001,str);
EGLOG(ExitGames::Common::DebugLevel::INFO, L"

Player Property init: %s", ev.toString().UTF8Representation().cstr());
mLoadBalancingClient.getLocalPlayer().addCustomProperties(ev);
}

output :
Player Property init: {1001="12345"}

but when I try to get info back using getCustomProperties is gives null, I tried for both local user & other user (other device) both returns null

void NetworkLogic::joinRoomEventAction(int playerNr, const ExitGames::Common::JVector<int>& /*playernrs*/, const ExitGames::LoadBalancing::Player& player)
{
EGLOG(ExitGames::Common::DebugLevel::INFO, L"%ls joined the game", player.getName().cstr());

EGLOG(ExitGames::Common::DebugLevel::INFO,L"--- Local Player ----%s",mLoadBalancingClient.getLocalPlayer().getCustomProperties().toString().UTF8Representation().cstr());

EGLOG(ExitGames::Common::DebugLevel::INFO,L"~~~~just joined player Info :~~~~~ %s",player.getCustomProperties().toString().UTF8Representation().cstr());

mOutputListener->writeLine(L"");
mOutputListener->writeLine(ExitGames::Common::JString(L"player ") + playerNr + L" " + player.getName() + L" has joined the game");
}


output :
line: 252 - iOS joined the game
2013-07-01 14:47:32,398527 INFO NetworkLogic.cpp joinRoomEventAction() line: 253 - --- Local Player ---- {}
2013-07-01 14:47:32,398838 INFO NetworkLogic.cpp joinRoomEventAction() line: 255 - ~~~~just joined player Info :~~~~~ {}


Am I doing it right way? other there is some other way to attached custom properties to player?

Thanks ,
Paresh

Comments

  • Hi Paresh.

    For Properties Photon currently only supports nByte and JString as key types, but you are using an int key. That's the reason, why it is not working.
    The LoadBalancing layer reserves the key type nByte for internal usage, so that custom properties have to be of type JString.

    You can also pass C strings and they will automatically be converted into JStrings:
    [code2=cpp]ev.put(L"key",str);
    ev.put("key",str);[/code2]
    Wide strings should be preferred, as they match the internal representation of JString, so that conversion is just a trivial wrapping, while with narrow strings a utf8 to ucs2/ucs4 (depending on the wchar_t size of the patform, which means ucs4 for iOS) conversion will take place.
  • thanks Kaiserludi for your quick reply.
    It worked as expected.

    One suggestion : It will be great, If you can add error message for any other type of format used in hash table other then JString.

    Thanks once again,
    Paresh
  • Hi Kaiserludi,

    I successfully able to set properties for player in loadbalancing cpp demo.
    I added "Photon-cpp" & "Common-ccp" to project as a reference.
    but when I import loadbalancing cpp project (as a copy) into my project & runs, it giving me following error:

    #include "Photon-cpp/inc/PhotonPeer.h" file not found.

    I followed same steps which I used for previous SDK, but never face this issue. It's not able to find the included file in project so I tried changing header search path but fail to solved it. I did know it's some file path error but I spend hours for it, don't know whats going wrong :cry:

    do you have any idea about it? It wasn't there while I import loadbalancing from previous SDK but why it's coming now?
    is there any changes in current SDK for importing loadbalancing cpp?

    Thanks in advance,
    Paresh
  • We have changed the include structure with 3.2.2.0
    You should now look for the headers two level higher in the path.
    So when you for example currently search for headers to include in myGameFolder/3rdParty/Photon-cpp/inc and in myGameFolder/3rdParty/Common-cpp/inc, then you should now instead just search for them in myGameFolder/3rdParty.
    Accordingly you should no longer include "PhotonPeer.h", but "Photon-cpp/inc/PhotonPeer.h"

    The reason for this change is, that it prevents against filename clashes: If one of your headers or of the ones from another library that you use is the same as one of the Photon headers, that isn't a problem, as not "filename.h", but the more unique "Photon-cpp/inc/filename.h" gets included.
  • Paresh
    edited July 2013
    thanks, it's working fine now. :)

    for others who facing same problem,
    in project properties, only change header search path for photon-cpp & common-cpp as mentioned by Kaiserludi (wrong information removed)
    don't touch library search paths.

    Thanks,
    Paresh
  • Paresh wrote:
    (if you are using loadbalancing also, then leave loadbalancing path as it is)
    That shouldn't actually work, if you use the original LoadBalancing project instead of some modified copy.

    For example the header search path in demo_loadBalancing previously has been
    ../../../Common-cpp/inc/Helpers;../inc;../../../LoadBalancing-cpp/inc;../../../Photon-cpp/inc;../../../Common-cpp/inc

    Now it's just
    ../inc;../../..;
  • I am using latest loadbalancing & my header search path in xcode was

    "$(PROJECT_NAME)/photon"
    "$(PROJECT_NAME)/photon/LoadBalancing-cpp/inc"


    & it was working fine. that's why I thought only common-cpp & photon-cpp was need to change.
    after your above post I deleted loadbalancing path & checked again, it's working fine. So you are very much right about that. :)

    Thanks for the correction.
  • Ah, of course.
    That way Xcode has just searched an additional folder, which didn't hurt as everything could already be found in the first one.
  • Hi,

    I am trying to change custom properties of player in runtime, but they are not reflecting at other users.
    I am overriding function onPlayerPropertiesChange in Network logic but not receiving any call for the same.

    Following are the steps:
    1. In constructor I am setting few properties for player
    2. In joinRoomReturn & joinRandomRoomReturn I receive all properties properly, now I am adding one more properties to it using addCustomProperties()
    3. waiting for onPlayerPropertiesChange() to give call back but it never come.

    is it normal behavior? or there is some other way to get player properties in runtime?

    Edit:
    I just dig into it & found that implementation for addCustomProperties inside peer is calling following method:

    [code2=cpp]bool Peer::opSetPropertiesOfPlayer(int playerNr, const Hashtable& properties)
    {
    OperationRequestParameters op;
    op.put(ParameterCode::PROPERTIES, ValueObject<Hashtable>(properties));
    op.put(Lite::ParameterCode::ACTORNR, ValueObject<nByte>(playerNr));

    bool broadcast = true;
    op.put(ParameterCode::BROADCAST, ValueObject<bool>(broadcast));

    return super::opCustom(OperationRequest(OperationCode::SET_PROPERTIES, op), true);
    }[/code2]

    but there is no implementation for OperationCode::SET_PROPERTIES inside
    void Client::onOperationResponse(const OperationResponse& operationResponse)
    is it the issue?

    Thanks,
    Paresh
  • Hi Paresh.

    No, that's not the issue. Setting a property won't trigger a call to onOperationResponse () on the client, that sets the property (no need for that, as that client already knows the new property value, as it has set it itself), but will trigger a call to onEvent() with EventCode::PROPERTIES_CHANGED on all other clients, that are currently inside the same room.

    So if client A sets a property, client B, C and D in the same room will all get a call onPlayerPropertiesChange(), but A itself won't get this callback as the change will be done immediately on the local client.
  • thanks for quick reply.
    I got your point but it still unclear that how will I trace it at other clients. I am following loadbalancing demo where we are implementing only ExitGames::LoadBalancing::Listener.

    I am creating object of ExitGames::LoadBalancing::Client can you please help me how to trace onEvent() in NetworkLogic class, I tried extending ExitGames::LoadBalancing::Client along with ExitGames::LoadBalancing::Listener but it giving error at NetworkLogic constructor. Do I need to remove ExitGames::LoadBalancing::Client object mLoadBalancingClient & override all virtual method from Client class?
    or I can trace onEvent with current implementation (like Loadbalancing demo) using some other method?

    thanks
  • Just set a breakpoint or add some debug out in onPlayerPropertiesChange().
    About your error when trying to extend the LoadBalancingClient class: Well, without the error message I can't really help much on that one.
  • I have just remembered, that we have recently fixed a bug in version 3.2.3.1 that prevented setting player properties when being inside a room.
    Afaik you are testing with Client version 3.2.2.0, right?

    Please check, if its works for you with version 3.2.3.2:
    https://dl.dropboxusercontent.com/u/429 ... ud_SDK.zip
  • thanks for quick reply,
    I debug with breakpoint but it never goes there, & yes I was using 3.2.2.0.

    I will try with new SDK & revert to you.
  • Hi Kaiserludi ,

    It did work like charm. It did reflect to 'B' & 'C' when property of 'A' is change.
    but new hash table that I received looks incomplete. It only reflect what change is made but how will receiver know for which player that change is made

    Following is the way I am doing it.
    In init i set all properties along with UP_CHAIR_ID = -1
    for 2nd Player in join random room return I am updating UP_CHAIR_ID as followes:
    [code2=cpp]ExitGames::Common::Hashtable ev;
    ev.put(UP_LOCAL_ID, "2");//for first player it will be 1
    mLoadBalancingClient.getLocalPlayer().addCustomProperties(ev);[/code2]

    following is the 1st player's onPlayerPropertiesChange method which return new changes for player 2
    [code2=cpp]void NetworkLogic::onPlayerPropertiesChange(const ExitGames::Common::Hashtable& newchanges)
    {
    CCLog("********** player properties changes ************* %s", newchanges.toString().UTF8Representation().cstr());
    for(int i=0; i<mLoadBalancingClient.getCurrentlyJoinedRoom().getPlayers().getSize(); i++) // this is to just cross check if property is reflecting along with other properties which I guess will not reflect immediately here
    {
    ExitGames::LoadBalancing::Player temp = mLoadBalancingClient.getCurrentlyJoinedRoom().getPlayers().getElementAt(i);
    CCLog("Player %d Data : %s",i, temp.getCustomProperties().toString().UTF8Representation().cstr());
    }
    }

    output:
    Cocos2d: ********** player properties changes ************* {"UP_CHAIR_ID"=2}
    Cocos2d: Player 0 Data : {"UP_LOSE_COUNT"=22, "UP_WIN_COUNT"=15, "UP_TOTAL_CHIPS"=27000, "UP_CHAIR_ID"="-1"}
    Cocos2d: Player 1 Data : {"UP_LOSE_COUNT"=14, "UP_WIN_COUNT"=47, "UP_TOTAL_CHIPS"=177000, "UP_CHAIR_ID"="-1"}[/code2]

    Main concern here is, how to decide property change is for which player?
    am i missing something?
    Please guide.

    Edit:
    I come up with an alternative,
    instead of updating only one property, if I update one more unique property along with UP_CHAIR_ID for example say USER_ID then I am receiving both property which I can use to identify which user have sent the event. but is it the only way?

    thanks.
  • You are right. That has really been suboptimal.
    Just added the playerNr :-)
    https://dl.dropboxusercontent.com/u/429 ... ud_SDK.zip
  • thanks, it's working as expected. :D
  • Hi,

    I have one more query about properties,

    When I received joinRoomEventAction for any player I get all properties for that player properly but when I run following for loop to get all players data I didn't get custom properties properly.

    so for example if I update player 1's chair id (custom property) from -1(default) to 1 in his join room return method, later when player 2 joins in player one receive player 2 properties in joinRoomEventAction but inside for loop it still shows player 1 chair id property as -1.

    [code2=cpp]void NetworkLogic::joinRoomEventAction(int playerNr, const ExitGames::Common::JVector<int>& playernrs, const ExitGames::LoadBalancing::Player& player)
    {
    CCLog("player %d %s has joined the game.", playerNr, player.getCustomProperties().toString().UTF8Representation().cstr());

    for(int i=0; i<mLoadBalancingClient.getCurrentlyJoinedRoom().getPlayers().getSize(); i++)
    {
    ExitGames::LoadBalancing::Player player2 = mLoadBalancingClient.getCurrentlyJoinedRoom().getPlayers().getElementAt(i);
    CCLog("Properties of %d are %s",i,player2.getCustomProperties().toString().UTF8Representation().cstr()); // here it shows -1 for chair id
    }

    }[/code2]

    what I conclude is mLoadBalancingClient.getCurrentlyJoinedRoom().getPlayers().getElementAt(i) returning old values for players. is there any way that I can update these values? or something which will refresh data for the same.

    thanks.
  • no one around to help me out? I have struct here for a long now :cry:
  • Stefan is sik now, so, we ask you to wait a little
    We are very sorry
  • ohh... that's sad, thanks for your quick reply.

    get well soon, Stefan :)
  • Sure, that joinRoomReturn() gets actually called for player 1?
    If I understand you correctly, then player 1 creates the room, so it should only get a call to createRoomReturn().
  • it's joinRoomEventAction() not joinRoomReturn().

    player one create room at that time createRoomReturn() called which is right inside this I set custom property for player1. now when player2 joins in joinRoomEventAction() get called, I am checking mLoadBalancingClient.getCurrentlyJoinedRoom().getPlayers().getElementAt(i) inside joinRoomEventAction of player1.

    I guess I used confusing variable in that code, please have a look at it again:
    [code2=cpp]void NetworkLogic::joinRoomEventAction(int playerNr, const ExitGames::Common::JVector<int>& playernrs, const ExitGames::LoadBalancing::Player& player)
    {
    CCLog("player %d %s has joined the game.", playerNr, player.getCustomProperties().toString().UTF8Representation().cstr()); //this will give properties of new player who joins in
    for(int i=0; i<mLoadBalancingClient.getCurrentlyJoinedRoom().getPlayers().getSize(); i++) // this will iterate through all players
    {
    ExitGames::LoadBalancing::Player tempPlayer = mLoadBalancingClient.getCurrentlyJoinedRoom().getPlayers().getElementAt(i);
    CCLog("Properties of %d are %s",i,tempPlayer.getCustomProperties().toString().UTF8Representation().cstr()); // here it shows -1 for chair id for player1 which is suppose to be new value
    }
    }[/code2]

    so now my question...
    mLoadBalancingClient.getCurrentlyJoinedRoom().getPlayers().getElementAt(i) returning old values for players. is there any way that I can update these values? or something which will refresh data for the same. If I have 4 players & 5th one joins in, above for loop still giving old custom properties of previous players.

    thanks
  • I could reproduce it.
    The mentioned issue itself could already be fixed, but we are still working on other issues with the synchronization of the local player properties.
  • by when I can expect it, with all issues fixed?
    mentioned for loop was quite handy at many scenario, if I can get it working it will be great help.
  • a small question:
    is there any limit on custom properties? or any guideline for it?
    currently I am assigning 12 custom properties for a players, which I am feeling getting quite large, I am expecting it to reach 15 by the end of the project. :o

    thanks.
  • Probably by tuesday evening my time / wednesday morning your time.

    There is no limit except of a theoretical limit that is given by the fact, that there are only 2^48 (2 to the power of 48) different valid string keys.
    Just keep in mind that everytime that you change the value of a property you will trigger a property changed operation that goes out to all other clients in the room, so if you change too many properties, that have too big payload-sizes, too often, you could end up at bombarding your clients with more incoming data than they are able to handle.
    So, 15 properties, that all have a byteArray[100000] as value and get all updated 20 times a second, will most definitely get you into trouble, but 150 properties with int values, that all only get updated 5 times per hour, won't be a problem at all.
    In general, if you need to update something multiple times a second, then you should raise an event instead of setting a property. Properties are more thought for values, that don't change too often.
    If you want to change the values of multiple properties inside the same frame, then it's maybe worth to put everything into a queue and then change all at once instead of each on its own, so that you only generate one properties change operation instead of many and save overhead.
  • thanks for the brief information.
    I have kept property changes to very minimum, plus all property changes are int values. So I hope it will not create much problem.
  • I have just sent you a pm with download links for prerelease versions of the AndroidNDK and the iOS Photon cloud client SDKs, that include a fix for that local player properties synchronization issue.