Using Extension Messages

From Multiverse

Jump to: navigation, search

Contents

Overview

Extension messages are highly flexible messages that are useful when developing server plug-ins. An extension message can contain a set of string key/value pairs, where the values can be complex types such as lists and sets as well as primitive types such as strings and integers.

Use extension messages to communicate:

Defining an extension message

Like all messages, an extension message has a message type, by default WorldManagerClient.MSG_TYPE_EXTENSION. However, an extension message also has a sub-type, that is the message type on the Client, distinct from the server message type.

The ExtensionMessage classes are inner (nested) classes of multiverse.server.plugins.WorldManagerClient:

Both are sub-classes of PropertyMessage.

Inter-server extension messages

Using extension messages for inter-server communication requires less programming than using standard message types, because you don't need to define a message class. However, extension messages are less efficient because values are stored in a dictionary rather than message class data members.

Publishing

For sending messages between server plug-ins, an extension message works just like a PropertyMessage or TargetedPropertyMessage. An extension message is better in this scenario because it is not automatically forwarded to perceiving clients. The default PropertyMessage message type causes object properties to be automatically forward to perceiving clients. You could use a different message type with PropertyMessage and achieve the same effect as using ExtensionMessage.

An extension message is easier to use because you don't need to define a custom message class. You should still define a new MessageType so subscribers can filter for your exact message. However, extension messages are less efficient because values are stored in a key-value dictionary rather than class data members. The inefficiency is only an issue if the message rates exceed 1000 messages per second. Extension messages are ideal for infrequent activity like quests and trading.

Here is an example of publishing a subject extension message. The second parameter to the ExtensionMessage constructor is the message sub-type. We're setting it to empty string because this message is not intended to be sent to a client.

ExtensionMessage message = new ExtensionMessage(MSG_TYPE_PRIZE_SHEEP,"",sheepOid);
message.setProperty("fuseDelay","5");
message.setProperty("blastRadius","8000");
Engine.getAgent().sendBroadcast(message);

public static final MessageType MSG_TYPE_PRIZE_SHEEP = MessageType.intern("prize.sheep");

Subscribing

Subscribe to extension messages as you would any message, using MessageTypeFilter. For example:

myFilter = MessageTypeFilter()
myFilter.addType(MSG_TYPE_PRIZE_SHEEP)
myFilter.addType(MSG_TYPE_WOOL_SWEATER)
Engine.getAgent().createSubscription(myFilter, my_plugin)

For more information, see Multiverse Messaging System.

Server-to-client extension messages

Other than object properties, extension messages are the only way to customize server-to-client communication.

The proxy server forwards extension messages from other server plug-ins to the Client when:

  • The message type is WorldManagerClient.MSG_TYPE_EXTENSION.

OR:

OR

If you define your own MessageType, then you must add it to the proxy with ProxyPlugin.addExtraPlayerExtensionMessageType(). You must call this method before you register and activate the plug-in. The extension messages that MARS uses are added in multiverse/config/common/proxy.py.

The message type communicated to the client is different than the server message type. The Client has an extension message "sub-type" string. The sub-type is set with the setExtensionType() method on ExtensionMessage or TargetedExtensionMessage. Remember to set this value before publishing client-bound extension messages.

For example, the following code illustrates sending a TargetedExtensionMessage to a client:

 TargetedExtensionMessage message = new TargetedExtensionMessage(playerOid);
 message.setExtensionType("weather.conditions");
 message.setProperty("sky","cloudy");
 message.setProperty("rain",true);
 message.setProperty("lightning",0.10);
 Engine.getAgent().sendBroadcast(message);

Then, for example, use the following Client Python script to handle the message:

def HandleWeatherUpdate(props):
    """Handle an update of the weather conditions from the server."""
    if props.has_key("rain"):
        if props["rain"]:
            PlayRainParticleEffect()
        else:
            StopRainParticleEffect()
 
ClientAPI.Network.RegisterExtensionMessageHandler("weather.conditions", HandleWeatherUpdate)

Now, when the Client receives an extension message with the "weather.conditions" subtype, it calls the handler function HandleWeatherUpdate().

Client-to-server extension messages

Use extension messages to send customized messages from the Client to server plug-ins. Use the Client API method Network.SendExtensionMessage() to send an extension message to the proxy server. The proxy server may handle the message itself or forward it to another server plug-in.

For example, the following code sends an extension message from the Client to the proxy server with a list object containing some data:

def SendSheepMessage():
    props = {}
    props['name'] = 'Dolly'
    props['color'] = 'Black'
    props['age'] = 2
    ClientAPI.Network.SendExtensionMessage(0, False, "proxy.SHEEP_INFO", props)

Handling extension messages from the client

Once sent, there are several options for handling the extension message on the server side:

