How to make a basic LSL-based API
A best-practice guide on how to make an API script for your Second Life™ product. These are my 2 cents.
A best-practice guide on how to make an API script for your Second Life™ product. These are my 2 cents.
This text assumes that both your product and the third-party product are attached (body or HUD) to the same owner.
Providing an API for your scripted products can be a game changer. Customers expect compatibility, and rightly so. Scripts in different objects can communicate with each other through so-called channels. A channel is a numbered communication entity, and scripts can listen or send to that channel. The problem: You have no control about which script is listening and sending. Malicious scripts could try to break your product (denial of service) by flooding your channel or sending absurd messages. They could scam users (your customers) or divulge private information.
Hence, what you'd want to have is an API script under your control that hides the channel number and controls the information flow by relaying channel messages to linked messages (and vice versa). The API script is distributed to third parties for integration in their products.
Don't distribute the API script with modify permission.
You choose channel numbers for incoming and outgoing messages in your product and your API script. Channels numbers should be negative, high arbitrary numbers (9 digits), so they can't be easily detected. It may be security by obscurity, but there's currently no other way in LSL.
Example:
integer to_product_channel = -625483362;
integer from_product_channel = -388961111;
The to_product_channel is used in your product to receive messages, the from_product_channel is used to send messages to your API script. Do not disclose these numbers.
A third party script needs to filter the linked messages from your API in order to only process relevant messages. To achieve that, you should specify a constant used as number argument in llMessageLinked() calls. Make sure the constant is also arbitrary to avoid collisions with other products.
Example:
integer MY_API_IDENTIFIER = 24680;
Messages from the from_product_channel should be then relayed to a linked message with aforementioned MY_API_IDENTIFIER. All valid (please add a validity check) linked messages with MY_API_IDENTIFIER should either be handled by the API script or relayed back to your product on to_product_channel.
A third-party product that chooses to use you API most likely wants to know if your product is available/attached/active, or not. On startup the third-party product may want send a request for a status update, which should be instantly answered.
Example (third party script):
llMessageLinked(LINK_SET, MY_API_IDENTIFIER, "get_status", "");
Example (API script)
link_message(integer sender_num, integer num, string msg, key id) {
if (num == MY_API_IDENTIFIER) {
if (msg == "get_status") {
string status_update;
// TODO: retrieve status update from your product
llMessageLinked(LINK_SET, MY_API_IDENTIFIER,
"status|" + status_update, "");
}
}
}
The format and contents of status_update is entirely up to your product. You can also add more message types, like events, configuration changes etc.
Your API script should automatically send a linked message for every status change. The status message should look like the one above:
llMessageLinked(LINK_SET, MY_API_IDENTIFIER, "status|" + status_update, "");
If you choose to use the exact same format for status as your channel-based API, you can replicate it and won't have to update the API script when you change your status message contents or format. However, you may want to add an API version number to the status_update.
To avoid flooding, add an llSleep(1.0) after each llMessageLinked(). Obviously, you can reduce the sleep time, however, I wouldn't go under 0.1 seconds.
LSL listeners to channels add to the overall sim lag. To avoid having a listener open in your API script, even if not needed, it's useful to add an on/off feature to your API script. It could be used like this:
llMessageLinked(LINK_SET, MY_API_IDENTIFIER, "activate", "");
llMessageLinked(LINK_SET, MY_API_IDENTIFIER, "deactivate", "");
You may think that if you're API isn't needed, why don't they just delete the API script? Well, a third-party product that uses your API could also have an on/off button, or simply wants the user to decide whether or not to use the integration of your product. For such cases, you can help to reduce lag.