Sending/receiving classes

Post here your questions about the Unity / .Net / Mono / Windows 8 / Windows Phone 8 API for SFS2X

Moderators: Lapo, Bax

ThomasLund
Posts: 1297
Joined: 14 Mar 2008, 07:52
Location: Sweden

Sending/receiving classes

Postby ThomasLund » 25 Mar 2011, 07:44

I just wanted to give a heads up on this little buried gem in the new API.

You can pretty easily send and receive class structures between C# and Java on the backend without having to create intermediate SFSObject structures.

I will soon make a small example project, but here are some of the details on how to get started on this.

First off - read more about this advanced feature here
http://docs2x.smartfoxserver.com/Advanc ... ialization

To get this working in C#, there is one additional nugget of information that you need. In your code you need to add this:

Code: Select all

         DefaultSFSDataSerializer.RunningAssembly = Assembly.GetExecutingAssembly();

otherwise the serialization wont work. Also remember (as stated in the linked documentation page) that your class/package names need to match on server and on client side.

Below are some cut'n'pastes from a test case I got. It should serve as example until I get a small project together that you can run.

First up - we make a test class which is what we want to transport

Code: Select all

   public class TestObject : SerializableSFSType {
      
      public int intField;
      public string strField;
      public bool boolField;
      public double doubleField;
      
      public NestedObject nestedField;
      
      public ArrayList arrayField;
      public Hashtable dictField;
            
      public bool IsEqual(TestObject anotherObject) {
         if (this.intField!=anotherObject.intField) return false;
         if (this.strField!=anotherObject.strField) return false;
         if (this.boolField!=anotherObject.boolField) return false;
         if (this.doubleField!=anotherObject.doubleField) return false;
                  
         return true;
      }
   
      #region SerializableSFSType implementation
      public string GetJavaPackageName ()
      {
         return "";
      }
      
      #endregion
      
   }


And here is a usage example where my test code serializes and deserializes the object. Note that here it doesnt actually send it to the server. But inserting a send/receive is trivial.

Code: Select all

   [Test]
      public void TestClass() {
         DefaultSFSDataSerializer serializer = DefaultSFSDataSerializer.Instance;
         DefaultSFSDataSerializer.RunningAssembly = Assembly.GetExecutingAssembly();
         
         TestObject testObj = new TestObject();
         testObj.intField = 10;
         testObj.strField = "test";
         testObj.boolField = true;
         testObj.doubleField = 1.421;
         testObj.nestedField = new NestedObject();
         testObj.nestedField.intField = 20;
         
         testObj.arrayField = new ArrayList();
         testObj.arrayField.Add((int)1);
         testObj.arrayField.Add("testArray");
         
         testObj.dictField = new Hashtable();
         testObj.dictField["test1"] = false;
         testObj.dictField["test2"] = "testDict";
         
         ISFSObject obj = serializer.Cs2Sfs(testObj);
         Assert.IsNotNull(obj);
         TestObject receivedTestObj = serializer.Sfs2Cs(obj) as TestObject;
         Assert.IsNotNull(receivedTestObj);
         
         Assert.IsTrue(receivedTestObj.IsEqual(testObj));
         Assert.IsNotNull(receivedTestObj.nestedField);
         Assert.AreEqual(testObj.nestedField.intField, receivedTestObj.nestedField.intField);
         
         Assert.IsNotNull(receivedTestObj.arrayField);
         Assert.AreEqual(2, receivedTestObj.arrayField.Count);
         Assert.AreEqual(1, (int)receivedTestObj.arrayField[0]);
         Assert.AreEqual("testArray", (string)receivedTestObj.arrayField[1]);
         
         Assert.IsNotNull(receivedTestObj.dictField);
         Assert.AreEqual(2, receivedTestObj.dictField.Count);
         Assert.AreEqual(false, (bool)receivedTestObj.dictField["test1"]);
         Assert.AreEqual("testDict", (string)receivedTestObj.dictField["test2"]);
               
      }


Some important notes to get this working:
1. Only non-generic ArrayList and Hashtable work in C#
2. Hashtable can have only use string type as the key
3. The java extension must be in __lib__ folder to allow sending classes from C# (AS3) -> Java
4. C# class needs this: DefaultSFSDataSerializer.RunningAssembly = Assembly.GetExecutingAssembly();
5. Package names need to match in the C# and Java class

Previous requirement of C# class having
   public string GetJavaPackageName ()
     {
        return "";
     }
