From 14528970004df9e678f8e9285bdac8fbcccf4d38 Mon Sep 17 00:00:00 2001 From: Andrew Smith Date: Mon, 28 Oct 2024 08:49:14 -0400 Subject: [PATCH 1/8] feat(ext): restart a release #575 --- apps/PlayniteWebPlugin/src/PlayniteWeb.cs | 10 +++++++++- .../Services/Subscribers/ISubscribeToPlayniteWeb.cs | 1 + .../Services/Subscribers/Mqtt/PlayniteWebSubscriber.cs | 5 +++++ .../src/TopicManager/SubscribeTopics.cs | 1 + 4 files changed, 16 insertions(+), 1 deletion(-) diff --git a/apps/PlayniteWebPlugin/src/PlayniteWeb.cs b/apps/PlayniteWebPlugin/src/PlayniteWeb.cs index 98ffe8a18..98a5441bc 100755 --- a/apps/PlayniteWebPlugin/src/PlayniteWeb.cs +++ b/apps/PlayniteWebPlugin/src/PlayniteWeb.cs @@ -272,7 +272,8 @@ public override void OnApplicationStopped(OnApplicationStoppedEventArgs args) subscriber.OnStartRelease -= Subscriber_OnStartRelease; subscriber.OnInstallRelease -= Subscriber_OnInstallRelease; subscriber.OnUninstallRelease -= Subscriber_OnUninstallRelease; - subscriber.OnStopRelease -= Subscriber_OnStopRelease; + subscriber.OnStopRelease -= Subscriber_OnStopRelease; + subscriber.OnRestartRelease -= Subscriber_OnRestartRelease; gameUpdates.Dispose(); platformUpdates.Dispose(); @@ -281,6 +282,12 @@ public override void OnApplicationStopped(OnApplicationStoppedEventArgs args) } + private void Subscriber_OnRestartRelease(object sender, Release e) + { + this.Subscriber_OnStopRelease(sender, e); + this.Subscriber_OnStartRelease(sender, e); + } + private void Subscriber_OnStopRelease(object sender, Release e) { if (!isPcPlatform(e.Platform)) @@ -328,6 +335,7 @@ public override void OnApplicationStarted(OnApplicationStartedEventArgs args) subscriber.OnInstallRelease += Subscriber_OnInstallRelease; subscriber.OnUninstallRelease += Subscriber_OnUninstallRelease; subscriber.OnStopRelease += Subscriber_OnStopRelease; + subscriber.OnRestartRelease += Subscriber_OnRestartRelease; gameUpdates.Subscribe(e => HandleGameUpdated(this, e)); platformUpdates.Subscribe(e => HandlePlatformUpdated(this, e)); diff --git a/apps/PlayniteWebPlugin/src/Services/Subscribers/ISubscribeToPlayniteWeb.cs b/apps/PlayniteWebPlugin/src/Services/Subscribers/ISubscribeToPlayniteWeb.cs index 6eac5536b..daeccdea8 100755 --- a/apps/PlayniteWebPlugin/src/Services/Subscribers/ISubscribeToPlayniteWeb.cs +++ b/apps/PlayniteWebPlugin/src/Services/Subscribers/ISubscribeToPlayniteWeb.cs @@ -11,5 +11,6 @@ internal interface ISubscribeToPlayniteWeb event EventHandler OnInstallRelease; event EventHandler OnUninstallRelease; event EventHandler OnStopRelease; + event EventHandler OnRestartRelease; } } diff --git a/apps/PlayniteWebPlugin/src/Services/Subscribers/Mqtt/PlayniteWebSubscriber.cs b/apps/PlayniteWebPlugin/src/Services/Subscribers/Mqtt/PlayniteWebSubscriber.cs index bb7495e49..146ba7b89 100755 --- a/apps/PlayniteWebPlugin/src/Services/Subscribers/Mqtt/PlayniteWebSubscriber.cs +++ b/apps/PlayniteWebPlugin/src/Services/Subscribers/Mqtt/PlayniteWebSubscriber.cs @@ -42,6 +42,7 @@ private Task Client_ConnectedAsync(MqttClientConnectedEventArgs args) public event EventHandler OnInstallRelease; public event EventHandler OnUninstallRelease; public event EventHandler OnStopRelease; + public event EventHandler OnRestartRelease; private Task MesssageReceived(MqttApplicationMessageReceivedEventArgs args) { @@ -69,6 +70,10 @@ private Task MesssageReceived(MqttApplicationMessageReceivedEventArgs args) { eventHandler = OnStopRelease; } + else if (args.ApplicationMessage.Topic == topicBuilder.GetSubscribeTopic(SubscribeTopics.RequestRestartRelease) && OnRestartRelease != null) + { + eventHandler = OnRestartRelease; + } if (eventHandler == null) { diff --git a/apps/PlayniteWebPlugin/src/TopicManager/SubscribeTopics.cs b/apps/PlayniteWebPlugin/src/TopicManager/SubscribeTopics.cs index ad59bdc50..a7b8377d0 100755 --- a/apps/PlayniteWebPlugin/src/TopicManager/SubscribeTopics.cs +++ b/apps/PlayniteWebPlugin/src/TopicManager/SubscribeTopics.cs @@ -7,5 +7,6 @@ public static class SubscribeTopics public const string RequestInstallRelease = "game/install"; public const string RequestUninstallRelease = "game/uninstall"; public const string RequestStopRelease = "game/stop"; + public const string RequestRestartRelease = "game/restart"; } } From dd097bf58bb43fc9a99a07f14fecfd6283b1c9bd Mon Sep 17 00:00:00 2001 From: Andrew Smith Date: Mon, 28 Oct 2024 13:26:56 +0000 Subject: [PATCH 2/8] feat(ui,api): restart release --- apps/playnite-web/src/queryHooks/restartRelease.ts | 3 --- .../gameRelease/resolvers/Mutation/restartGameRelease.ts | 1 + 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/apps/playnite-web/src/queryHooks/restartRelease.ts b/apps/playnite-web/src/queryHooks/restartRelease.ts index ec858d654..7bfcd447f 100755 --- a/apps/playnite-web/src/queryHooks/restartRelease.ts +++ b/apps/playnite-web/src/queryHooks/restartRelease.ts @@ -1,9 +1,6 @@ import { gql } from '@apollo/client/core/core.cjs' import { useMutation } from '@apollo/client/react/hooks/hooks.cjs' import { GameRelease } from 'apps/playnite-web/.generated/types.generated' -import _ from 'lodash' - -const { merge } = _ const Restart_Game_Release_Mutation = gql` mutation restartGameRelease($releaseId: String!) { diff --git a/apps/playnite-web/src/server/graphql/modules/gameRelease/resolvers/Mutation/restartGameRelease.ts b/apps/playnite-web/src/server/graphql/modules/gameRelease/resolvers/Mutation/restartGameRelease.ts index 4b5a087c7..39fd2aa5b 100755 --- a/apps/playnite-web/src/server/graphql/modules/gameRelease/resolvers/Mutation/restartGameRelease.ts +++ b/apps/playnite-web/src/server/graphql/modules/gameRelease/resolvers/Mutation/restartGameRelease.ts @@ -52,6 +52,7 @@ export const restartGameRelease: NonNullable< id: release.id, gameId: release.gameId, name: release.name, + processId: release.processId, platform: { id: platform.id, name: platform.name, From 0840c023fc0106cd03b414b0ae103de91c83f7bb Mon Sep 17 00:00:00 2001 From: Andrew Smith Date: Mon, 28 Oct 2024 13:27:21 +0000 Subject: [PATCH 3/8] game action, no optimistic updates --- .../src/queryHooks/startRelease.ts | 30 +------------------ .../src/queryHooks/stopRelease.ts | 29 ------------------ 2 files changed, 1 insertion(+), 58 deletions(-) diff --git a/apps/playnite-web/src/queryHooks/startRelease.ts b/apps/playnite-web/src/queryHooks/startRelease.ts index 6f2806271..b9668152e 100755 --- a/apps/playnite-web/src/queryHooks/startRelease.ts +++ b/apps/playnite-web/src/queryHooks/startRelease.ts @@ -2,8 +2,6 @@ import { gql } from '@apollo/client/core/core.cjs' import { useMutation } from '@apollo/client/react/hooks/hooks.cjs' import { GameRelease } from 'apps/playnite-web/.generated/types.generated' import _ from 'lodash' -import { PlaylistEntity } from '../server/graphql/resolverTypes' -import { AllPlaylists } from './playlists' const { merge } = _ @@ -16,32 +14,6 @@ const Activate_Mutation = gql` ` const useStartRelease = () => { - return useMutation<{ startGameRelease: GameRelease }>(Activate_Mutation, { - update: (cache, mutationResult) => { - let data = cache.readQuery<{ playlists: Array }>({ - query: AllPlaylists, - }) - if (data) { - cache.writeQuery({ - query: AllPlaylists, - data: merge({}, data, { - playlists: data.playlists.map((playlist) => - merge({}, playlist, { - games: playlist.games.map((game) => - merge({}, game, { - releases: game.releases.map((release) => - merge({}, release, { - runState: 'launching', - }), - ), - }), - ), - }), - ), - }), - }) - } - }, - }) + return useMutation<{ startGameRelease: GameRelease }>(Activate_Mutation) } export { Activate_Mutation, useStartRelease } diff --git a/apps/playnite-web/src/queryHooks/stopRelease.ts b/apps/playnite-web/src/queryHooks/stopRelease.ts index 5d8e3484b..8f66ddec6 100755 --- a/apps/playnite-web/src/queryHooks/stopRelease.ts +++ b/apps/playnite-web/src/queryHooks/stopRelease.ts @@ -2,8 +2,6 @@ import { gql } from '@apollo/client/core/core.cjs' import { useMutation } from '@apollo/client/react/hooks/hooks.cjs' import { GameRelease } from 'apps/playnite-web/.generated/types.generated' import _ from 'lodash' -import { PlaylistEntity } from '../server/graphql/resolverTypes' -import { AllPlaylists } from './playlists' const { merge } = _ @@ -18,33 +16,6 @@ const Stop_Game_Release_Mutation = gql` const useStopRelease = () => { return useMutation<{ stopGameRelease: GameRelease }>( Stop_Game_Release_Mutation, - { - update: (cache, mutationResult) => { - let data = cache.readQuery<{ playlists: Array }>({ - query: AllPlaylists, - }) - if (data) { - cache.writeQuery({ - query: AllPlaylists, - data: merge({}, data, { - playlists: data.playlists.map((playlist) => - merge({}, playlist, { - games: playlist.games.map((game) => - merge({}, game, { - releases: game.releases.map((release) => - merge({}, release, { - runState: 'stopping', - }), - ), - }), - ), - }), - ), - }), - }) - } - }, - }, ) } export { Stop_Game_Release_Mutation, useStopRelease } From 0d0f776ca7cfe027385cbcc6e99335e0f14aba90 Mon Sep 17 00:00:00 2001 From: Andrew Smith Date: Mon, 28 Oct 2024 13:41:36 +0000 Subject: [PATCH 4/8] fix(api): run state update breaking when stopping --- .../server/mqttUpdater/handlers/persistGameReleaseState.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/apps/playnite-web/src/server/mqttUpdater/handlers/persistGameReleaseState.ts b/apps/playnite-web/src/server/mqttUpdater/handlers/persistGameReleaseState.ts index f6e5da3f3..77f08b7e1 100755 --- a/apps/playnite-web/src/server/mqttUpdater/handlers/persistGameReleaseState.ts +++ b/apps/playnite-web/src/server/mqttUpdater/handlers/persistGameReleaseState.ts @@ -1,10 +1,12 @@ import createDebugger from 'debug' +import _ from 'lodash' import { HandlerOptions } from '..' import type { IHandlePublishedTopics } from '../IHandlePublishedTopics' const debug = createDebugger( 'playnite-web/game-db-updater/handler/persistGameReleaseState', ) +const { merge } = _ const topicMatch = /^playnite\/.*\/response\/game\/state$/ @@ -87,7 +89,10 @@ const create = }, ) - options.pubsub.publish('releaseRunStateChanged', release) + options.pubsub.publish( + 'releaseRunStateChanged', + merge({}, release, { runState: { id: newState } }), + ) } catch (e) { console.error(e) } From 3c3b40b07154777fec135f1a3d7af47c4b3a0ce0 Mon Sep 17 00:00:00 2001 From: Andrew Smith Date: Mon, 28 Oct 2024 13:57:48 +0000 Subject: [PATCH 5/8] address broken run state notifications --- .../resolvers/GameReleaseStateSubscriptionPayload.ts | 3 +++ .../mqttUpdater/handlers/persistGameReleaseState.ts | 10 ++++++---- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/apps/playnite-web/src/server/graphql/modules/gameRelease/resolvers/GameReleaseStateSubscriptionPayload.ts b/apps/playnite-web/src/server/graphql/modules/gameRelease/resolvers/GameReleaseStateSubscriptionPayload.ts index 6bf62bd5c..30d59df01 100755 --- a/apps/playnite-web/src/server/graphql/modules/gameRelease/resolvers/GameReleaseStateSubscriptionPayload.ts +++ b/apps/playnite-web/src/server/graphql/modules/gameRelease/resolvers/GameReleaseStateSubscriptionPayload.ts @@ -1,5 +1,8 @@ import type { GameReleaseStateSubscriptionPayloadResolvers } from './../../../../../../.generated/types.generated' export const GameReleaseStateSubscriptionPayload: GameReleaseStateSubscriptionPayloadResolvers = { + runState: async (parent, _args, _ctx) => { + return (parent.runState ?? 'installed') as unknown as string + }, /* Implement GameReleaseStateSubscriptionPayload resolver logic here */ } diff --git a/apps/playnite-web/src/server/mqttUpdater/handlers/persistGameReleaseState.ts b/apps/playnite-web/src/server/mqttUpdater/handlers/persistGameReleaseState.ts index 77f08b7e1..ac37d738f 100755 --- a/apps/playnite-web/src/server/mqttUpdater/handlers/persistGameReleaseState.ts +++ b/apps/playnite-web/src/server/mqttUpdater/handlers/persistGameReleaseState.ts @@ -89,10 +89,12 @@ const create = }, ) - options.pubsub.publish( - 'releaseRunStateChanged', - merge({}, release, { runState: { id: newState } }), - ) + options.pubsub.publish('releaseRunStateChanged', { + id: release.id, + gameId: release.gameId, + processId: release.processId ?? null, + runState: newState, + }) } catch (e) { console.error(e) } From 489bc65b772db9c2486e57809854b626012d4760 Mon Sep 17 00:00:00 2001 From: Andrew Smith Date: Mon, 28 Oct 2024 14:28:30 +0000 Subject: [PATCH 6/8] restart is not blocked by a running release --- .../gameRelease/resolvers/Mutation/restartGameRelease.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/apps/playnite-web/src/server/graphql/modules/gameRelease/resolvers/Mutation/restartGameRelease.ts b/apps/playnite-web/src/server/graphql/modules/gameRelease/resolvers/Mutation/restartGameRelease.ts index 39fd2aa5b..7ded6453d 100755 --- a/apps/playnite-web/src/server/graphql/modules/gameRelease/resolvers/Mutation/restartGameRelease.ts +++ b/apps/playnite-web/src/server/graphql/modules/gameRelease/resolvers/Mutation/restartGameRelease.ts @@ -1,4 +1,3 @@ -import { GraphQLError } from 'graphql' import { Platform, Release } from '../../../../../data/types.entities' import { fromString } from '../../../../../oid' import type { MutationResolvers } from './../../../../../../../.generated/types.generated' @@ -17,10 +16,6 @@ export const restartGameRelease: NonNullable< value: releaseId, })) as Array - if (release.runState.id === 'running') { - throw new GraphQLError('Game is already running') - } - await _ctx.updateQueryApi.executeUpdate( { entityType: 'Release', From cebd628682f97bd9b3422663d9f84eb674273a0a Mon Sep 17 00:00:00 2001 From: Andrew Smith Date: Mon, 28 Oct 2024 14:29:28 +0000 Subject: [PATCH 7/8] correctly update UI on run state change events --- apps/playnite-web/src/queryHooks/gameById.ts | 4 ++-- apps/playnite-web/src/queryHooks/playlists.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/playnite-web/src/queryHooks/gameById.ts b/apps/playnite-web/src/queryHooks/gameById.ts index 28c1d45cf..6dccc565d 100755 --- a/apps/playnite-web/src/queryHooks/gameById.ts +++ b/apps/playnite-web/src/queryHooks/gameById.ts @@ -40,8 +40,8 @@ const useGameById = (opts: QueryHookOptions) => { useEffect(() => { q.refetch() }, [ - sub.data?.releaseActivationStateChanged?.id, - sub.data?.releaseActivationStateChanged?.state, + sub.data?.releaseRunStateChanged?.id, + sub.data?.releaseRunStateChanged?.state, ]) return q diff --git a/apps/playnite-web/src/queryHooks/playlists.ts b/apps/playnite-web/src/queryHooks/playlists.ts index 32c4aba9b..bc5471180 100755 --- a/apps/playnite-web/src/queryHooks/playlists.ts +++ b/apps/playnite-web/src/queryHooks/playlists.ts @@ -44,8 +44,8 @@ const usePlaylists = () => { useEffect(() => { q.refetch() }, [ - sub.data?.releaseActivationStateChanged?.id, - sub.data?.releaseActivationStateChanged?.state, + sub.data?.releaseRunStateChanged?.id, + sub.data?.releaseRunStateChanged?.state, ]) return q From 562ec009310f2a98083d1ea633a98c55dcdd4efe Mon Sep 17 00:00:00 2001 From: Andrew Smith Date: Tue, 29 Oct 2024 08:16:43 -0400 Subject: [PATCH 8/8] successfully restart a game Delay is needed after stopping a game and before starting it. #575 --- apps/PlayniteWebPlugin/src/Models/Release.cs | 26 ++++++++++++++++++++ apps/PlayniteWebPlugin/src/PlayniteWeb.cs | 11 ++++++++- 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/apps/PlayniteWebPlugin/src/Models/Release.cs b/apps/PlayniteWebPlugin/src/Models/Release.cs index 6e415a5f5..dc2e71bdd 100755 --- a/apps/PlayniteWebPlugin/src/Models/Release.cs +++ b/apps/PlayniteWebPlugin/src/Models/Release.cs @@ -6,6 +6,13 @@ namespace PlayniteWeb.Models { + public enum RunState + { + Running, + Installed, + Uninstalled + } + public class Release : IIdentifiable { private readonly Playnite.SDK.Models.Game game; @@ -23,6 +30,25 @@ public Release(Playnite.SDK.Models.Game game, Platform platform) this.platform = platform; } + [DontSerialize] + public RunState RunState + { + get + { + if (game.IsRunning) + { + return RunState.Running; + } + else if (game.IsInstalled) + { + return RunState.Installed; + } + else + { + return RunState.Uninstalled; + } + } + } public int? ProcessId { get; set; } public Guid Id => game.Id; public string Name => game.Name; diff --git a/apps/PlayniteWebPlugin/src/PlayniteWeb.cs b/apps/PlayniteWebPlugin/src/PlayniteWeb.cs index 98a5441bc..daa61decc 100755 --- a/apps/PlayniteWebPlugin/src/PlayniteWeb.cs +++ b/apps/PlayniteWebPlugin/src/PlayniteWeb.cs @@ -285,7 +285,7 @@ public override void OnApplicationStopped(OnApplicationStoppedEventArgs args) private void Subscriber_OnRestartRelease(object sender, Release e) { this.Subscriber_OnStopRelease(sender, e); - this.Subscriber_OnStartRelease(sender, e); + Task.Delay(3000).ContinueWith(t => this.Subscriber_OnStartRelease(sender, e)); } private void Subscriber_OnStopRelease(object sender, Release e) @@ -295,6 +295,11 @@ private void Subscriber_OnStopRelease(object sender, Release e) return; } + if (e.RunState != RunState.Running) + { + return; + } + try { var gameProcess = Process.GetProcessById(e.ProcessId.Value); @@ -392,7 +397,11 @@ private void Subscriber_OnStartRelease(object sender, Release release) if (!isPcPlatform(release.Platform)) { return; + } + if (release.RunState == RunState.Running) + { + return; } if (!release.IsInstalled)