How can we help you?

talespire:// URL Scheme

Chairmander
March 10, 2023

URL Anatomy

All TaleSpire URLs start with talespire://. From there on, we have some number of path segments.

We call the first path segment the "behavior identifier", this tells TaleSpire how to interpret the rest of the segments.

Dice Links

Example: talespire://dice/d12 (behavior identifier: dice)

This gets TaleSpire to bring the dice rolls specified in the URL into the “tray” ready to be rolled

Note: Currently, we only support basic dice rolls with the "dice" behavior. In the future, we will support more complex dice rolls

Next, let's take a more complex URL and break it down. We'll use this as our example: talespire://dice/Eldritch%20Blast:1d10/4d6-1d4/4d10+2/3d20+1-d6+2

First we'll ignore the scheme and behavior identifier, which leaves us with these path segments: Eldritch%20Blast:1d10/4d6-1d4/4d10+2/3d20+1-d6+2

Each path segment specifies a "dice group". The roll results of each dice group get summed separately. You can see an example result from the above roll here:

A dice group is parsed, right-to-left, case insensitively, with the following regex:

(\+|\-|)\d*D\d+(\+\d+|\-\d+|)

The C# code to do this is:

var diceGroup = Regex.Matches(pathSegment, @"(\+|\-|)(\d*)D(\d+)(\+\d+|\-\d+|)", RegexOptions.RightToLeft | RegexOptions.IgnoreCase);

The extra brackets in the C# regex above were added so that the Groups field contains the most useful data already separated. Naturally, you will need to reverse the results to get them in the correct order.

The regex is the most accurate specification, but here is a simpler (though maybe less precise) version with extra details:

[optional name + ":"][optional operator][optional count]D[sides][optional modifier]

where:

  • name: a name can be added to a dice group which will name the roll in the chat/dice history, as well as show the name of the roll in the result pop-up after rolling

  • operator: currently, the only operators supported are + and -

  • count: is the number of dice of that kind. Zero is not valid, but if it is not specified at all, then the count defaults to one.

  • D: is the only uppercase in the regex to clearly distinguish it from the \ds. The regex is always case insensitive.

  • sides: is the number of sides of the die. Currently we only support the standard TaleSpire dice. We will need to expand to support dice modding.

  • modifier: here, you can specify an integer to add or subtract. It is always of the form +N or -N where N is a positive integer.

Notes:

  • Negation: As the operator is part of the group, a valid dice URL is talespire://dice/-d6. This results in a negated dice roll. Note that talespire://dice/d4-d6 is supported but talespire://dice/d4--d6 is not.

  • Right-to-left: The right-to-left matching is critical to get the correct result: Parsed left-to-right talespire://dice/d6-2d12-1 matches as d6-2 and d12-1, rather than what we want which is d6, -2d12-1.
    You can experiment with the right-to-left option by browsing here and clicking the .Net button.
    Remember to select the "case insensitive" option on regexplanet before clicking the "Test" button.

  • Garbage is ignored: Due to lack of strictness in the specification, it is valid, although ugly, to have ignored text in the URL. For example: talespire://dice/horsed6horse-2d12-1horse is parsed to d6 and -2d12-1. Arguably this should be made stricter.

  • Only first name is shown: Currently the result pop-up, as well as the chat history tab only show the name of the initial dice group, ignoring the others. It is also worth to note that the UI currently does not handle long names particularly well.

 

Asset links

Example: talespire://asset/0b9f9061-a6e4-4126-829f-1deb43147f49 (behavior identifier: asset)

The asset links are fairly straightforward: the path segment after the “asset” behavior identifier is simply the UUID of an asset. Once clicked and parsed by TaleSpire the asset of the corresponding UUID will be put into the hand ready to place.

Possible assets are Tiles, Props and Minis.

The asset UUIDs (as well as some other metadata) can be found in the index.json file inside TaleSpire’s install directory.

Published-Board links

Example: talespire://published-board/TGVwaWRzdGFkdA==/c82b15b84b2fbebfb33929922c9f3fbe (behavior identifier: published-board)

These are links to board shares. As opposed to slab strings which contain all the necessary tile data within themselves, board shares are simply unique links to representations of a board stored on our servers. Clicking the link will cause TaleSpire to create a (shallow) copy of the board. Editing anything on the board as the one who shared it will not update the shared board state.

The anatomy of the url is as follows:

talespire://published-board/[encoded-name]/[published-board-id]
  • The encoded name is a utf8 string which has been base64 encoded, and then has had all forward-slashes replaced with underlines (/_)

  • The published board id is a hex representation of a 16 byte ID. The ID refers to a published board on the server.

Go To Bookmark links

Example: talespire://goto/bookmark/89d1e76a49688d42b5ce62ae511415d6 (behavior identifier: goto/bookmark )

Clicking the link causes TaleSpire to move the camera to the bookmark with the linked ID

The anatomy of the URL is as follows:

talespire://goto/bookmark/[bookmark-id]

The bookmark id is a hex representation of a 16 byte ID. The ID refers to a bookmark on the server.

Stat Names links

Example: talespire://stat-names/AQAIBENvb2wEU3RhdAVOYW1lcwRUaGF0A0NhbgJCZQZDb3BpZWQDTm93 (behavior identifier: stat-names)

This tells TaleSpire to update the currently open campaign’s stat name setup. The stat names are base64 encoded into the URL and can be extracted directly.

The anatomy of the URL is as follows:

talespire://stat-names/[stats-data]

the stats-data is a base64 encoded string

Processing the string:
- replace all underscores in string with forward-slashes (_ -> /)
- Base64 decode the string

The resulting binary data is laid out as follows:

