• Examples (iOS)
• Examples (Java/Android)
• Examples (C++)
Server API Documentation

 

» BattleFarm

» Overview

BattleFarm is a fast-paced realtime multiplayer action game made with Adobe Flash. The game was created in 2008 as a case study to highlight the productivity and performance features of SmartFoxServer PRO and its accompanying tools: SmartFoxBits and the BlueBox. The ActionScript 2 source code of the game was never released... until today, after we decided to port it to AS3 and of course SmartFoxServer 2X.

Just like in 2008, this complete game example is aimed at showing the capabilities of SmartFoxServer 2X when creating realtime multiplayer games, the stunning performance of its BlueBox 2X module and the flexibility of the SmartFoxBits 2X components in creating user interfaces.

In BattleFarm two players compete with each other and against the time to collect the fruits found in the game maze, and using bombs to hit the opponents in order to make them loose what they have collected.
When the timer goes to zero, an exit door (in the form of a pit) will randomly appear on the map, and the player with the highest number of collected items will be able to go out and win the round. In this last phase the other player can try to block the opponent or make him loose his fruits so that he can't leave the map.

>> DOWNLOAD the source files <<

IMPORTANT
The BattleFarm game and tutorial are distributed for educational purposes only. You must retain all the copyright notices appearing in the user interface, source code and any other accompanying file.

Please read the license file contained in the game folder for more informations.
Contact us if you are interested in buying a commercial license which removes the above restrictions.

» Getting started

» Running the game

In order to run the game follow these steps:

  1. copy the battleFarm folder from the /deploy/extensions folder to your SFS2X installation folder, under /SFS2X/extensions;
  2. copy the BattleFarm.zone.xml file (containing the Zone configuration) from the /deploy/zones folder to your SFS2X installation folder, under /SFS2X/zones;
  3. start SmartFoxServer 2X;
  4. make sure the client will connect to the right IP address by editing the <ip> entry in the xml files available under the /deploy/client/config folder;
  5. open the /deploy/client/index.html file in a browser.

» Source code setup

The client-side assets are contained in the /source/client folder and they don't require a specific setup: simply open the .fla file in Adobe Flash. All the classes linked by the main document and game assets can be found under the /source/client/code folder.

BattleFarm features a server-side Extension, as described further on. In order to access its code, create and setup a new project in your Java IDE of choice as described in the Writing a basic Extension document. Copy the content of the /source/server/src folder to your project's source folder and link the libraries contained in the /source/server/lib folder.

» Game architecture

The architecture of the game is pretty simple: there is one main lobby Room defined statically in the BattleFarm Zone configuration. Each users is initially connected to the lobby and he can choose to join a game or create a new one. Each game is a separate Room.
A server-side Java Extension is responsible for handling the game logic and coordinating the players' events using the highly efficient binary protocol of SmartFoxServer 2X.

The Lobby system was built in just a few hours with the SmartFoxBits components, which allowed to quickly arrange the typical lobby elements such as the Rooms list, users list, public and private chat boxes, etc. Almost all user interaction in the lobby is handled by the SmartFoxBits, which don't require additional coding except for the more advanced skinning and styling requirements. The SmartFoxBits provide a simple and effective way to skin the visual components so that they perfectly integrate with the rest of the GUI design.

In particular, each component is responsible of the following tasks:

From the code organization point of view, on the client-side the main document class takes care of instantiating the different "scenes" which compose the application: login, chat (the lobby) and the actual game.
On the server-side the standard approach to Extension development is used, together with some good OOP practice.

» Features highlights

» BlueBox

SmartFoxServer 2X features the BlueBox 2X, a module specifically designed to allow connections behind firewall and proxies through and advanced HTTP tunneling technique. If a direct socket connection is not available, the client is transparently redirected to the BlueBox.

The BlueBox enables players under restricted network configurations to play and enjoy fast multiplayer applications and games with little to no noticeable performance loss. The great news is that it is not necessary to write different code depending on the type of connection: SmartFoxServer and its client API handle the "tunneled" connections behind the scenes, making them appear, from outside, just like standard socket connections.

The main advantage of the BlueBox compared to other tunneling solutions is that you can fine tune it to the requirements of your application enabling very good performance even with real time games, like BattleFarm itself proves.

