Skip to content

Commit

Permalink
stop release (#597)
Browse files Browse the repository at this point in the history
Closes #574
  • Loading branch information
andrew-codes authored Oct 26, 2024
2 parents 975d99f + 4ee1ff5 commit a512e6b
Show file tree
Hide file tree
Showing 42 changed files with 233 additions and 154 deletions.
4 changes: 2 additions & 2 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"features": {
"ghcr.io/devcontainers/features/docker-in-docker:2": {},
"ghcr.io/devcontainers/features/node:1": {
"version": "22.5.0",
"version": "22.10.0",
"nodeGypDependencies": true
},
"ghcr.io/devcontainers/features/sshd:1": {
Expand Down Expand Up @@ -82,4 +82,4 @@
"3000:3000",
"3001:3001"
]
}
}
9 changes: 5 additions & 4 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
/.yarn/** linguist-vendored
/.yarn/releases/* binary
/.yarn/plugins/**/* binary
/.pnp.* binary linguist-generated
/.yarn/** linguist-vendored
/.yarn/releases/* binary
/.yarn/plugins/**/* binary
/.pnp.* binary linguist-generated
text=auto
9 changes: 6 additions & 3 deletions .github/workflows/pull-request-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,12 @@ jobs:
run: corepack enable
- name: Install deps
run: yarn
- name : Generate Docs
- name: Generate Docs
run: yarn run docs/generate
- uses: stefanzweifel/git-auto-commit-action@v5
with:
commit_message: "[skip ci] docs: automated update to docs\n\nskip-checks: true"
file_pattern: "docs/**"
file_pattern: 'docs/**'

