Skip to content

How does this work?

Cody Swendrowski edited this page Dec 31, 2019 · 2 revisions

Looking for the nerdy technical stuff? You've come to the right place!

The Discord GameSDK and you

Let's say you and your friend both download a hot new game, "Ultimate Rock Paper Scissors Adventure" (which we'll shorten to "URPSA").

The URPSA devs has embedded the Discord GameSDK into their product, and now you and your friend wanna take advantage of it.

On install (or at least at first run), URPSA tells your local Desktop Discord "Hey! I'm URPSA with Discord Game ID 123ABC. You can run me on this machine by calling D:\Games\URPSA.exe". This teaches Discord everything it needs to know about URPSA for now.

Later on, you launch the game and create a lobby (max size 2 players, 1 current player). URPSA assigns your lobby the ID Lobby182, and then tells Discord "Hey, the player is currently playing game 123ABC, they are currently Waiting in Lobby, their Party size is 2 with a current size of 1, and the Join secret is Lobby182.

(Excepting the name Foundry VTT,) your Discord Rich Presence now looks like so:

Your friend "Bob" sees your status and hits Ask to join. You accept (in Discord), and Bob's Discord starts launching game 123ABC with Join secret Lobby182. On Bob's machine, Discord was taught to launch E:\MyGames\URPSA.exe, so it does. Once URPSA launches, the Discord GameSDK informs the game that "Hey, you should join someone using secret Lobby182. URPSA handles actually using that info to get Bob connected, and now Bob is in your lobby! On Bob's machine URPSA sends the same info to Discord, but now the party size is 2 of 2.

Everyone is now in the Lobby, so they can advance to Playing the game, which removes the Join link and updates their statuses in Discord accordingly

Additionally, Discord GameSDK provides voice via a parallel Discord network. It's all of the same quality and connection options as the main Discord app, but dedicated to GameSDK usage only. It's goal is "provide easy voice lobbies for games". URPSA might allow you to talk smack with your opponent via this network. Since the network is separate, you can talk to a friend about strategy in the Discord app while also talking smack with your opponent in URPSA. The downside of this is that you can't cross the streams - despite both being powered by Discord, GameSDK voice lobbies can never talk with App voice lobbies.

How this Module abuses works with the Discord GameSDK

There's a few core problems with the above approach when it comes to adding Rich Presence to Foundry -

  1. Foundry (unless your PC is the host), is not installed on your machine, it runs in the browser
  2. The browser cannot talk to Discord through the GameSDK due to sandboxing
  3. The Companion API running does not always mean the User is in Foundry

How do we solve this? You might have guessed it - the Companion API.

The Companion API

The Companion solves some of problem #1 and all of #2. As far as Discord is concerned, the Companion API is the game. The first time you run the Companion, it registers itself with Discord saying "You can run me at C:\Downloads\RichPresenceApi.exe". The Companion also registers itself with your system with a custom schema so that foundryvtt-richpresence://run will run the Companion, but we'll get to that in a bit.

As problem #3 mentions however, just because the Companion is running doesn't mean we should tell Discord that the User is in Foundry. Even if we did, we don't have any info to send yet! To actually get information, we need the Foundry Module to tell us what's going on in the game (if it's running!). To receive that information, the Companion exposes an HTTP API (hence why it's called the Companion API), solving problem #2 - the browser sandbox allows us to still send HTTP messages. If the Companion hasn't heard from the API in a bit (roughly a minute), then the Companion shuts down, clearing any Rich Presence currently set on the way out.

None of this gets us a Rich Presence yet, just the ability to set, clear, and handle joins.

The Foundry Module

Rich Presence isn't very cool if you don't have game information to set, but luckily for us, the Foundry Module has complete access to the game state!

The Module allows consumers to configure messages, such as Playing as [[game.user.character.name]], replacing [[ ]] syntax with dynamic values from Foundry. This text (along with current users, total users, world ID, and foundry remote URL) get shipped to the Companion API for display.

If the Companion API can't be reached, the Module attempts to launch it using the (previously configured) foundryvtt-richpresence://run custom scheme.

If the API can be reached, the Companion takes care of displaying the Rich Presence, updating from the module every 15 seconds.

Once the User leaves the page, the Module tells the Companion API to remove status and close itself. If the API misses this message because the browser window closed too soon, it'll do the same itself after a minute of no messages. No need for you to launch nor close the Companion yourself after first launch unless you want to leave the companion running constantly!

The Module also has buttons for control of Voice - using websockets for a persistent and real-time connection, clicking a button in Foundry will almost immediately join, leave, mute, or deafen voice chat. Discord GameSDK will also send the Module info about who is currently talking (delivered through the Companion as always), which is rendered in Foundry.

Handling Joins

With Data flowing in from the Module and being set in Discord by the Companion, the last thing to discuss is Joins - we want to be able to have our friends join our Foundry instance, but the Companion still isn't Foundry!

To work around this, the Module sends along the Remote URL for the Foundry instance, such as http://myfoundryinstance.com, along with a "match id" of the World Id (just in case you run multiple games out of the same Foundry instance). The Companion then tells Discord "you can join match 13th Age Campaign using secret http://myfoundryinstance.com/join". When another User clicks join, their instance of the Companion will launch and be told "Join 13th Age Campaign using http://myfoundryinstance.com/join". The Companion then tells your local shell "hey can you open the default browser to http://myfoundryinstance.com/join?". Bam, now you've joined.