Skip to content

Commit

Permalink
Merge pull request #40 from KrystianLesniak/develop
Browse files Browse the repository at this point in the history
Release: 1.7.0
  • Loading branch information
KrystianLesniak committed Nov 12, 2023
2 parents c167d0e + f60a9a7 commit 567d251
Show file tree
Hide file tree
Showing 19 changed files with 235 additions and 70 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

***

GamesLauncher is a plugin for [Flow launcher](https://github.com/Flow-Launcher/Flow.Launcher) that simplifies the process of searching for and launching games from multiple libraries. It provides quick and convenient access to your favorite games across various platforms, including Steam, Epic Games, Xbox, Amazon, and more libraries to come.
GamesLauncher is a plugin for [Flow launcher](https://github.com/Flow-Launcher/Flow.Launcher) that simplifies the process of searching for and launching games from multiple libraries. It provides quick and convenient access to your favorite games across various platforms, including Steam, Epic Games, Xbox, and more.

![Capture](docs/capture.gif)

Expand All @@ -16,6 +16,7 @@ GamesLauncher is a plugin for [Flow launcher](https://github.com/Flow-Launcher/F
* Steam
* Epic Games Launcher
* Xbox
* Ubisoft Connect
* Amazon Games
* [Custom Shortcuts](#custom-shortcuts)

Expand Down
1 change: 1 addition & 0 deletions src/GamesLauncher.Common/Settings/MainSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@ public class MainSettings
public bool SynchronizeSteam { get; set; } = true;
public bool SynchronizeXbox { get; set; } = true;
public bool SynchronizeAmazon { get; set; } = true;
public bool SynchronizeUbisoft { get; set; } = true;
}
}
23 changes: 10 additions & 13 deletions src/GamesLauncher.Platforms/Game.cs
Original file line number Diff line number Diff line change
@@ -1,32 +1,29 @@
using Flow.Launcher.Plugin;
using static Flow.Launcher.Plugin.Result;

namespace GamesLauncher.Platforms
{
public class Game
public class Game : Result
{
public new string SubTitle => Platform;

internal Game(string title,
Func<ActionContext, ValueTask<bool>> runTask,
string? iconPath,
IconDelegate? iconDelegate,
string platform,
Func<Task> runTask,
string? iconPath,
IconDelegate? iconDelegate = null,
UninstallAction? uninstallAction = null)
{
Title = title;
RunTask = runTask;
IconPath = iconPath;
IconDelegate = iconDelegate;
IcoPath = iconPath;
Icon = iconDelegate;
Platform = platform;
UninstallAction = uninstallAction;
}

public string Title { get; }
public Func<ActionContext, ValueTask<bool>> RunTask { get; }
public string InternalGameId => $"{Platform}_{Title}";
public Func<Task> RunTask { get; set; }
public UninstallAction? UninstallAction { get; }
public string? IconPath { get; }
public IconDelegate? IconDelegate { get; }
public string Platform { get; }
public string InternalGameId => $"{Platform}_{Title}";

}

Expand Down
5 changes: 4 additions & 1 deletion src/GamesLauncher.Platforms/GamesLauncher.Platforms.csproj
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net7.0-windows</TargetFramework>
Expand Down Expand Up @@ -34,6 +34,9 @@
<None Update="Icons\steam.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="Icons\ubisoft.ico">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="Icons\xbox.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
Expand Down
Binary file added src/GamesLauncher.Platforms/Icons/ubisoft.ico
Binary file not shown.
10 changes: 5 additions & 5 deletions src/GamesLauncher.Platforms/PlatformsManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@
using GamesLauncher.Common.Settings;
using GamesLauncher.Platforms.SyncEngines;
using GamesLauncher.Platforms.SyncEngines.Amazon;
using GamesLauncher.Platforms.SyncEngines.Common.Interfaces;
using GamesLauncher.Platforms.SyncEngines.Epic;
using GamesLauncher.Platforms.SyncEngines.Steam;
using GamesLauncher.Platforms.SyncEngines.Ubisoft;
using System.Diagnostics;

namespace GamesLauncher.Platforms
Expand Down Expand Up @@ -39,11 +41,6 @@ await Parallel.ForEachAsync(Engines, async (engine, ct) =>
#endif
}

public Game? GetGame(string title, string platform)
{
return AllSynchronizedGames.FirstOrDefault(x => x.Title == title && x.Platform == platform);
}

private IEnumerable<ISyncEngine> InitializeEngines(MainSettings settings)
{
var engines = new List<ISyncEngine>();
Expand All @@ -57,6 +54,9 @@ private IEnumerable<ISyncEngine> InitializeEngines(MainSettings settings)
if (settings.SynchronizeSteam)
engines.Add(new SteamSyncEngine(publicApi));

if (settings.SynchronizeUbisoft)
engines.Add(new UbisoftSyncEngine(publicApi));

if (settings.SynchronizeAmazon)
engines.Add(new AmazonSyncEngine(publicApi));

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using Flow.Launcher.Plugin;
using GamesLauncher.Platforms.SyncEngines.Amazon.Readers;
using GamesLauncher.Platforms.SyncEngines.Common.Interfaces;
using System.Collections.Concurrent;

namespace GamesLauncher.Platforms.SyncEngines.Amazon
Expand Down Expand Up @@ -39,15 +40,15 @@ public async Task SynchronizeGames()
}


private Func<ActionContext, ValueTask<bool>> GetGameRunTask(string gameAppIdString)
private Func<Task> GetGameRunTask(string gameAppIdString)
{
var uriString = "amazon-games://play/" + gameAppIdString;

return (context) =>
return () =>
{
publicApi.OpenAppUri(new Uri(uriString));
return ValueTask.FromResult(true);
return Task.CompletedTask;
};
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
using GamesLauncher.Platforms.SyncEngines.Common.ControlPanelUninstall.Models;
using Microsoft.Win32;
using System.Collections.Concurrent;

namespace GamesLauncher.Platforms.SyncEngines.Common.ControlPanelUninstall
{
internal static class ControlPanelUninstall
{
internal static async Task<IEnumerable<UninstallProgram>> GetPrograms()
{
var programs = new ConcurrentBag<UninstallProgram>();

var registryHives = new RegistryHive[] { RegistryHive.LocalMachine, RegistryHive.CurrentUser };
var registryViews = new RegistryView[] { RegistryView.Registry32, RegistryView.Registry64 };

await Parallel.ForEachAsync(registryHives, async (hive, ct) =>
{
await Parallel.ForEachAsync(registryViews, async (view, ct) =>
{
SearchPrograms(hive, view, programs);
await Task.CompletedTask;
});
});

return programs;


static void SearchPrograms(RegistryHive hive, RegistryView view, ConcurrentBag<UninstallProgram> programs)
{
const string uninstallRootKey = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\";

using var baseKey = RegistryKey.OpenBaseKey(hive, view);

using var uninstallSubKey = baseKey.OpenSubKey(uninstallRootKey);
if (uninstallSubKey == null)
return;

foreach (var programUninstallSubKeyName in uninstallSubKey.GetSubKeyNames())
{
try
{
using var prog = baseKey.OpenSubKey(uninstallRootKey + programUninstallSubKeyName);
if (prog == null)
continue;

programs.Add(new UninstallProgram()
{
DisplayIcon = prog.GetValue("DisplayIcon")?.ToString(),
DisplayName = prog.GetValue("DisplayName")?.ToString() ?? string.Empty,
UninstallCommand = prog.GetValue("UninstallString")?.ToString(),
SubKeyName = programUninstallSubKeyName
});
}
catch (System.Security.SecurityException)
{
}
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
namespace GamesLauncher.Platforms.SyncEngines.Common.ControlPanelUninstall.Models
{
internal class UninstallProgram
{
public string DisplayName { get; internal set; } = string.Empty;
public string SubKeyName { get; internal set; } = string.Empty;
public string? DisplayIcon { get; internal set; }
public string? UninstallCommand { get; internal set; }
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace GamesLauncher.Platforms.SyncEngines;
namespace GamesLauncher.Platforms.SyncEngines.Common.Interfaces;

internal interface ISyncEngine
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Flow.Launcher.Plugin;
using GamesLauncher.Platforms.SyncEngines.Common.Interfaces;
using GamesLauncher.Platforms.SyncEngines.Epic.Models;
using Microsoft.Win32;
using Newtonsoft.Json;
Expand Down Expand Up @@ -69,15 +70,14 @@ private static async Task<IEnumerable<EpicGame>> GetEpicGamesFromMetadata()
return epicGames;
}

private Func<ActionContext, ValueTask<bool>> PrepareRunTask(string catalogNamespace, string catalogItemId, string appName)
private Func<Task> PrepareRunTask(string catalogNamespace, string catalogItemId, string appName)
{
var launchUriString = $"com.epicgames.launcher://apps/{catalogNamespace}%3A{catalogItemId}%3A{appName}?action=launch&silent=true";

return (context) =>
return async () =>
{
publicApi.OpenAppUri(new Uri(launchUriString));
return ValueTask.FromResult(true);
await Task.CompletedTask;
};
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using Flow.Launcher.Plugin;
using GamesLauncher.Common;
using GamesLauncher.Platforms.SyncEngines.Common.Interfaces;

namespace GamesLauncher.Platforms.SyncEngines
{
Expand Down Expand Up @@ -43,13 +44,12 @@ public async Task SynchronizeGames()
SynchronizedGames = syncedGames;
}

private Func<ActionContext, ValueTask<bool>> GetGameRunTask(string fullPath)
private Func<Task> GetGameRunTask(string fullPath)
{
return (context) =>
return async () =>
{
publicApi.ShellRun($"start \"\" \"{fullPath}\"");
return ValueTask.FromResult(true);
await Task.CompletedTask;
};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using GameFinder.Common;
using GameFinder.RegistryUtils;
using GameFinder.StoreHandlers.Steam;
using GamesLauncher.Platforms.SyncEngines.Common.Interfaces;
using NexusMods.Paths;

namespace GamesLauncher.Platforms.SyncEngines.Steam
Expand Down Expand Up @@ -47,15 +48,14 @@ private Game MapSteamGameToGame(SteamGame steamGame)
);
}

private Func<ActionContext, ValueTask<bool>> GetGameRunTask(string gameAppIdString)
private Func<Task> GetGameRunTask(string gameAppIdString)
{
var uriString = $"steam://launch/{gameAppIdString}/Dialog";

return (context) =>
return async () =>
{
publicApi.OpenAppUri(new Uri(uriString));
return ValueTask.FromResult(true);
await Task.CompletedTask;
};
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
using Flow.Launcher.Plugin;
using GamesLauncher.Platforms.SyncEngines.Common.ControlPanelUninstall;
using GamesLauncher.Platforms.SyncEngines.Common.ControlPanelUninstall.Models;
using GamesLauncher.Platforms.SyncEngines.Common.Interfaces;

namespace GamesLauncher.Platforms.SyncEngines.Ubisoft
{
internal class UbisoftSyncEngine : ISyncEngine
{
public string PlatformName => "Ubisoft Connect";
public IEnumerable<Game> SynchronizedGames { get; private set; } = Array.Empty<Game>();


private readonly string UbisoftConnectUninstallRegistryKeyStart = "Uplay Install ";

private readonly IPublicAPI publicApi;

public UbisoftSyncEngine(IPublicAPI publicApi)
{
this.publicApi = publicApi;
}

public async Task SynchronizeGames()
{
var ubiUninstallPrograms = (await ControlPanelUninstall.GetPrograms())
.Where(x => x.SubKeyName.StartsWith(UbisoftConnectUninstallRegistryKeyStart));

if (!ubiUninstallPrograms.Any())
return;

var syncedGames = new List<Game>();

foreach (var ubiUninstallProgram in ubiUninstallPrograms)
{
syncedGames.Add(new Game(
title: ubiUninstallProgram.DisplayName,
platform: PlatformName,
runTask: GetGameRunTask(ubiUninstallProgram),
iconPath: ubiUninstallProgram.DisplayIcon ?? Path.Combine("Icons", "ubisoft.ico"),
uninstallAction: GetUninstallAction(ubiUninstallProgram)
));
}

SynchronizedGames = syncedGames;
}

private Func<Task> GetGameRunTask(UninstallProgram ubiUninstallProgram)
{
var gameId = ubiUninstallProgram.SubKeyName.Replace(UbisoftConnectUninstallRegistryKeyStart, string.Empty).Trim();
var uriString = $"uplay://launch/{gameId}/0";

return async () =>
{
publicApi.OpenAppUri(new Uri(uriString));
await Task.CompletedTask;
};
}

private UninstallAction? GetUninstallAction(UninstallProgram ubiUninstallProgram)
{
var uninstallCommand = ubiUninstallProgram.UninstallCommand;
var subKeyName = ubiUninstallProgram.SubKeyName;

if (uninstallCommand == null) return null;

return new UninstallAction(async () =>
{
publicApi.ShellRun(uninstallCommand);
for (int i = 0; i < 12; i++)
{
var programs = await ControlPanelUninstall.GetPrograms();
if (programs.Any(x => x.SubKeyName.Equals(subKeyName)) == false)
break;
await Task.Delay(TimeSpan.FromSeconds(5));
}
await SynchronizeGames();
});
}
}
}
Loading

0 comments on commit 567d251

Please sign in to comment.