bus
- bus [boolean]
- Enables bus system.
About the bus system
The bus system's goal is to allow different OpenKore instances to easily communicate with each other, and to allow external tools to easily communicate with a running OpenKore instance.
The bus is a communication channel which supports broadcast communication as well as private communication. One can compare it to an open street: anyone can shout a message to everybody (broadcast communication) or whisper a message into someone else's ears (private communication).
Furthermore, the bus system is based on discrete messages instead of byte streams.
Examples of using the Bus
$messageID
is a string identifier (name) for the message.
$args
can be either a reference to a hash or an array that contains only scalar (string or numeric) value's. Which implies that it cannot be a complex datatype.
Send a message in OpenKore
OpenKore acts as a bus client here, using the globally stored variable $bus which holds a Bus::Client object.
use Globals qw($bus);
if ($bus->getState == Bus::Client::CONNECTED) { $bus->send($messageID, $args); }
- If you do not check for CONNECTED, your message will be queued and it will be sent when the Bus connects.
Receive a message in OpenKore
OpenKore acts as a bus client here, using the globally stored variable $bus which holds a Bus::Client object.
use Globals qw($bus); my $on_message_received;
Load:
sub bus_message_received { my (undef, undef, $message) = @_; if ($message->{messageID} eq $messageID) { # its a message we're interested in (having $messageID name) # $args is at $message->{args} } } $on_message_received = $bus->onMessageReceived->add(undef, \&bus_message_received);
Unload:
$bus->onMessageReceived->remove($on_message_received);
Standalone script
Everything is the same as above, the only difference is that you need to start the Bus and iterate it in your main loop. (which kore does for you) Because bus clients can communicate with each other, a script like this can communicate with an OpenKore bus client. (ex. Such bus script could be your interface to send SMS text-messages from your kore instances)
#!/usr/bin/env perl use lib '<path to OpenKore src dir>'; use Bus::Client; use Bus::Handlers; use constant BUS_USER_AGENT => 'Meaningful name of your script'; # use this to connect to local bus server, otherwise specify host and port my $bus = new Bus::Client(host => undef, port => undef, userAgent => BUS_USER_AGENT); my $bus_message_handlers = new Bus::Handlers($bus); # (add here listener for receiving Bus messages, if needed) while () { # (add here your code, and send Bus messages, if needed) $bus->iterate; sleep 1; }
Protocol description
I call the message format the "Simple Serializable Message" (SSM). This message format is binary.
A message contains the following information:
- A message identifier (MID). This is a string, which can be anything.
- A list of arguments. This is either a list of key-value pairs (a key-value map), or a list of scalars (an array).
A message is very comparable to a function call. Imagine the following C++ function:
void copyFile(string from, string to); copyFile("foo.txt", "bar.txt");
- The message ID would be "copyFile".
- The key/value pairs would look like this:
from = foo.txt to = bar.txt
Message structure
Note that all integers are big-endian.
Header
Each message starts with a header:
struct { // Header uint32 length; // The length of the entire message, in bytes. uint8 options; // The message type: 0 = key-value map, 1 = array. uint8 MID_length; // The message ID's length. char MID[MID_length]; // The message ID, as a UTF-8 string. } Header;
If options is set to 0, then what comes after the header is a list of MapEntry structures, until the end of the message.
If options is set to 1, then what comes after the header is a list of ArrayEntry structures, until the end of the message.
Key-value map entry
struct { uint8 key_length; // Length of the key. char key[key_length]; // UTF-8 string. uint8 value_type; // Value type: 0 = binary, 1 = UTF-8 string, 2 = unsigned integer uint24 value_length; // Length of the value. char value[value_length]; // The value data. } MapEntry;
Array entry
struct { uint8 type; // Like MapEntry.value_type uint24 length; char value[length]; } ArrayEntry;