All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.github.kawaxte.presence.DiscordRPC Maven / Gradle / Ivy

The newest version!
package io.github.kawaxte.presence;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.nio.file.attribute.FileAttribute;
import java.text.MessageFormat;
import java.util.Arrays;
import java.util.Collections;
import java.util.EnumMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;

/**
 * The {@code DiscordRPC} class provides static methods to initialize, shutdown, update and
 * clear the presence of the application.
 * 

* It also loads the native library for the current platform using the * {@link #loadLibraryForPlatform()} method. *

* * @author Kawaxte */ public final class DiscordRPC { static { loadLibraryForPlatform(); } /** * Private constructor to prevent instantiation. */ private DiscordRPC() { throw new UnsupportedOperationException(String.format("%s is not instantiable", DiscordRPC.class.getName())); } /** * Initialises the Rich Presence. * * @param applicationId The application ID of the application to be initialised. * @param handlers The event handlers to be registered with the Rich Presence. * @param autoRegister Whether or not to automatically register the application with Discord. * @param optionalSteamId The Steam ID of the game to be initialised. This is optional and can be * {@code null}. * @throws NullPointerException if {@code applicationId} or {@code handlers} is {@code null}. * @see Introducing Rich Presence - Initialization */ public static void initialise(final String applicationId, DiscordEventHandlers handlers, final boolean autoRegister, final String optionalSteamId) { Objects.requireNonNull(applicationId, "applicationId must not be null"); Objects.requireNonNull(handlers, "handlers must not be null"); IDiscordRPC.INSTANCE.Discord_Initialize(applicationId, handlers, autoRegister ? 1 : 0, optionalSteamId); } /** * Shuts down the Rich Presence. *

* This method should be called when the application is closed to ensure that the Discord Rich * Presence API is shut down properly. *

* * @see Introducing Rich Presence - Shutting Down */ public static void shutdown() { IDiscordRPC.INSTANCE.Discord_Shutdown(); } /** * Runs the callbacks for the Rich Presence. *

* This method should be called regularly to ensure that the callbacks are run. *

* * @see Introducing Rich Presence - So, How Does It Work? */ public static void runCallbacks() { IDiscordRPC.INSTANCE.Discord_RunCallbacks(); } /** * Updates the Rich Presence. *

* This method should be called after building a {@link DiscordRichPresence} object to update the * Rich Presence. *

* * @param presence The {@code DiscordRichPresence} object to be updated. * @throws NullPointerException if {@code presence} is {@code null}. * @see Introducing Rich Presence - Updating Presence */ public static void updatePresence(final DiscordRichPresence presence) { Objects.requireNonNull(presence, "presence must not be null"); IDiscordRPC.INSTANCE.Discord_UpdatePresence(presence); } /** * Clears the Rich Presence. *

* This method should be called whenever the user wants to clear the Rich Presence (e.g. when the * user closes the application). *

*/ public static void clearPresence() { IDiscordRPC.INSTANCE.Discord_ClearPresence(); } /** * Responds to a request to join the user's game. * * @param userId The user ID of the user who sent the join request. * @param reply The reply to be sent to the user. This should be one of the following: *
    *
  • 0 - No Reply
  • *
  • 1 - Reply
  • *
  • 2 - Ignore
  • *
* @throws NullPointerException if {@code userId} is {@code null}. * @throws IllegalArgumentException if {@code reply} is not between 0 and 2. * @see Introducing Rich Presence - Joining */ public static void respond(final String userId, int reply) { Objects.requireNonNull(userId, "userId must not be null"); if (reply < 0 || reply > 2) { throw new IllegalArgumentException("reply must be between 0 and 2"); } IDiscordRPC.INSTANCE.Discord_Respond(userId, reply); } /** * Updates the event handlers for the Rich Presence. *

* This method should be called if the user doesn't want to register all event handlers at * initialisation. Keep in mind that this will overwrite any previously registered event * handlers. * * @param handlers The {@code DiscordEventHandlers} object containing the event handlers to * be registered. See {@link DiscordEventHandlers} for more information. * @throws NullPointerException if {@code handlers} is {@code null}. * @see Introducing Rich Presence - Shutting Down */ public static void updateHandlers(DiscordEventHandlers handlers) { Objects.requireNonNull(handlers, "handlers must not be null"); IDiscordRPC.INSTANCE.Discord_UpdateHandlers(handlers); } /** * Registers the application with Discord. * * @param applicationId The application ID of the application to be registered. * @param command The command to be used to launch the application. * @throws NullPointerException if either {@code applicationId} or {@code command} is * {@code null}. */ public static void register(final String applicationId, final String command) { Objects.requireNonNull(applicationId, "applicationId must not be null"); Objects.requireNonNull(command, "command must not be null"); IDiscordRegister.INSTANCE.Discord_Register(applicationId, command); } /** * Registers a Steam game with Discord. * * @param applicationId The application ID of the application to be registered. * @param steamId The Steam ID of the game to be registered. * @throws NullPointerException if either {@code applicationId} or {@code steamId} is * {@code null}. */ public static void registerSteamGame(final String applicationId, final String steamId) { Objects.requireNonNull(applicationId, "applicationId must not be null"); Objects.requireNonNull(steamId, "steamId must not be null"); IDiscordRegister.INSTANCE.Discord_RegisterSteamGame(applicationId, steamId); } /** * Loads the native library and deletes the library after the JVM exits. * * @param p The {@code Path} object to be used to load the library. * @param url The {@code URL} object to be used to load the library. * @throws RuntimeException if the library could not be loaded. */ private static void loadLibrary(Path p, URL url) { try { Files.copy(url.openStream(), p, StandardCopyOption.REPLACE_EXISTING); } catch (IOException ioe) { throw new RuntimeException(MessageFormat.format("\"{0}\" could not be loaded", p), ioe); } finally { p.toFile().deleteOnExit(); System.load(p.toAbsolutePath().toString()); } } /** * Loads the native library for the current platform. *

* It creates a temporary directory in the user's home (or %TEMP% on Windows) and copies the * library to that directory. The library is then loaded and deleted after the JVM exits. *

*

* The library is copied to the following directories: *

    *
  • Linux: ~/.discord-rpc
  • *
  • macOS: ~/Library/Application Support/discord-rpc
  • *
  • Windows: %TEMP%/discord-rpc
  • *
*

* * @throws RuntimeException if the library could not be loaded or if the directory to copy the * library to could not be created. * @see ClassLoader#getSystemResource(String) * @see Files#createDirectories(Path, FileAttribute[]) */ private static void loadLibraryForPlatform() { String libraryName = System.mapLibraryName("discord-rpc"); String userHome = System.getProperty("user.home") .replaceAll("[^a-zA-Z0-9_\\\\/\\-.]", "_"); EPlatform platform = EPlatform.getPlatform(); Map platformLookup = Collections.unmodifiableMap( new EnumMap(EPlatform.class) {{ put(EPlatform.LINUX, Paths.get(userHome, ".discord-rpc").toString()); put(EPlatform.MACOS, Paths.get(userHome, "Library", "Application Support", "discord-rpc").toString()); put(EPlatform.WINDOWS, Paths.get(System.getenv("TEMP"), "discord-rpc").toString()); }} ); String libraryPath = String.join(File.separator, platformLookup.get(platform), libraryName); URL libraryFileUrl = ClassLoader.getSystemResource(String.format("%s/%s", platform == EPlatform.WINDOWS && System.getProperty("os.arch").equals("amd64") && System.getProperty("sun.arch.data.model").equals("64") ? "amd64" : "x86", libraryName)); try { Files.createDirectories(Paths.get(platformLookup.get(platform))); } catch (IOException ioe) { throw new RuntimeException(MessageFormat.format("Directory \"{0}\" could not be created", platformLookup.get(platform)), ioe); } finally { loadLibrary(Paths.get(libraryPath), libraryFileUrl); } } /** * The {@code EPlatform} enum represents the different platforms that are supported by * DiscordRPC. * * @author Kawaxte */ enum EPlatform { LINUX("nux"), MACOS("mac"), WINDOWS("win"); static final String OS_NAME; static { OS_NAME = System.getProperty("os.name") != null ? System.getProperty("os.name").toLowerCase(Locale.ROOT) : ""; } private final List osNames; /** * Constructs a new {@code EPlatform} with the specified operating system names. * * @param osNames The operating system names to be used to identify the platform. */ EPlatform(String... osNames) { this.osNames = Collections.unmodifiableList(Arrays.asList(osNames)); } /** * Returns the platform that the current operating system is running on. * * @return The {@code EPlatform} object representing the platform that the current operating * system is running on. */ public static EPlatform getPlatform() { return Arrays.stream(values()) .filter(platform -> platform.osNames.stream().anyMatch(OS_NAME::contains)) .findFirst() .orElse(null); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy