GenericMessageUDP
From OpenSimulator
(Created page with "=Introduction= Within the LLUDP protocol, there is a message type called GenericMessage. This has the following structure (taken from a viewer's app_settings/message_templat...") |
|||
(3 intermediate revisions by one user not shown) | |||
Line 1: | Line 1: | ||
+ | {{Languages|GenericMessageUDP}} | ||
=Introduction= | =Introduction= | ||
Line 23: | Line 24: | ||
= Example = | = Example = | ||
− | Typically, we would create an OpenSimulator [[IRegionModule|region module]] to subscribe to the GenericMessages with certain Method names. | + | |
+ | == Sending == | ||
+ | Typically, we would create an OpenSimulator [[IRegionModule|region module]] to subscribe to the GenericMessages with certain Method names. There is an example GenericMessagingModule at https://github.com/justincc/GenericMessagingModule which does exactly this. We will extract the important parts here. | ||
+ | |||
+ | The first thing to do is to have the region module subscribe to incoming GenericMessages from viewers with a particular method name. Here's the code snippet. | ||
+ | |||
+ | <source lang="csharp"> | ||
+ | public void AddRegion(Scene scene) | ||
+ | { | ||
+ | scene.EventManager.OnNewClient += HandleNewClient; | ||
+ | } | ||
+ | |||
+ | private void HandleNewClient(IClientAPI client) | ||
+ | { | ||
+ | client.AddGenericPacketHandler("test", HandleGenericMessage); | ||
+ | } | ||
+ | |||
+ | private void HandleGenericMessage(object sender, string method, List<string> args) | ||
+ | { | ||
+ | IClientAPI client = (IClientAPI)sender; | ||
+ | |||
+ | m_log.DebugFormat( | ||
+ | "[GENERIC MESSAGE]: Received message with method {0}, args {1} from {2} in {3}", | ||
+ | method, string.Join("|", args.ToArray()), client.Name, client.Scene.Name); | ||
+ | } | ||
+ | </source> | ||
+ | |||
+ | As you can see, the module first subscribes for a notification when a new client connects. When this is triggered, it subscribes its HandleGenericMessage method to be called whenever a GenericMessage with the Method paramter "test" is received. In a real appliation, you would to use some much more specific name to avoid a clash with any future Linden Lab methods or other OpenSimulator extensions. | ||
+ | |||
+ | Now we need some way to generate this GenericMessage. This has to be done in the context of an existing client session, so the easiest way is to the use TestClient program in [https://github.com/openmetaversefoundation/libopenmetaverse the libopenmetaverse library]. Unfortunately, you will likely need to compile a bleeding edge version of this library (since June 20th 2014) as the required commands have only just been added. | ||
+ | |||
+ | Once built, TestClient can be started as | ||
+ | |||
+ | $ TestClient.exe | ||
+ | |||
+ | and then a user account logged in with a command such as | ||
+ | |||
+ | # login Ima User mypassword http://localhost:9000 | ||
+ | |||
+ | Now we can send a generic message with the command | ||
+ | |||
+ | # sendgeneric test oh my | ||
+ | |||
+ | Which will send a GenericMessage with a Method of "test" and Parameters of "oh" and "my". If the example GenericMessagingModule is installed, then it should print these received details to the OpenSimulator log. | ||
+ | |||
+ | == Receiving == | ||
+ | In the example GenericMessageModule above, receipt of the GenericMessage from the client triggers a reply GenericMessage. The full HandleGenericMessage method is | ||
+ | |||
+ | <source lang="csharp"> | ||
+ | private void HandleGenericMessage(object sender, string method, List<string> args) | ||
+ | { | ||
+ | IClientAPI client = (IClientAPI)sender; | ||
+ | |||
+ | m_log.DebugFormat( | ||
+ | "[GENERIC MESSAGE]: Received message with method {0}, args {1} from {2} in {3}", | ||
+ | method, string.Join("|", args.ToArray()), client.Name, client.Scene.Name); | ||
+ | |||
+ | string replyMethod = "reply"; | ||
+ | List<string> replyArgs = new List<string>() { "one", "two" }; | ||
+ | |||
+ | client.SendGenericMessage(replyMethod, UUID.Zero, replyArgs); | ||
+ | |||
+ | m_log.DebugFormat( | ||
+ | "[GENERIC MESSAGE]: Replied with message{0}, args {1} to {2} in {3}", | ||
+ | replyMethod, string.Join("|", replyArgs.ToArray()), client.Name, client.Scene.Name); | ||
+ | } | ||
+ | </source> | ||
+ | |||
+ | In this case, the message sent back has a method of "reply" and parameters of "one" and "two". | ||
+ | |||
+ | This will be received by the TestClient that sent the message. Unfortunately, there's currently no way to selectively display received messages to its console. However, it does have a "logpacket" function that allows you to log a given number of received packets to a separate text file. For instance, if you run the TestClient command | ||
+ | |||
+ | # logpacket 100 packets.log | ||
+ | |||
+ | shortly before triggering the sendgeneric command, you should see the reply from the region module in the packets.log file. | ||
+ | |||
+ | = Further steps = | ||
+ | Of course, once a region module receives a generic UDP message, it is not limited to just replying with a GenericMessage to the client. Any behaviour can be triggered, such as performing some processing and then sending GenericMessages back to every client/viewer connected to the region. | ||
+ | |||
+ | = Caveats = | ||
+ | |||
+ | Although GenericMessages work in this test scenario, I (justincc) am not currently aware of anybody actually using the mechanism for sending and receiving arbitary UDP from a libopenmetaverse client/modified viewer to an OpenSimulator region module. So whilst it should work, further use might reveal problems of which we are not yet aware. | ||
+ | |||
+ | There shouldn't be a size limit problem, though one has to be carefully not to exceed the UDP size limit. I believe that messages under 4K bytes should be fine. | ||
+ | |||
+ | Also, one has to know the exact format of messages flying back and forth - they are not self-describing. This is expected in a UDP context where the idea is very quickly receive and process time-critical data. However, if you need to move data back and forth that is not so time-critical and would benefit from greater reliability, you might want to consider implementing a new HTTP [[capabilities|capability]] in your region module for communication back and forth with the client. The issue here is that to send data directly to the client (rather than as a reply to some other request), one might need to implement a custom event queue message, which might not yet be possible in OpenSimulator. |
Latest revision as of 07:09, 5 March 2015
Languages: |
English Français |
Contents |
[edit] Introduction
Within the LLUDP protocol, there is a message type called GenericMessage. This has the following structure (taken from a viewer's app_settings/message_template.msg file).
GenericMessage Low 261 NotTrusted Zerocoded { AgentData Single { AgentID LLUUID } { SessionID LLUUID } { TransactionID LLUUID } } { MethodData Single { Method Variable 1 } { Invoice LLUUID } } { ParamList Variable { Parameter Variable 1 } }
Linden Lab uses this message for some miscellaneous functionality. However, because we can switch messages on the Method parameter, we can also use GenericMessage as a carrier of arbitrary UDP data between the client/viewer and the simulator, as GenericMessages can flow both ways.
[edit] Example
[edit] Sending
Typically, we would create an OpenSimulator region module to subscribe to the GenericMessages with certain Method names. There is an example GenericMessagingModule at https://github.com/justincc/GenericMessagingModule which does exactly this. We will extract the important parts here.
The first thing to do is to have the region module subscribe to incoming GenericMessages from viewers with a particular method name. Here's the code snippet.
public void AddRegion(Scene scene) { scene.EventManager.OnNewClient += HandleNewClient; } private void HandleNewClient(IClientAPI client) { client.AddGenericPacketHandler("test", HandleGenericMessage); } private void HandleGenericMessage(object sender, string method, List<string> args) { IClientAPI client = (IClientAPI)sender; m_log.DebugFormat( "[GENERIC MESSAGE]: Received message with method {0}, args {1} from {2} in {3}", method, string.Join("|", args.ToArray()), client.Name, client.Scene.Name); }
As you can see, the module first subscribes for a notification when a new client connects. When this is triggered, it subscribes its HandleGenericMessage method to be called whenever a GenericMessage with the Method paramter "test" is received. In a real appliation, you would to use some much more specific name to avoid a clash with any future Linden Lab methods or other OpenSimulator extensions.
Now we need some way to generate this GenericMessage. This has to be done in the context of an existing client session, so the easiest way is to the use TestClient program in the libopenmetaverse library. Unfortunately, you will likely need to compile a bleeding edge version of this library (since June 20th 2014) as the required commands have only just been added.
Once built, TestClient can be started as
$ TestClient.exe
and then a user account logged in with a command such as
# login Ima User mypassword http://localhost:9000
Now we can send a generic message with the command
# sendgeneric test oh my
Which will send a GenericMessage with a Method of "test" and Parameters of "oh" and "my". If the example GenericMessagingModule is installed, then it should print these received details to the OpenSimulator log.
[edit] Receiving
In the example GenericMessageModule above, receipt of the GenericMessage from the client triggers a reply GenericMessage. The full HandleGenericMessage method is
private void HandleGenericMessage(object sender, string method, List<string> args) { IClientAPI client = (IClientAPI)sender; m_log.DebugFormat( "[GENERIC MESSAGE]: Received message with method {0}, args {1} from {2} in {3}", method, string.Join("|", args.ToArray()), client.Name, client.Scene.Name); string replyMethod = "reply"; List<string> replyArgs = new List<string>() { "one", "two" }; client.SendGenericMessage(replyMethod, UUID.Zero, replyArgs); m_log.DebugFormat( "[GENERIC MESSAGE]: Replied with message{0}, args {1} to {2} in {3}", replyMethod, string.Join("|", replyArgs.ToArray()), client.Name, client.Scene.Name); }
In this case, the message sent back has a method of "reply" and parameters of "one" and "two".
This will be received by the TestClient that sent the message. Unfortunately, there's currently no way to selectively display received messages to its console. However, it does have a "logpacket" function that allows you to log a given number of received packets to a separate text file. For instance, if you run the TestClient command
# logpacket 100 packets.log
shortly before triggering the sendgeneric command, you should see the reply from the region module in the packets.log file.
[edit] Further steps
Of course, once a region module receives a generic UDP message, it is not limited to just replying with a GenericMessage to the client. Any behaviour can be triggered, such as performing some processing and then sending GenericMessages back to every client/viewer connected to the region.
[edit] Caveats
Although GenericMessages work in this test scenario, I (justincc) am not currently aware of anybody actually using the mechanism for sending and receiving arbitary UDP from a libopenmetaverse client/modified viewer to an OpenSimulator region module. So whilst it should work, further use might reveal problems of which we are not yet aware.
There shouldn't be a size limit problem, though one has to be carefully not to exceed the UDP size limit. I believe that messages under 4K bytes should be fine.
Also, one has to know the exact format of messages flying back and forth - they are not self-describing. This is expected in a UDP context where the idea is very quickly receive and process time-critical data. However, if you need to move data back and forth that is not so time-critical and would benefit from greater reliability, you might want to consider implementing a new HTTP capability in your region module for communication back and forth with the client. The issue here is that to send data directly to the client (rather than as a reply to some other request), one might need to implement a custom event queue message, which might not yet be possible in OpenSimulator.