SFS2X Docs / AdvancedTopics / game-api
» The Game API
SmartFoxServer 2X provides a new set of client and server API specifically designed for game creation and management, including public and private games, game invitations, User and Room matching and lots more. The new Game API are based on three fundamental building blocks:
- Match Expressions: they allow to create search criteria in a very natural way which can then be passed to the MatchingEngine to perform any type of queries on Rooms and Users
- Invitations: this is a generic Invitation system that allows you to manage multiple invitations to private games, but which can also be employed in many other activities
- SFSGame class: the SFSGame is new type of Room specifically designed for managing games and providing unique services for advanced game management
These three components are highly versatile and can be either used singularly for a specific task or orchestrated together to create advanced server behaviors, as you will learn in a few seconds.
» Overview
The SmartFoxServer class exposes a getAPIManager() method which in turn allows you to access all the different API subsets in the SFS2X framework. The APIManager provides:
- getSFSApi(): returns a reference to the basic SFS2X API
- getBuddyApi(): returns a reference to the Buddy List API subset
- getGameApi(): returns a reference to the Game API subset
We will now concentrate on the Game API.
» Match Expressions
Match Expressions are built like "if" conditions in any common programming language: they work like queries in a database. Here's a server-side example to get you started, but this is similar on the client side:
MatchExpression exp = new MatchExpression('rank', NumberMatch.GREATER_THAN, 5).and('country', StringMatch.EQUALS, 'Italy');
Expressions are made of three elements:
- Variable name
- Match operator
- Value
Additionally any number of expressions can be linked together with a logical AND / OR operator, just like in regular code. In the above example we have created an expression that will check for a rank value > 5 and a country value == "Italy".
But... where is this set of conditions applied to? Normally expressions are used to match Users (via their User Variables) and Rooms (via their Room Variables). The standard SFS2X API provide two useful methods called findUsers() and findRooms() where you can execute any Match Expression and obtain the filtered set of Users/Rooms.
A small example will clarify this better:
List<User> matchingUsers = sfsApi.findUsers(zone.getUserList(), exp, 50);
The first argument provides a list of User objects where to search, in this case all Users inside the current Zone. The second parameter is the Match Expression we just created at the beginning of this section, and the last number (50) is an optional limit for the amount of returned elements. In this case we want the search for no more than 50 matching elements (if we passed 0 the search would return all Users that match).
The search options are not just limited to User/Room Variables name. In fact the Matching engine provides two extra classes, RoomProperties and UserProperties, where you can access many specific attributes of the Room and User classes.
This is an example of matching specific Room properties and Variables:
// Prepare match expression MatchExpression exp = new MatchExpression(RoomProperties.IS_GAME, BoolMatch.EQUALS, true) .and(RoomProperties.HAS_FREE_PLAYER_SLOTS, BoolMatch.EQUALS, true) .and("isGameStarted", BoolMatch.EQUALS, false); // Search Rooms List<Rooms> joinableRooms = sfsApi.findRooms(zone.getRoomListFromGroup("chess"), exp, 0);
The above code will match all game Rooms that have at least one free player slot and where the isStarted variable is set to false. Additionally the search will be performed only in the Room Group called "chess".
» Advanced features
The Match Expressions offer advanced capabilities for searching through nested data structures such as SFSObject and SFSArray. This is done via a very simple dot-syntax expressions. Here's an example of how it works:
MatchExpression exp = new MatchExpression("europe.italy.capital", StringMatch.EQUALS, "Rome");
The above example goes down deep into an SFSObject called europe, taking the italy object (another SFSObject) and finally reading its String field capital and matching it with another String. Here is one more examples using SFSObject and SFSArray:
MatchExpression exp = new MatchExpression("europe.italy.majorCities.3.name", StringMatch.EQUALS, "Milan");
From the italy object we obtain a majorCities SFSArray and we grab the third item in it (the .3 expression means 'give me the element at index == 3'). The item is again an SFSObject whose name property we finally compare to a String.
The power of Match Expression doesn't end here. You can run multiple passes of matching if you need complex searches to be performed. For example you can run a first match and obtain a list of filtered Rooms and then use it to apply another expression to further refine your search, and so on and so forth.
Also you will learn more interesting usages of Match Expressions in conjunction with the SFSGame class later in this very article.
For more details on Match Expression we recommend to consult the javadoc, under the com.smartfoxserver.v2.entities.match package.
» Invitations
The invitation system included in SmartFoxServer 2X provides a generic framework for sending invitations to one ore more connected users and easily manage their responses and the expiry of the invitation itself. Invitations can be used to challenge players in a game, invite buddies to the user's place, ask permissions for specific tasks such as adding the invited user in the Buddy List, etc.
Creating an invitation is very simple, as four basic parameters are needed:
- Inviter: the User starting the invitation
- Invitee: the invited User/Player/Buddy
- Expiry time: the amount of seconds allowed for the invitee to reply
- Custom parameters: a generic SFSObject contatining specific invitation parameters (a message, a picture, game details, etc)
The invited User will receive the invitation and will be able to reply within the specified amount of time. A simple ACCEPT or REFUSE code is all it takes to reply plus an optional SFSObject with response parameters, if needed. In case no response is sent to the server within the expected number of seconds, the invitation will be considered as "refused".
public class GameInvitation extends SFSExtension { private ISFSGameApi gameAPI; @Override public void init() { gameAPI = SmartFoxServer.getInstance().getAPIManager().getGameApi(); } public void sendInvitation() { // Prepare Invitation object User inviter = getParentZone().getUserByName("Fozzie"); User invitee = getParentZone().getUserByName("Gonzo"); // We set the timeout for a reply to 50 seconds Invitation invitation = new SFSInvitation(inviter, invitee, 50); // Send the invitation gameAPI.sendInvitation(invitation, new InvitationCallback() { @Override public void onRefused(Invitation invObj, ISFSObject params) { // Handle the refused invitation... } @Override public void onExpired(Invitation invObj) { // Handle the expired invitation... } @Override public void onAccepted(Invitation invObj, ISFSObject params) { // Handle the accepted invitation... } }); } }
For more details about Invitations check the javadoc, specifically the SFSGameAPI class and the com.smartfoxserver.v2.entities.invitation package. Additional informations are also available in the client side API documentation.
» SFSGame
The SFSGame class extends the normal capabilities of a Room, adding the ability to set the Game as public or private and providing a list of invited people that the system will invite in the game. Additionally the system will be able to invite more people if the number of players is not sufficient to start the game.
Each game can be configured to match specific types of Users by providing a Match Expression. The expression contains criteria that are checked against each User that wants to join the game and provides a mean for filtering players.
Let's see an example: user Kermit has the following two User Variables set:
- Rank: 10
- BestScore: 2500
He wants to play, so he chooses a public SFSGame and attempts to join it. Unfortunately the SFSGame expression is set as follows: (Rank > 10) OR (BestScore > 3000).
Any attempt to join the Game will be refused because the player doesn't match the SFSGame criteria.
Creating a Room of type SFSGame is similar to creating a normal Room. On the client side you will find the CreateRoomRequest class and the CreateSFSGameRequest class, both taking a settings object: specifically RoomSettings and SFSGameSettings.
The following is a quick overview of the additional parameters that an SFSGame can take, compared to a regular Room:
- isGamePublic: a public game can be joined by any player whose variables match the SFSGame player Match Expression. If no expression is used the game will be joinable by any User. Private games, instead, are based on invitations provided by the SFSGame creator (see invitedPlayers property below) so they usually don't need to specify a Match Expression.
- minPlayersToStartGame: the minimum number of players to start the game. If the game is already running and the number of players goes below this limit the game will be stopped (see the notifyGameStartedViaRoomVariable property below).
- invitedPlayers: (private games only) a list of players invited in the SFSGame. Each player will receive an invitation event and will be able to reply within the amount of time (see invitationExpiryTime property below).
- searchableRooms: (private games only) a list of Rooms where the Game API can search for more players to invite. The API will look for more players if the number of people invited is smaller than the value set in the minPlayersToStartGame property. This way you can add your friends to the game and let the system find more players to start it. This mechanism will work only when the game is started for the first time, not every time the number of users goes below the minimum value.
If you need to search and invite more players every time the game stops you can easily do it via the SFSApi.findUsers() method. - leaveLastJoinedRoom: auto-remove players from their previous Room after successful join in case they received an invitation and accepted it.
- playerMatchExpression: an expression to match players willing to play the game; by default no expression is used.
- spectatorMatchExpression: an expression to match spectators willing to watch the game; by default no expression is used.
- invitationExpiryTime: the amount of time allowed for invited players to accept / refuse.
- invitationParams: optional custom parameters to be sent with the invitation. These could provide details about the inviter, the game, an invitation message, etc.
- notifyGameStartedViaRoomVariable: automatically update a reserved Room Variable to signal that the game is started/stopped. The Room Variable uses the global setting to be broadcast outside of the Room. This can be used on the client side to show the game state in a list of available games.
The reserved Room Variables are found in the com.smartfoxserver.v2.entities.variables.ReservedRoomVariables class.
Finally it is important to note that the SFSGame class extends SFSRoom and can be treated as any other Room in the system. SFSGame objects can be used and passed around to any method or function that works with the base Room interface (this is valid for both server and client side).
» Quick game join
Another feature offered by the Game API (both client and server side) is the QuickJoinGame request. By providing a Match Expression and a list of Rooms (of type SFSGame) or a Room Group the system can search for matching Rooms and immediately teleport the player in the game action.
As usual an example will clarify the concept:
// Prepare a match expression MatchExpression expr = new MatchExpression("rank", NumberMatch.GREATER_THAN, 3).And("rank", NumberMatch.LESS_THAN, 8); // Provide an array of Room Groups where we want the search to take place List<string> whereToSearch = new List<string>(){"poker", "blackJack"}; // Fire the request and jump into the game! sfs.Send(new Sfs2x.Requests.Game.QuickJoinGameRequest(expr, whereToSearch));
// Prepare a match expression var expr = new SFS2X.MatchExpression("rank", SFS2X.NumberMatch.GREATER_THAN, 3).and("rank", SFS2X.NumberMatch.LESS_THAN, 8); // Provide an array of Room Groups where we want the search to take place var whereToSearch = ["poker", "blackJack"]; // Fire the request and jump into the game! sfs.send(new SFS2X.QuickJoinGameRequest(expr, whereToSearch));
// Prepare a match expression var expr:MatchExpression = new MatchExpression("rank", NumberMatch.GREATER_THAN, 3).and("rank", NumberMatch.LESS_THAN, 8); // Provide an array of Room Groups where we want the search to take place var whereToSearch:Array = ["poker", "blackJack"]; // Fire the request and jump into the game! sfs.send(new QuickJoinGameRequest(expr, whereToSearch));
The above client code attempts to quickly join the user in any game from the poker or blackJack Room Groups where the game rank variable is greater than 3 and less than 8. Behind the scenes the system will also make sure that the Rooms are of type SFSGame and that there is at least one player slot available. If no Room matching these criteria is found a join error will be fired back at the user.
The QuickJoinGame feature works exclusively with Rooms of type SFSGame, which support a Match Expression; all other Room types will be ignored.