EGCustomType implementation in Obj-c

edited August 2013 in Native
Hello.

I'm currently developing a hosted server infrastructure for our iOS multiplayer game.

SERVER: I have the latest Photon Server (3.0 I think?) and I have a fully working lobby and game system.

CLIENT: The client is an iOS app that uses the Photon Objective-C library.

The app is a card game, and one of the things I want to keep intact - source-wise - is the codebase. Currently I'm transmitting from server to client and vice-versa a "Card" object, consisting of only two shorts: num and suit.

My goal is to implement the serialization protocols so that Photon sends my custom objects (Card being one of them). Here is my current setup:

SERVER:
In the beginning of my server loop, I have this:
[code2=csharp]Protocol.TryRegisterCustomType(typeof(Card), (byte)'C', Card.Serialize, Card.Deserialize);[/code2]
In Card.cs I implemented the functions:
[code2=csharp]public static byte[] Serialize(object obj)
{
Card card = (Card)obj;
byte[] data = new byte[sizeof(short)*2];

BitConverter.GetBytes((short)card.num).CopyTo(data,0);
BitConverter.GetBytes((short)card.suit).CopyTo(data, sizeof(short));
return data;
}
public static object Deserialize(byte[] data)
{
// not implemented yet since I'm only testing Server=>Client - which means Server has to Serialize while Client has to Deserialize
}[/code2]

CLIENT:
I have Automatic Reference Counting on.
In the beginning of my game loop (AppDelegate.m) I added:
[code2=objc][EGCustomTypeUtils initClass:(nByte)'C' :[Card class]];[/code2]
In Card.m I implemented the following methods:

[code2=objc]-(short)serialize:(unsigned char *const)retVal
{
// not needed yet
return 0;
}

-(void)dealloc
{
NSLog(@DELETING %@", self);
}

-(void)deserialize:(const unsigned char * const)pData :(short)length
{
num = (short)*pData;
suit = (short)*(pData+sizeof(short));
}

- (void) duplicate:(id<EGCustomType> const)retVal
{
// not sure what I'm supposed to do here.
// I looked at the example source code that came with the client SDK but it did not compile
// if (retVal)
// *(Card *)retVal = *(Card *)self;
}[/code2]



Here's my problem:
Whenever the server tries to send an Event with a Card as a parameter, I receive it correctly in "deserialize:" (I have verified the values), but the object gets dealloc'ed BEFORE the "onEvent" callback is called.

Another mystery: dealloc gets called TWO more times after deserialize, and if I send a packet with TWO Card objects instead, I would get them in this order: deserialize,dealloc,dealloc,deserialize,dealloc,dealloc.. so it's deallocing each card I receive twice.. I'm not sure if I'm supposed to explicitly retain them, that's why I mentioned ARC.

I would greatly appreciate some help on this. Please let me know if any more information is needed.

Comments

  • Here is the interface and implementation of class SampleCustomType in demo_advanced_objc inside the Client SDK:

    [code2=objc]#import "Common-objc/inc/EGCustomType.h"

    #define TYPECODE 1

    @interface SampleCustomType : NSObject <EGCustomType>
    {
    @protected
    nByte mFoo;
    nByte mBar;
    }

    - (id) init;
    - (id) init:(nByte) foo :(nByte)bar;
    - (id) initWithCustomType:(SampleCustomType*)toCopy;
    - (void) setData:(nByte)foo :(nByte)bar;

    @end[/code2]

    [code2=objc]#import "Foundation/NSString.h"
    #import "demo_photon_CustomType_objc.h"

    @implementation SampleCustomType

    - (id) init
    {
    if(self=[super init])
    {
    mFoo = 0;
    mBar = 0;
    }
    return self;
    }

    - (id) init:(nByte)foo :(nByte)bar
    {
    if(self=[super init])
    {
    mFoo = foo;
    mBar = bar;
    }
    return self;
    }

    - (id) initWithCustomType:(SampleCustomType*)toCopy
    {
    if(self=[super init])
    {
    mFoo = toCopy->mFoo;
    mBar = toCopy->mBar;
    }
    return self;
    }

    - (void) dealloc
    {
    [self cleanup];
    [super dealloc];
    }

    - (nByte) TypeCode
    {
    return TYPECODE;
    }

    - (void) setData:(nByte)foo :(nByte)bar
    {
    mFoo = foo;
    mBar = bar;
    }


    // CustomType callbacks

    - (void) cleanup
    {
    }

    - (bool) compare:(const SampleCustomType*)other
    {
    return mFoo == other->mFoo && mBar == other->mBar;
    }

    - (void) duplicate:(SampleCustomType* const)retVal
    {
    if(retVal)
    {
    retVal->mFoo = mFoo;
    retVal->mBar = mBar;
    }
    }

    - (void) deserialize:(const nByte* const)pData :(short)length
    {
    if(length!=2)
    return; // something went wrong
    mFoo = pData[0];
    mBar = pData[1];
    }

    - (short) serialize:(nByte* const)retVal
    {
    if(retVal)
    {
    retVal[0] = mFoo;
    retVal[1] = mBar;
    }
    return 2;
    }

    - (NSString*) typeToString
    {
    return [NSString stringWithFormat:@(;%@);", NSStringFromClass(self.class)];
    }

    - (NSString*) toString
    {
    return [self toString:false];
    }

    - (const NSString*) toString:(bool)withTypes
    {
    return [NSString stringWithFormat:@&lt;%@%d, %@%d&gt;", withTypes?[NSString stringWithFormat:@(;%@);", EG_STR_UCHAR]:@&quot;", mFoo, withTypes?[NSString stringWithFormat:@(;%@);", EG_STR_UCHAR]:@&quot;", mBar];
    }
    @end[/code2]


    Your deserialize implementation looks definitely incorrect. You are dereferencing the first byte of the serialized data and are then casting the result to short, then you are jumping to the third byte, dereference it and cast the result into a short.
    Imagine, your serialized data looks like 0x12 0x34 0x56 0x78, then your first short would end up to be 0x00 0x34 and the second would be 0x00 0x78.
    I don't think that that is your intention.

    So you should first cast and then dereference, not the other way round:
    [code2=objc]-(void)deserialize:(const unsigned char * const)pData :(short)length
    {
    num = *(short*)pData;
    suit = *(1+(short*)pData);
    }[/code2]

    Your outcommented duplicate implementation also doesn't look right.

    It could look like this:
    [code2=objc]- (void) duplicate:(SampleCustomType* const)retVal
    {
    if(retVal)
    {
    retVal->num = num;
    retVal->suit = suit;
    }
    }[/code2]

    Something like your *(Card *)retVal = *(Card *)self would more be the C++ way of doing it (with this instead of self of course), but won't work in objC.

    Counting the calls to dealloc only makes sense if you also count the calls to duplicate. If the lib has internally made a copy of the object, then it of course will later release the original and the copy, if it has made 2 copies, then it will to release 3 objects, so dealloc will be called 3 times, although only one object has been past to your app, and so on. You should more look at the amount of calls to dealloc and at the timings, when they are happening as an implementation detail. The copy that gets passed to you will be valid until the end of the callback, as long as you don't invalidate it yourself.
  • Ah! I didn't realize "duplicate" gets the actual instance and not byte values.

    That makes a lot of sense now, thanks a lot for your quick reply!

    Although, one thing still puzzles me. Why would it need to duplicate 5+ times? Oh well, I guess as long as it's working, for now I won't worry about it.
Sign In or Register to comment.