In BattleFarm, in order to be able to force the BlueBox connection for demonstration purposes, two separate client configuration files have been created (see the /deploy/client/config folder in the game package): in the sfs-config_http.xml file the <port> parameter has been set to 0000, which causes the switch to the tunneling solution. The file to be loaded is determined by an external variable passed to the game's SWF file by the container html page (see the client code snippets below).

After a connection to the server is established, the Connector's led in the bottom right corner of the game shows the connection type:

» Server-side Extension

While most of the lobby features are automatically managed by SmartFoxServer and SmartFoxBits, the game logic must be custom developed using a server-side Extension plugged at Zone level, which allows to control the status of all the game Rooms running in the application.

During a match, each client sends the following requests to the Extension:

The followings are the commands or responses sent by the Extension to the clients:

In BattleFarm all the communication during the game action is performed using the efficient SFS2X binary protocol to pack the messages, which allows sending small messages very fast and with optimal bandwidth usage. The default TCP network protocol is used to transfer the messages from the client to the server and viceversa.

» Player synchronization

In order to keep a perfect synchronization between players we only need to send a minimal amount of updates and use a mix of client-side and server-side validation to achieve the best user experience.

Instead of validating each player movement on the server-side before actually animating the characters, we perform this check on the client side and notify the server about it. This way the game always feels smooth and highly responsive, regardless of the network lag.

Collision checking with the collectible items is done on the server-side instead, because we need to make sure that only one player grabs an item from the map in case both of them step on the same item at the same time.

The bomb explosion is controlled on the server-side: when a user drops a bomb the client notifies the server which in turn starts a countdown to the explosion. When the timer expires all clients are informed that the explosion should take place and the animation is performed on the client.

One of the most important aspects of the client-side of the game is using time-based animations, which ensure consistent execution of all game animations regardless of the computer CPU speed, memory, video card, etc.
In fact one of the difficulties of client synchronization is represented by the potential differences in rendering performance of the Flash Player. In other words a player running an old computer with a few hundreds of megabytes of RAM would experience slower animation rendering than another player running a shiny new multi-core machine with several gigabytes of RAM.
By ensuring that each animation takes the exact same time on any machine we eliminate the risk of additional client lag and we just need to deal with the possible delays caused by the network lag.

» Handling slower connections

In order to keep up with possible connection slow downs the client maintains a queue of moves sent by the server and executes them serially by grabbing the first item from the queue and processing it before moving on to next one.

If the client connection becomes congested for a while, many of the incoming messages aren't received for some time until the network becomes available again: at that point all the messages suddenly arrive and populate the client queue.
If those messages are too many, the client automatically skips them in order to keep up with the latest character positions, and the player will probably notice a certain amount of frame-skip in the opponent animations.

The following diagram shows a "regular" client queue state. As new messages are enqueued in the client they are processed and rendered:

In case the client can't keep up with the server message speed rate (e.g. after a network congestion) older messages are discarded to keep up with the latest game state:

This technique is very effective to avoid breaking the game synchronization even in case of clients with problematic connections.

» Client code highlights