version :: u16 - This is currently 1
number-of-stats :: u8
names :: utf8-string[number-of-stats]

utf8-string is laid out as follows:
byte-count :: u8
utf8-data :: u8[byte-count]

> We use the following notation above
> u8: 8bit unsigned integer (byte in c#)
> u16: 16bit unsigned integer (ushort in c#

Creature-Blueprint links

When you copy a creature, TaleSpire saves a blueprint of the creature to the system clipboard in the form of a creature-blueprint URL.

The anatomy of the URL is as follows:

talespire://creature-blueprint/[blueprint-data]

the blueprint-data is a base64 encoded string

Processing the string:
- replace all underscores in string with forward-slashes (_ -> /)
- Base64 decode the string

The resulting binary data is laid out as follows:


version :: u16 - This is currently 1
number-of-stats :: u8 - this number MUST be <= 150
names :: utf8-string[number-of-stats] - 
number-of-morph-ids :: u8 - this number MUST be <= 10
morph-ids :: uuid[number-of-morph-ids]
active-morph-index :: u8
morph-scales :: packed-morph-scales
RESERVED :: u16
RESERVED :: u16
RESERVED :: u16
RESERVED :: u16
RESERVED :: u16
RESERVED :: u16
RESERVED :: u16
RESERVED :: u16
RESERVED :: u8[3]
hp :: stat
stat-0 :: stat
stat-1 :: stat
stat-2 :: stat
stat-3 :: stat
stat-4 :: stat
stat-5 :: stat
stat-6 :: stat
stat-7 :: stat
torch-hide-fly-state :: torch-hide-fly-state
number-of-emote-slot-overrides :: u8 - this number MUST be <= 16
slot-overrides :: slot-override[number-of-emote-slot-overrides]
number-of-active-emote-ids :: u8 - this number MUST be <= 16
active-emote-ids :: uuid[number-of-active-emote-ids]

utf8-string is laid out as follows:
byte-count :: u8
utf8-data :: u8[byte-count]

packed-morph-scales is a u64 with data bit-packed into it.
It stores up to 10 scales, each taking 6 bits:
to get the nth scale you:
- shift out the 6 bits you are interested in
- divide the value by 4 to get the scale

stat is laid out as follows:
value :: f32
max :: f32

torch-hide-fly-state is a u8 with data bit-packed into it.
bit 0: torch enabled
bit 1: explicitly hidden
bit 2: flying enabled

slot-override is laid out as follows:
id :: uuid
index :: u16

e.g. ((packed-morph-scales >> (6 * n)) & 0b111111) / 4f

> We use the following notation above
>
> u8: 8bit unsigned integer (byte in c#)
> u16: 16bit unsigned integer (ushort in c#)
> u32: 32bit unsigned integer (uint in c#)
> u64: 64bit unsigned integer (ulong in c#)
> f32: 32bit ieee754 binary floating point number (float in c#)
> uuid: 128bit id

TaleSpire URL Relay

Windows

The URLs get passed onto TaleSpire by adding a registry key on install that tells windows what .exe to run when a talespire:// URL is launched. As we don't want lots of copies of TaleSpire starting we have TaleSpireUrlRelay.exe, which either

  • launches TaleSpire and passes the URL as a command-line argument

  • use some form of IPC to send the URL to the running instance of TaleSpire

Using the Links from typical browsers is as simple as clicking on it, and telling the browser to allow to open talespire:// links with the TaleSpireUrlRelay.exe (and for convenience possibly also allow to always do that without asking).

Notes:

  • TaleSpire must not be run as administrator for the TaleSpireUrlRelay to work (unless the Relay is also run as administrator)

Linux

While Linux support isn’t official, TaleSpire does run pretty well using Steam Proton and we are looking into officially supporting Linux (via Proton) in the future. However, the registry keys for Windows obviously don’t work when installing on Linux, so to get the URL relay to work you have to tell your Linux system to forward the talespire:// scheme to the UrlRelay (and open said Relay using Wine).

LividJava from the Discord Community has created a shell script to automatically create the needed desktop entry for getting TaleSpire URLs work - you can get the newest version of it from GitHub: https://github.com/LividJava/talespire-linux-uri

Keep in mind that this is code written by community members so no guarantees for safety and correctness can be given! Check the script yourself, or follow the instructions for setting it up manually:

For manual registration of the link forwarding you need to create a .desktop file at~/.local/share/applications/xdg-talespire.desktop:

[Desktop Entry]
Type=Application
Name=TaleSpire
Exec=/bin/bash -c 'env WINEPREFIX="/home/$USER/.steam/steam/steamapps/compatdata/720620/pfx" WINEESYNC=1 "/home/$USER/.steam/steam/steamapps/common/Proton 6.13/dist/bin/wine64" "/home/$USER/.steam/steam/steamapps/common/TaleSpire/TaleSpireUrlRelay.exe" %u'
StartupNotify=false
MimeType=x-scheme-handler/talespire;

And then add the mime type to xdg:

xdg-mime default xdg-talespire.desktop x-scheme-handler/talespire

Of note:

  • The actual path to wine64will likely differ from system to system based on: Installed Proton version(s) and default library location. If the library location is left to default it may still vary between distributions.

  • The actual path for the WINEPREFIX (= Wine’s working directory) depends on the install location of TaleSpire (which “library folder” in Steam it’s installed to), in the example above shows the default location in ~/.steam/. The same obviously also applies to the path to the TaleSpireUrlRelay.exe.

  • Some people needed to use WINEFSYNC=1 instead of WINEESYNC=1.

(Thanks Doskious and Adriannom on Discord for figuring this out!)

PS: For the official Linux support this setup should be done automatically, this is just a manual workaround while support is still unofficial.