Pipes, or text streams, are a foundational element of the ARES design. They allow transport of text between programs, which enables a wide variety of applications that are completely beyond the reach of any software environment that lacks them. Text processing pipelines were a 'killer feature' of UNIX, and the ARES implementation is similar in its purpose and flexibility, albeit with some simplifications.
In a normal script, text output is accomplished using llRegionSayTo(target, 0, text). This works as follows:
In an ARES program, text output uses the print(stream, user, text) macro, where stream and user are both UUIDs. This does the following:
script tells io daemon that user added a message to stream
forwards message
In the base case, if the stream UUID isn't currently assigned to a pipe it is assumed to refer to an object, and the text is sent to it. The result is similar to using llRegionSayTo(stream, 0, text) directly, although the io daemon will perform some collation (very long messages will be split.)
However, the power of text pipes comes from combining them in less trivial arrangements. By registering a pipe object with the io daemon, the programmer can create automated chat listeners from existing tools, forward messages through multiple filters in interesting ways, scrape output to process with devices, and even implement complete communications terminals that allow ARES to interface with systems outside of Second Life, often with little to no modification of existing LSL code.
Pipes are used in ARES for a wide variety of applications, most notably implementation of the chat filter (vox) system. However they are also the intermediary format for most userspace scripts that communicate via private chat channels, such as the input handler itself and the ARES Xanadu software client. Most of the development documentation pertains to the pipe system to some extent, as this routing was vital (in earlier versions of ARES) to minimizing the number of scripts that had to be kept running at all times.
Managing pipes
To see a list of currently-extant pipes on the system, use the command @input debug:
· 00000000-0000-0000-0000-000000000010 · 0 · 2 · · · 00000000-0000-0000-0000-000000000020 · 0 · 2 · · · 00000000-0000-0000-0000-000000000100 · 0 · 2 · · · 000369b6-0003-69b6-0003-69b6000369b6 · 9 · 1 · nav · 00000000-0000-0000-0000-000000000000 · 50495045-0000-0000-0000-000000000000 · 0 · 3 · pipet · 00000000-0000-0000-0000-000000000000 · 00000000-0000-0000-0000-000000000001 · 10 · 1 · _input · d69ca06e-aa22-49e4-86e1-42677e26f3f5 · 43524553-5841-4e41-4455-48414e444c45 · 11 · 1 · _parc · 00000000-0000-0000-0000-000000000000 · 00000000-0000-0000-0000-000000000a01 · 0 · 3 · filter lisp 50 · 00000000-0000-0000-0000-000000000a02 · 00000000-0000-0000-0000-000000000a02 · 0 · 4 · _input output · d69ca06e-aa22-49e4-86e1-42677e26f3f5 io Memory: 228 bytes deallocated; 21594 freeThis provides output from the _io daemon, which cannot be accessed via the usual @service ... syntax.
The output may be interpreted as a table, labeled like so:
| ID | listener | type | command | next pipe ID |
|---|---|---|---|---|
| 00000000-0000-0000-0000-000000000010 | 0 | 2 | ||
| 00000000-0000-0000-0000-000000000020 | 0 | 2 | ||
| 00000000-0000-0000-0000-000000000100 | 0 | 2 | ||
| 000369b6-0003-69b6-0003-69b6000369b6 | 9 | 1 | nav | 00000000-0000-0000-0000-000000000000 |
| 50495045-0000-0000-0000-000000000000 | 0 | 3 | pipet | 00000000-0000-0000-0000-000000000000 |
| 00000000-0000-0000-0000-000000000001 | 10 | 1 | _input | d69ca06e-aa22-49e4-86e1-42677e26f3f5 |
| 43524553-5841-4e41-4455-48414e444c45 | 11 | 1 | _parc | 00000000-0000-0000-0000-000000000000 |
| 00000000-0000-0000-0000-000000000a01 | 0 | 3 | filter lisp 50 | 00000000-0000-0000-0000-000000000a02 |
| 00000000-0000-0000-0000-000000000a02 | 0 | 4 | _input output | d69ca06e-aa22-49e4-86e1-42677e26f3f5 |
These columns have the following definitions:
- ID: A UUID indicating the pipe ID. When a message is received by _io on a UUID that is present in this column, then the message is stored for subsequent processing rather than immediately being sent to an object as a chat message.
- listener: If the pipe is of the input pipe type (type 1), then it receives chat messages from an llListen() handler. This will then be forwarded on to the rest of the chain, if one exists. If the pipe is of type 2 (output), then this field instead holds the output channel.
- type: A numeric indicator of the pipe's type:
- Input
- Output
- Invoke
- Notify
- File
- Invoke (volatile)
- Notify (volatile)
- Transport
- Transport (volatile)
- command: If the pipe is not of types 2, 8, or 9, then this string will be called with the appropriate system signal. If the pipe is of type 2 (output), then the command is interpreted as the recipient UUID.
- next pipe ID: The value to provide as the output stream (outs) during invocation.
To reset the _io daemon, use the command @input debug restart. The normal syntax, @service io restart, will not work due to the _io daemon's stream-oriented design.