diff options
Diffstat (limited to 'src/main/java/net/arikia/dev')
11 files changed, 747 insertions, 0 deletions
diff --git a/src/main/java/net/arikia/dev/drpc/DiscordEventHandlers.java b/src/main/java/net/arikia/dev/drpc/DiscordEventHandlers.java new file mode 100644 index 00000000..3e5ccbbd --- /dev/null +++ b/src/main/java/net/arikia/dev/drpc/DiscordEventHandlers.java @@ -0,0 +1,90 @@ +package net.arikia.dev.drpc; + +import com.sun.jna.Structure; +import net.arikia.dev.drpc.callbacks.*; + +import java.util.Arrays; +import java.util.List; + +/** + * @author Nicolas "Vatuu" Adamoglou + * @version 1.5.1 + * <p> + * Object containing references to all event handlers registered. No callbacks are necessary, + * every event handler is optional. Non-assigned handlers are being ignored. + */ +public class DiscordEventHandlers extends Structure { + + /** + * Callback called when Discord-RPC was initialized successfully. + */ + public ReadyCallback ready; + /** + * Callback called when the Discord connection was disconnected. + */ + public DisconnectedCallback disconnected; + /** + * Callback called when a Discord error occurred. + */ + public ErroredCallback errored; + /** + * Callback called when the player joins the game. + */ + public JoinGameCallback joinGame; + /** + * Callback called when the player spectates a game. + */ + public SpectateGameCallback spectateGame; + /** + * Callback called when a join request is received. + */ + public JoinRequestCallback joinRequest; + + @Override + public List<String> getFieldOrder() { + return Arrays.asList("ready", "disconnected", "errored", "joinGame", "spectateGame", "joinRequest"); + } + + public static class Builder { + + DiscordEventHandlers h; + + public Builder() { + h = new DiscordEventHandlers(); + } + + public Builder setReadyEventHandler(ReadyCallback r) { + h.ready = r; + return this; + } + + public Builder setDisconnectedEventHandler(DisconnectedCallback d) { + h.disconnected = d; + return this; + } + + public Builder setErroredEventHandler(ErroredCallback e) { + h.errored = e; + return this; + } + + public Builder setJoinGameEventHandler(JoinGameCallback j) { + h.joinGame = j; + return this; + } + + public Builder setSpectateGameEventHandler(SpectateGameCallback s) { + h.spectateGame = s; + return this; + } + + public Builder setJoinRequestEventHandler(JoinRequestCallback j) { + h.joinRequest = j; + return this; + } + + public DiscordEventHandlers build() { + return h; + } + } +} diff --git a/src/main/java/net/arikia/dev/drpc/DiscordRPC.java b/src/main/java/net/arikia/dev/drpc/DiscordRPC.java new file mode 100644 index 00000000..b16974ec --- /dev/null +++ b/src/main/java/net/arikia/dev/drpc/DiscordRPC.java @@ -0,0 +1,234 @@ +package net.arikia.dev.drpc; + +import com.sun.jna.Library; +import com.sun.jna.Native; + +import java.io.*; + +/** + * @author Nicolas "Vatuu" Adamoglou + * @version 1.5.1 + * <p> + * Java Wrapper of the Discord-RPC Library for Discord Rich Presence. + */ +public final class DiscordRPC { + + //DLL-Version for Update Check (soon). + private static final String DLL_VERSION = "3.4.0"; + private static final String LIB_VERSION = "1.6.2"; + + static { + loadDLL(); + } + + /** + * Method to initialize the Discord-RPC. + * + * @param applicationId ApplicationID/ClientID + * @param handlers EventHandlers + * @param autoRegister AutoRegister + */ + public static void discordInitialize(String applicationId, DiscordEventHandlers handlers, boolean autoRegister) { + DLL.INSTANCE.Discord_Initialize(applicationId, handlers, autoRegister ? 1 : 0, null); + } + + /** + * Method to register the executable of the application/game. + * Only applicable when autoRegister in discordInitialize is false. + * + * @param applicationId ApplicationID/ClientID + * @param command Launch Command of the application/game. + */ + public static void discordRegister(String applicationId, String command) { + DLL.INSTANCE.Discord_Register(applicationId, command); + } + + /** + * Method to initialize the Discord-RPC within a Steam Application. + * + * @param applicationId ApplicationID/ClientID + * @param handlers EventHandlers + * @param autoRegister AutoRegister + * @param steamId SteamAppID + * @see DiscordEventHandlers + */ + public static void discordInitialize(String applicationId, DiscordEventHandlers handlers, boolean autoRegister, String steamId) { + DLL.INSTANCE.Discord_Initialize(applicationId, handlers, autoRegister ? 1 : 0, steamId); + } + + /** + * Method to register the Steam-Executable of the application/game. + * Only applicable when autoRegister in discordInitializeSteam is false. + * + * @param applicationId ApplicationID/ClientID + * @param steamId SteamID of the application/game. + */ + public static void discordRegisterSteam(String applicationId, String steamId) { + DLL.INSTANCE.Discord_RegisterSteamGame(applicationId, steamId); + } + + /** + * Method to update the registered EventHandlers, after the initialization was + * already called. + * + * @param handlers DiscordEventHandler object with updated callbacks. + */ + public static void discordUpdateEventHandlers(DiscordEventHandlers handlers) { + DLL.INSTANCE.Discord_UpdateHandlers(handlers); + } + + /** + * Method to shutdown the Discord-RPC from within the application. + */ + public static void discordShutdown() { + DLL.INSTANCE.Discord_Shutdown(); + } + + /** + * Method to call Callbacks from within the library. + * Must be called periodically. + */ + public static void discordRunCallbacks() { + DLL.INSTANCE.Discord_RunCallbacks(); + } + + /** + * Method to update the DiscordRichPresence of the client. + * + * @param presence Instance of DiscordRichPresence + * @see DiscordRichPresence + */ + public static void discordUpdatePresence(DiscordRichPresence presence) { + DLL.INSTANCE.Discord_UpdatePresence(presence); + } + + /** + * Method to clear(and therefor hide) the DiscordRichPresence until a new + * presence is applied. + */ + public static void discordClearPresence() { + DLL.INSTANCE.Discord_ClearPresence(); + } + + /** + * Method to respond to Join/Spectate Callback. + * + * @param userId UserID of the user to respond to. + * @param reply DiscordReply to request. + * @see DiscordReply + */ + public static void discordRespond(String userId, DiscordReply reply) { + DLL.INSTANCE.Discord_Respond(userId, reply.reply); + } + + //Load DLL depending on the user's architecture. + private static void loadDLL() { + String name = System.mapLibraryName("discord-rpc"); + OSUtil osUtil = new OSUtil(); + String finalPath; + String dir; + + if (osUtil.isMac()) { + dir = "darwin"; + } else if (osUtil.isWindows()) { + boolean is64bit = System.getProperty("sun.arch.data.model").equals("64"); + dir = (is64bit ? "win-x64" : "win-x86"); + } else { + dir = "linux"; + } + + finalPath = "/" + dir + "/" + name; + + try { + File f = File.createTempFile("drpc", name); + + try (InputStream in = DiscordRPC.class.getResourceAsStream(finalPath); OutputStream out = openOutputStream(f)) { + copyFile(in, out); + f.deleteOnExit(); + } catch (IOException e) { + e.printStackTrace(); + } + + System.load(f.getAbsolutePath()); + } catch(Exception e) { + e.printStackTrace(); + } + } + + private static void copyFile(final InputStream input, final OutputStream output) throws IOException { + byte[] buffer = new byte[1024 * 4]; + int n; + while (-1 != (n = input.read(buffer))) { + output.write(buffer, 0, n); + } + } + + private static FileOutputStream openOutputStream(final File file) throws IOException { + if (file.exists()) { + if (file.isDirectory()) { + throw new IOException("File '" + file + "' exists but is a directory"); + } + if (!file.canWrite()) { + throw new IOException("File '" + file + "' cannot be written to"); + } + } else { + final File parent = file.getParentFile(); + if (parent != null) { + if (!parent.mkdirs() && !parent.isDirectory()) { + throw new IOException("Directory '" + parent + "' could not be created"); + } + } + } + return new FileOutputStream(file); + } + + //------------------------ Taken from apache commons ------------------------------// + + /** + * Enum containing reply codes for join request events. + * + * @see net.arikia.dev.drpc.callbacks.JoinRequestCallback + */ + public enum DiscordReply { + /** + * Denies the join request immediately. + * Currently behaving the same way like DiscordReply.IGNORE. + */ + NO(0), + /** + * Accepts the join request, requesting player received a JoinGameCallback. + * + * @see net.arikia.dev.drpc.callbacks.JoinGameCallback + */ + YES(1), + /** + * Denies the join request by letting it time out(10s). + */ + IGNORE(2); + + /** + * Integer reply code send to Discord. + */ + public final int reply; + + DiscordReply(int reply) { + this.reply = reply; + } + } + + //JNA Interface + private interface DLL extends Library { + //DLL INSTANCE = Native.load("discord-rpc", DLL.class); + DLL INSTANCE = Native.loadLibrary("discord-rpc", DLL.class); + + void Discord_Initialize(String applicationId, DiscordEventHandlers handlers, int autoRegister, String optionalSteamId); + void Discord_Register(String applicationId, String command); + void Discord_RegisterSteamGame(String applicationId, String steamId); + void Discord_UpdateHandlers(DiscordEventHandlers handlers); + void Discord_Shutdown(); + void Discord_RunCallbacks(); + void Discord_UpdatePresence(DiscordRichPresence presence); + void Discord_ClearPresence(); + void Discord_Respond(String userId, int reply); + } +} diff --git a/src/main/java/net/arikia/dev/drpc/DiscordRichPresence.java b/src/main/java/net/arikia/dev/drpc/DiscordRichPresence.java new file mode 100644 index 00000000..d45c0a63 --- /dev/null +++ b/src/main/java/net/arikia/dev/drpc/DiscordRichPresence.java @@ -0,0 +1,232 @@ +package net.arikia.dev.drpc; + +import com.sun.jna.Structure; + +import java.util.Arrays; +import java.util.List; + +/** + * @author Nicolas "Vatuu" Adamoglou + * @version 1.5.1 + */ +public class DiscordRichPresence extends Structure { + + /** + * State of the player's current party. + */ + public String state; + /** + * Details to the current game-session of the player. + */ + public String details; + /** + * Unix timestamp for the start of the game + */ + public long startTimestamp; + /** + * Unix timestamp for when the game will end + */ + public long endTimestamp; + /** + * Name of the uploaded image for the large profile artwork. + */ + public String largeImageKey; + /** + * Tooltip for the largeImageKey + */ + public String largeImageText; + /** + * Name of the uploaded image for the small profile artwork. + */ + public String smallImageKey; + /** + * Tooltip for the smallImageKey + */ + public String smallImageText; + /** + * Id of the player's party, lobby, or group. + */ + public String partyId; + /** + * Current size of the player's party, lobby, or group. + */ + public int partySize; + /** + * Maximum size of the player's party, lobby, or group. + */ + public int partyMax; + /** + * Unused. + */ + @Deprecated + public String matchSecret; + /** + * Unique hashed string for Spectate button. + */ + public String spectateSecret; + /** + * Unique hashed string for chat invitations and Ask to Join. + */ + public String joinSecret; + /** + * Unused. + */ + @Deprecated + public int instance; + + @Override + public List<String> getFieldOrder() { + return Arrays.asList("state", "details", "startTimestamp", "endTimestamp", "largeImageKey", "largeImageText", "smallImageKey", "smallImageText", "partyId", "partySize", "partyMax", "matchSecret", "spectateSecret", "joinSecret","instance"); + } + + /*+ + * Builder object provided to easily assemble DiscordRichPresence objects without having to add a huge assignment Block. + * No method is essential, not called methods/unassigned fields are simply ignored and not applied in the final DiscordRichPresence + * seen inside the Discord client. + */ + public static class Builder { + + private DiscordRichPresence p; + + /** + * Initiates a new instance of the Presence builder. + * + * @param state String representing the player's current state. + * @see DiscordRichPresence + */ + public Builder(String state) { + p = new DiscordRichPresence(); + p.state = state; + } + + /** + * Sets the details field of the DiscordRichPresence object. + * + * @param details String representing details to the player's current state. + * @return Current Builder object. + * @see DiscordRichPresence + */ + public Builder setDetails(String details) { + p.details = details; + return this; + } + + /** + * Sets the starting timestamps of the DiscordRichPresence object, to activate the timer display. + * + * @param start Long Unix Timestamp representing the starting point of the timer. + * @return Current Builder object. + * @see DiscordRichPresence + */ + public Builder setStartTimestamps(long start) { + p.startTimestamp = start; + return this; + } + + /** + * Sets the ending timestamps of the DiscordRichPresence object, to activate the timer display. + * + * @param end Long Unix Timestamp representing the ending point of the timer. + * @return Current Builder object. + * @see DiscordRichPresence + */ + public Builder setEndTimestamp(long end) { + p.endTimestamp = end; + return this; + } + + /** + * Sets the large image fields of the DiscordRichPresence object. key cannot be null when text is not null. + * + * @param key String key assigned to the image asset inside of the Discord Application. + * @param text String text shown as hover text when hovering over the image of the presence. + * @return Current Builder object. + * @see DiscordRichPresence + */ + public Builder setBigImage(String key, String text) { + if ((text != null && !text.equalsIgnoreCase("")) && key == null) + throw new IllegalArgumentException("Image key must not be null when assigning a hover text."); + + p.largeImageKey = key; + p.largeImageText = text; + return this; + } + + /** + * Sets the small image fields of the DiscordRichPresence object. key cannot be null when text is not null. + * + * @param key String key assigned to the image asset inside of the Discord Application. + * @param text String text shown as hover text when hovering over the image of the presence. + * @return Current Builder object. + * @see DiscordRichPresence + */ + public Builder setSmallImage(String key, String text) { + if ((text != null && !text.equalsIgnoreCase("")) && key == null) + throw new IllegalArgumentException("Image key must not be null when assigning a hover text."); + + p.smallImageKey = key; + p.smallImageText = text; + return this; + } + + /** + * Sets the party information for the "Party" section of the user's presence. + * + * @param party Unique String given to the party as identifier. + * @param size Integer representing the current size of the user's party. + * @param max Integer representing the maximal size of the user's party. + * @return Current Builder object. + * @see DiscordRichPresence + */ + public Builder setParty(String party, int size, int max) { + p.partyId = party; + p.partySize = size; + p.partyMax = max; + return this; + } + + /** + * Unused. + */ + @Deprecated + public Builder setSecrets(String match, String join, String spectate) { + p.matchSecret = match; + p.joinSecret = join; + p.spectateSecret = spectate; + return this; + } + + /** + * Sets the secret fields of the DiscordRichPresence object. + * + * @param join Unique String containing necessary information passed to the joining player. + * @param spectate Unique String containing necessary information passed to the spectating player. + * @return Current Builder object. + * @see DiscordRichPresence + */ + public Builder setSecrets(String join, String spectate) { + p.joinSecret = join; + p.spectateSecret = spectate; + return this; + } + + /** + * Unused. + */ + @Deprecated + public Builder setInstance(boolean i) { + p.instance = i ? 1 : 0; + return this; + } + + /** + * Returns the fully finished DiscordRichPresence object. Non-assigned fields are being ignored. + * + * @return The build DiscordRichPresence object. + * @see DiscordRichPresence + */ + public DiscordRichPresence build() { + return p; + } + } +} diff --git a/src/main/java/net/arikia/dev/drpc/DiscordUser.java b/src/main/java/net/arikia/dev/drpc/DiscordUser.java new file mode 100644 index 00000000..7c735393 --- /dev/null +++ b/src/main/java/net/arikia/dev/drpc/DiscordUser.java @@ -0,0 +1,39 @@ +package net.arikia.dev.drpc; + +import com.sun.jna.Structure; + +import java.util.Arrays; +import java.util.List; + +/** + * @author Nicolas "Vatuu" Adamoglou + * @version 1.5.1 + * <p> + * Object containing information about a Discord user. + */ +public class DiscordUser extends Structure { + + /** + * The userId of the player asking to join. + */ + public String userId; + /** + * The username of the player asking to join. + */ + public String username; + /** + * The discriminator of the player asking to join. + */ + public String discriminator; + /** + * The avatar hash of the player asking to join. + * + * @see <a href="https://discordapp.com/developers/docs/reference#image-formatting">Image Formatting</a> + */ + public String avatar; + + @Override + public List<String> getFieldOrder() { + return Arrays.asList("userId", "username", "discriminator", "avatar"); + } +}
\ No newline at end of file diff --git a/src/main/java/net/arikia/dev/drpc/OSUtil.java b/src/main/java/net/arikia/dev/drpc/OSUtil.java new file mode 100644 index 00000000..74ca35c6 --- /dev/null +++ b/src/main/java/net/arikia/dev/drpc/OSUtil.java @@ -0,0 +1,25 @@ +package net.arikia.dev.drpc; + +/** + * @author DeJay + * @version 1.6.1 + * <p> + * Class containing utils for detecting the user's OS. + */ +public final class OSUtil { + + public boolean isMac() { + return getOS().toLowerCase() + .startsWith("mac"); + } + + public boolean isWindows() { + return getOS().toLowerCase() + .startsWith("win"); + } + + public String getOS() { + return System.getProperty("os.name").toLowerCase(); + } + +}
\ No newline at end of file diff --git a/src/main/java/net/arikia/dev/drpc/callbacks/DisconnectedCallback.java b/src/main/java/net/arikia/dev/drpc/callbacks/DisconnectedCallback.java new file mode 100644 index 00000000..ccc11a24 --- /dev/null +++ b/src/main/java/net/arikia/dev/drpc/callbacks/DisconnectedCallback.java @@ -0,0 +1,21 @@ +package net.arikia.dev.drpc.callbacks; + +import com.sun.jna.Callback; + +/** + * @author Nicolas "Vatuu" Adamoglou + * @version 1.5.1 + * <p> + * Interface to be implemented in classes that will be registered as "DisconnectedCallback" Event Handler. + * @see net.arikia.dev.drpc.DiscordEventHandlers + **/ +public interface DisconnectedCallback extends Callback { + + /** + * Method called when disconnected. + * + * @param errorCode Error code returned on disconnection. + * @param message Message containing details about the disconnection. + */ + void apply(int errorCode, String message); +} diff --git a/src/main/java/net/arikia/dev/drpc/callbacks/ErroredCallback.java b/src/main/java/net/arikia/dev/drpc/callbacks/ErroredCallback.java new file mode 100644 index 00000000..f5d736d7 --- /dev/null +++ b/src/main/java/net/arikia/dev/drpc/callbacks/ErroredCallback.java @@ -0,0 +1,21 @@ +package net.arikia.dev.drpc.callbacks; + +import com.sun.jna.Callback; + +/** + * @author Nicolas "Vatuu" Adamoglou + * @version 1.5.1 + * <p> + * Interface to be implemented in classes that will be registered as "ErroredCallback" Event Handler. + * @see net.arikia.dev.drpc.DiscordEventHandlers + **/ +public interface ErroredCallback extends Callback { + + /** + * Method called when a error occurs. + * + * @param errorCode Error code returned. + * @param message Message containing details about the error. + */ + void apply(int errorCode, String message); +} diff --git a/src/main/java/net/arikia/dev/drpc/callbacks/JoinGameCallback.java b/src/main/java/net/arikia/dev/drpc/callbacks/JoinGameCallback.java new file mode 100644 index 00000000..c4e865db --- /dev/null +++ b/src/main/java/net/arikia/dev/drpc/callbacks/JoinGameCallback.java @@ -0,0 +1,20 @@ +package net.arikia.dev.drpc.callbacks; + +import com.sun.jna.Callback; + +/** + * @author Nicolas "Vatuu" Adamoglou + * @version 1.5.1 + * <p> + * Interface to be implemented in classes that will be registered as "JoinGameCallback" Event Handler. + * @see net.arikia.dev.drpc.DiscordEventHandlers + **/ +public interface JoinGameCallback extends Callback { + + /** + * Method called when joining a game. + * + * @param joinSecret Unique String containing information needed to let the player join. + */ + void apply(String joinSecret); +} diff --git a/src/main/java/net/arikia/dev/drpc/callbacks/JoinRequestCallback.java b/src/main/java/net/arikia/dev/drpc/callbacks/JoinRequestCallback.java new file mode 100644 index 00000000..ddcc9614 --- /dev/null +++ b/src/main/java/net/arikia/dev/drpc/callbacks/JoinRequestCallback.java @@ -0,0 +1,22 @@ +package net.arikia.dev.drpc.callbacks; + +import com.sun.jna.Callback; +import net.arikia.dev.drpc.DiscordUser; + +/** + * @author Nicolas "Vatuu" Adamoglou + * @version 1.5.1 + * <p> + * Interface to be implemented in classes that will be registered as "JoinRequestCallback" Event Handler. + * @see net.arikia.dev.drpc.DiscordEventHandlers + **/ +public interface JoinRequestCallback extends Callback { + + /** + * Method called when another player requests to join a game. + * + * @param request Object containing all required information about the user requesting to join. + * @see DiscordUser + */ + void apply(DiscordUser request); +} diff --git a/src/main/java/net/arikia/dev/drpc/callbacks/ReadyCallback.java b/src/main/java/net/arikia/dev/drpc/callbacks/ReadyCallback.java new file mode 100644 index 00000000..292ce2fd --- /dev/null +++ b/src/main/java/net/arikia/dev/drpc/callbacks/ReadyCallback.java @@ -0,0 +1,23 @@ +package net.arikia.dev.drpc.callbacks; + +import com.sun.jna.Callback; +import net.arikia.dev.drpc.DiscordUser; + +/** + * @author Nicolas "Vatuu" Adamoglou + * @version 1.5.1 + * <p> + * Interface to be implemented in classes that will be registered as "ReadyCallback" Event Handler. + * @see net.arikia.dev.drpc.DiscordEventHandlers + **/ +public interface ReadyCallback extends Callback { + + /** + * Method called when the connection to Discord has been established. + * + * @param user Object containing all required information about the user executing the app. + * @see DiscordUser + **/ + void apply(DiscordUser user); +} + diff --git a/src/main/java/net/arikia/dev/drpc/callbacks/SpectateGameCallback.java b/src/main/java/net/arikia/dev/drpc/callbacks/SpectateGameCallback.java new file mode 100644 index 00000000..28af7329 --- /dev/null +++ b/src/main/java/net/arikia/dev/drpc/callbacks/SpectateGameCallback.java @@ -0,0 +1,20 @@ +package net.arikia.dev.drpc.callbacks; + +import com.sun.jna.Callback; + +/** + * @author Nicolas "Vatuu" Adamoglou + * @version 1.5.1 + * <p> + * Interface to be implemented in classes that will be registered as "SpectateGameCallback" Event Handler. + * @see net.arikia.dev.drpc.DiscordEventHandlers + **/ +public interface SpectateGameCallback extends Callback { + + /** + * Method called when joining a game. + * + * @param spectateSecret Unique String containing information needed to let the player spectate. + */ + void apply(String spectateSecret); +} |