This was attached as a supplementary document to the other notes I found.
It seems that the avatar side of the communication isn't without complexity, either - due to a complete lack of wiki maintenance ...less than optimal bookkeeping practices, the only source of information about the systems to interact with Nature seem to be the built-in documentation, which leaves much to be desired.
(If one is curious about that, it seems to be available via the incantation /figura docs globals server_packets and its various branches.)
To act as reference material in the meantime, I have created this supplementary document, which should hopefully provide the reader with a reasonable understanding of how the API works.
Overview of server_packets
Internally called ServerPacketsAPI, server_packets functions very similarly to the much more familiar pings, with a few slight differences.
To receive data, use the function definition syntax you're probably familiar with:
function server_packets.packet_id(data)
-- Handle data here!
end
Compared to pings, server_packets receivers always have exactly one parameter, through which a Buffer is passed containing the packet's data.
Make sure to close the Buffer you are given! If you don't, the buffer slot will continue to be considered "used" and you will eventually run out of buffers (from my research, most people are unaware there is a limit on buffer count at all, but it exists as part of the permissions system and this is one of the easiest ways to have latent bugs.)
The other main difference from pings is that sending data is explicit and not activated by calling the receiver function - that means calling server_packets.packet_id() does not send anything. To send packets, use server_packets:sendPacket(id, data), where id is a string and data is a Buffer.
Make sure to rewind the Buffer before sending it.
Quick Introduction to Buffers
The canonical documentation for Buffers is accessible via the incantation /figura docs globals data Buffer.
It's also available on FIGS or the Figura Wiki.
Buffers provide a convenient interface for reading structured data like the one Nature provides. It's almost like it's based on something else...
Creating new buffers: data:createBuffer()
To create a new buffer, use data:createBuffer(), which returns a new Buffer object.
You can also specify the initial allocated size by providing it as an argument to createBuffer, but this is not required unless you're working under really tight permissions constraints as Figura will auto-expand buffers by default.
Cleaning up after yourself: :close()
As mentioned in the warning callout above, there is a limit on the number of buffers you can have, and it's tied to the permission system.
- On Default, the limit is 4 buffers.
- On High, the limit is 16 buffers.
- On MAX, the limit is 32 buffers.
To free up a buffer, call close() on it. This will render the contents unusable.
local buf = data:createBuffer()
-- (later...)
buf:close()
Buffers function with a 'cursor' that moves around the data as you read and write to it.
Each time data is read or written, that action is taken at the position of the cursor and the cursor is automatically advanced to the next byte. This makes it convenient to read or write many values in sequence.
However, it is still useful to be able to move the cursor around manually - notably, to read back the data written to a buffer, the cursor has to be moved ("rewound") back to position 0:
buf:writeByte(5)
buf:writeDouble(3.14159)
buf:setPosition(0) -- move back to the beginning
This section only discusses types that Nature cares about. Other types are supported by buffers - to see that, go to the canonical documentation.
There are methods to write data in various formats to a buffer. They all move the cursor to the end of the data written, so calling them multiple times in sequence will concatenate the data.
For things described as 'x-byte integers', use these names:
:write(n): 1 byte, such as type, dir for patterns, and rows and cols for matrices.
:writeInt(n): 4 bytes, which is primarily used to describe the length of things
For 'doubles', use:
:writeDouble(n): 8 bytes, used for both the value of numbers and the x, y, and z of vectors.
Similar to writing data, Buffers are versatile in what they can read. Each read method also similarily moves the cursor to the end of the data read.
:read(): 1 byte, such as type, dir for patterns, and rows and cols for matrices.
:readShort(): 2 bytes - the protocol version
:readInt(): 4 bytes, usually lengths but also quite a few configuration parameters
:readDouble(): 8 bytes, used for both the value of numbers and the x, y, and z of vectors, as well as the sending power-related configuration values.