SmartFoxServer 2X – Example Java Extension for Client-Server Communication (via Events and Requests)

11 01 2011

Over the last two days, I’ve been playing with multiplayer Flash applications using SmartFoxServer 2X. Having got the basic app up and running, the next step was to get events and requests (including data transfer) working between client and server. I spent a lot of time today reading over documentation, forums and apis, only to find there wasn’t a quick example of what I needed. So, for the benefit of the internets, here’s one I made earlier…!

Overview
In this example, we have part of an AS3 file used in our client application. The client attempts to connect to the SmartFoxServer 2X instance and join a room. Once we’ve joined the room (I’ve trimmed out all the necessary event listeners in the example, but they’re easy to work out), the server will send a response to the client with some data. On the client side, we’ll dispatch an event for the server to listen to, resulting in a message displayed in the SmartFoxServer console.

The Java Extension Classes
We’ll set up 3 java classes. One is the main extension class, the other two are our event/request handlers that are called as a result of server events or client requests. The key elements from the classes are blow:

Main Class: SimpleSmartFoxServerJavaExtension. This is the main extension class and is where we’ll set up our handlers.

package simple.test;
public class SimpleSmartFoxServerJavaExtension extends SFSExtension
{
       @Override
       public void init()
       {
               trace("Initialising SmartFox Extension example");
               addRequestHandler("example_request", SimpleRequestHandler.class);
               addEventHandler(SFSEventType.USER_JOIN_ROOM, UserJoinedRoomHandler.class);
       }

       @Override
       public void destroy()
       {
               super.destroy();
               removeRequestHandler("example_request");
               removeEventHandler(SFSEventType.USER_JOIN_ROOM);
       }
}

Example class to handle client requests

package simple.test;
@Instantiation(InstantiationMode.SINGLE_INSTANCE)
public class SimpleRequestHandler extends BaseClientRequestHandler
{
       @Override
       public void handleClientRequest(User arg0, ISFSObject arg1)
       {
               trace("We've just handled the example_request that was dispatched by the client");
       }
}

Example class to handle server events

package simple.test;
public class UserJoinedRoomHandler extends BaseServerEventHandler
{
       @Override
       public void handleServerEvent(ISFSEvent arg0) throws SFSException
       {
               User user = (User)arg0.getParameter(SFSEventParam.USER);
               trace("Event Received. User has joined the Room: " + user.getName());

               //Same string as per SmartFoxClient's onExtensionResponse() function.
               //Returns some mock data to the client
               send("server_return_data", getReturnData(), user);
       }

       public ISFSObject getReturnData()
       {
               ISFSObject returnData = SFSObject.newInstance();
               returnData.putInt("foo", 123);
               return returnData;
       }

}

So once we’ve got our basic java classes written, we’ll want to update our ActionScript 3 class to work correctly with these examples…

The AS3 Code
Here is the shortened (there’s no event listeners. You’ll need those!) code for working with events/requests. To save space in the example, I’ve bunched everything into the constructor with comments. Each step should have its own event listeners to check for successful completion before progressing.

public class SmartFoxClient
{
       private var smartFoxServer : SmartFox;

       public function SmartFoxClient()
       {
               smartFoxServer = new SmartFox();

               //I'm not including the standard login etc events, just the one for listening
               //for the example server extension responses.
               //We're going to connect to a room. Once we've connected to the room ok, the server should
               //dispatch an extension response via UserJoinedRoomHandler, which is caught in the
               //onExtensionResponse() function below.
               smartFoxServer.addEventListener(SFSEvent.EXTENSION_RESPONSE, onExtensionResponse);

               //Connect to your server instance
               smartFoxServer.connect("localhost", 9933);

               //Once connected (use event listeners), we want to Login to a SmartFoxServer Zone:
               smartFoxServer.send(new LoginRequest("MyUsername", "MyPassword", "Zone, e.g SimpleChat"));

               //Once logged in to the zone (again, use event listeners), we can join a room:
               smartFoxServer.send(new JoinRoomRequest("MyRoomName"));

               //At this point, we have hopefully successfully joined the SmartFoxServer room, and now we'll
               //test that the server can pick up our events and accompanying data object...
               smartFoxServer.send(new ExtensionRequest("example_request", anISFSObject));

               //You should see the trace message from SimpleRequestHandler output in your SmartFoxServer console.
       }