As soon as the LoginScene is initialized, the loadConfig method is called and all the required listeners are added to the SmartFox client API, whose reference is stored in the main document class (which in turn got it from the Connector's instance). The method also takes care of loading the proper client configuration file depending on an external variable passed to the SWF by the html page.

	private function loadConfig():void
	{
		// activate smartfox listeners 
		refDocument.smartFox.addEventListener(SFSEvent.CONFIG_LOAD_FAILURE, onConfigLoadFailure);
		refDocument.smartFox.addEventListener(SFSEvent.CONFIG_LOAD_SUCCESS, onConfigLoadSuccess);
		refDocument.smartFox.addEventListener(SFSEvent.CONNECTION, onConnection);
		refDocument.smartFox.addEventListener(SFSEvent.CONNECTION_LOST, onConnectionLost);
		refDocument.smartFox.addEventListener(SFSEvent.LOGIN, onLogin);
		
		// load smartfox XML config file
		// a separate config file is loaded in case we need to force the BlueBox usage
		// (a parameter passed by the html page to the swf file url forces this)
		if (root.loaderInfo.parameters.conn == "http")
			refDocument.smartFox.loadConfig("config/sfs-config_http.xml", true);
		else
			refDocument.smartFox.loadConfig("config/sfs-config_socket.xml", true);
	}

The onLogin handler moves the game to the ChatScene, where the player joins the initial lobby Room. In this scene the Create button allows users to create new matches based on the selected map and title. During the Room creation process, a Room Variable is also set to save the id of the selected map.

	private function createNewGame(evt:MouseEvent):void
	{
		if (name_ti.text != "")
		{				
			var roomFound:Boolean = false;
			
			for (var i:int = 0; i < refDocument.smartFox.roomList.length; i++)
			{
				if ((refDocument.smartFox.roomList[i] as Room).name == name_ti.text)
				{
					roomFound = true;
					break;
				}
			}
			
			if (!roomFound) // prevent create room errors
			{
				// create room variable
				var roomVar:SFSRoomVariable = new SFSRoomVariable("map", refDocument.availableMaps[refDocument.selectedMapIndex].id);
				roomVar.isPrivate = false;
				roomVar.isPersistent = true;
				
				// add room variable to an array
				var roomVars:Array = new Array();
				roomVars.push(roomVar);
			
				// create room settings
				var roomSettings:RoomSettings = new RoomSettings(name_ti.text);
				roomSettings.password = password_ti.text;
				roomSettings.maxUsers = 2;
				roomSettings.maxSpectators = 0;
				roomSettings.isGame = true;
				roomSettings.variables = roomVars;
				
				// create new game room with above parameters and join it
				refDocument.smartFox.send(new CreateRoomRequest(roomSettings, true, refDocument.smartFox.lastJoinedRoom));
			}
		}
	}

In the GameScene the onGameExtensionResponse listener takes care of handling all commands and responses sent by the server-side Extension, dispatching them to specific sub-handler methods.

	private function onGameExtensionResponse(evt:SFSEvent):void
	{
		var extParams:SFSObject = evt.params.params;
		
		switch (evt.params.cmd)
		{
			case "map":
				initGameMap(extParams);
				break;
							
			case "go":
				startGame();
				break;
			
			case "mv":
				handleOpponentMove(extParams.getInt("px"), extParams.getInt("py"));
				break;
							
			case "bb":
				handleOpponentBomb(extParams.getInt("bId"), extParams.getInt("bx"), extParams.getInt("by"));
				break;
	
			case "gi":
				handlePickItem(extParams.getInt("px"), extParams.getInt("py"), extParams.getInt("who"), extParams.getInt("sc"));
				break;
	
			case "xp":
				handleExplosion(extParams.getInt("bb"), extParams.getInt("uid"), extParams.getInt("bx"), extParams.getInt("by"), extParams.getInt("kp1"), extParams.getUtfString("it1"), extParams.getInt("kp2"), extParams.getUtfString("it2"), extParams.getInt("sc1"), extParams.getInt("sc2"));
				break;
			
			case "od":
				handleOpenDoor(extParams.getInt("px"), extParams.getInt("py"));
				break;
			
			case "win":
				handleWinner(extParams.getInt("id"));
				break;
			
			case "stop":
				handleStopGame();
				break;
	
			default:
				break;
		}
	}

NOTE
This example makes use of some of the SmartFoxBits user interface components. Please read the corresponding paragraph in the introduction to the ActionScript 3 examples.

» Server code highlights

Following the recommended approach in Extensions development, on the server-side each request sent by the clients is handled by a dedicated handler. Also, a list of games in progress is maintained (to save the status of each game) and a global game controller takes care of all the time-based events in all games (bombs explosions, exit pit appearance).

	@Override
	public void init()
	{
		// load maps information
		gameMapsInfoBean = GameMapBsn.loadMaps(this);
		
		// initialize games list
		games = new ConcurrentHashMap<Integer,GameBean>();
		
		// initialize game controller
		gameController = new GameController(this);
		gameController.start();
		
		// register request handlers
		
		// get map list 
		addRequestHandler(Commands.CMD_MAP_LIST, MapListHandler.class);
		
		// ready to start a game
		addRequestHandler(Commands.CMD_READY, ReadyHandler.class);
		
		// restart game
		addRequestHandler(Commands.CMD_RESTART, RestartHandler.class);
		
		// player movements
		addRequestHandler(Commands.MV, MovementHandler.class);
		
		// handle bombs requests
		addRequestHandler(Commands.BOMB, BombHandler.class);
		
		// register event handlers
		
		...
	}

» More resources

You can learn more about the mentioned features by consulting the following resources: