This is the online version of the mediatransport documentation.

This page is built from the latest code on GitHub. It may describe newer features that you may not necessarily have, even on the latest Modrinth/CurseForge version!

Entries which are blurred are spoilers. Click to reveal them, but be aware that they may spoil endgame progression. Alternatively, click here to get a version with all spoilers showing.

It appears that these constructs called avatars are quite powerful, if a bit annoying to work with.

Warped SignTransports

I appear to have discovered the notes of a collective of people who have also discovered the techniques of Hexcasting. I've copied down what I could understand of the notes, but the last part is so filled with jargon as to be incomprehensible. Perhaps I need to understand more about the artistry of these 'avatars' I see mentioned throughout?


It appears that Nature created new patterns for communicating specifically with avatars after the artists used more primitive methods to communicate rapidly. As seems to be standard, Nature has twisted the appearance of the now-familiar iotas in exchange for this increased efficiency and throughput; they appear as boxes and random symbols by my eyes.


The avatars and their artists seem to not have this problem, and have figured out how to decode the mess. Here's the rest of their notes for reference. To be honest, I don't understand much of what's going on here.


We've discovered (created?) a few new spells. They facilitate direct communication between avatars and hexes; no more getting kicked for spam!

Some of the more data-oriented of us have decoded the format. The entire thing is byte-aligned and uses buffers for data transfer. (that means we have to remember to close them!)


Based on what we've seen, each iota starts with a single-byte type followed by zero or more bytes of data. All multibyte forms are big endian. (that's the default for the Buffer read methods)


The format in its entirety is described here: Iota Transport Protocol


Moreover, Nature doesn't take kindly to being asked to send megabytes of data at a time; to prevent such (ab)use, there is a system of so-called "sending power". It refills quite rapidly (under normal circumstances), and every transmission-related action consumes some.


When you run out of "sending power", trying to submit more iota will result in a mishap. To avoid running into the limit, Quota's Reflection can be useful.


BundleIota Transport Protocol

Loading error

It appears that mediatransport's styles failed to load. Something's wrong with this book's setup.

{% include "mediatransport:mediatransport_shared.css.jinja" %}

This documentation is for protocol version 1.

As previously described, an iota consists of a single-byte type followed by some amount of data, depending on the type.

type 1 byte
data ...

Each iota type has a different type value and has a different format for data.

Built-in types

ff: Garbage

All these types have no data. Also, since Garbage is used to represent decoding failures, it's generally a bad idea to create it on purpose.

ff 1 byte

02: True and 03: False

02 1 byte
03 1 byte

04: Null

04 1 byte

05: Double