       public function onExtensionResponse(evt : SFSEvent) : void
       {
               trace("Got an expansion response");
               var params : ISFSObject = evt.params.params;
               var cmd : String = evt.params.cmd;

               switch(cmd)
               {
                       //Refer to the java UserJoinedRoomHandler class to see where
                       //this string is sent from
                       case "server_return_data":
                               trace("Returned value of params.foo: " + params.getInt("foo");
                               break;
               }
       }

       …

Getting it Working
Once you’ve got your client and server code ready, the first thing you’ll need to do is wire up your extension via the SmartFoxServer Admin tool. To do this, you’ll need to follow these basic steps:

  • Compile the java source into a jar file. You’ll end up with one file – we’ll call ours extensionExample.jar
  • Copy this jar file into the “extensions” folder of your SmartFoxServer installation. For example, on my Mac’s local install, this would be something like: /Applications/SFS2X-RC1a/SFS2X/extensions/exampleExtension/exampleExtension.jar.
  • Fire up your SmartFoxServer and access your admin client.
  • Navigate to the “Zone Configurator” section and select the Zone that you chose to connect to in your AS3 code.
  • Below the list of Zones are buttons to add/remove/edit them. Edit your chosen zone and proceed to the Zone Extensions tab.
  • The two most important fields here are the Name and File ones. Name refers to the extension name. In this case, it would be set to “exampleExtension”. The File field is for the main class (including package name) of your extension jar. In the example here, it would be “simple.test.SimpleSmartFoxServerJavaExtension”. Once you’ve completed these fields, apply your changes.
  • Now for the fun bit! Restart your SmartFoxServerInstance. If you follow the console logs, you should see your extension being initialised (if you kept the traces in). This is where you’ll also see any stacktraces, enabling you to patch up any issues.
  • Fire up your Flash application and follow any steps required to test the communication. Make sure to keep an eye on the SmartFoxServer console as well as the flashlog. If all goes well, you’ll see the expected output! If not, don’t worry as the console should at least give you a good clue as to why not.

Issues I Encountered
One issue I found once the code was compiling ok was that there were other extensions trying to intercept my test events. They threw errors stating that that extension didn’t handle my custom events (obviously!). In my case, I was able to disable the other extension as it wasn’t needed. This stopped the error, but a better solution would need to be found in you were in need of multiple extensions.

Useful Links
And finally, here are some useful links to the 2X APIs and other documentation:

Hopefully that’s of some use to someone! Apologies for any typos, this was thrown together while fighting off a cold! :)





TweenLite and Overwritten Tweens

26 11 2010

I encountered an interesting issue in Moshi today, which I think may be useful to explain to anyone else using TweenLite.

A lot of Moshi Monsters is powered by commands, and often we have many commands that run at the same time. A lot of those commands are used to manipulate display objects and move them within the playarea. Today, I spotted a bug where a tween’s callback wasn’t being called on complete, and wasn’t sure why this was happening.

My first guess was to look into whether there was some unintended garbage collection occurring. After a while, I realised this wasn’t the case and started to look a bit deeper into the commands that were being run at the time. It turned out that if you were speedy enough with the mouse, you could get multiple commands to fire at once, affecting the same target.

In the below scenario (a very simplified version of the issue I encountered) we have 2 tweens, both affecting the same target clip. One tweens the clip to a new X position, and the other tweens to a new Y. Both tweens have callbacks that affect the target in different ways:

private var testClip : MovieClip;

public function ExampleOfTweenOverwrite()
{
	//Draw an initial green square on the test clip
	testClip = new MovieClip();
	testClip.graphics.beginFill(0x00FF00);
	testClip.graphics.drawRect(0, 0, 50, 50);
	testClip.graphics.endFill();

	this.addChild(testClip);

	doTweens();
}

private function doTweens() : void
{
	//Move the test clip to (300,300)
	TweenLite.to(testClip, 0.5, {x : 300, onComplete : firstTweenComplete});
	TweenLite.to(testClip, 0.1, {y : 300, onComplete : secondTweenComplete});
}

public function firstTweenComplete() : void
{
	//Rotate our test clip a bit
	testClip.rotation = 15;
}

public function secondTweenComplete() : void
{
	//Re-colour our test clip red
	testClip.graphics.beginFill(0xFF0000);
	testClip.graphics.drawRect(0, 0, 50, 50);
	testClip.graphics.endFill();
}

So, the target clip starts off as a green square. We then want to tween it to the position 300,300. When it reaches its target x or y it will rotate slightly and/or redraw red.

Initially, I would have expected the green square in the above example to end up at 300,300, be drawn red, AND be rotated. This is not what happens.

In TweenLite, you can’t by default have two tweens associated to the same target. If you try to do this, the first tween gets overwritten. This means that in the above example, you end up with a red square, not rotated, at coords 0,300.

When you have many commands that might affect the same target, it’s important to know that the tweens can get overwritten. This is why in the code I was working on today, the callback was never getting called.

To fix this issue, I had to change the “overwrite” value of the tweens, such that neither could overwrite the other. The default value of this parameter is “ALL_IMMEDIATE” (1), which means that the overwrite is carried out immediately when any tween is created. If you want all tweens to be carried out without being overwritten, you can do so by updating this value to zero. This prevents any tween conflicts and doesn’t allow it to be overwritten.

So now, we update our doTweens function to be the following:

private function doTweens() : void
{
	TweenLite.to(testClip, 0.5, {x : 300, onComplete : firstTweenComplete, overwrite : 0});
	TweenLite.to(testClip, 0.1, {y : 300, onComplete : secondTweenComplete, overwrite : 0});
}

Note the new overwrite params.

Now when we run the code, we get the expected output. The square is moved to (300,300), it is re-coloured red, and is slightly rotated. :)

If you have any issues with non-completing tweens, I recommend looking at the Greensock page for more info on the OverwriteManager and its possible settings.





Tweet Coding

20 02 2009

Yesterday, Grant Skinner (creator of the awesome GTween library) came up with a competition for all those AS3 developers out there. The aim? Make something funky in 140 characters or less using the “gimme” code provided on: http://gskinner.com/playpen/tweetcoding.html

Within a few hours, there were some amazing tweets submitted, some of which just blow my mind. All submissions can be found here. One of my current favourites is this one of a tree, which is just brilliant!

Needless to say, I couldn’t resist the challenge, so have created Painted Rain, which is _supposed_ to look like drops of paint falling into water and blending together. Was really tough to do in 140 characters! Here’s the link to the swf, and the code:

h=r();l=600;o[i++]={a:h*l,b:r()*l,c:h*30,d:h*1e7};g.clear();for each(e in o){g.beginFill(e.d,20/e.c);g.drawCircle(e.a,e.b,e.c++)}i>150?i=0:i

You can keep up to date on the contest by searching for #tweetcoding on Twitter!