Marauroa 3.8

From Arianne
Jump to: navigation, search

There are a number of small new features in Marauroa 3.8 that will be explained on this page.

Please note, the term "Marauroa users" refers to developers using Marauroa in their own software. It does not refer to the end user of the programs developed using Marauroa.

Version compatibility


Different protocol versions of client and server can now talk to each other
Reason for change 
Some other features (see below) required a change of the Marauroa protocol. But it is impractical to force all users to update their clients when a new server version is installed. This is especially true because there is a playdeb Ubuntu package for Stendhal that is usually a couple of days behind.
Impact on Marauroa users 
There is no negative impact on projects using Marauroa because games usually have to check the version of the game client anyway and normally this implies a version of the marauroa.jar distributed with it.
The client sends the first message with the protocol version it prefers. The server will check if it understands this version. If it does understand that version it will make sure that it sends only messages to the client with up to that version.
So an old client which speaks version 31 can talk to a server which speaks version 33 (Marauroa 3.8). The server will automatically downgrade all answers to version 31. As a result some information like details in the S2CCharacterList Message will be missing, of course.
To makes things compatible the other way round, the server will now accept messages up to version 40. It will reply with the highest protocol version it can support.
If we want to make some changes to the client-->server protocol that cannot be understood by an old server, we need a little jump in protocol version numbers. But I guess this case is rather unlikely. The normal use case are outdated clients.


The protocol version is now stored in a new column along the blob
Reason for change 
The new map feature made it necessary to change the way RPObjects are serialized.
Impact on Marauroa users 
The first Marauroa server start after an update might take a couple of seconds because a protocolVersion column is added automatically to the tables rpobject and rpzone.
As characters and zones are stored using the serialization mechanism, chances to the serialization protocol break saved content. Marauroa needs a way to learn the version of the protocol used for saving the blob in order to load it correctly.

Protocol extensions

Details in character list

The character list on login now contains the RPObject with details about all owned characters
Reason for change 
Player should have more information in the choose character dialog.
Impact on Marauroa users 
A new optional method onAvailableCharacterDetails in ClientFramework will provide the additional data, onAvailableCharacters is still called so old programs will continue to work without adjustments.
When the client uses at least protocol version 32 on login, the server will include details of the characters belonging to that account.

Stendhal choose character.png

Maps attributes

There is a new map datatype that is capable of storing key value pairs.
Reason for change 
There are many kinds of map like data structures in Stendhal (buddy list, ignore list, quest states, visited zones, ...). The previous way of using an RPSlot with exactly one RPObject was inconvenient.
Impact on Marauroa users 
There are new methods in RPObject to work with the maps
To use a map attribute you need to define an attribute with type map in the RPClass. It is done the same way as normal attributes for Strings or Integers. Here a short example on how to define a map attribute as it is done in Stendhal for the buddy list:
player.addAttribute("buddies", Type.MAP, Definition.PRIVATE);

Afterwards you can access the map with several methods defined in RPObject to put or get values. To put a key value pair in a certain map you need to call the put method for maps:

rpobject.put(mapname, key, value);

To get a value stored for a key in a map you need to use the get method for maps:

rpobject.get(mapname, key);

The put and get methods provide methods to store/access String, Integer and Boolean values like they are provided for the existing attributes.

public void put(String mapname, String key, String value)
public void put(String mapname, String key, int value)
public void put(String mapname, String key, boolean value)
public String get(String mapname, String key)
public int get(String mapname, String key)
public boolean get(String mapname, String key)

If you need to check if a certain map is present in a RPObject you can to this by calling


It returns true if within the RPObject has a map with mapname and false if not.

To access the map as a whole thing it is possible to obtain a copy via the getMap method.

Map<String, String> theWholeMap = rpobject.getMap(mapname);

As a convenience method RPObject can return all maps put together in one map:

public Map<String, Map<String, String>> maps()

An example usage of the maps can be found in the Player class of Stendhal. The buddies and their online statusses of a player are stored as a map within the player object. The class PlayerRPClass shows how to define a map attribute in a RPClass.

Performance optimization

Asynchronous login

Login events are now processed asynchronously
Reason for change 
On login a number of database queries have to be done and some of them are relatively slow (up to 100ms) so this slowed down turn execusion.
Impact on Marauroa users 
No changes required
On login Marauroa has to verify the username and password and check if the account was banned. In addition to that it checks if the account was blocked because of too many failed login attempts. And if all is well, it loads a list of all characters belonging to that account, including character details. All in all this can take up to 100ms on a large database with many logged login events.
Although login events have always been outside the normal turn loop, they used a common synchronization lock. In Marauroa 3.8 all of this happens without holding the lock. Only the small and very fast step of placing a selected character into the world is synchronized.
As a nice side effect: There are many messages exchanged during the login process and since processing of them does not have to wait for the turn to end anymore, logins are a bit faster. If we want to change database storage to a meta model in the future, we will not have to worry about it being a bit slower than the blob anymore.

Baking of RPClasses

Faster access to inherited attributes.
Reason for change 
Improve performance in games with deep RPClass inheritance hierarchies, many objects, and lots of redundant put() and get() calls
Impact on Marauroa users 
New methods in RPClass: bake() and bakeAll(). You don't have to use them but if you do, it will make a snapshot of the inheritance hierarchy to optimize performances. Further changes in super classes will not be propagated to children.
I was wondering why Marauroa is so much faster on Marboard with 100,000 objects and a turn time of 30ms than it is in Stendhal with less objects and a turn time of 300ms. Using a profiler it turned out that a deeply nested inheritance hierarchy slows down calls to RPObject.get() and RPObject.put(). Those calls need to find the attribute definition (get() because of static attributes and put() in order to validate the datatype). And Stendhal does a huge amount of get()s and put()s every turn.
Baking a class is similar to baking a cake: After you have thrown in all the ingredients, the cake is baked which causes the ingredients to melt together. In Marauroa: After all the attributes are defined in the inheritance hierarchy, baking means that the attribute of parent classes are melted into the child classes. Just like you cannot take the eggs out of a baked cake, further changes to attributes are not visible in child classes.