JavaScript Extensions Dev.
• Examples (iOS)
• Examples (Java/Android)
• Examples (C++)
Server API Documentation

 

» JavaScript Extensions: a quick start

SmartFoxServer 2X allows developers to write their server side code to extend the server's capabilities, integrate it with other technologies and manage the game state in a centralized place.

The diagram above shows a simplified version of the server stack, where at the bottom client connect via a number of protocols and can invoke both the public server API and the custom calls exposed by the game developer via Extensions.

In simple terms SmartFoxServer Extensions are to games what PHP/JSP pages are to websites. They allow for custom code to run on the server side and add entirely new application logic.

In SmartFoxServer 2X Extensions can be attached to a Zone or a Room, depending on the scope of the Extension itself. A Room Extension has a smaller scope, dealing with events and calls of that Room, while a Zone Extension can listen for a much larger number of events and control all Rooms.

» Let's write an Extension

	function init()
	{
		addRequestHandler("sum", onSumRequest);
		
		trace("Simple JS Example inited");
	}
	
	function destroy()
	{
		trace("Simple JS Example destroyed");
	}
	
	function onSumRequest(params, sender) 
	{
		var n1 = params.getInt("n1");
		var n2 = params.getInt("n2");
	
		var response = new SFSObject();
		response.putInt("res", n1 + n2);
	
		send("sum", response, [sender]);
	}

Every Extension starts with an init() method, which is called by the server at start up. Here we can initialize global objects or data structures and register event handlers. In this example we declare a command called "sum" which is handled by our onSumRequest() function.

The function extracts the parameters from an SFSObject, used to transport data between client and server, and sends the response back. You can think of an SFSObject as a simple dictionary object to store key/value pairs, with the added benefit of declaring exactly what type each data is. This in turn allows to optimize the network usage.
More information on SFSObject is available in this document; also check the JSDoc.

Extensions can also declare a destroy() method but it's not mandatory. It is required only for deallocating resources that require manual release such as active database connections, open files, or scheduled tasks.

As a side note, the trace() function allows you to output log information on the server side.

