Using Extension Messages
From Multiverse
| Multiverse Servers |
|
Installing • Installing on Linux • Running • Troubleshooting • FAQ • Release Notes • Updating • JMX Monitoring & Mgmt. |
| Infrastructure |
|
Platform Architecture • Registering a World • Proxy Server • Event Handling • World Manager • Voice Server |
| Messaging System |
|
Perception Messaging • Using Extension Messages • Message Marshalling • Multi-subject Messaging • Message Catalog |
| Object Architecture |
|
World Instancing • Server Object Search • Server Regions • Server Markers |
| Scalability and Performance |
| Reference |
|
File Layout • Property File • Logging • API |
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:
- Between server plug-ins.
- From a server plug-in to Clients.
- From Clients to a server plug-in.
- From one Client to another.
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:
-
ExtensionMessage: A subject message. -
TargetedExtensionMessage: A target message.
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:
- You have added the message type with
ProxyPlugin.addExtraPlayerExtensionMessageType()and the message is:
- Targeted at a player, that is, using
TargetedExtensionMessage.
- Targeted at a player, that is, using
- OR
- About an object perceived by a player, that is, using
ExtensionMessage.
- About an object perceived by a player, that is, using
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:
- Handle the message in the proxy server, using a proxy extension hook.
- Forward the message to other server plug-ins by publishing it.
- Forward extension message to another client, which in effect simulates peer-to-peer messaging.
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
MessageTypeobject.
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)