Use only one of these options for each extension message.

Handling extension messages in the proxy server

The proxy server itself can handle a client extension message using a proxy extension hook. The hook may do anything with the message, including ignoring it.

Proxy extension hooks implement ProxyExtensionHook.processExtensionEvent(). The method receives the client extension message, the sending Player, and the ProxyPlugin. Event processing for the sending player will block until processExtensionEvent() returns.

Register proxy extension hooks using ProxyPlugin.addProxyExtensionHook(String subType, ProxyExtensionHook hook). All hooks registered for the sub-type will be called. Proxy extension hooks supercede other proxy extension message handling.

Add proxy extension hooks to the file multiverse/config/world-name/extensions_proxy.py.

The platform includes two proxy extension hooks:

multiverse.mars.objects.GenerateObjectProxyHook
Generate object identified by template name. Object is created at named marker or at current player location. Sampleworld maps sub-type "proxy.GENERATE_OBJECT" to this hook.
multiverse.server.objects.InstanceEntryProxyHook
Change the player instance. See instancing via client event. Sampleworld maps sub-type "proxy.INSTANCE_ENTRY" to this hook.

The Sampleworld script /Scripts/MarsStandardCommands.py contains examples of invoking both of these hooks. For example, for GenerateObjectProxyHook:

def HandleGenerate(args_str):
   args = args_str.split()
   if len(args) < 1:
       ClientAPI.Write("Insufficient args to generate command")
       return

   props = {}
   ii = 0
   while ii < len(args):
       arg = args[ii]
       if arg == "-P":
           props["persistent"] = 1
       elif arg == "-m":
           ii = ii + 1
           props["marker"] = args[ii]
       elif len(arg) > 0 and arg[0] != "-":
           break
       else:
           print "unknown option " + arg
           ii = ii + 1

   if ii >= len(args):
       ClientAPI.Write("Missing template name")
       return

   props["template"] = " ".join(args[ii:])

   ClientAPI.Network.SendExtensionMessage(0, False, "proxy.GENERATE_OBJECT", props)

Publishing extension messages

To make the proxy server publish extension messages it receives, to forward them to other server plug-ins, register the message subtype using ProxyPlugin.registerExtensionSubtype(). This method requires two arguments:

  • The subtype (a string)
  • The message type, a MessageType object.

Then, whenever the proxy server receives a message from the client with the specfied subtype, the proxy server publishes an extension message with the specified server MessageType. It ignores client extension messages that lack a registered sub-type.

If the client extension message was targeted (targetId parameter was non-zero), the proxy server publishes a TargetedExtensionMessage, otherwise it publishes an ExtensionMessage. The message subject is the sending player OID.

Register extension subtypes in multiverse/config/world-name/extensions_proxy.py

For example, the code below registers the sub-type "farm.PLANT" and maps it to a server MessageType:

proxyPlugin.registerExtensionSubtype("farm.PLANT", FarmClient.MSG_TYPE_PLANT)

To send a "farm.PLANT" extension message from the client:

props = {}
props["crop"] = "tomato"
props["field"] = 5
ClientAPI.Network.SendExtensionMessage(0, False, "farm.PLANT", props)

Additionally, for the servers to process client extension messages properly, you must take the standard steps described in Multiverse Messaging System for adding a new message: add it to your message catalog, and add its "advertisement." For example:

MessageCatalog.addMsgTypeTranslation(mvMessageCatalog, FarmClient.MSG_TYPE_PLANT)

Add the message to your world's proxy server advertisements in multiverse/config/world-name/process-name-ads.txt:

FarmClient.MSG_TYPE_PLANT

NOTE: Use care when designing client extension messages. The facility allows the client to inject messages into the server. The proxy sub-type registry acts like a firewall, allowing only those messages the server is designed to handle. However, by default the proxy server does not filter or validate the message content. For example, a "take object" extension message should validate that the object is:

  • Close enough to the player
  • Owned by the player (or owned by no one)
  • Meets other relevant criteria

Client-to-client messages

The client can target an extension message to another player by setting parameter clientTargeted to true (see Network.SendExtensionMessage().) In this case, the targetId parameter identifies the target player. The extension message will be forwarded as-is to the target player's client. If the player is not logged in, the message is ignored.

The extension message is only forwarded to the target client if ProxyPlugin.allowClientToClientMessage() returns true. The default implementation always returns false. Subclass ProxyPlugin to provide your own implementation. The method receives the sending Player, the target oid, and the extension message. The method may modify the extension message, otherwise the message is forwarded unaltered to the target client.

In this example, "raidMembers" contains player oids:

props = {}
props["text"] = "Attack now!"
for target in raidMembers:
    ClientAPI.Network.SendExtensionMessage(target, True, "raid.ALERT", props)
Personal tools