Finally, something interesting! Hexcasting uses IEEE 754 double-width floats (that's 8 bytes, 64 bits), so that's what's being used here for value.

05 1 byte
value 8 bytes

06: Pattern

In honor of Hexxy, patterns are type 6. They're a bit complicated:

  • First is a byte representing the dir. This is based on some internal representation, and appears to be as follows: 0 = north east, 1 = east, 2 = south east, 3 = south west, 4 = west, and 5 = north west.
  • Next is a four-byte integer holding the number of angles in the pattern (effectively, strokes minus 1, or the length)
  • Following that are the angles, each 1 byte. Once again this appears to be based on some internal representation, as follows: 0 = forward, 1 = right, 2 = right back, 3 = back, 4 = left back, and 5 = left.
06 1 byte
dir 1 byte
length 4 bytes
angles =length*1 bytes

07: Vector

A vector is three doubles in sequence - x, y, and z.

07 1 byte
x 8 bytes
y 8 bytes
z 8 bytes

08: List

Lists are the primary structuring tool that Hexcasting has, and are similar to patterns - first is a four-byte length, followed by that many iota of any type.

08 1 byte
length 4 bytes
iotas ...

It seems that avatars being able to bring entire structures of iota into being is a bit too much for Nature (...well, by default); you may receive garbage in return

MoreIotas types

01: String

Strings, like the rest of the variable-length iotas, have a 4-byte length followed by the data (in this case, the string.) Strings are expected to be encoded in UTF-8.

01 1 byte
length 4 bytes
string =length*1 bytes

40: Matrix

Matrices are two-dimensional arrays of doubles.

  • First is a single byte for the number of rows, followed by another byte for the number of cols.
  • This is followed by the contents of the matrix - one double for each entry. Each row is written out in sequence (ex. for a 2 by 2 matrix, the order would be row 1 column 1, followed by row 1 column 2, and then row 2 after that)
40 1 byte
rows 1 byte
cols 1 byte
contents =rows*cols*8 bytes

Hexpose types

50: Text

It appears that this format is not suitable for communicating the decorative properties of text (also called 'display') iota. Nevertheless, the actual text content can be transmitted. Text iota have the same format as strings:

50 1 byte
length 4 bytes
string =length*1 bytes

Non-iotas

These don't seem to represent iota at all! They can't be sent to Nature and only appear under special circumstances.

fe: Configuration Data

This glob of information is Nature's repsonse to Query Configuration.

  • First is the protocol's version as a two-byte 'short'. This documentation is written for version 1.
  • Next is the maximum amount of data that can be sent with Submit Iota, in bytes.
  • That is followed by the maximum amount of data that can be sent with Submit Iota II...
  • and the maximum amount of data that can be received from an avatar.
  • That's followed by the maximum amount of 'sending power' that can be held at a time, as a double precision floating point...
  • and how much 'sending power' is regained per tick...
  • and finally, how much 'sending power' Submit Iota II costs.
fe 1 byte
version 2 bytes
max_send 4 bytes
max_inter 4 bytes
max_recv 4 bytes
max_power 8 bytes
power_regen_rate 8 bytes
inter_cost 8 bytes

Crimson SignFSB Channels

... oh, I remember now!


Loading error

It appears that mediatransport's styles failed to load. Something's wrong with this book's setup.

{% include "mediatransport:mediatransport_shared.css.jinja" %}

Data sent via Submit Iota is sent to server_packets.transport_received, like so:

function server_packets.transport_received(data)
    data:close()
end

Data from other casters via Submit Iota II are sent to a different channel: server_packets.transport_external_received

function server_packets.transport_external_received(data)
    data:close()
end

Finally, to send data, use :sendPacket with "transport_send":

server_packets:sendPacket("transport_send", buffer)
buffer:close()

BookFigura API Documentation

This was attached as a supplementary document to the other notes I found.


Loading error

It appears that mediatransport's styles failed to load. Something's wrong with this book's setup.

{% include "mediatransport:mediatransport_shared.css.jinja" %}

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()

Moving the cursor around: :setPosition(pos)

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

Writing Data

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.

Reading Data

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.

Music DiscTransport Mishaps

Various things can go wrong when interacting with the esoteric language of avatar transports.


Transport Init Failure

Figura server isn't initialized ...

Occurs if the server is misconfigured or if this universe is an "integrated server". Causes grey sparks and a general feeling of disappointment.


Not a Submittable Iota

can't send ... because that type of iota can't be sent

That type of iota can't be submitted to Nature for transmission. Causes black sparks.

Need support for an iota type? Ask on GitHub!


Corrupt Iota

can't send ... because it's corrupted or ill-formed

Something has gone horribly wrong. Causes black sparks, and probably should be reported on the relevant GitHub issue tracker.


Matrix Too Large

can't send ... because it's too big
can't send ... because it has too many rows
can't send ... because it has too many columns

The matrix you're trying to send has too many entries to be sent, or more than 255 rows or columns. Causes black sparks.


Send Too Large

can't send: calculated size at least ...

The iota you're sending is larger than the maximum allowed size. Causes black sparks.

Note: Server administrators can change the limit in the config.


Send Other Too Large

can't send to other players: calculated size at least ...

The iota you're sending to another player's avatar is larger than the maximum allowed size. Causes black sparks.

Note: Server administrators can change the limit in the config.


Nothing to Accept

Tried to accept an iota, but there's nothing to accept (i.e. the queue is empty.) Causes white sparks and the screams of the tormented minds to echo within my ears.


Too Fast

I ran out of 'sending power'. Perhaps I should consider consolidating usages of Submit Iota? Causes orange sparks and the sound of something breaking.


it looks like most of these errors only occur when sending (well, "submitting") data from within nature's domain. nature seems to be much more lenient in accepting bad data... in that it doesn't crash anything and instead outputs its favorite flavor of not-error: garbage!
unfortunately this makes it hard to determine why data isn't making it across the boundary. we've compiled a list of reasons to look out for:


- data is of a type (that's the first byte) that nature has no representation for
- the transmission is missing some data at the end
- the data is too large (seems like this could be different limit than the one going in the other direction...)
- a variant of the above: the same matrix size restrictions apply (though it's impossible to have too many rows or columns, the total area could still be too much)


also note that lists can also partially fail to transfer, leading to the entire decoder potentially derailing and interpreting types as data and vice versa. good luck!


ArrowTransports: Index

Related Chapters

An index of all the entries related to transports and Figura.


A list of all the patterns I've discovered, as well as what they do.

Spruce SignTransport Patterns

Submit Iota (sendable →)

Your browser does not support visualizing patterns. Pattern code: edwdwad

Sends the iota on the top of the stack into the ether to be received by my current avatar. Can fail for various reasons. Always costs a fixed 1 'sending power.'


Accept Iota (→ sendable | garbage)

Your browser does not support visualizing patterns. Pattern code: edwdwwaa

Retrieves the next iota from the inbound queue, or mishaps if the queue is empty.


Semaphore's Refl. (→ number)

Your browser does not support visualizing patterns. Pattern code: edwdwq

Queries the number of iotas in the queue.


Submit Iota II (player, sendable →)

Your browser does not support visualizing patterns. Pattern code: wewdwwwdwaawawdwawaqwdwwdada

Sends the iota on the top of the stack to be received by the player second from the top of the stack. It seems to have a separate receiver from the other one. Usually costs 2 'sending power', but this could be different depending on the server configuration.


Quota's Reflection (→ number)

Your browser does not support visualizing patterns. Pattern code: edwdwda

Queries the amount of 'sending power' remaining.


Query Configuration

Your browser does not support visualizing patterns. Pattern code: edwdwwdde

Asks Nature to send information about its preferences to my current avatar. Costs 1 'spending power'. See the protocol documentation for details.


This category is hexdoc only (not in in-game book) and contains information for server administrators installing mediatransport and FSB.

Crimson StaffAbout FSB

mediatransport relies on Figura Server Backend (also called FSB) to function. FSB is a feature in the development builds of Figura for 1.20.1 that enables hosting avatars and pings on the server itself instead of relying on the global backend.


FSB doesn't work on Singleplayer or LAN worlds, so you might want to host a dedicated server to test your hexes out. Thanks for playing our addon! :)

Figura FSB builds on GitHub


Crimson Staffmediatransport config

mediatransport has various server configuration options to limit what data can be sent between avatars and casting environments.


Size Restrictions

All values in bytes.

maximumSendSize: maximum amount of data that can be sent through Submit Iota.

maximumInterSendSize: maximum amount of data that can be sent through Submit Iota II.

maximumRecvSize: maximum amount of data that can be received from Figura. If this is exceeded, garbage will be pushed into the queue instead of the actual data.

matrixMaxArea: MoreIotas only; sets the maximum matrix area (width * height) that can be sent or received, separately from other restrictions and the maximum row/column count of 255.


Filters

mediatransport has three separate filter configuration sets for controlling what types of iota can be sent. Each one has a mode and a list of entries.

The mode can either be "Block" (forbids certain types, 'blocklist') or "Allow" (only allows certain types, 'allowlist').

The list contains the list of resource locations to apply the filter with. For example, to disallow sending lists, set the mode to "Block" and the list to ["hexcasting:list"].

Note that allowing an iota type won't magically make it sendable if mediatransport lacks encoders or decoders for that type.


Sending Filters

Just like the size limits, there are separate filters for Submit Iota and Submit Iota II.

sendFilter and sendFilterMode apply to Submit Iota. interSendFilter and interSendFilterMode apply to Submit Iota II.

If an iota is blocked by these filters, it will cause an 'bad type' mishap on the relevant iota (even if it's nested in a list.)


Receiving Filters

recvFilter and recvFilterMode control what types of iota can be received from Figura.

Any iota that are blocked by this filter are replaced with Garbage, but any containing iota will be unaffected. (ex. a list containing an invalid iota will be received as a list containing garbage instead, provided lists are allowed.)