unit_tests_linux:
name: Verify PR - Tests (Linux)
Expand Down Expand Up @@ -262,6 +262,9 @@ jobs:
fetch-depth: 0
- name: Add msbuild to PATH
uses: microsoft/setup-msbuild@v2
- name: Download Playnite Toolbox
run: |-
mkdir -p .tools/Playnite && curl "${{ secrets.PLAYNITE_TOOLBOX_URL }}" -o Playnite.zip && unzip Playnite.zip -d .tools/Playnite
- name: Install jq
run: curl -L -o jq.exe https://github.com/stedolan/jq/releases/latest/download/jq-win64.exe
- name: Read Node version
Expand All @@ -288,7 +291,7 @@ jobs:
env:
NODE_ENV: production
- name: Package projects
run: yarn nx run-many --target=build --parallel --verbose --exclude='*,!tag:windows'
run: yarn nx run-many --target=package --parallel --verbose --exclude='*,!tag:windows'
env:
NODE_ENV: production
- name: Upload artifacts
Expand Down
Binary file modified .yarn/install-state.gz
Binary file not shown.
39 changes: 24 additions & 15 deletions _docs/api/mqtt.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,18 +77,19 @@ type Payload = {
## Subscribed Messages by Playnite Web
| Topic | Description/Purpose |
| :----------------------------------- | :----------------------------------------------------------------------------------- |
| `playnite/{deviceId}/library` | Triggers a Playnite Web sync. |
| `playnite/{deviceId}/game/start` | Triggers Playnite to start the release. |
| `playnite/{deviceId}/game/install` | Triggers Playnite to install a release; only valid if release is on the PC platform. |
| `playnite/{deviceId}/game/uninstall` | Triggers Playnite to uninstall a release; only if installed. |
| Topic | Description/Purpose |
| :-------------------------------- | :----------------------------------------------------------------------------------- |
| `playnite/request/library` | Triggers a Playnite Web sync. |
| `playnite/request/game/start` | Triggers Playnite to start the release. |
| `playnite/request/game/install` | Triggers Playnite to install a release; only valid if release is on the PC platform. |
| `playnite/request/game/uninstall` | Triggers Playnite to uninstall a release; only if installed. |
| `playnite/request/game/stop` | Triggers Playnite to stop a release, if running. |
### `playnite/{deviceId}/library`
### `playnite/request/library`
Triggers a Playnite Web sync. There is no payload.
### `playnite/{deviceId}/game/start`
### `playnite/request/game/start`
Triggers Playnite to start the release.
Expand All @@ -103,7 +104,7 @@ type Payload = {
}
```
### `playnite/{deviceId}/game/install`
### `playnite/request/game/install`
Triggers Playnite to install a release; only valid if release is on the PC platform.
Expand All @@ -118,11 +119,7 @@ type Payload = {
}
```
```ts
type Payload = {}
```
### `playnite/{deviceId}/game/uninstall`
### `playnite/request/game/uninstall`
Triggers Playnite to uninstall a release; only if installed.
Expand All @@ -137,6 +134,18 @@ type Payload = {
}
```
### `playnite/request/game/stop`
Triggers Playnite to stop a game process; if one is running.
```ts
type Payload = {}
type Payload = {
game: {
id: string // Release.id
processId: int // Process ID provided in MQTT game state message.
platform: {
id: string // Platform.id
}
}
}
```
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Install the following software on your local development machine:
2. Bash (this guide assumes a bash shell)
3. [vscode](https://code.visualstudio.com/Download)
4. [Docker](https://www.docker.com/products/docker-desktop/) (for Mongodb and MQTT dependencies)
5. [Node.js@>=22.5.0](https://nodejs.org/en/download/package-manager) (recommend using `nvm` to manage Node.js installations)
5. [Node.js@>=22.10.0](https://nodejs.org/en/download/package-manager) (recommend using `nvm` to manage Node.js installations)
- [nvm for OSX](https://github.com/nvm-sh/nvm)
- [nvm for Windows](https://github.com/coreybutler/nvm-windows)
6. [yarn@^4.0.0](https://yarnpkg.com/getting-started)
Expand Down
39 changes: 33 additions & 6 deletions apps/PlayniteWebPlugin/src/PlayniteWeb.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
using PlayniteWeb.UI;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reactive;
using System.Reactive.Linq;
Expand Down Expand Up @@ -229,11 +230,10 @@ public override void OnGameInstalled(OnGameInstalledEventArgs args)

public override void OnGameStarted(OnGameStartedEventArgs args)
{
var game = GameFromRelease(args.Game);
var release = game.Releases.FirstOrDefault(r => r.Id == args.Game.Id);
var release = ReleaseFromPlayniteGame(args.Game);
release.ProcessId = args.StartedProcessId;
var gameStatePublisher = new PublishGameState(GameState.running, (IMqttClient)publisher, topicManager, serializer);
Task.WaitAll(gameStatePublisher.Publish(game).ToArray());
Task.WaitAll(gameStatePublisher.Publish(release).ToArray());
}

public override void OnGameStarting(OnGameStartingEventArgs args)
Expand Down Expand Up @@ -270,7 +270,8 @@ public override void OnApplicationStopped(OnApplicationStoppedEventArgs args)
subscriber.OnUpdateLibrary -= Publisher_LibraryRefreshRequest;
subscriber.OnStartRelease -= Subscriber_OnStartRelease;
subscriber.OnInstallRelease -= Subscriber_OnInstallRelease;
subscriber.OnUninstallRelease -= Subscriber_OnUninstallRelease;
subscriber.OnUninstallRelease -= Subscriber_OnUninstallRelease;
subscriber.OnStopRelease -= Subscriber_OnStopRelease;

gameUpdates.Dispose();
platformUpdates.Dispose();
Expand All @@ -279,6 +280,32 @@ public override void OnApplicationStopped(OnApplicationStoppedEventArgs args)

}

private void Subscriber_OnStopRelease(object sender, Release e)
{
if (!isPcPlatform(e.Platform))
{
return;
}

try
{
var gameProcess = Process.GetProcessById(e.ProcessId.Value);
gameProcess.Kill();
}
catch (Exception ex)
{
var process = Process.GetProcessesByName(e.Name).FirstOrDefault();
if (process == null)
{
process = Process.GetProcesses().Where(p =>p.MainWindowTitle.Contains(e.Name)).FirstOrDefault();

if (process == null) { return; }
}

process.Kill();
}
}

private async Task HandlePublisherConnected(MqttClientConnectedEventArgs args)
{
await publisher.PublishStringAsync(topicManager.GetPublishTopic(PublishTopics.Connection()), serializer.Serialize(new Connection(_version, ConnectionState.online)), MqttQualityOfServiceLevel.ExactlyOnce, retain: false, cancellationToken: default);
Expand All @@ -298,8 +325,8 @@ public override void OnApplicationStarted(OnApplicationStartedEventArgs args)
subscriber.OnUpdateLibrary += Publisher_LibraryRefreshRequest;
subscriber.OnStartRelease += Subscriber_OnStartRelease;
subscriber.OnInstallRelease += Subscriber_OnInstallRelease;
subscriber.OnUninstallRelease += Subscriber_OnUninstallRelease;

subscriber.OnUninstallRelease += Subscriber_OnUninstallRelease;
subscriber.OnStopRelease += Subscriber_OnStopRelease;

gameUpdates.Subscribe(e => HandleGameUpdated(this, e));
platformUpdates.Subscribe(e => HandlePlatformUpdated(this, e));
Expand Down
4 changes: 2 additions & 2 deletions apps/PlayniteWebPlugin/src/PlayniteWeb.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<IntermediateOutputPath>C:\Users\andrew\AppData\Local\Temp\vs3D76.tmp\Debug\</IntermediateOutputPath>
<IntermediateOutputPath>C:\Users\andrew\AppData\Local\Temp\vsF246.tmp\Debug\</IntermediateOutputPath>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
Expand All @@ -30,7 +30,7 @@
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<IntermediateOutputPath>C:\Users\andrew\AppData\Local\Temp\vs3D76.tmp\Release\</IntermediateOutputPath>
<IntermediateOutputPath>C:\Users\andrew\AppData\Local\Temp\vsF246.tmp\Release\</IntermediateOutputPath>
</PropertyGroup>
<ItemGroup>
<Reference Include="Microsoft.Bcl.AsyncInterfaces, Version=8.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
Expand Down
2 changes: 1 addition & 1 deletion apps/PlayniteWebPlugin/src/Services/ObjectDeserializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ public class ObjectDeserializer : IDeserializeObjects

public T Deserialize<T>(string data)
{
var options = new JsonSerializerOptions(JsonSerializerDefaults.General)
var options = new JsonSerializerOptions(JsonSerializerDefaults.Web)
{
Converters = { new TypeConverter() }
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,11 +70,11 @@ private Task Client_ApplicationMessageReceivedAsync(MqttApplicationMessageReceiv

public void StartConnection(IApplyPublisherOptions<IMqttClient> options)
{
client = options.ApplyOptions(client);
if (!IsConnected)
if (IsConnected)
{
return;
}
client = options.ApplyOptions(client);
}

public Task StartDisconnect()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,6 @@ internal interface ISubscribeToPlayniteWeb
event EventHandler<Release> OnStartRelease;
event EventHandler<Release> OnInstallRelease;
event EventHandler<Release> OnUninstallRelease;
event EventHandler<Release> OnStopRelease;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,18 @@ namespace PlayniteWeb.Services.Subscribers.Models
{
internal class StartReleasePayload
{
public string ReleaseId { get; set; }
public string PlatformId { get; set; }
public StartReleasePayloadGame Game { get; set; }
}

public class StartReleasePayloadGame
{
public string Id { get; set; }
public int? ProcessId { get; set; }
public StartReleasePayloadPlatform Platform { get; set; }
}

public class StartReleasePayloadPlatform
{
public string Id { get; set; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,17 @@ public PlayniteWebSubscriber(IMqttClient mqtt, IManageTopics topicBuilder, IDese

private Task Client_ConnectedAsync(MqttClientConnectedEventArgs args)
{
var subscribeTopics = typeof(SubscribeTopics).GetFields(System.Reflection.BindingFlags.Public).Select(field => field.GetValue(null)).ToList();
var subscribeTopics = typeof(SubscribeTopics).GetFields().Select(field => field.GetValue(null)).ToList();

return Task.WhenAll(subscribeTopics.Select(topic => mqtt.SubscribeAsync(new MqttTopicFilterBuilder().WithTopic(topicBuilder.GetSubscribeTopic(topic.ToString())).Build())));
Task.WaitAll(subscribeTopics.Select(topic => mqtt.SubscribeAsync(new MqttTopicFilterBuilder().WithTopic(topicBuilder.GetSubscribeTopic(topic.ToString())).Build())).ToArray());
return Task.CompletedTask;
}

public event EventHandler<Task> OnUpdateLibrary;
public event EventHandler<Release> OnStartRelease;
public event EventHandler<Release> OnInstallRelease;
public event EventHandler<Release> OnUninstallRelease;
public event EventHandler<Release> OnStopRelease;

private Task MesssageReceived(MqttApplicationMessageReceivedEventArgs args)
{
Expand All @@ -63,24 +65,30 @@ private Task MesssageReceived(MqttApplicationMessageReceivedEventArgs args)
{
eventHandler = OnUninstallRelease;
}
else if (args.ApplicationMessage.Topic == topicBuilder.GetSubscribeTopic(SubscribeTopics.RequestStopRelease) && OnStopRelease != null)
{
eventHandler = OnStopRelease;
}

if (eventHandler == null)
{
return Task.WhenAll(task);
}

var payloadData = deserializer.Deserialize<StartReleasePayload>(args.ApplicationMessage.ConvertPayloadToString());
var platformId = Guid.Parse(payloadData.PlatformId);
var platformId = Guid.Parse(payloadData.Game.Platform.Id);
var platform = _api.Database.Platforms.FirstOrDefault(p => p.Id.Equals(platformId));
var releaseId = Guid.Parse(payloadData.ReleaseId);
var releaseId = Guid.Parse(payloadData.Game.Id);
var release = _api.Database.Games.FirstOrDefault(g => g.Id.Equals(releaseId));
if (release == null)
{
LogManager.GetLogger().Debug($"Game with ID {releaseId} not found for installation.");
return Task.WhenAll(task);
}

eventHandler.Invoke(this, new Release(release, platform));
var targetRelease = new Release(release, platform);
targetRelease.ProcessId = payloadData.Game.ProcessId;
eventHandler.Invoke(this, targetRelease);

return Task.WhenAll(task);
}
Expand Down
1 change: 1 addition & 0 deletions apps/PlayniteWebPlugin/src/TopicManager/SubscribeTopics.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@ public static class SubscribeTopics
public const string RequestStartRelease = "game/start";
public const string RequestInstallRelease = "game/install";
public const string RequestUninstallRelease = "game/uninstall";
public const string RequestStopRelease = "game/stop";
}
}
2 changes: 1 addition & 1 deletion apps/playnite-web/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ async function run(mqttClient: AsyncMqttClient) {
)

const signingKey = process.env.SECRET ?? 'secret'
const yoga = createYoga('/api', signingKey, mqttClient, dataApi, dataApi)
const yoga = createYoga('/api', signingKey, mqttClient)

const cspOrigins = (process.env.CSP_ORIGINS ?? '')
.split(',')
Expand Down
14 changes: 13 additions & 1 deletion apps/playnite-web/src/queryHooks/playlists.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { gql } from '@apollo/client/core/core.cjs'
import { useQuery } from '@apollo/client/react/hooks/hooks.cjs'
import { useEffect } from 'react'
import { Playlist } from '../../.generated/types.generated'
import { useSubscribeReleaseActivationState } from './subscribeReleaseActivationState'

const AllPlaylists = gql`
query Playlist {
Expand Down Expand Up @@ -36,7 +38,17 @@ const AllPlaylists = gql`
`

const usePlaylists = () => {
return useQuery<{ playlists: Array<Playlist> }>(AllPlaylists)
const q = useQuery<{ playlists: Array<Playlist> }>(AllPlaylists)
const sub = useSubscribeReleaseActivationState()

useEffect(() => {
q.refetch()
}, [
sub.data?.releaseActivationStateChanged?.id,
sub.data?.releaseActivationStateChanged?.state,
])

return q
}

export { AllPlaylists, usePlaylists }
Loading

0 comments on commit a512e6b

Please sign in to comment.