Client to Server Messages
Sending a Message from the Client to the Server
Sending a message from the client to the server is the most complex of the three message sending systems as it involves a few extra steps to make the server aware of the incoming message. To make it easier an existing example will be used through the steps below which outline what needs to be done. This example is used to send a Place Claim Object message from the client to the server.
1) Create a MessageType
MessageTypes are defined in the AGIS code in the Client.java files found in the plugins package. They are a simple string lookup as shown by this example from VoxelClient.java:
public static final MessageType MSG_TYPE_PLACE_CLAIM_OBJECT = MessageType.intern("voxel.PLACE_CLAIM_OBJECT");
The intern string needs to be unique. You can simply copy an existing MessageType entry and paste it, renaming both the variable name and intern name.
2) Create the MessageHook class
The MessageHook is the class that will pick up the message and do stuff with it. It needs to be created in the *Plugin.java file (replacing the * with the relevant plugin type).
In the example provided it will pick up the place claim object message and check the user can place the claim object before adding it to the claim at the specified location. The Hook class must implement the Hook interface and have the processMessage() function as shown in the example given below from VoxelPlugin.java:
class PlaceClaimObjectHook implements Hook { public boolean processMessage(Message msg, int flags) { ExtensionMessage gridMsg = (ExtensionMessage)msg; OID playerOid = gridMsg.getSubject(); Log.debug("CLAIM: got place object"); // Get the claim ID property from the message - change this to get your own properties int claimID = (Integer)gridMsg.getProperty("claim"); ... return true; } }
You will want to add your own code in the … area, along with changing the getProperty() lines of code.
3) Add the MessageType to the plugins filter and register the Hook class
At the top of each Plugin class there is a MessageFilter and a bunch of Hook registrations. These are needed to tell the plugin to listen for the specified message from the client (or other areas of the server) and then tell it which Hook it should run when it gets the message of that type.
In the VoxelPlugin you will see in the onActivate() function:
getHookManager().addHook(VoxelClient.MSG_TYPE_PLACE_CLAIM_OBJECT, new PlaceClaimObjectHook());
and a bit further below:
filter.addType(VoxelClient.MSG_TYPE_PLACE_CLAIM_OBJECT);
You will need to change these to use your new MessageType and MessageHook.
You can now build your AGIS code if wanted.
4) Add the MessageType and intern to the server python scripts
To enable the Proxy server to receive these messages from the client the MessageType now needs to be defined in the worldmessages.py and extensions_proxy.py scripts. These are both found in the config/world folder.
worldmessages.py:
MessageCatalog.addMsgTypeTranslation(aoMessageCatalog, VoxelClient.MSG_TYPE_PLACE_CLAIM_OBJECT) Add your MessageType like above making sure you get the Client type correct.
extensions_proxy.py:
proxyPlugin.registerExtensionSubtype("voxel.PLACE_CLAIM_OBJECT", VoxelClient.MSG_TYPE_PLACE_CLAIM_OBJECT)
This is just the reverse of when you defined the MessageType. You will find all these at the bottom of the file.
5) Send the message from the client
Now we are finally done with the server and we need to send the message from the client. The easiest way to do this is to copy an existing example. From WorldBuilderUI.cs:
// Need a dictionary to store all the props in Dictionary<string, object> props = new Dictionary<string, object>(); // Now add the properties to be sent to the server. Send as many as you want (within reason) props.Add("claim", activeClaim.id); props.Add("gameObject", itemBeingPlaced.itemEffectValues[effectPositions[0]]); props.Add("loc", currentReticle.transform.position); props.Add("orient", currentReticle.transform.rotation); props.Add("itemID", itemBeingPlaced.templateId); props.Add("itemOID", itemBeingPlaced.ItemId); // The actual command to send the message NetworkAPI.SendExtensionMessage(ClientAPI.GetPlayerOid(), false, "voxel.PLACE_CLAIM_OBJECT", props);
The format is simple, create a Dictionary to store the props, add the props then send the message. One thing to note in the command to send the props is the MessageType intern is used as the second last parameter.
Most variable types can be sent in the props message. All simple types (such as bool, int, float) can be sent along with some object types (String, OID). Some lists and dictionaries can be sent but they can be a little problematic so often best to avoid where possible.
Note that the first parameter in the NetworkAPI.SendExtensionMessage (in this case, ClientAPI.GetPlayerOid()) is sent as the subject of the message. The subject can then be retrieved from the message on the server using OID playerOid = msg.getSubject();
Final Notes
- It is very helpful to have a debug message near the start of the MessageHook to confirm that the message made it to the server.
- If a message makes it to the server but doesn’t appear to run the rest of your code check the properties you are sending and the types. If the server gets a property type it doesn’t expect it may just stop running.
- It can be helpful to put a breakpoint on the the line in your Unity script that sends the extension message to ensure it is being sent, or a debug message just after it.
- Check the server errors.out log file to help debug why your message may not have made it. Sometimes it may say your message needs to be added to a -ads file. These can be found in the config/world folder. Just add exactly what the error message says.