From 54bb9a35bbd7a9856c57941aeccc5ba34926b7ad Mon Sep 17 00:00:00 2001 From: iHDeveloper <20463031+iHDeveloper@users.noreply.github.com> Date: Thu, 10 Dec 2020 10:35:13 +0300 Subject: [PATCH] feat(discord): Ability to change details of the game Used to set the gameplay name (e.g. Josharias Survival, Metal Renegades, etc...) It will show on discord in this format "Game: " - Improve the buffer change trigger and handler - Ability to change details in the buffer - Apply the new format for the rich presence -- Details -> "Game: " -- Party State -> "Playing Solo" / "Playing Online" / "Hosting" - Fix: Triggering `Thread#setEnabled` with keeping the connection alive - Fix: No safe shutdown to the RPC thread --- .idea/checkstyle-idea.xml | 2 +- .../discordrpc/DiscordRPCBuffer.java | 40 +++++++++++++- .../discordrpc/DiscordRPCSubSystem.java | 15 ++++++ .../discordrpc/DiscordRPCSystem.java | 12 +++-- .../discordrpc/DiscordRPCThread.java | 53 +++++++++++++------ 5 files changed, 99 insertions(+), 23 deletions(-) diff --git a/.idea/checkstyle-idea.xml b/.idea/checkstyle-idea.xml index 9d52d6a75e7..8b34de0df67 100644 --- a/.idea/checkstyle-idea.xml +++ b/.idea/checkstyle-idea.xml @@ -10,7 +10,7 @@ - + diff --git a/subsystems/DiscordRPC/src/main/java/org/terasology/engine/subsystem/discordrpc/DiscordRPCBuffer.java b/subsystems/DiscordRPC/src/main/java/org/terasology/engine/subsystem/discordrpc/DiscordRPCBuffer.java index c428fae860b..e08ddb5056b 100644 --- a/subsystems/DiscordRPC/src/main/java/org/terasology/engine/subsystem/discordrpc/DiscordRPCBuffer.java +++ b/subsystems/DiscordRPC/src/main/java/org/terasology/engine/subsystem/discordrpc/DiscordRPCBuffer.java @@ -11,14 +11,43 @@ * It helps avoiding allocating unnecessary objects for the rich presence. */ public final class DiscordRPCBuffer { + private String details; private String state; private OffsetDateTime startTimestamp; private boolean changed; + /** + * Resets the buffer data + */ + public synchronized void reset() { + this.details = null; + this.state = null; + this.startTimestamp = null; + this.changed = true; + } + + /** + * Sets the details of the current game + * + * @param details Details about the current game (null for nothing) + */ + public synchronized void setDetails(String details) { + this.details = details; + } + + /** + * Gets the details about the current game + * + * @return Detail about the current game + */ + public synchronized String getDetails() { + return details; + } + /** * Sets the current party status * - * @param state The current party status + * @param state The current party status (null for nothing) */ public synchronized void setState(String state) { this.state = state; @@ -62,6 +91,15 @@ public synchronized boolean hasChanged() { return changed; } + /** + * Check if the buffer is empty + * + * @return if the buffer is empty + */ + public synchronized boolean isEmpty() { + return this.details == null && this.state == null && this.startTimestamp == null; + } + /** * Resets the buffer's change state to false */ diff --git a/subsystems/DiscordRPC/src/main/java/org/terasology/engine/subsystem/discordrpc/DiscordRPCSubSystem.java b/subsystems/DiscordRPC/src/main/java/org/terasology/engine/subsystem/discordrpc/DiscordRPCSubSystem.java index aa09123700c..8c725fb0ffa 100644 --- a/subsystems/DiscordRPC/src/main/java/org/terasology/engine/subsystem/discordrpc/DiscordRPCSubSystem.java +++ b/subsystems/DiscordRPC/src/main/java/org/terasology/engine/subsystem/discordrpc/DiscordRPCSubSystem.java @@ -108,6 +108,21 @@ public static void discover() { getInstance().thread.discover(); } + /** + * Resets the current rich presence data + */ + public static void reset() { + getInstance().thread.getBuffer().reset(); + } + + /** + * Sets the name of the gameplay the player is playing (e.g. Custom, Josharias Survival, etc...) + * @param name the name of the gameplay + */ + public static void setGameplayName(String name) { + getInstance().thread.getBuffer().setDetails("Game: " + name); + } + /** * Sets the current game/party status for the player (e.g. Playing Solo, Idle, etc...) * diff --git a/subsystems/DiscordRPC/src/main/java/org/terasology/engine/subsystem/discordrpc/DiscordRPCSystem.java b/subsystems/DiscordRPC/src/main/java/org/terasology/engine/subsystem/discordrpc/DiscordRPCSystem.java index b30d2b465c4..946ba0721ec 100644 --- a/subsystems/DiscordRPC/src/main/java/org/terasology/engine/subsystem/discordrpc/DiscordRPCSystem.java +++ b/subsystems/DiscordRPC/src/main/java/org/terasology/engine/subsystem/discordrpc/DiscordRPCSystem.java @@ -41,7 +41,8 @@ public void initialise() { @Override public void preBegin() { - DiscordRPCSubSystem.setState(getGame()); + DiscordRPCSubSystem.setGameplayName("Custom"); + DiscordRPCSubSystem.setState(getPartyState()); DiscordRPCSubSystem.setStartTimestamp(null); } @@ -52,6 +53,7 @@ public void postBegin() { @Override public void shutdown() { + DiscordRPCSubSystem.reset(); DiscordRPCSubSystem.setState("In Main Menu"); DiscordRPCSubSystem.setStartTimestamp(null); } @@ -66,18 +68,18 @@ public void onAfk(AfkEvent event, EntityRef entityRef) { DiscordRPCSubSystem.setState("Idle"); DiscordRPCSubSystem.setStartTimestamp(null); } else { - DiscordRPCSubSystem.setState(getGame()); + DiscordRPCSubSystem.setState(getPartyState()); DiscordRPCSubSystem.setStartTimestamp(OffsetDateTime.now()); } } - public String getGame() { + public String getPartyState() { NetworkMode networkMode = networkSystem.getMode(); String mode = "Playing Online"; if (networkMode == NetworkMode.DEDICATED_SERVER) { - mode = "Hosting | " + game.getName(); + mode = "Hosting"; } else if (networkMode == NetworkMode.NONE) { - mode = "Solo | " + game.getName(); + mode = "Playing Solo"; } return mode; } diff --git a/subsystems/DiscordRPC/src/main/java/org/terasology/engine/subsystem/discordrpc/DiscordRPCThread.java b/subsystems/DiscordRPC/src/main/java/org/terasology/engine/subsystem/discordrpc/DiscordRPCThread.java index 0d579e8906e..b0acfa73e3f 100644 --- a/subsystems/DiscordRPC/src/main/java/org/terasology/engine/subsystem/discordrpc/DiscordRPCThread.java +++ b/subsystems/DiscordRPC/src/main/java/org/terasology/engine/subsystem/discordrpc/DiscordRPCThread.java @@ -54,15 +54,17 @@ public void start() { } public synchronized void stop() { - thread.interrupt(); + synchronized (thread) { + thread.interrupt(); + } } - public synchronized void discover() { + public synchronized void discover() { if (enabled && connected) { return; } - reset(); + reset(true); connectedBefore = true; } @@ -73,8 +75,7 @@ public synchronized void enable() { } enabled = true; - - reset(); + autoReconnect = true; if (waiting && thread.isAlive()) { synchronized (thread) { @@ -84,14 +85,20 @@ public synchronized void enable() { } public synchronized void disable() { + disable(false); + } + + public synchronized void disable(boolean keepConnectionAlive) { if (!enabled) { return; } enabled = false; - reset(); - autoReconnect = false; + reset(!keepConnectionAlive); + if (!keepConnectionAlive) { + autoReconnect = false; + } if (waiting && thread.isAlive()) { synchronized (thread) { @@ -194,14 +201,17 @@ public void run() { /* Update the rich presence and keeping the connection alive */ while (connected) { synchronized (this) { - /* Allocate a new rich presence when the buffer has changed */ - if (enabled && buffer.hasChanged()) { - lastRichPresence = build(); - buffer.resetState(); - } - /* Ping the ipc connection with an rich presnece to keep the connection alive */ if (enabled) { + /* Allocate a new rich presence when the buffer has changed */ + if (buffer.hasChanged() && buffer.isEmpty()) { + lastRichPresence = null; + buffer.resetState(); + } else if (buffer.hasChanged()) { + lastRichPresence = build(); + buffer.resetState(); + } + ipcClient.sendRichPresence(lastRichPresence); } else { ipcClient.sendRichPresence(null); @@ -221,6 +231,14 @@ public void run() { } public synchronized void setEnabled(boolean enabled) { + if (this.enabled != enabled) { + if (enabled) { + enable(); + } else { + disable(true); + } + } + this.enabled = enabled; } @@ -235,16 +253,19 @@ public synchronized DiscordRPCBuffer getBuffer() { private RichPresence build() { return new RichPresence.Builder() .setLargeImage(DISCORD_APP_DEFAULT_IMAGE) + .setDetails(buffer.getDetails()) .setState(buffer.getState()) .setStartTimestamp(buffer.getStartTimestamp()) .build(); } - private void reset() { + private void reset(boolean resetConnection) { tries = 0; autoReconnect = true; - connectedBefore = false; - connected = false; + if (resetConnection) { + connectedBefore = false; + connected = false; + } } }