Discord Rich Presence
Overview
The discord utility of LabyMod allows users to utilize the
Discord Rich Presence feature. The user can then share his current server and game (for example the specific lobby he
is on) using Discord.
Info
If you want your logo to be shown (and meet the requirements), make a Pull Request at LabyMod/server-media.
How it works
LabyMod is directly connected to the Discord API and serves as a bridge between the Minecraft server and Discord. The
communication is done using the plugin message channel LMC
with the key discord_rpc
.
Requirements
- LabyMod API (or use the Protocol directly)
The "secrets" explained
spectateSecret
: This is the secret that a user needs to spectate a game.joinSecret
: This is the secret that a user needs to join a gamematchSecret
: An id that is unique for each game on your server. Discord uses this to determine whether people are playing together
Other clients will use the spectateSecret
and the joinSecret
to spectate or join their friends game, so they need to be
unique for each user and should be valid only once (to prevent spamming)
All required id's or secrets must be specified in UUID format.
Send the secrets to the client
// The LabyMod client sends a "INFO" message in the "LMC" channel on join
if ( messageKey.equals( "INFO" ) ) {
JsonObject obj = new JsonObject();
// Enter your server IP here (The client uses this domain to join the server)
String domain = ".play.example.com";
// Add all secrets
addSecret( obj, "hasMatchSecret", "matchSecret", user.getGameServerId(), domain );
addSecret( obj, "hasSpectateSecret", "spectateSecret", user.getSpectateSecret(), domain );
addSecret( obj, "hasJoinSecret", "joinSecret", user.getJoinSecret(), domain );
LabyModProtocol.sendLabyModMessage( player, "discord_rpc", obj );
}
/**
* Add a secret key to the json object
* @param jsonObject json object
* @param hasKey has key name
* @param key key name
* @param secret the secret key
* @return json object
*/
public JsonObject addSecret( JsonObject jsonObject, String hasKey, String key, UUID secret, String domain) {
jsonObject.addProperty( hasKey, true );
jsonObject.addProperty( key, secret.toString() + ":" + domain );
return jsonObject;
}
Listen on incoming secret keys (User redeems a key)
Discord is distributing the secrets to the friends of the users, and when a friend chooses to spectate his friend through Discord, he is submitting that secret to the gameserver himself to authenticate itself. This means the server has to remember the active secrets it sends out so it can match them to the player and action they correspond to, so the friend can either join the game, the party or start spectating.
// User wants to join a friend -> he sends the secret to the server using the pluginchannel as before
if ( messageKey.equals( "discord_rpc" ) ) {
JsonObject obj = jsonElement.getAsJsonObject();
// Redeem cooldown (To prevent abuse) [optional]
if ( user.getLastDiscordSecretRedeem() + 1000 > System.currentTimeMillis() ) {
return;
}
user.setLastDiscordSecretRedeem( System.currentTimeMillis() );
// User has spectate secret
if ( obj.has( "spectateSecret" ) ) {
// Do make sure there is proper error handling in place. Client may send secrets that are not in UUID format!
UUID spectateSecret = UUID.fromString( obj.get( "spectateSecret" ).getAsString() );
// Get corresponding player, it may be necessary to replace this with a database on multiserver networks
User secretOwner = null;
for ( User user : <ALL_USERS_ON_NETWORK> ) {
UUID secret = user.getSpectateSecret();
if ( secret != null && secret.equals( spectateSecret ) ) {
secretOwner = user;
break;
}
}
// Move player to secret owner
if ( secretOwner != null && secretOwner.isOnline() ) {
// TODO: Move the 'user' to the 'secretOwner' on the server
// Create new secret (To prevent abuse)
secretOwner.createNewSpectateSecret();
// Send the new secret key to the owner (To prevent abuse)
String domain = "play.example.com";
LabyModProtocol.sendLabyModMessage( secretOwner, "discord_rpc", addSecret( new JsonObject(), "hasSpectateSecret", "spectateSecret", secretOwner.getSpectateSecret(), domain ) );
}
}
// User has join secret for the party
if ( obj.has( "joinSecret" ) ) {
UUID joinSecret = UUID.fromString( obj.get( "joinSecret" ).getAsString() );
// Get secret owner
User secretOwner = null;
for ( User user : <ALL_USERS_ON_NETWORK> ) {
UUID secret = user.getJoinSecret();
if ( secret != null && secret.equals( joinSecret ) ) {
secretOwner = user;
break;
}
}
// Move player to secret owner
if ( secretOwner != null && secretOwner.isOnline() ) {
// TODO: Move the 'user' to the 'secretOwner' on the server
// Create new secret (To prevent abuse)
secretOwner.createNewJoinSecret();
// Send the new secret key to the owner (To prevent abuse)
String domain = "play.example.com";
LabyModProtocol.sendLabyModMessage( secretOwner, "discord_rpc", addSecret( new JsonObject(), "hasJoinSecret", "joinSecret", secretOwner.getJoinSecret(), domain ) );
// TODO: Add 'user' to party of 'secretOwner'
}
}
}
Display current gamemode in Discord
private void updateGameInfo( Player player, boolean hasGame, String gamemode, long startTime, long endTime ) {
String domain = "play.example.com";
// Only for discord rpc users (Just set an flag to true when a LabyMod user joins the network) [optional]
if ( !user.isDiscordRPCActive() )
return;
// Create game json object
JsonObject obj = new JsonObject();
obj.addProperty( "hasGame", hasGame );
if ( hasGame ) {
obj.addProperty( "game_mode", gamemode );
obj.addProperty( "game_startTime", startTime ); // Set to 0 for countdown
obj.addProperty( "game_endTime", endTime ); // // Set to 0 for timer
}
// Send to user
LabyModProtocol.sendLabyModMessage( player, "discord_rpc", obj );
}
Display the party in Discord
You can send also a party invitation to other Discord users with the party information.
Method to update the party info:
private void updatePartyInfo( Player player, boolean hasParty, UUID partyLeaderUUID, int partySize, int maxPartyMembers ) {
String domain = "play.example.com";
// Only for discord rpc users (Just set an flag to true when a LabyMod user joins the network) [optional]
if ( !user.isDiscordRPCActive() )
return;
// Create party json object
JsonObject obj = new JsonObject();
obj.addProperty( "hasParty", hasParty );
if ( hasParty ) {
obj.addProperty( "partyId", partyLeaderUUID.toString() + ":" + domain );
obj.addProperty( "party_size", partySize );
obj.addProperty( "party_max", maxPartyMembers );
}
// Send to user
LabyModProtocol.sendLabyModMessage( player, "discord_rpc", obj );
}