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

 

» Java/Android Client API Recipes

Now that we have learned the basics of creating a project with the Java client API, we can take a look at a series of recipes to help us build more sophisticated games and apps.

The examples that follow below are equally valid for plain Java (J2SE, J2EE) and Android based applications and you can easily adapt them to other JVM-supported languages such as Kotlin, Scala, Groovy etc., if you so desire.

» The request format

Before we 'get cooking' let's take a look at the basic format of client side requests. From the initial examples you might have noticed that both the login request and join request look very similar in the way they are created and handled.

With a few exceptions, most of the client-side requests work with the same pattern:

Sending a request from client side always entails an asynchronous operation where we first send a request object to the server, such as LoginRequest or JoinRoomRequest (as seen in the connector examples), and later we handle the response via a dedicated event listener.

Inside the event listener we can extract a number of parameters from the event variable, that are documented in the SFSEvent class, from the Java API documentation.

» Multiple ways of handling events

In Java there are multiple ways to pass an handler to the SmartFox.addEventListener() method. To avoid potential confusion let's review them briefly here.

For starters, this is the complete method signature we are working with:

		addEventListener(SFSEvent event, IEventListener listener);

The IEventListener interface is part of the API and exposes a single method:

		@FunctionalInterface
		public interface IEventListener 
		{
			void dispatch(BaseEvent evt) throws SFSException;
		}

The traditional Java approach would be to pass an object implementing the interface or an inline anonymous class, also implementing IEventListener.

		client.addEventListener(SFSEvent.CONNECTION, new IEventListener() 
		{
			public void dispatch(BaseEvent evt) throws SFSException 
			{
				boolean success = (boolean) evt.getArguments().get("success"));

				if (success)
				{
					// Success logic here...
				}
				else
				{
					// Failure logic here...
				}
			}
		});

Prior to Java 8 this was essentially the only way to work with event listeners, apart from creating a separate class and passing it to the method, instead of inlining it.

You can notice however that IEventListener is marked with a @FunctionalInterface annotation, which was introduced in Java 8 as part of the functional-style paradigm.

These days the recommended approach is to pass a method reference via the double colon operator, as shown in the previous examples and tutorials. In alternative, for short handlers, you can also use a lambda such as below:

		client.addEventListener(SFSEvent.CONNECTION, (BaseEvent evt) -> {

			boolean success = (boolean) evt.getArguments().get("success"));
			
			// More logic here...
		});

» Recipes

» Creating a Room

Rooms have lots of options that can be configured. The API provide a specific Room configuration object called RoomSettings, where we can set every facet of the Room we are about to build:

		private void createRoom() 
		{
			client.addEventListener(SFSEvent.ROOM_ADD, (BaseEvent evt) -> {
					System.out.println("Room created: " + evt.getArguments().get("room"));
			});
			
			client.addEventListener(SFSEvent.ROOM_CREATION_ERROR, (BaseEvent evt) -> { 
			{
					System.out.println("Room creation failed: " + evt.getArguments().get("errorMessage"));
			});
			
			// Create a new chat Room
			RoomSettings settings = new RoomSettings("My Chat Room");
			settings.setMaxUsers(40);
			settings.setGroupId("chat");
			
			client.send(new CreateRoomRequest(settings));
		}	

Learn more from the API documentation: Create Rooms, RoomSettings

» Sending and receiving chat messages

Once the client has joined a Room we can start sending and receiving both private and public chat messages.

		private void publicMessage() 
		{
			client.addEventListener(SFSEvent.PUBLIC_MESSAGE, (BaseEvent evt) -> {
				
					// Messages are forwarded to its sender so we need to check
					// if this is our own message
					User sender = (User)evt.getArguments().get("sender");
					
					if (sender == sfs.getMySelf())
						System.out.println("I said:" + evt.getArguments().get("message"));
					else
						System.out.println("User " + sender.getName() + " said:" + evt.getArguments().get("message"));
			});
		
			// Send a public message
			client.send(new PublicMessageRequest("Hello everyone!"));
		}

Learn more from the API documentation: Public chat messages, Private chat messages

» Setting User Variables

UserVariables are useful to store custom properties for each user/player. These can be used to track game state, player progression, inventories and more.

		private void userVariables() 
		{
			client.addEventListener(SFSEvent.USER_VARIABLES_UPDATE, (BaseEvent evt) -> {

				List changedVars = (List) evt.getArguments().get("changedVars");
				User user = (User) evt.getArguments().get("user");
			
				// Check if the user changed his x and y user variables
				if (changedVars.indexOf("x") != -1 || changedVars.indexOf("y") != -1)
				{
					// Move the user avatar to a new position
					...
				}
			});
			
			// Create some User Variables
			List userVars = new ArrayList();
			userVars.add(new SFSUserVariable("avatarType", "SwedishCook"));
			userVars.add(new SFSUserVariable("country", "Sweden"));
			userVars.add(new SFSUserVariable("x", 10));
			userVars.add(new SFSUserVariable("y", 5));
			
			client.send(new SetUserVariablesRequest(userVars));
		}

Learn more from the API documentation: Setting UserVariables.

» Sending and receiving Extension messages

Extension Requests are custom messages that you can send to your own server side code. This way the client can communicate with the game's server side logic and receive updates.

If you are not familiar with Extensions and how they work, we highly recommend to read this introductory article.

The following example shows how to send a couple of custom parameters to the server side Extension described in the article above, which takes two integers and returns their sum.

		void sendExtData()
		{
			client.addEventListener(SFSEvent.EXTENSION_RESPONSE, (BaseEvent evt) -> {
				
				String cmd = (String) evt.getArguments().get("cmd");
				ISFSObject params = (ISFSObject) evt.getArguments().get("params");

				if (cmd.equals("sum"))
				{
					int result = params.getInt("res");
					System.out.println("Result of sum is: " + result);
				}
			});

			var params = new SFS2X.SFSObject();
    		params.putInt("n1", 100);
    		params.putInt("n2", 50);
 
    		sfs.send( new SFS2X.ExtensionRequest("sum", params) );
		}

The cmd parameter contains the name of the Extension response. First, we need to check that it matches the request name ("sum") and then we can extract the parameters expected from server-side. In this case it is an integer with name "res".

Learn more from the API documentation: ExtensionRequest.

» Initializing and using the UDP protocol

SmartFoxServer 2X uses mainly TCP as the transport protocol as it guarantees packet ordering and delivery. However UDP is also supported and can be used with custom Extensions.

Before we can send UDP packets we need to send an InitUDP request from client-side:

		private void startUDP() 
		{
			client.addEventListener(SFSEvent.UDP_INIT, (BaseEvent evt) -> {
				
				boolean success = (boolean) evt.getArguments().get("success");

				if (success)
				{
					// Connection successful: execute an Extension call via UDP
					sfs.send( new ExtensionRequest("udpTest", new SFSObject(), null, true) );
				)

				else
				{
					System.out.println("UDP initialization failed!");
				}
			});
			
			client.initUdp();
		}

When we call the initUdp() method the client will attempt to reach the server via UDP and let us know if it was successful. In case of a failure there might be a firewall on the client side preventing the communication.

Sending an Extension request via UDP rather than TCP is done by specifying and extra flag in the ExtensionRequest(...) constructor called useUDP which, by default, is set to false.

Learn more from the API documentation: Initialize UDP from client side.