is not needed anymore
Last edited by ThomasLund on 02 Jul 2011, 06:47, edited 1 time in total.
Full Control - maker of Unity/C# and Java SFS API and indie games
Follow on twitter: http://twitter.com/thomas_h_lund
xms_wufucan@126.com
Posts: 4
Joined: 31 Mar 2011, 03:32

I trid, but failed

Postby xms_wufucan@126.com » 31 Mar 2011, 03:49

There is an error in C# file.
Witch namespace is SerializableSFSType in?
ThomasLund
Posts: 1297
Joined: 14 Mar 2008, 07:52
Location: Sweden

Postby ThomasLund » 31 Mar 2011, 08:14

namespace Sfs2X.Protocol.Serialization
Full Control - maker of Unity/C# and Java SFS API and indie games

Follow on twitter: http://twitter.com/thomas_h_lund
User avatar
Whiskey
Posts: 45
Joined: 28 Oct 2010, 06:27
Location: The Netherlands
Contact:

Postby Whiskey » 11 May 2011, 09:01

Hi Thomas,

would you care to also post the server-side java code for the example above and how to pass the object? I'm trying to get this to work, but having some difficulty. I read the documentation you pointed to, but there the getClass and putClass are used, which you don't mention. I did notice that the server tries to deserialize the object even without a requesthandler being defined: I get a warning in the log saying:

Code: Select all

11 May 2011 11:46:24,637 WARN  [SocketReader] v2.protocol.SFSProtocolCodec     - Error deserializing request: com.smartfoxserver.v2.exceptions.SFSRuntimeException: java.lang.ClassNotFoundException: /TestObject
ThomasLund
Posts: 1297
Joined: 14 Mar 2008, 07:52
Location: Sweden

Postby ThomasLund » 24 May 2011, 10:04

Client snippet that works with the advanced tutorial will be posted one of these days!

/T
Full Control - maker of Unity/C# and Java SFS API and indie games

Follow on twitter: http://twitter.com/thomas_h_lund
User avatar
Whiskey
Posts: 45
Joined: 28 Oct 2010, 06:27
Location: The Netherlands
Contact:

Postby Whiskey » 24 May 2011, 10:05

Awesome!
ThomasLund
Posts: 1297
Joined: 14 Mar 2008, 07:52
Location: Sweden

Postby ThomasLund » 25 May 2011, 10:41

In relation to the advanced tutorial, here are some snippets that send classes to the server as in the tutorial

Client side - object

Code: Select all

using System;
using Sfs2X.Protocol.Serialization;

namespace sfs2x.extension.test.serialization.model
{
   public class WaterFloodSpell: SerializableSFSType
   {
      string id ="myId";
       int hitPoints =100;
       int count = 7;
     
      public WaterFloodSpell ()
      {
      }
   }
}


Full C# console client

Code: Select all

using System;
using System.Collections;

using System.Collections.Generic;

using Sfs2X;
using Sfs2X.Core;
using Sfs2X.Entities;
using Sfs2X.Requests;
using Sfs2X.Logging;
using Sfs2X.Entities.Data;

using sfs2x.extension.test.serialization.model;
using Sfs2X.Protocol.Serialization;

namespace DemoClient
{
   class ClientSideApp
   {
      bool isStoped = false;
      private SmartFox smartFox;
      private string serverIP = "127.0.0.1";
      private string serverPort = "9933";
      public string zoneName = "SimpleChat";
      public string roomName = "The Lobby";
      private string userName = "userName";
         
      public void Start ()
      {
         try
         {
            bool debug = false;
            smartFox = new SmartFox (debug);
            smartFox.ThreadSafeMode = false;
         }
         catch (Exception e)
         {
            Console.WriteLine("loginErrorMessage = " + e.ToString ());
         }
         RegisterSFSSceneCallbacks ();
         smartFox.Connect (serverIP, Convert.ToInt32 (serverPort));
         Console.WriteLine("Init SmartFoxServer OK");
      }

      public bool IsStoped {
         get { return this.isStoped; }
      }

      public void OnExtensionResponse (BaseEvent evt)
      {
         string cmd = (string)evt.Params["cmd"];
         SFSObject dataObject = (SFSObject)evt.Params["params"];
         Console.WriteLine("send to server cmd = " + dataObject.GetDump());
      }
   

      public void OnConnection (BaseEvent evt)
      {
         bool success = (bool)evt.Params["success"];
         string error = (string)evt.Params["error"];
         Console.WriteLine("On Connection callback got: " + success + " (error : <" + error + ">)");
         if (success)
         {
            SmartFoxConnection.Connection = smartFox;
            if (smartFox.IsConnected)
               smartFox.Send (new LoginRequest (userName, "", zoneName));
         }
         else
         {
            Console.WriteLine("Connection Error");
         }
      }