Let's now build a small client, connect to the server and test our custom server code:

	public class ExtensionTester : MonoBehaviour
	{
		private SmartFox sfs;
	
		void Start()
		{
			// Create SmartFox client instance
			sfs = new SmartFox();
			
			// Add event listeners
			sfs.AddEventListener(SFSEvent.CONNECTION, OnConnection);
			sfs.AddEventListener(SFSEvent.CONNECTION_LOST, OnConnectionLost);
	        sfs.AddEventListener(SFSEvent.LOGIN, OnLogin);
		    sfs.AddEventListener(SFSEvent.LOGIN_ERROR, OnLoginError);
		    sfs.AddEventListener(SFSEvent.EXTENSION_RESPONSE, OnExtensionResponse);
			
			// Set connection parameters
			ConfigData cfg = new ConfigData();
			cfg.Host = "127.0.0.1";
			cfg.Port = 9933;
			cfg.Zone = "BasicExamples";
				
			// Connect to SFS2X
			sfs.Connect(cfg);
		}
	
		void Update()
		{
			if (sfs != null)
				sfs.ProcessEvents();
		}
	
		private void OnConnection(BaseEvent evt)
		{
			if ((bool)evt.Params["success"])
			{
				Debug.Log("Connected");
				
				// Send login request
				sfs.Send(new Sfs2X.Requests.LoginRequest(""));
			}
			else
				Debug.LogError("Connection failed");
		}
		
		private void OnConnectionLost(BaseEvent evt)
		{
			Debug.Log("Disconnected");
		}
	
		private void OnLogin(BaseEvent evt)
		{
			Debug.Log("Logged in as: " + sfs.MySelf.name);
		 
			// Send test request to Extension
		    ISFSObject params = SFSObject.NewInstance();
		    params.PutInt("n1", 25);
		    params.PutInt("n2", 17);
		 
		    sfs.Send(new Sfs2x.Requests.ExtensionRequest("sum", params));
		}
		
		private void OnLoginError(BaseEvent evt)
		{
			Debug.LogError("Login error: " + (string)evt.Params["errorMessage"]);
		}
		
		private void OnExtensionResponse(BaseEvent evt)
		{
			// Retrieve response object
			ISFSObject responseParams = (SFSObject)evt.Params["params"];
			
		    Debug.Log("Result: " + responseParams.GetInt("res"));
		}
	}
	window.onload = function()
	{
		// Set connection parameters
		var config = {};
		config.host = "127.0.0.1"
		config.port = 8080;
		config.zone = "BasicExamples";
	
		// Create SmartFox client instance
		sfs = new SFS2X.SmartFox(config);
	
		// Add event listeners
		sfs.addEventListener(SFS2X.SFSEvent.CONNECTION, onConnection, this);
		sfs.addEventListener(SFS2X.SFSEvent.CONNECTION_LOST, onConnectionLost, this);
	    sfs.addEventListener(SFS2X.SFSEvent.LOGIN, onLogin, this);
	    sfs.addEventListener(SFS2X.SFSEvent.LOGIN_ERROR, onLoginError, this);
	    sfs.addEventListener(SFS2X.SFSEvent.EXTENSION_RESPONSE, onExtensionResponse, this);
	
		// Connect to SFS2X
		sfs.connect();
	};
	
	function onConnection(evtParams)
	{
		if (evtParams.success)
		{
			console.log("Connected");
			
			// Send login request
			sfs.send(new SFS2X.LoginRequest(""));
		}
		else
			console.error("Connection failed");
	}
	
	function onConnectionLost(evtParams)
	{
		console.log("Disconnected");
	}
	
	function onLogin(evtParams) 
	{
	    console.log("Logged in as: " + sfs.mySelf.name);
		
		// Send test request to Extension
	    var params = new SFS2X.SFSObject();
	    params.putInt("n1", 25);
	    params.putInt("n2", 17);
	 
	    sfs.send(new SFS2X.ExtensionRequest("sum", params));
	}
	 
	function onLoginError(evtParams) 
	{
	    console.log("Login error: " + evtParams.errorMessage);
	}
	 
	function onExtensionResponse(evtParams) 
	{
		// Retrieve response object
		var responseParams = evtParams.params;
		
	    console.log("Result: " + responseParams.getInt("res"));
	}
	public class ExtensionTester extends Sprite
	{	
	    private var sfs:SmartFox
	    
	    public function ExtensionTester()
	    {
	    	// Create SmartFox client instance
			sfs = new SmartFox();
			
			// Add event listeners
			sfs.addEventListener(SFSEvent.CONNECTION, onConnection);
	        sfs.addEventListener(SFSEvent.CONNECTION_LOST, onConnectionLost);
	        sfs.addEventListener(SFSEvent.LOGIN, onLogin);
		    sfs.addEventListener(SFSEvent.LOGIN_ERROR, onLoginError);
		    sfs.addEventListener(SFSEvent.EXTENSION_RESPONSE, onExtensionResponse);
			
			// Set connection parameters
			var cfg:ConfigData = new ConfigData();
			cfg.host = "127.0.0.1";
			cfg.port = 9933;
			cfg.zone = "BasicExamples";
				
			// Connect to SFS2X
			sfs.connectWithConfig(cfg);
	    }
	    
	    private function onConnection(evt:SFSEvent):void
	    {
	        if (evt.params.success)
	        {
	        	trace("Connected");
	        	
	        	// Send login request
				sfs.send(new LoginRequest(""));
	        }
	        else
	            trace("Connection failed");
	    }
	    
	    private function onConnectionLost(evt:SFSEvent):void
	    {
		    trace("Disconnected");
	    }
	    
	    private function onLogin(evt:SFSEvent):void
		{
		    trace("Logged in as: " + sfs.mySelf.name);
		 
			// Send test request to Extension
		    var params:ISFSObject = new SFSObject();
		    params.putInt("n1", 25);
		    params.putInt("n2", 17);
		 
		    sfs.send(new ExtensionRequest("sum", params));
		}
		 
		private function onLoginError(evt:SFSEvent):void
		{
		    trace("Login error: " + evt.params.errorMessage);
		}
		 
		private function onExtensionResponse(evt:SFSEvent):void
		{
			// Retrieve response object
			var responseParams:ISFSObject = evt.params.params as SFSObject;
			
		    trace("Result: " + responseParams.getInt("res"));
		}
	}

