The MMO API
The MMO API allows Users and game objects in a Room to interact based on their proximity in virtual space. At the core of this API is the MMORoom, a specialized type of Room that uses an Area of Interest (AoI) to determine which events and updates should be sent to each User.
Overview
The MMORoom extends the functionality of a regular Room by adding an Area of Interest (AoI in short) to determine the spatial range of the events that will be received by users. The AoI parameter represents the area within which users will affect each other, for example when sending public messages, updating User Variables, etc.
By default the MMORoom does not fire the regular USER_ENTER_ROOM or USER_EXIT_ROOM client-side events when users enter or leave the room. Instead the Room's user list is updated via the PROXIMITY_LIST_UPDATE event which provides a delta of the current user list, within the AoI.
In other words the proximity list replaces the regular user list on the client side, optimizing the number of updates that the user receives. On the server side the full user list of the Room is still accessible in its entirety.
Intended use
As suggested by the class name, MMORoom objects are useful to create very large, virtually unlimited areas that can contain thousands of players without overloading clients with updates. The MMORoom can be configured to throttle the PROXIMITY_LIST_UPDATE events in order to optimize the network traffic.
Joining the MMORoom and setting the User position
In contrast to a regular Room, the MMORoom needs to know where each user is located in the 2D or 3D space. The coordinates system is abstract and generic, allowing developers to use any unit of measure (pixels, inches, meters, miles, etc) defined by either 32 bit integers or floating point values.
When a user joins the MMORoom his position in the world is still undefined and therefore he will be in a state of limbo until the first SetUserPosition request is sent. In order to avoid users spending too much time in this invisible state each MMORoom can be configured to allow a time out value after which the users will be removed from the Room (NOTE: the time out applies only for users that haven't set their initial position in the room).
As mentioned in the overview, there are no USER_ENTER/EXIT_ROOM events fired to other users as in regular Rooms. The way in which players are updated about other user changes in their proximity (in other words users entering or leaving the AoI) is via the client PROXIMITY_LIST_UPDATE event. All other Room-related events will work like in regular Rooms, including the USER_COUNT_CHANGE which keeps users in the same Room Group updated about the total number of clients in each Room.
Let's take a look at a visual example:

The green area around our player (marked as "ME") represents its AoI: all of the users falling within that area will be able to see our player an exchange messages/events with him. Also, all the players inside the AoI represent the current Room's user list from our client's perspective. In this specific case we will see just one other player, Piggy, where "see" means receiving events related to that player and being able to interact with it.
As expected during the game the players position will change: each client must keep sending the SetUserPosition request on a regular basis in order to make the system aware of their new positions. The rate at which the position should be set can vary a lot, depending on the type of game. Check the tutorials linked at the bottom of the page for a more in-depth discussion on this subject.
Let's suppose that on the next move user Fozzie is entering the AoI and user Piggy is leaving it: the next PROXIMITY_LIST_UPDATE event will reflect this new situation. In particular the event provides two lists containing all the users that have entered the AoI and all those who have left it since the last update. From a client side API perspective this is how the PROXIMITY_LIST_UPDATE event is handled:
private void OnProximityListUpdate(BaseEvent evt)
{
var added = (List<User>) evt.params["addedUsers"];
var removed = (List<User>) evt.params["removedUsers"];
// Add Users that have entered the proximity list
foreach (User user in added)
{
// Obtain the coordinates at which the User "appeared" in our range
Vec3D entryPoint = user.AoiEntryPoint;
// Add new avatar in the scene (PSEUDO CODE)
AvatarSprite avatarSprite = new AvatarSprite();
avatarSprite.x = entryPoint.px;
avatarSprite.y = entryPoint.py;
...
}
// Remove Users that have left the proximity list
foreach (User user in removed)
{
// Remove the User avatar from the scene...
}
}
Here we show how to cycle through the addedUsers and removedUsers lists to take care of the rendering side of things.
The MMORoom coordinate system always works with a 3D system (X, Y, Z) that can be reduced to X and Y for 2D applications, by ignoring the Z component.
User Variables and Room Variables
UserVariables work normally in MMORooms, affecting only those Users who are within the AoI of the request sender. RoomVariables, instead, need to be used with parsimony to avoid generating heavy traffic towards all clients. Since RoomVariables contain data that interests all users in the Room, updating them often will generate large broadcast updates with the risk of overwhelming the clients. What is critical is the rate at which they are updated more than the number of variables used.
Map limits
The MMORoom object accepts a pair of Vec3D parameters representing the limits of the virtual map on the three axis (X, Y, Z). In a 2D and 2.5D application the developer can just use the X and Y coordinates leaving the Z value always set to zero. It is highly recommended to set these limits to restrain the user movement within the range of your virtual world. This way illegal movements can be detected on the server side and denied by the system.
Security Risks
NOTE Without setting map limits there's a potential risk that malicious users could exhaust the system memory by trying to fill very large number of spaces for extreme coordinate values.
Obtaining the entry point of other Users
It is usually helpful to know at which coordinates a user has entered the player's AoI. Typically the client code will need to know this position to render a sprite/avatar in the correct spot. By default the MMORoom always sends the entry position of each new User. If this bit of information is not necessary in your application it can be turned off to save extra traffic.
The usage of this feature is demonstrated in the previous code example, by reading the User.aoiEntryPosition value.
It is important to understand that, as the property name says, these are the coordinates of a user when he first entered the AoI of the player, not the current coordinates. In other words the SmartFoxServer API doesn't keep such coordinates in synch with the server automatically. It is the responsibility of the developer to find the best strategy to synchronize the users positions on the clients depending on the game type, the client-server lag, etc. Check the tutorials linked at the bottom of the page for a more in-depth discussion on this subject.
Creating an MMORoom
The MMORoom is created by sending a CreateRoomRequest from client side or its equivalent from server-side (see the SFSApi.createRoom method). The only difference is the settings object which must be of class MMORoomSettings.
Let's see the an example:
// Add room-related event listeners during the SmartFox instance setup
sfs.AddEventListener(SFSEvent.ROOM_JOIN, OnRoomJoin);
// Create the Room and autojoin it
MMORoomSettings cfg = new MMORoomSettings("New MMORoom");
cfg.defaultAOI = new Vec3D(30, 25, 12);
cfg.maxUsers = 5000;
cfg.maxSpectators = 0;
cfg.mapLimits = new MapLimits(new Vec3D(-3000, -500, -3000), new Vec3D(3000, 500, 3000));
cfg.userMaxLimboSeconds = 20;
sfs.Send(new CreateRoomRequest(cfg, true));
The MMORoomSettings class extends the regular RoomSettings class adding a few new parameters specific for the MMO API:
- defaultAOI: the default Area of Interest, expressed as a Vec3D object.
- mapLimits: the boundaries of the virtual world, defined by two Vec3D points (lower and upper bounds).
- userMaxLimboSeconds: the maximum amount of time (in seconds) that a user can remain in limbo state before being removed from the Room. The default is 50 seconds, but in the example above we have set it to 20 seconds.
Determining the correct entry point on the map
In a real game scenario it is usually required a bit of extra logic to find the right position to spawn the player: areas that are non-walkable and spots that are already occupied by other players can't be used, for example.
Typically it's best to implement the spawn logic on the server side, as we can control the whole map and everyones's position. There can be different approaches for spawnint the player, such as:
- reusing the previous player's position (e.g. the last location since they left last time)
- a random position within the same area the player was the last time
- a random spawn-point within a list of teleport areas available in the map
- randomly choose any free place
- etc.
There are a couple of ways to handle spawn points that we recommend:
-
Join from server side The client sends a request to the Extension to join an MMORoom, the server executes the join request, calculates the entry point and calls the SFSAPI.setUserPosition(...) method right away.
-
Join from client, set position from server The client sends a regular JoinRoomRequest and handles the relative response. The server Extension is also listening to the ROOM_JOIN event and when it triggers the entry-point logic is executed and the SFSAPI.setUserPosition(...) call is made. In both cases the client will receive the PROXIMITY_LIST_UPDATE event with the local user list.
Extensions and user's AoI
The MMO API not provide the ability of sending custom events and data to a specific set of users within a certain Area of Interest. This is done by using the getProximityList() method which returns the list of Users that are currently within the AoI of another User. Here's an example:
MMORoom mmoRoom = (MMORoom) getParentZone().getRoomByName("VirtualMMO");
User piggy = mmoRoom.getUserByName("Piggy");
List<User> recipients = mmoRoom.getProximityList(piggy);
// Make sure we have 1 or more recipients
if (recipients.size() > 0)
{
ISFSObject message = new SFSObject();
// ...populate the SFSObject with custom data...
// Send the object to the selected users
send("customMessage", message, recipients);
}
Here we get a reference to our MMORoom and to a specific user (Piggy). Next we obtain the proximity list for that user and if there are any recipients available we send them a custom message.
MMOItems and MMOItem Variables
The MMORoom also provides support for a new entity called MMOItem which represent a non-player entity inside the map. MMOItems can be used as bonuses, triggers, bullets, or any other non-player object that can be handled using the MMORoom's rules of visibility (AoI).
This means that whenever one or more MMOItem falls within the AoI of a Player, it is notified to via a PROXIMITY_LIST_UPDATE event. The following is the complete list of parameters provided by this event:
| Name | Type | Description |
|---|---|---|
| room | MMORoom | the target MMORoom |
| addedUsers | Array/List | the list of new visible players |
| removedUsers | Array/List | the list of removed players |
| addedItems | Array/List | the list of new visible MMOItems |
| removedItems | Array/List | the list of removed MMOItems |
Server-side only
MMOItems and MMOItem Variables can ONLY be defined and updated from the server side. Clients cannot create or modify them.
Creating MMOItems
Let's see how to create an MMOItem from a server Extension:
private void createMMOItem()
{
// Reference to the game's MMORoom
var targetRoom = getParentZone().getRoomByName("My MMO Room");
// Prepare a list of variables for the MMOItem
var variables = new LinkedList<MMOItemVariable>();
variables.add(new MMOItemVariable("type", "bonus"));
variables.add(new MMOItemVariable("points", 250));
variables.add(new MMOItemVariable("active", true));
// Create the MMOItem
MMOItem mmoItem = new MMOItem(variables);
// Deploy the MMOItem in the MMORoom's map
getMMOApi().setMMOItemPosition(mmoItem, new Vec3D(50, 40, 0), targetRoom);
}
In order to deploy a new MMOItem inside the Room we need three things:
- a reference MMORoom, which we obtain via the Extension's Zone.getRoomByName() method
- a list of variables that will add meaningful properties to the MMOItem itself; in this case we are defining a bonus object with a certain score value and a flag to know if it's active in the world or not;
- the MMOItem object itself which is created in conjuction with the list of variables.
We finally call the setMMOItemPosition(...) to deploy the item which will appear in the next PROXIMITY_LIST_UPDATE on the client side int the addedItems property of the event.
Extending the MMOItem class
Depending on the type of game that we are developing we may need to create hundreds of MMOItems to describe bonuses, triggers, bullets and similar non-player entities in the game.
For this purpose the MMOItem class can be extended to create new items with their own properties and behaviors. Here's an example:
public class LaserBeamItem extends MMOItem
{
public static final String TYPE_RED = "red";
public static final String TYPE_BLUE = "blue";
private String type;
private int strength;
public LaserBeamItem(String type)
{
super();
this.type = type;
if (type.equals(TYPE_RED))
this.strength = 100;
else if (type.equals(TYPE_BLUE))
this.strength = 200;
}
public String getType()
{
return type;
}
public int getStrength()
{
return strength;
}
}
Fine tuning the MMORoom performance
Update speed
You may wonder why the MMORoom doesn't send every proximity update in realtime. The answer is that doing so would likely saturate the bandwidth available to the user. To get around this limitation the MMORoom provides a parameter called proximityListUpdateMillis (default = 250 milliseconds) which aggregates all proximity updates in a certain timeframe and sends them to the client in one batch.
Configuration
The proximityListUpdateMillis setting can be configured from the AdminTool -> Zone Configurator -> Rooms -> MMO Rooms Settings.
The value of this setting is strictly dependent on the type of game/application. Point-and-click MMOs typically don't require very frequent updates and can work with values ranging between 200 and 500 milliseconds. On the other hand real-time action games and simulations will probably need more responsive updates.
Our recommendation is to start experimenting with a low value (e.g. 50ms) and gradually increase it until you notice visual artifacts or delays that could deteriorate the user experience.
Optimizing update sizes
Each proximity update will carry the User Variables of each User entering the AoI. The more variables you use and the longer their names and values are, the heavier each update message will be. Example:
In order to optimize bandwidth usage it is recommended to keep variable names and values as short as possible. Here's the a more optimized version of the snippet above:
Rendering area vs AoI
Especially in 2D and 2.5D projects it is important to choose the right AoI size in relation to the viewport. As discussed in the Overview section, the AoI represents the range of user interactions, while the viewport is the actual visible area in the client application.
A good rule of thumb is to set the AoI slightly larger than the viewport. This will allow new Users/MMOItems to be loaded a little before they actually appear on screen. Additionally you can use the extra space to create a smooth fade-in/fade-out effect for when avatars enter or exit the rendering area.

In this screenshot we show the difference between the rendering area and the AoI. Notice how the AoI (the outer square) is slightly larger than the rendering area (the inner square).
Further reading
To learn more about the MMO API we recommend the following resources:
- Simple MMO example tutorial [TODO link]
- Advanced MMO API techniques [TODO link]
- Server-side Javadoc (com.smartfoxserver.mmo package) [TODO link]
For more details on the MMO API, consult the server-side Javadoc under the com.smartfoxserver.mmo package and the client API documentation.