      public void OnDebugMessage (BaseEvent evt)
      {
         string message = (string)evt.Params["message"];
         Console.WriteLine("[DEBUG:] " + message);
      }

      public void OnLogin (BaseEvent evt)
      {
         if (evt.Params.ContainsKey ("success") && !(bool)evt.Params["success"])
         {
            Console.WriteLine("[ERROR]: login error" );
         } else
         {
            smartFox.Send (new JoinRoomRequest (roomName));
         }
      }
      void OnRoomList (SFSObject roomList)
      {
         Console.WriteLine("OnRoomList" );
      }

      public void Quit()
      {
         smartFox.Disconnect ();
         UnregisterSFSSceneCallbacks ();
         isStoped = true;
      }
      
      void OnJoinRoom (BaseEvent evt)
      {
         Room room = (Room)evt.Params["room"];
         Console.WriteLine("OnJoinRoom room = "+ room.Name);
          SendTestMessage ();
      }
   
      void OnConnectionLost (BaseEvent evt)
      {
         Console.WriteLine("OnConnectionLost");
         UnregisterSFSSceneCallbacks ();
      }
      
      public void  SendTestMessage ()
      {      
         try
         {
            SFSObject info = new SFSObject();
            info.PutBool("IsActive", true);
            info.PutInt("TheNumber", 42);
            info.PutClass("spell", new WaterFloodSpell());
            smartFox.Send(new ExtensionRequest("test", info));
            Console.WriteLine("send to server cmd = " + info.GetDump());
         }
         catch(Exception e)
         {
            Console.WriteLine("SEND ERROR e = " + e.Message);
         }
      }
      
      private void RegisterSFSSceneCallbacks ()
      {
         smartFox.AddEventListener (SFSEvent.CONNECTION, OnConnection);
         smartFox.AddEventListener (SFSEvent.CONNECTION_LOST, OnConnectionLost);
         smartFox.AddEventListener (SFSEvent.LOGIN, OnLogin);
         smartFox.AddEventListener (SFSEvent.EXTENSION_RESPONSE, OnExtensionResponse);
         smartFox.AddEventListener(SFSEvent.DEBUG_MESSAGE, OnDebugMessage);
         smartFox.AddEventListener (SFSEvent.ROOM_JOIN, OnJoinRoom);
   }
      
      private void UnregisterSFSSceneCallbacks ()
      {
         smartFox.RemoveEventListener (SFSEvent.CONNECTION, OnConnection);
         smartFox.RemoveEventListener (SFSEvent.CONNECTION_LOST, OnConnectionLost);
         smartFox.RemoveEventListener (SFSEvent.LOGIN, OnLogin);
         smartFox.RemoveEventListener (SFSEvent.EXTENSION_RESPONSE, OnExtensionResponse);
         smartFox.RemoveEventListener(SFSEvent.DEBUG_MESSAGE, OnDebugMessage);
         smartFox.RemoveEventListener (SFSEvent.ROOM_JOIN, OnJoinRoom);
      }
   }
   //class
}
//namespace



Main class

Code: Select all

using System;
using System.Timers;

namespace DemoClient
{
   class MainClass
   {
      public static ClientSideApp _clientSideApp;
      public static void Main (string[] args)
      {
         _clientSideApp = new ClientSideApp();
         _clientSideApp.Start();
         while(!_clientSideApp.IsStoped)
         {
         }
         _clientSideApp.Quit();
      }
   }
}
Full Control - maker of Unity/C# and Java SFS API and indie games

Follow on twitter: http://twitter.com/thomas_h_lund
User avatar
Whiskey
Posts: 45
Joined: 28 Oct 2010, 06:27
Location: The Netherlands
Contact:

Postby Whiskey » 25 May 2011, 11:12

Thanks! One question: what about GetJavaPackageName ?

I got the suggestion that that is needed client side so that serverside the class definition (that needs to be put in __lib__) can be located. I haven't been able to analyze much today because I'm not at the office, but it seems that will solve my earlier problem. However, I don't see it in these snippets?
Lightnet
Posts: 19
Joined: 13 Apr 2011, 02:01

Postby Lightnet » 25 May 2011, 18:41

How to get the data back into class when send to the server and back again.

WaterFloodSpell waterspell = dataObject.GetClass("spell") as WaterFloodSpell;

