From 8fd84f9d137848024fe94c084f52b370b5ef0304 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Thu, 19 Sep 2024 10:03:16 -0400 Subject: [PATCH 1/6] Functioning Discord Rich Presence! --- .vscode/settings.json | 5 + hmm.json | 23 ++- project.hxp | 7 +- source/funkin/InitState.hx | 38 ++-- source/funkin/api/discord/Discord.hx | 91 --------- source/funkin/api/discord/DiscordClient.hx | 188 ++++++++++++++++++ source/funkin/play/PlayState.hx | 126 ++++++++---- source/funkin/play/character/BaseCharacter.hx | 9 +- source/funkin/ui/freeplay/FreeplayState.hx | 5 +- source/funkin/ui/mainmenu/MainMenuState.hx | 5 +- source/funkin/ui/story/StoryMenuState.hx | 5 +- 11 files changed, 331 insertions(+), 171 deletions(-) delete mode 100644 source/funkin/api/discord/Discord.hx create mode 100644 source/funkin/api/discord/DiscordClient.hx diff --git a/.vscode/settings.json b/.vscode/settings.json index 227cb94ec..8dc6d0e6b 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -106,6 +106,11 @@ "target": "hl", "args": ["-debug"] }, + { + "label": "Windows / Debug (Discord)", + "target": "windows", + "args": ["-debug", "-DFEATURE_DEBUG_FUNCTIONS", "-DFEATURE_DISCORD_RPC"] + }, { "label": "Windows / Debug (FlxAnimate Test)", "target": "windows", diff --git a/hmm.json b/hmm.json index edc0529aa..42c63abed 100644 --- a/hmm.json +++ b/hmm.json @@ -7,13 +7,6 @@ "ref": "a1eab7b9bf507b87200a3341719054fe427f3b15", "url": "https://github.com/FunkinCrew/FlxPartialSound.git" }, - { - "name": "discord_rpc", - "type": "git", - "dir": null, - "ref": "2d83fa863ef0c1eace5f1cf67c3ac315d1a3a8a5", - "url": "https://github.com/FunkinCrew/linc_discord-rpc" - }, { "name": "flixel", "type": "git", @@ -115,6 +108,13 @@ "ref": "147294123f983e35f50a966741474438069a7a8f", "url": "https://github.com/FunkinCrew/hxcpp-debugger" }, + { + "name": "hxdiscord_rpc", + "type": "git", + "dir": null, + "ref": "main", + "url": "https://github.com/MAJigsaw77/hxdiscord_rpc" + }, { "name": "hxjsonast", "type": "git", @@ -155,6 +155,13 @@ "ref": "fe3368f611a84a19afc03011353945ae4da8fffd", "url": "https://github.com/FunkinCrew/lime" }, + { + "name": "linc_discord-rpc", + "type": "git", + "dir": null, + "ref": "2d83fa863ef0c1eace5f1cf67c3ac315d1a3a8a5", + "url": "https://github.com/FunkinCrew/haxe-discord-rpc" + }, { "name": "mconsole", "type": "git", @@ -212,4 +219,4 @@ "url": "https://github.com/fponticelli/thx.semver" } ] -} +} \ No newline at end of file diff --git a/project.hxp b/project.hxp index 1193a9cd4..6d46b9278 100644 --- a/project.hxp +++ b/project.hxp @@ -457,10 +457,9 @@ class Project extends HXProject { // Should default to true on workspace builds and false on release builds. REDIRECT_ASSETS_FOLDER.apply(this, isDebug() && isDesktop()); - // Should be true on release, non-tester builds. + // Should be true on desktop, release, non-tester builds. // We don't want testers to accidentally leak songs to their Discord friends! - // TODO: Re-enable this. - FEATURE_DISCORD_RPC.apply(this, false && !FEATURE_DEBUG_FUNCTIONS.isEnabled(this)); + FEATURE_DISCORD_RPC.apply(this, isDesktop() && !FEATURE_DEBUG_FUNCTIONS.isEnabled(this)); // Should be true only on web builds. // Audio context issues only exist there. @@ -618,7 +617,7 @@ class Project extends HXProject { } if (FEATURE_DISCORD_RPC.isEnabled(this)) { - addHaxelib('discord_rpc'); // Discord API + addHaxelib('hxdiscord_rpc'); // Discord API } if (FEATURE_NEWGROUNDS.isEnabled(this)) { diff --git a/source/funkin/InitState.hx b/source/funkin/InitState.hx index f71de00f4..51640b0a0 100644 --- a/source/funkin/InitState.hx +++ b/source/funkin/InitState.hx @@ -1,42 +1,42 @@ package funkin; -import funkin.data.freeplay.player.PlayerRegistry; -import funkin.ui.debug.charting.ChartEditorState; -import funkin.ui.transition.LoadingState; -import flixel.FlxState; import flixel.addons.transition.FlxTransitionableState; import flixel.addons.transition.FlxTransitionSprite.GraphicTransTileDiamond; import flixel.addons.transition.TransitionData; +import flixel.FlxSprite; +import flixel.FlxState; import flixel.graphics.FlxGraphic; import flixel.math.FlxPoint; import flixel.math.FlxRect; -import flixel.FlxSprite; import flixel.system.debug.log.LogStyle; import flixel.util.FlxColor; -import funkin.util.macro.MacroUtil; -import funkin.util.WindowUtil; -import funkin.play.PlayStatePlaylist; -import openfl.display.BitmapData; -import funkin.data.story.level.LevelRegistry; -import funkin.data.notestyle.NoteStyleRegistry; -import funkin.data.freeplay.style.FreeplayStyleRegistry; -import funkin.data.event.SongEventRegistry; -import funkin.data.stage.StageRegistry; import funkin.data.dialogue.conversation.ConversationRegistry; import funkin.data.dialogue.dialoguebox.DialogueBoxRegistry; import funkin.data.dialogue.speaker.SpeakerRegistry; import funkin.data.freeplay.album.AlbumRegistry; +import funkin.data.freeplay.player.PlayerRegistry; +import funkin.data.freeplay.style.FreeplayStyleRegistry; +import funkin.data.notestyle.NoteStyleRegistry; import funkin.data.song.SongRegistry; +import funkin.data.event.SongEventRegistry; +import funkin.data.stage.StageRegistry; +import funkin.data.story.level.LevelRegistry; +import funkin.modding.module.ModuleHandler; import funkin.play.character.CharacterData.CharacterDataParser; import funkin.play.notes.notekind.NoteKindManager; -import funkin.modding.module.ModuleHandler; +import funkin.play.PlayStatePlaylist; +import funkin.ui.debug.charting.ChartEditorState; import funkin.ui.title.TitleState; +import funkin.ui.transition.LoadingState; import funkin.util.CLIUtil; import funkin.util.CLIUtil.CLIParams; +import funkin.util.macro.MacroUtil; import funkin.util.TimerUtil; import funkin.util.TrackerUtil; +import funkin.util.WindowUtil; +import openfl.display.BitmapData; #if FEATURE_DISCORD_RPC -import Discord.DiscordClient; +import funkin.api.discord.DiscordClient; #end /** @@ -125,10 +125,10 @@ class InitState extends FlxState // DISCORD API SETUP // #if FEATURE_DISCORD_RPC - DiscordClient.initialize(); + DiscordClient.instance.init(); - Application.current.onExit.add(function(exitCode) { - DiscordClient.shutdown(); + lime.app.Application.current.onExit.add(function(exitCode) { + DiscordClient.instance.shutdown(); }); #end diff --git a/source/funkin/api/discord/Discord.hx b/source/funkin/api/discord/Discord.hx deleted file mode 100644 index 9dd513bf7..000000000 --- a/source/funkin/api/discord/Discord.hx +++ /dev/null @@ -1,91 +0,0 @@ -package funkin.api.discord; - -import Sys.sleep; -#if FEATURE_DISCORD_RPC -import discord_rpc.DiscordRpc; -#end - -class DiscordClient -{ - #if FEATURE_DISCORD_RPC - public function new() - { - trace("Discord Client starting..."); - DiscordRpc.start( - { - clientID: "814588678700924999", - onReady: onReady, - onError: onError, - onDisconnected: onDisconnected - }); - trace("Discord Client started."); - - while (true) - { - DiscordRpc.process(); - sleep(2); - // trace("Discord Client Update"); - } - - DiscordRpc.shutdown(); - } - - public static function shutdown() - { - DiscordRpc.shutdown(); - } - - static function onReady() - { - DiscordRpc.presence( - { - details: "In the Menus", - state: null, - largeImageKey: 'icon', - largeImageText: "Friday Night Funkin'" - }); - } - - static function onError(_code:Int, _message:String) - { - trace('Error! $_code : $_message'); - } - - static function onDisconnected(_code:Int, _message:String) - { - trace('Disconnected! $_code : $_message'); - } - - public static function initialize() - { - var DiscordDaemon = sys.thread.Thread.create(() -> { - new DiscordClient(); - }); - trace("Discord Client initialized"); - } - - public static function changePresence(details:String, ?state:String, ?smallImageKey:String, ?hasStartTimestamp:Bool, ?endTimestamp:Float) - { - var startTimestamp:Float = if (hasStartTimestamp) Date.now().getTime() else 0; - - if (endTimestamp > 0) - { - endTimestamp = startTimestamp + endTimestamp; - } - - DiscordRpc.presence( - { - details: details, - state: state, - largeImageKey: 'icon', - largeImageText: "Friday Night Funkin'", - smallImageKey: smallImageKey, - // Obtained times are in milliseconds so they are divided so Discord can use it - startTimestamp: Std.int(startTimestamp / 1000), - endTimestamp: Std.int(endTimestamp / 1000) - }); - - // trace('Discord RPC Updated. Arguments: $details, $state, $smallImageKey, $hasStartTimestamp, $endTimestamp'); - } - #end -} diff --git a/source/funkin/api/discord/DiscordClient.hx b/source/funkin/api/discord/DiscordClient.hx new file mode 100644 index 000000000..c722762b0 --- /dev/null +++ b/source/funkin/api/discord/DiscordClient.hx @@ -0,0 +1,188 @@ +package funkin.api.discord; + +#if FEATURE_DISCORD_RPC +import hxdiscord_rpc.Discord; +import hxdiscord_rpc.Types; +import sys.thread.Thread; + +class DiscordClient +{ + static final CLIENT_ID:String = "814588678700924999"; + + public static var instance(get, never):DiscordClient; + static var _instance:Null = null; + + static function get_instance():DiscordClient + { + if (DiscordClient._instance == null) _instance = new DiscordClient(); + if (DiscordClient._instance == null) throw "Could not initialize singleton DiscordClient!"; + return DiscordClient._instance; + } + + var handlers:DiscordEventHandlers; + + private function new() + { + trace('[DISCORD] Initializing event handlers...'); + + handlers = DiscordEventHandlers.create(); + + handlers.ready = cpp.Function.fromStaticFunction(onReady); + handlers.disconnected = cpp.Function.fromStaticFunction(onDisconnected); + handlers.errored = cpp.Function.fromStaticFunction(onError); + } + + public function init():Void + { + trace('[DISCORD] Initializing connection...'); + + // Discord.initialize(CLIENT_ID, handlers, true, null); + Discord.Initialize(CLIENT_ID, cpp.RawPointer.addressOf(handlers), 1, null); + + createDaemon(); + } + + var daemon:Thread = null; + + function createDaemon():Void + { + daemon = Thread.create(doDaemonWork); + } + + function doDaemonWork():Void + { + while (true) + { + trace('[DISCORD] Performing client update...'); + + #if DISCORD_DISABLE_IO_THREAD + Discord.updateConnection(); + #end + + Discord.runCallbacks(); + Sys.sleep(2); + } + } + + public function shutdown():Void + { + trace('[DISCORD] Shutting down...'); + + Discord.shutdown(); + } + + public function setPresence(params:DiscordClientPresenceParams):Void + { + trace('[DISCORD] Updating presence... (${params})'); + + Discord.updatePresence(buildPresence(params)); + } + + function buildPresence(params:DiscordClientPresenceParams):DiscordRichPresence + { + var presence = DiscordRichPresence.create(); + + // Presence should always be playing the game. + presence.type = DiscordActivityType_Playing; + + // Text when hovering over the large image. We just leave this as the game name. + presence.largeImageText = "Friday Night Funkin'"; + + // State should be generally what the person is doing, like "In the Menus" or "Pico (Pico Mix) [Freeplay Hard]" + presence.state = cast(params.state, Null); + // Details should be what the person is specifically doing, including stuff like timestamps (maybe something like "03:24 elapsed"). + presence.details = cast(params.details, Null); + + // The large image displaying what the user is doing. + // This should probably be album art. + // IMPORTANT NOTE: This can be an asset key uploaded to Discord's developer panel OR any URL you like. + // presence.largeImageKey = "icon"; + presence.largeImageKey = "https://f4.bcbits.com/img/a3122193953_16.jpg"; + // The small inset image for what the user is doing. + // This can be the opponent's health icon? + presence.smallImageKey = cast(params.smallImageKey, Null); + + // Start timestamp, used to power elapsed/remaining data + // presence.startTimestamp + // End timestamp, used to power elapsed/remaining data + // presence.endTimestamp + + return presence; + } + + // TODO: WHAT THE FUCK get this pointer bullfuckery out of here + private static function onReady(request:cpp.RawConstPointer):Void + { + trace('[DISCORD] Client has connected!'); + + final username:String = request[0].username; + final globalName:String = request[0].username; + final discriminator:Int = Std.parseInt(request[0].discriminator); + + if (discriminator != 0) + { + trace('[DISCORD] User: ${username}#${discriminator} (${globalName})'); + } + else + { + trace('[DISCORD] User: @${username} (${globalName})'); + } + } + + private static function onDisconnected(errorCode:Int, message:cpp.ConstCharStar):Void + { + trace('[DISCORD] Client has disconnected! ($errorCode) "${cast (message, String)}"'); + } + + private static function onError(errorCode:Int, message:cpp.ConstCharStar):Void + { + trace('[DISCORD] Client has received an error! ($errorCode) "${cast (message, String)}"'); + } + + // public var type(get, set):DiscordActivityType; + // public var state(get, set):String; + // public var details(get, set):String; + // public var startTimestamp(get, set):Int; + // public var endTimestamp(get, set):Int; + // public var largeImageKey(get, set):String; + // public var largeImageText(get, set):String; + // public var smallImageKey(get, set):String; + // public var smallImageText(get, set):String; + // + // + // public var partyId(get, set) + // public var partySize(get, set) + // public var partyMax(get, set) + // public var partyPrivacy(get, set) + // + // public var buttons(get, set) + // + // public var matchSecret(get, set) + // public var joinSecret(get, set) + // public var spectateSecret(get, set) +} + +typedef DiscordClientPresenceParams = +{ + /** + * The first row of text below the game title. + */ + var state:String; + + /** + * The second row of text below the game title. + * Use `null` to display no text. + */ + var details:Null; + + /** + * A large, 4-row high image to the left of the content. + */ + var ?largeImageKey:String; + + /** + * A small, inset image to the bottom right of `largeImageKey`. + */ + var ?smallImageKey:String; +} +#end diff --git a/source/funkin/play/PlayState.hx b/source/funkin/play/PlayState.hx index 0b2b8846d..22ba22b71 100644 --- a/source/funkin/play/PlayState.hx +++ b/source/funkin/play/PlayState.hx @@ -15,8 +15,8 @@ import flixel.tweens.FlxEase; import flixel.tweens.FlxTween; import flixel.ui.FlxBar; import flixel.util.FlxColor; -import flixel.util.FlxTimer; import flixel.util.FlxStringUtil; +import flixel.util.FlxTimer; import funkin.api.newgrounds.NGio; import funkin.audio.FunkinSound; import funkin.audio.VoicesGroup; @@ -44,12 +44,12 @@ import funkin.play.cutscene.dialogue.Conversation; import funkin.play.cutscene.VanillaCutscenes; import funkin.play.cutscene.VideoCutscene; import funkin.play.notes.NoteDirection; +import funkin.play.notes.notekind.NoteKindManager; import funkin.play.notes.NoteSplash; import funkin.play.notes.NoteSprite; import funkin.play.notes.notestyle.NoteStyle; import funkin.play.notes.Strumline; import funkin.play.notes.SustainTrail; -import funkin.play.notes.notekind.NoteKindManager; import funkin.play.scoring.Scoring; import funkin.play.song.Song; import funkin.play.stage.Stage; @@ -68,7 +68,7 @@ import openfl.display.BitmapData; import openfl.geom.Rectangle; import openfl.Lib; #if FEATURE_DISCORD_RPC -import Discord.DiscordClient; +import funkin.api.discord.DiscordClient; #end /** @@ -447,10 +447,10 @@ class PlayState extends MusicBeatSubState #if FEATURE_DISCORD_RPC // Discord RPC variables - var storyDifficultyText:String = ''; - var iconRPC:String = ''; - var detailsText:String = ''; - var detailsPausedText:String = ''; + var discordRPCDifficulty:String = ''; + var discordRPCIcon:String = ''; + var discordRPCDetailsText:String = ''; + var discordRPCDetailsPausedText:String = ''; #end /** @@ -984,7 +984,12 @@ class PlayState extends MusicBeatSubState } #if FEATURE_DISCORD_RPC - DiscordClient.changePresence(detailsPausedText, currentSong.song + ' (' + storyDifficultyText + ')', iconRPC); + DiscordClient.instance.setPresence( + { + state: '${currentChart.songName} (${discordRPCDifficulty})', + details: discordRPCDetailsPausedText, + smallImageKey: discordRPCIcon + }); #end } } @@ -1073,8 +1078,12 @@ class PlayState extends MusicBeatSubState } #if FEATURE_DISCORD_RPC - // Game Over doesn't get his own variable because it's only used here - DiscordClient.changePresence('Game Over - ' + detailsText, currentSong.song + ' (' + storyDifficultyText + ')', iconRPC); + DiscordClient.instance.setPresence( + { + state: '${currentChart.songName} [${discordRPCDifficulty}]', + details: 'Game Over - ${discordRPCDetailsText}', + smallImageKey: discordRPCIcon + }); #end } else if (isPlayerDying) @@ -1285,14 +1294,25 @@ class PlayState extends MusicBeatSubState Countdown.resumeCountdown(); #if FEATURE_DISCORD_RPC - if (startTimer.finished) + if (Conductor.instance.songPosition > 0) { - DiscordClient.changePresence(detailsText, '${currentChart.songName} ($storyDifficultyText)', iconRPC, true, - currentSongLengthMs - Conductor.instance.songPosition); + // DiscordClient.changePresence(detailsText, '${currentChart.songName} ($discordRPCDifficulty)', discordRPCIcon, true, + // currentSongLengthMs - Conductor.instance.songPosition); + DiscordClient.instance.setPresence( + { + state: '${currentChart.songName} [${discordRPCDifficulty}]', + details: discordRPCDetailsText, + smallImageKey: discordRPCIcon + }); } else { - DiscordClient.changePresence(detailsText, '${currentChart.songName} ($storyDifficultyText)', iconRPC); + DiscordClient.instance.setPresence( + { + state: '${currentChart.songName} [${discordRPCDifficulty}]', + details: discordRPCDetailsText, + smallImageKey: discordRPCIcon + }); } #end @@ -1318,16 +1338,28 @@ class PlayState extends MusicBeatSubState #end #if FEATURE_DISCORD_RPC - if (health > Constants.HEALTH_MIN && !paused && FlxG.autoPause) + if (health > Constants.HEALTH_MIN && !isGamePaused && FlxG.autoPause) { - if (Conductor.instance.songPosition > 0.0) DiscordClient.changePresence(detailsText, currentSong.song - + ' (' - + storyDifficultyText - + ')', iconRPC, true, - currentSongLengthMs - - Conductor.instance.songPosition); + if (Conductor.instance.songPosition > 0.0) + { + DiscordClient.instance.setPresence( + { + state: '${currentChart.songName} [${discordRPCDifficulty}]', + details: discordRPCDetailsText, + smallImageKey: discordRPCIcon + }); + } else - DiscordClient.changePresence(detailsText, currentSong.song + ' (' + storyDifficultyText + ')', iconRPC); + { + DiscordClient.instance.setPresence( + { + state: '${currentChart.songName} [${discordRPCDifficulty}]', + details: discordRPCDetailsText, + smallImageKey: discordRPCIcon + }); + // DiscordClient.changePresence(detailsText, '${currentChart.songName} ($discordRPCDifficulty)', discordRPCIcon, true, + // currentSongLengthMs - Conductor.instance.songPosition); + } } #end @@ -1344,8 +1376,15 @@ class PlayState extends MusicBeatSubState #end #if FEATURE_DISCORD_RPC - if (health > Constants.HEALTH_MIN && !paused && FlxG.autoPause) DiscordClient.changePresence(detailsPausedText, - currentSong.song + ' (' + storyDifficultyText + ')', iconRPC); + if (health > Constants.HEALTH_MIN && !isGamePaused && FlxG.autoPause) + { + DiscordClient.instance.setPresence( + { + state: '${currentChart.songName} [${discordRPCDifficulty}]', + details: discordRPCDetailsText, + smallImageKey: discordRPCIcon + }); + } #end super.onFocusLost(); @@ -1641,6 +1680,10 @@ class PlayState extends MusicBeatSubState iconP2.zIndex = 850; add(iconP2); iconP2.cameras = [camHUD]; + + #if FEATURE_DISCORD_RPC + discordRPCIcon = currentCharacterData.opponent; + #end } // @@ -1758,26 +1801,19 @@ class PlayState extends MusicBeatSubState function initDiscord():Void { #if FEATURE_DISCORD_RPC - storyDifficultyText = difficultyString(); - iconRPC = currentSong.player2; + discordRPCDifficulty = PlayState.instance.currentDifficulty.replace('-', ' ').toTitleCase(); - // To avoid having duplicate images in Discord assets - switch (iconRPC) - { - case 'senpai-angry': - iconRPC = 'senpai'; - case 'monster-christmas': - iconRPC = 'monster'; - case 'mom-car': - iconRPC = 'mom'; - } - - // String that contains the mode defined here so it isn't necessary to call changePresence for each mode - detailsText = isStoryMode ? 'Story Mode: Week $storyWeek' : 'Freeplay'; - detailsPausedText = 'Paused - $detailsText'; + // Determine the details strings once and reuse them. + discordRPCDetailsText = PlayStatePlaylist.isStoryMode ? 'Story Mode: Week ${PlayStatePlaylist.campaignId}' : 'Freeplay'; + discordRPCDetailsPausedText = 'Paused - $discordRPCDetailsText'; // Updating Discord Rich Presence. - DiscordClient.changePresence(detailsText, '${currentChart.songName} ($storyDifficultyText)', iconRPC); + DiscordClient.instance.setPresence( + { + state: '${currentChart.songName} [${discordRPCDifficulty}]', + details: discordRPCDetailsText, + smallImageKey: discordRPCIcon + }); #end } @@ -1972,7 +2008,13 @@ class PlayState extends MusicBeatSubState #if FEATURE_DISCORD_RPC // Updating Discord Rich Presence (with Time Left) - DiscordClient.changePresence(detailsText, '${currentChart.songName} ($storyDifficultyText)', iconRPC, true, currentSongLengthMs); + DiscordClient.instance.setPresence( + { + state: '${currentChart.songName} (${discordRPCDifficulty})', + details: discordRPCDetailsText, + smallImageKey: discordRPCIcon + }); + // DiscordClient.changePresence(detailsText, '${currentChart.songName} ($discordRPCDifficulty)', discordRPCIcon, true, currentSongLengthMs); #end if (startTimestamp > 0) diff --git a/source/funkin/play/character/BaseCharacter.hx b/source/funkin/play/character/BaseCharacter.hx index 365c8d112..e29131d46 100644 --- a/source/funkin/play/character/BaseCharacter.hx +++ b/source/funkin/play/character/BaseCharacter.hx @@ -323,6 +323,11 @@ class BaseCharacter extends Bopper this.cameraFocusPoint = new FlxPoint(charCenterX + _data.cameraOffsets[0], charCenterY + _data.cameraOffsets[1]); } + public function getHealthIconId():String + { + return _data?.healthIcon?.id ?? Constants.DEFAULT_HEALTH_ICON; + } + public function initHealthIcon(isOpponent:Bool):Void { if (!isOpponent) @@ -332,7 +337,7 @@ class BaseCharacter extends Bopper trace('[WARN] Player 1 health icon not found!'); return; } - PlayState.instance.iconP1.configure(_data.healthIcon); + PlayState.instance.iconP1.configure(_data?.healthIcon); PlayState.instance.iconP1.flipX = !PlayState.instance.iconP1.flipX; // BF is looking the other way. } else @@ -342,7 +347,7 @@ class BaseCharacter extends Bopper trace('[WARN] Player 2 health icon not found!'); return; } - PlayState.instance.iconP2.configure(_data.healthIcon); + PlayState.instance.iconP2.configure(_data?.healthIcon); } } diff --git a/source/funkin/ui/freeplay/FreeplayState.hx b/source/funkin/ui/freeplay/FreeplayState.hx index 15633ff9d..d8842d812 100644 --- a/source/funkin/ui/freeplay/FreeplayState.hx +++ b/source/funkin/ui/freeplay/FreeplayState.hx @@ -55,6 +55,9 @@ import lime.utils.Assets; import openfl.display.BlendMode; import funkin.data.freeplay.style.FreeplayStyleRegistry; import funkin.data.song.SongData.SongMusicData; +#if FEATURE_DISCORD_RPC +import funkin.api.discord.DiscordClient; +#end /** * Parameters used to initialize the FreeplayState. @@ -313,7 +316,7 @@ class FreeplayState extends MusicBeatSubState #if FEATURE_DISCORD_RPC // Updating Discord Rich Presence - DiscordClient.changePresence('In the Menus', null); + DiscordClient.instance.setPresence({state: 'In the Menus', details: null}); #end var isDebug:Bool = false; diff --git a/source/funkin/ui/mainmenu/MainMenuState.hx b/source/funkin/ui/mainmenu/MainMenuState.hx index a436848d2..80da33a35 100644 --- a/source/funkin/ui/mainmenu/MainMenuState.hx +++ b/source/funkin/ui/mainmenu/MainMenuState.hx @@ -28,7 +28,7 @@ import funkin.ui.story.StoryMenuState; import funkin.ui.Prompt; import funkin.util.WindowUtil; #if FEATURE_DISCORD_RPC -import Discord.DiscordClient; +import funkin.api.discord.DiscordClient; #end #if newgrounds import funkin.ui.NgPrompt; @@ -55,8 +55,7 @@ class MainMenuState extends MusicBeatState override function create():Void { #if FEATURE_DISCORD_RPC - // Updating Discord Rich Presence - DiscordClient.changePresence("In the Menus", null); + DiscordClient.instance.setPresence({state: "In the Menus", details: null}); #end FlxG.cameras.reset(new FunkinCamera('mainMenu')); diff --git a/source/funkin/ui/story/StoryMenuState.hx b/source/funkin/ui/story/StoryMenuState.hx index 18614d414..09af08b22 100644 --- a/source/funkin/ui/story/StoryMenuState.hx +++ b/source/funkin/ui/story/StoryMenuState.hx @@ -24,6 +24,9 @@ import funkin.ui.transition.LoadingState; import funkin.ui.transition.StickerSubState; import funkin.util.MathUtil; import openfl.utils.Assets; +#if FEATURE_DISCORD_RPC +import funkin.api.discord.DiscordClient; +#end class StoryMenuState extends MusicBeatState { @@ -218,7 +221,7 @@ class StoryMenuState extends MusicBeatState #if FEATURE_DISCORD_RPC // Updating Discord Rich Presence - DiscordClient.changePresence('In the Menus', null); + DiscordClient.instance.setPresence({state: 'In the Menus', details: null}); #end } From 35aa8731d0e94a6dfb7f97eabbf950e066c51906 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Thu, 19 Sep 2024 11:13:05 -0400 Subject: [PATCH 2/6] Better status messages, buttons, updated album art, basic chart editor support --- source/funkin/api/discord/DiscordClient.hx | 22 ++++-- source/funkin/play/PlayState.hx | 78 +++++++++++++------ .../ui/debug/charting/ChartEditorState.hx | 17 ++++ source/funkin/util/Constants.hx | 7 +- 4 files changed, 93 insertions(+), 31 deletions(-) diff --git a/source/funkin/api/discord/DiscordClient.hx b/source/funkin/api/discord/DiscordClient.hx index c722762b0..3b6e891d3 100644 --- a/source/funkin/api/discord/DiscordClient.hx +++ b/source/funkin/api/discord/DiscordClient.hx @@ -96,16 +96,28 @@ class DiscordClient // The large image displaying what the user is doing. // This should probably be album art. // IMPORTANT NOTE: This can be an asset key uploaded to Discord's developer panel OR any URL you like. + + // TODO: Make this use the song's album art. // presence.largeImageKey = "icon"; - presence.largeImageKey = "https://f4.bcbits.com/img/a3122193953_16.jpg"; + presence.largeImageKey = "https://f4.bcbits.com/img/a0746694746_16.jpg"; + // The small inset image for what the user is doing. // This can be the opponent's health icon? presence.smallImageKey = cast(params.smallImageKey, Null); - // Start timestamp, used to power elapsed/remaining data - // presence.startTimestamp - // End timestamp, used to power elapsed/remaining data - // presence.endTimestamp + // NOTE: In previous versions, this showed as "Elapsed", but now shows as playtime and doesn't look good + // presence.startTimestamp = time - 10; + // presence.endTimestamp = time + 30; + + final button1:DiscordButton = DiscordButton.create(); + button1.label = "Play on Web"; + button1.url = Constants.URL_NEWGROUNDS; + presence.buttons[0] = button1; + + final button2:DiscordButton = DiscordButton.create(); + button2.label = "Download"; + button2.url = Constants.URL_ITCH; + presence.buttons[1] = button2; return presence; } diff --git a/source/funkin/play/PlayState.hx b/source/funkin/play/PlayState.hx index 22ba22b71..aeba619ff 100644 --- a/source/funkin/play/PlayState.hx +++ b/source/funkin/play/PlayState.hx @@ -447,10 +447,7 @@ class PlayState extends MusicBeatSubState #if FEATURE_DISCORD_RPC // Discord RPC variables - var discordRPCDifficulty:String = ''; var discordRPCIcon:String = ''; - var discordRPCDetailsText:String = ''; - var discordRPCDetailsPausedText:String = ''; #end /** @@ -986,8 +983,10 @@ class PlayState extends MusicBeatSubState #if FEATURE_DISCORD_RPC DiscordClient.instance.setPresence( { - state: '${currentChart.songName} (${discordRPCDifficulty})', - details: discordRPCDetailsPausedText, + details: 'Paused - ${buildDiscordRPCDetails()}', + + state: buildDiscordRPCState(), + smallImageKey: discordRPCIcon }); #end @@ -1080,8 +1079,8 @@ class PlayState extends MusicBeatSubState #if FEATURE_DISCORD_RPC DiscordClient.instance.setPresence( { - state: '${currentChart.songName} [${discordRPCDifficulty}]', - details: 'Game Over - ${discordRPCDetailsText}', + details: 'Game Over - ${buildDiscordRPCDetails()}', + state: buildDiscordRPCState(), smallImageKey: discordRPCIcon }); #end @@ -1300,8 +1299,8 @@ class PlayState extends MusicBeatSubState // currentSongLengthMs - Conductor.instance.songPosition); DiscordClient.instance.setPresence( { - state: '${currentChart.songName} [${discordRPCDifficulty}]', - details: discordRPCDetailsText, + state: buildDiscordRPCState(), + details: 'Paused - ${buildDiscordRPCDetails()}', smallImageKey: discordRPCIcon }); } @@ -1309,8 +1308,8 @@ class PlayState extends MusicBeatSubState { DiscordClient.instance.setPresence( { - state: '${currentChart.songName} [${discordRPCDifficulty}]', - details: discordRPCDetailsText, + state: buildDiscordRPCState(), + details: 'Paused - ${buildDiscordRPCDetails()}', smallImageKey: discordRPCIcon }); } @@ -1344,8 +1343,8 @@ class PlayState extends MusicBeatSubState { DiscordClient.instance.setPresence( { - state: '${currentChart.songName} [${discordRPCDifficulty}]', - details: discordRPCDetailsText, + state: buildDiscordRPCState(), + details: buildDiscordRPCDetails(), smallImageKey: discordRPCIcon }); } @@ -1353,8 +1352,8 @@ class PlayState extends MusicBeatSubState { DiscordClient.instance.setPresence( { - state: '${currentChart.songName} [${discordRPCDifficulty}]', - details: discordRPCDetailsText, + state: buildDiscordRPCState(), + details: buildDiscordRPCDetails(), smallImageKey: discordRPCIcon }); // DiscordClient.changePresence(detailsText, '${currentChart.songName} ($discordRPCDifficulty)', discordRPCIcon, true, @@ -1380,8 +1379,8 @@ class PlayState extends MusicBeatSubState { DiscordClient.instance.setPresence( { - state: '${currentChart.songName} [${discordRPCDifficulty}]', - details: discordRPCDetailsText, + state: buildDiscordRPCState(), + details: buildDiscordRPCDetails(), smallImageKey: discordRPCIcon }); } @@ -1801,22 +1800,51 @@ class PlayState extends MusicBeatSubState function initDiscord():Void { #if FEATURE_DISCORD_RPC - discordRPCDifficulty = PlayState.instance.currentDifficulty.replace('-', ' ').toTitleCase(); - // Determine the details strings once and reuse them. - discordRPCDetailsText = PlayStatePlaylist.isStoryMode ? 'Story Mode: Week ${PlayStatePlaylist.campaignId}' : 'Freeplay'; - discordRPCDetailsPausedText = 'Paused - $discordRPCDetailsText'; // Updating Discord Rich Presence. DiscordClient.instance.setPresence( { - state: '${currentChart.songName} [${discordRPCDifficulty}]', - details: discordRPCDetailsText, + state: buildDiscordRPCState(), + details: buildDiscordRPCDetails(), smallImageKey: discordRPCIcon }); #end } + function buildDiscordRPCDetails():String + { + if (PlayStatePlaylist.isStoryMode) + { + return 'Story Mode: ${PlayStatePlaylist.campaignTitle}'; + } + else + { + if (isChartingMode) + { + return 'Chart Editor [Playtest]'; + } + else if (isPracticeMode) + { + return 'Freeplay [Practice]'; + } + else if (isBotPlayMode) + { + return 'Freeplay [Bot Play]'; + } + else + { + return 'Freeplay'; + } + } + } + + function buildDiscordRPCState():String + { + var discordRPCDifficulty = PlayState.instance.currentDifficulty.replace('-', ' ').toTitleCase(); + return '${currentChart.songName} [${discordRPCDifficulty}]'; + } + function initPreciseInputs():Void { PreciseInputManager.instance.onInputPressed.add(onKeyPress); @@ -2010,8 +2038,8 @@ class PlayState extends MusicBeatSubState // Updating Discord Rich Presence (with Time Left) DiscordClient.instance.setPresence( { - state: '${currentChart.songName} (${discordRPCDifficulty})', - details: discordRPCDetailsText, + state: buildDiscordRPCState(), + details: buildDiscordRPCDetails(), smallImageKey: discordRPCIcon }); // DiscordClient.changePresence(detailsText, '${currentChart.songName} ($discordRPCDifficulty)', discordRPCIcon, true, currentSongLengthMs); diff --git a/source/funkin/ui/debug/charting/ChartEditorState.hx b/source/funkin/ui/debug/charting/ChartEditorState.hx index 2e259d3f5..458e5d5fb 100644 --- a/source/funkin/ui/debug/charting/ChartEditorState.hx +++ b/source/funkin/ui/debug/charting/ChartEditorState.hx @@ -2274,8 +2274,25 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState this.openBackupAvailableDialog(welcomeDialog); } } + + #if FEATURE_DISCORD_RPC + updateDiscordRPC(); + #end } + #if FEATURE_DISCORD_RPC + function updateDiscordRPC():Void + { + funkin.api.discord.DiscordClient.instance.setPresence( + { + // TODO: Make this display the song name and update when it changes. + // state: '${currentSongName} [${selectedDifficulty}]', + state: null, + details: 'Chart Editor [Charting]' + }); + } + #end + function setupWelcomeMusic() { this.welcomeMusic.loadEmbedded(Paths.music('chartEditorLoop/chartEditorLoop')); diff --git a/source/funkin/util/Constants.hx b/source/funkin/util/Constants.hx index cf58d191a..b2fa46aea 100644 --- a/source/funkin/util/Constants.hx +++ b/source/funkin/util/Constants.hx @@ -78,7 +78,12 @@ class Constants /** * Link to download the game on Itch.io. */ - public static final URL_ITCH:String = 'https://ninja-muffin24.itch.io/funkin/purchase'; + public static final URL_ITCH:String = 'https://ninja-muffin24.itch.io/funkin'; + + /** + * Link to play the game on Newgrounds. + */ + public static final URL_NEWGROUNDS:String = 'https://www.newgrounds.com/portal/view/770371'; /** * Link to the game's page on Kickstarter. From bfee63f588ab9836d81f16bd31af277a61af729d Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Thu, 19 Sep 2024 11:15:56 -0400 Subject: [PATCH 3/6] Update hmm.json --- hmm.json | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/hmm.json b/hmm.json index 42c63abed..0d57cfbc8 100644 --- a/hmm.json +++ b/hmm.json @@ -112,8 +112,8 @@ "name": "hxdiscord_rpc", "type": "git", "dir": null, - "ref": "main", - "url": "https://github.com/MAJigsaw77/hxdiscord_rpc" + "ref": "79955ce93f0392b245b3610b7697780c8618bc47", + "url": "https://github.com/FunkinCrew/hxdiscord_rpc" }, { "name": "hxjsonast", @@ -155,13 +155,6 @@ "ref": "fe3368f611a84a19afc03011353945ae4da8fffd", "url": "https://github.com/FunkinCrew/lime" }, - { - "name": "linc_discord-rpc", - "type": "git", - "dir": null, - "ref": "2d83fa863ef0c1eace5f1cf67c3ac315d1a3a8a5", - "url": "https://github.com/FunkinCrew/haxe-discord-rpc" - }, { "name": "mconsole", "type": "git", @@ -219,4 +212,4 @@ "url": "https://github.com/fponticelli/thx.semver" } ] -} \ No newline at end of file +} From 84d8c029208a4a94ba3fd78f3c5812edf39e2514 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Wed, 25 Sep 2024 00:39:37 -0400 Subject: [PATCH 4/6] Update art repo --- art | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/art b/art index bfca2ea98..cc347f563 160000 --- a/art +++ b/art @@ -1 +1 @@ -Subproject commit bfca2ea98d11a0f4dee4a27b9390951fbc5701ea +Subproject commit cc347f5630dc8182e62990c66753940a275202f8 From 5693d626b590bf00f3569e88e90f7475fb43c310 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Wed, 25 Sep 2024 03:58:43 -0400 Subject: [PATCH 5/6] Update so that album art works. --- art | 2 +- source/funkin/api/discord/DiscordClient.hx | 8 +++++-- source/funkin/play/PlayState.hx | 25 +++++++++++++++++++--- 3 files changed, 29 insertions(+), 6 deletions(-) diff --git a/art b/art index cc347f563..fbd3e3df7 160000 --- a/art +++ b/art @@ -1 +1 @@ -Subproject commit cc347f5630dc8182e62990c66753940a275202f8 +Subproject commit fbd3e3df77734606d88516770b71b56e6fa04bce diff --git a/source/funkin/api/discord/DiscordClient.hx b/source/funkin/api/discord/DiscordClient.hx index 3b6e891d3..3be2d4c87 100644 --- a/source/funkin/api/discord/DiscordClient.hx +++ b/source/funkin/api/discord/DiscordClient.hx @@ -7,7 +7,7 @@ import sys.thread.Thread; class DiscordClient { - static final CLIENT_ID:String = "814588678700924999"; + static final CLIENT_ID:String = "816168432860790794"; public static var instance(get, never):DiscordClient; static var _instance:Null = null; @@ -96,13 +96,17 @@ class DiscordClient // The large image displaying what the user is doing. // This should probably be album art. // IMPORTANT NOTE: This can be an asset key uploaded to Discord's developer panel OR any URL you like. + presence.largeImageKey = cast(params.largeImageKey, Null) ?? "album-volume1"; + + trace('[DISCORD] largeImageKey: ${presence.largeImageKey}'); // TODO: Make this use the song's album art. // presence.largeImageKey = "icon"; - presence.largeImageKey = "https://f4.bcbits.com/img/a0746694746_16.jpg"; + // presence.largeImageKey = "https://f4.bcbits.com/img/a0746694746_16.jpg"; // The small inset image for what the user is doing. // This can be the opponent's health icon? + // NOTE: Like largeImageKey, this can be a URL, or an asset key. presence.smallImageKey = cast(params.smallImageKey, Null); // NOTE: In previous versions, this showed as "Elapsed", but now shows as playtime and doesn't look good diff --git a/source/funkin/play/PlayState.hx b/source/funkin/play/PlayState.hx index aeba619ff..e544df341 100644 --- a/source/funkin/play/PlayState.hx +++ b/source/funkin/play/PlayState.hx @@ -447,6 +447,7 @@ class PlayState extends MusicBeatSubState #if FEATURE_DISCORD_RPC // Discord RPC variables + var discordRPCAlbum:String = ''; var discordRPCIcon:String = ''; #end @@ -987,6 +988,7 @@ class PlayState extends MusicBeatSubState state: buildDiscordRPCState(), + largeImageKey: discordRPCAlbum, smallImageKey: discordRPCIcon }); #end @@ -1081,6 +1083,8 @@ class PlayState extends MusicBeatSubState { details: 'Game Over - ${buildDiscordRPCDetails()}', state: buildDiscordRPCState(), + + largeImageKey: discordRPCAlbum, smallImageKey: discordRPCIcon }); #end @@ -1300,7 +1304,9 @@ class PlayState extends MusicBeatSubState DiscordClient.instance.setPresence( { state: buildDiscordRPCState(), - details: 'Paused - ${buildDiscordRPCDetails()}', + details: buildDiscordRPCDetails(), + + largeImageKey: discordRPCAlbum, smallImageKey: discordRPCIcon }); } @@ -1309,7 +1315,9 @@ class PlayState extends MusicBeatSubState DiscordClient.instance.setPresence( { state: buildDiscordRPCState(), - details: 'Paused - ${buildDiscordRPCDetails()}', + details: buildDiscordRPCDetails(), + + largeImageKey: discordRPCAlbum, smallImageKey: discordRPCIcon }); } @@ -1345,6 +1353,8 @@ class PlayState extends MusicBeatSubState { state: buildDiscordRPCState(), details: buildDiscordRPCDetails(), + + largeImageKey: discordRPCAlbum, smallImageKey: discordRPCIcon }); } @@ -1354,6 +1364,8 @@ class PlayState extends MusicBeatSubState { state: buildDiscordRPCState(), details: buildDiscordRPCDetails(), + + largeImageKey: discordRPCAlbum, smallImageKey: discordRPCIcon }); // DiscordClient.changePresence(detailsText, '${currentChart.songName} ($discordRPCDifficulty)', discordRPCIcon, true, @@ -1381,6 +1393,8 @@ class PlayState extends MusicBeatSubState { state: buildDiscordRPCState(), details: buildDiscordRPCDetails(), + + largeImageKey: discordRPCAlbum, smallImageKey: discordRPCIcon }); } @@ -1681,7 +1695,8 @@ class PlayState extends MusicBeatSubState iconP2.cameras = [camHUD]; #if FEATURE_DISCORD_RPC - discordRPCIcon = currentCharacterData.opponent; + discordRPCAlbum = 'album-${currentChart.album}'; + discordRPCIcon = 'icon-${currentCharacterData.opponent}'; #end } @@ -1807,6 +1822,8 @@ class PlayState extends MusicBeatSubState { state: buildDiscordRPCState(), details: buildDiscordRPCDetails(), + + largeImageKey: discordRPCAlbum, smallImageKey: discordRPCIcon }); #end @@ -2040,6 +2057,8 @@ class PlayState extends MusicBeatSubState { state: buildDiscordRPCState(), details: buildDiscordRPCDetails(), + + largeImageKey: discordRPCAlbum, smallImageKey: discordRPCIcon }); // DiscordClient.changePresence(detailsText, '${currentChart.songName} ($discordRPCDifficulty)', discordRPCIcon, true, currentSongLengthMs); From ac3e2b25609329e89ec2386c553357213d6a7a5f Mon Sep 17 00:00:00 2001 From: Cameron Taylor Date: Fri, 27 Sep 2024 17:45:54 -0400 Subject: [PATCH 6/6] discord api mac --- hmm.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hmm.json b/hmm.json index 0d57cfbc8..c13626023 100644 --- a/hmm.json +++ b/hmm.json @@ -112,7 +112,7 @@ "name": "hxdiscord_rpc", "type": "git", "dir": null, - "ref": "79955ce93f0392b245b3610b7697780c8618bc47", + "ref": "82c47ecc1a454b7dd644e4fcac7e91155f176dec", "url": "https://github.com/FunkinCrew/hxdiscord_rpc" }, { @@ -212,4 +212,4 @@ "url": "https://github.com/fponticelli/thx.semver" } ] -} +} \ No newline at end of file