-
Notifications
You must be signed in to change notification settings - Fork 2
Interacting with Discord Developer Portal
This script creates a RenPyDiscord
object stored in the discord
variable, which can be used to interact with Discord Rich Presence. It can only be run on Ren'Py 8 - Unlike Ren'Py 7, that one runs on Python 3, and the module this script depends on only has a Py3 version.
An Application set up on the Discord Developer Portal is required for every game supporting Rich Presence. After the App is created, you will receive the necessary Application ID to insert into settings.rpy, and it is also where all the images you plan on displaying in the presence need to be uploaded first.
Instructions on how all of that is done are found under the Wiki tab on this GitHub page, local copy of which is included in the code files, as mentioned above.
To get the script, download one of the releases on the right side of the GitHub page, under the Releases section. Here are the files that you need to put into your game folder:
- python-packages folder - contains the pypresence module that handles everything Discord-related
-
RenPy_Discord_Presence folder, containing this script's files:
- rich_presence.rpy holds the entire script code
- settings.rpy contains the two Related Variables described below.
- discord_developer_portal.md is a local copy of the Wiki tab on this GitHub page.
There are two versions for every release:
Project Version contains the whole code of this repository. It is a project that can be launched from the Ren'Py Launcher and that shows how simple it is to update the presence status from both screens and labels, utilizing the set
and update
methods. Simply launch the project and keep an eye out on your Discord profile.
Standalone Version does not contain the project files and only contains what you need, that is the files listed above.
There are two variables defined in the settings.rpy file that you need to set before using the code. Here is what they do and what their default value is:
application_id
takes a string with an Application ID of your Application set up on Discord Developer Portal.
define rich_presence.application_id = "10208ABCDEFGHIJ2795"
initial_state
takes a dictionary. Keys are strings of properties corresponding to presence elements (listed below), and values are their values.
This is the state shown in the presence anytime the game launches and/or enters the main menu.
define rich_presence.initial_state = { "details" : "Testing Discord Rich Presence.",
"state" : "It's super easy in Ren'Py 8!",
"large_image" : "lezalith",
"small_image" : "lezalith"}
Methods used to interact with the presence are bound to a defined RenPyDiscord
instance stored in the discord
variable.
Here are the core three:
discord.set
takes the keep_time
argument which is True
by default, to determine whether the Elapsed Time shown should be reset to 0:0 with this change.
As for the arguments that follow, they should correspond to presence elements - all are listed below.
If some properties are already set in the presence, they are discarded, and only the passed properties are kept.
discord.set(keep_time = False, details = "Setting new Discord Rich Presence.")
discord.update
takes keep_time
with default True
and properties corresponding to presence elements just like discord.set
does.
Difference between the two is that discord.update
keeps the current properties and only sets the ones provided.
discord.set(details = "Setting new Discord Rich Presence.")
discord.update(state = "State got changed while details stayed the same!")
Finally, discord.change_time
is a method specialized in changing the Time Elapsed. It takes the optional timestamp
argument, which is an epoch timestamp from which Time Elapsed is calculated. If it's None
, as it is by default, the time gets reset to 0:0.
discord.set(details = "Let's set Time Elapsed to 600 seconds, aka 10 minutes!")
discord.change_time(timestamp = time.time() + 600)
There are many things that can be shown inside Rich Presence. Below is a screenshot of a couple elements of Rich Presence highlighted, with their property name equivalent below. They are all listed below the screenshot with a short example using discord.update
.
In the preview project, dictionary with all the properties for this example is stored in the rich_presence.first_example
variable, and rich_presence.initial_state
is redirected to it.
details
takes a string, and is the upper line of text shown in the Presence.
discord.update(details = "Testing Discord Rich Presence.")
state
takes a string, and is the lower line of text shown in the Presence.
discord.update(state = "It's super easy in Ren'Py 8!")
start
takes an epoch timestamp from which it counts the Time Elapsed. However, start
should only be changed with the specialized change_time
method described above in List of Methods.
Snippet below shows it with the timestamp
not given, causing it to set Time Elapsed to 0:0.
discord.change_time()
large_image
takes a string that needs to correspond with an image uploaded onto the Discord Application.
It is the large image shown on the left side.
discord.update(large_image = "lezalith")
small_image
takes a string that needs to correspond with an image uploaded onto the Discord Application.
It is the smaller image, shown at the bottom right of the large_image
.
If no large_image
is set, small_image
is used in its place and no smaller image at the bottom right is shown.
discord.update(large_image = "lezalith", small_image = "lezalith")
time
is a special non-pypresence property that can be True
or False
.
If True
(the default), Elapsed Time is shown in the presence. If False
, it is hidden.
discord.update(time = False)
Overall, state shown on the screenshot can be accomplished with the following properties passed:
discord.set(details = "Testing Discord Rich Presence.",
state = "It's super easy in Ren'Py 8!",
large_image = "lezalith",
large_text = "Large Image Tooltip!",
small_image = "lezalith",
small_text = "Small Image Tooltip!",
time = True)
As you can see, there are two more variables included there that I haven't mentioned - large_text
and small_text
. These are text tooltips that appear when the respective images are hovered by a cursor.
Another screenshot covers all the remaining rich presence properties. state
was covered above, however it is required for the party_size
property to work.
In the preview project, dictionary with all the properties for this example is stored in the rich_presence.second_example
variable.
end
takes an epoch timestamp and calculates Time Left until that timestamp.
Setting this pushes start
completely aside and the Time Elapsed with it, displaying Time Left instead.
discord.update(end = time.time() + 3000)
party_size
takes a list of two ints. This is used for multiplayer games, where the two numbers represent the current party size and max party size respectively. We can still use it with visual novels if we're creative enough.
For the party_size
to be visible, state
has to be also provided.
discord.update(state = "Reading a Chapter", party_size = [1, 5])
buttons
takes a list of up to two dicts, and allows for buttons to be added into the Presence. The dict consists of two keys, label
being the text written on the button, and url
being the url opened upon clicking it.
Note: Clicking buttons in your own Presence - i.e. in your own profile - does nothing.
discord.update(buttons = [ dict(label = "Discord Presence Example Button", url = "https://github.com/Lezalith/RenPy_Discord_Presence"),
dict(label = "Lezalith's Promotion Button!", url = "https://www.lezcave.com") ]
Putting all of that together, state shown on the second example screenshot was created with these properties:
discord.set(state = "Reading a Chapter",
end = time.time() + 3000,
party_size = [1, 5],
buttons = [ dict(label = "Discord Presence Example Button", url = "https://github.com/Lezalith/RenPy_Discord_Presence"),
dict(label = "Lezalith's Promotion Button!", url = "https://www.lezcave.com") ])
Discord presence only gets updated for users who have the Discord desktop application installed. For players that do not have Discord installed, this entire code will simply do nothing. discord
variable is still defined as the RenPyDiscord
object, but none of its methods does anything.
This means that players with Discord can enjoy the benefits, and those who do not have it aren't hindered in any way.
All presence properties, along with time property that determines whether Time Elapsed is shown, are saved in save files and restored when the save is loaded.
Upon loading a save, Time Elapsed is reset to 0:0.
The presence should be fully compatible with rollback and rollforward features of Ren'Py.
Connecting to the Discord Rich Presence multiple times in quick succession will result in the connection not being established. This makes the program unresponsive and unable to be launched again for about the next 30s. I'd imagine it's a precation against malicious exploits on Discord's side, and cannot be affected by Python code.
In practice, this happens when you...
- ...launch and quit...
- ...reload...
- ...start and return to the main menu in...
...the game too many times too fast.
Here is an example of two textbutton
s in a screen, one with discord.set
and other with discord.update
.
screen screen_example():
vbox:
align (0.5, 0.5)
textbutton "This one sets the state only.":
action Function(discord.set, state = "Example State.")
textbutton "This sets details and leaves the state alone.":
action Function(discord.update, details = "Example Details.")
And here is the example label used inside the preview project that showcases all of the functions. Everything is explained by the dialogue lines.
label label_example():
"This label shows how discord presence can be changed inside labels!"
"After this line, {b}discord.set{/b} will be called to set presence to only contain a {b}state{/b} text line."
$ discord.set(state = "Update after the first line.")
"And it is so. Elapsed Time stayed the same."
"After this line, {b}discord.set{/b} is called again, with {b}keep_time = False{/b} argument passed, so that the time gets set to 0:0."
$ discord.set(state = "Second update, with the time reset.", keep_time = False)
"State updated and Elapsed Time reset."
"After this line, {b}discord.update{/b} is called to change the {b}details{/b} property, while keeping others ({b}state{/b} and {b}time{/b}) as they are."
$ discord.update(details = "This wasn't here before!")
"Presence now contains one more piece of info."
"After this line, {b}discord.change_time{/b} is called to change the Time Elapsed shown. The time is reset and 3000 seconds are added to it."
$ discord.change_time(timestamp = time.time() - 3000)
"3000 seconds is 50 minutes, so that's what Time Elapsed got set to."
"By default, the Time Elapsed is shown. It can be hidden by setting the {b}time{/b} property to False."
"It is done with {b}discord.update{/b} after this line."
$ discord.update(time = False)
"As you can see, Time Elapsed is no longer visible."
"Finally, after this line, {b}discord.clear{/b} is called to clear the presence."
$ discord.clear()
"Presence all cleared and hidden!"
"After this line, you will return to the main menu, and {b}initial_state{/b} will be restored."
return