I get an null from it. Just an example.
ThomasLund
Posts: 1297
Joined: 14 Mar 2008, 07:52
Location: Sweden

Postby ThomasLund » 25 May 2011, 20:11

OK - I'll expand it to cover a round trip object sending. Will take some days as I'm in the middle of Java API docs + some contract work

/T
Full Control - maker of Unity/C# and Java SFS API and indie games

Follow on twitter: http://twitter.com/thomas_h_lund
User avatar
Whiskey
Posts: 45
Joined: 28 Oct 2010, 06:27
Location: The Netherlands
Contact:

Postby Whiskey » 26 May 2011, 14:14

I would love to see that as well. After some testing, I found with GetDump of the object to send that after PutClass both Unity (before sending), and the server (after receiving and before sending back) report the contents of the object as:

Code: Select all

(class) test: nl.inthere.sfs2x.serialization.TestObject


However, when the object is received in Unity (and evt.Params["params" is casted to an SFSObject) the contents are reported as:

Code: Select all

(sfs_object) test:
   (utf_string) $C: nl.inthere.sfs2x.serialization.TestObject
   (sfs_array) $F:
      (sfs_object)
         (utf_string) V: Server generated class
         (utf_string) N: name
      
      (sfs_object)
         (int) V: 24
         (utf_string) N: id

The values are correct, but it is not classified as a class, so GetClass can't find it.
ThomasLund
Posts: 1297
Joined: 14 Mar 2008, 07:52
Location: Sweden

Postby ThomasLund » 02 Jul 2011, 06:48

Updated original post with 5 good to know bullets

/T
Full Control - maker of Unity/C# and Java SFS API and indie games

Follow on twitter: http://twitter.com/thomas_h_lund
User avatar
Zelek
Posts: 36
Joined: 24 Jun 2010, 22:36

Re: Sending/receiving classes

Postby Zelek » 06 Sep 2011, 01:42

ThomasLund wrote:Some important notes to get this working:
1. Only non-generic ArrayList and Hashtable work in C#
2. Hashtable can have only use string type as the key

These two requirements are making my code somewhat unpleasant to work with. Do you think this might change in a future release?
ThomasLund
Posts: 1297
Joined: 14 Mar 2008, 07:52
Location: Sweden

Postby ThomasLund » 07 Sep 2011, 09:13

No plans at the moment for that.

/Thomas
Full Control - maker of Unity/C# and Java SFS API and indie games

Follow on twitter: http://twitter.com/thomas_h_lund
Dilbertian
Posts: 6
Joined: 12 Sep 2010, 23:27

Serialization Attempt

Postby Dilbertian » 14 Sep 2011, 11:22

I understand that the namespaces have to match but am still trying to figure out what the rules are. For example, in my Unity project, I have a folder _MyCode and have created a sub-folder under that called Serialization, where I have placed this C# class which should be namespace "_MyCode.Serialization" as shown below. Does the Java side dictate what the namespaces will be or can I create it as such in C# and then do the same on the Java side?

Code: Select all

using System;
using Sfs2X.Protocol.Serialization;

namespace _MyCode.Serialization
{
   public class myNewClass : SerializableSFSType
   {
      private const int _MAXPROPS1= 6;
      private const int _MAXPROPS2= 6;
      
      private enum _PROPS1ENUM {
         Prop1A,
         Prop1B,
         Prop1C,
         Prop1D,
         Prop1E,
         Prop1F};
      
      private enum _PROPS2ENUM {
         Prop2A,
         Prop2B,
         Prop2C,
         Prop2D,
         Prop2E,
         Prop2F};
         
      private string _name;
      private int[] _props1;
      private float[] _props2;
      
      public myNewClass()
      {
         int i;
         
         _name="test";
         _props1=new int[_MAXPROPS1];
         _props2=new float[_MAXPROPS2];
         
         for (i=0; i < _MAXPROPS1; i++) {
            _props1[i]=i;
         }
         for (i=0; i < _MAXPROPS2; i++) {
            _props2[i]=(float)i;
         }
      }
      
      public string GetJavaPackageName()
      {
         return("");
      }
   }
}


Does this look like it will work like this, with the array of ints and floats - or do you see any other glaring issues with my attempt?

Are there any C# samples of this implementation (something simple like your example) which demonstrate this functionality? It seems that all of the more complex samples are only for Flash or AS3 while the 3 C# examples are more simplistic.

-Tim

Return to “SFS2X C# API”

Who is online

Users browsing this forum: No registered users and 1 guest