In the onLogin() function we wrap our parameters and send the command via the ExtensionRequest object. To handle the server response we make sure to register for the EXTENSION_RESPONSE event and read the result using the same key(s) used on the server side.

» Server side events

In addition to handling client requests a server-side Extension can also listen for a number of Server's events, such as login events, join room events etc.

Listening for server events is as simple as handling client requests: we just create a function and register it as event handler. Here's a basic example with no purpose other than demonstrating the functionality:

	function init()
	{
		addEventHandler(SFSEventType.USER_JOIN_ZONE, onNewUser);
		trace("Simple JS Example inited");
	}
	
	function destroy()
	{
		trace("Simple JS Example destroyed");
	}
	
	
	function onNewUser(event) 
	{
		user = event.getParameter(SFSEventParam.USER);
		trace("Welcome new user: " + user.getName());
	}

Each event provides a number of parameters that can be accessed as shown in the code. For a full list of events, check the documentation here; also follow the link to the Javadoc to get the list of parameters provided by each event.

» Accessing the API

To assist you with the development every JavaScript Extension provides a number of methods and API objects directly in the global scope.

A few of the most important are:

Member Description
addRequestHandler() Allows to register a new custom client request
addEventHandler() Allows to register a new custom server event
getParentZone() Get a reference to the parent Zone
getParentRoom() Get a reference to the parent Room (only if the Extension is attached to a Room)
getCurrentFolder()

Returns the current Extension folder

 

SFSObject A key/value, dictionary-like object used to send and receive data between client and server
SFSArray An indexed, array-like object used to send and receive data between client and server

Also the global scope provides access to a rich set of API, categorized in several groups:

FunctionDescription
getApi() Provides all main calls to create Rooms, set User/Room variables, join users, chat messages, banning/kicking etc...
getBuddyApi() Buddy related functionalities: add/remove/create etc...
getGameApi() Match making, Invitations, Quick join, public/private games etc...
getMMOApi() Provide all features related to MMORooms
getFileApi() Wide array of file system functions (move, copy, delete, create etc...)

For example joining a user in a Room requires a few lines of code:

	function joinUserInRoom(user, room)
	{
		getApi().joinRoom(user, room, "", false);
	}

You can find all the details about the global Extension objects in the JSDoc.

» Extension deployment

Finally we can take a look at how we can deploy our code to the server and run it. To create an extension you will need to create a new folder under SFS2X/extensions/

Inside this directory we'll add the JavaScript files for our extension. Next we open the AdminTool, select the Zone Configurator and click the Zone Extension tab.

First make sure the Type dropdown is set to JavaScript. then from the top dropdown select the Extension folder and finally the "Main class" which in this case is our main JavaScript file. We can now save and restart the server to activate the changes.

The Reload mode option set to AUTO allows to auto-reload an Extension every time a change is made to the main file, thus making it very quick to reload our code on the fly. This is convenient for development and testing but in production it is best to set this to MANUAL to avoid accidental reloads.

NOTE: if you want to attach the Extension to a specific Room instead of a Zone you can repeat the same exact process, this time selecting the target Room from the Zone Configurator.

» Next steps

Once you have familiarized with these concepts we highly recommend to move on to the next article in this section which will give you a more in-depth outlook on Extension development using JavaScript.