Skip to content

Commit

Permalink
- Sync the currently played game with the discord Presence API
Browse files Browse the repository at this point in the history
- Sync installed Gamevault games with Steam shortcuts
  • Loading branch information
Yelo420 committed Dec 3, 2024
1 parent 22cc560 commit 01ea89c
Show file tree
Hide file tree
Showing 14 changed files with 277 additions and 82 deletions.
5 changes: 3 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
# GameVault App Changelog

## 1.13.2
Recommended Gamevault Server Version: `v13.1.0`
## 1.14.0
Recommended Gamevault Server Version: `v13.1.2`
### Changes

- Sync the currently played game with the discord Presence API (+)
- Sync installed Gamevault games with Steam shortcuts (+)
- Bug fix: Media slider video was sometimes rendered on top of a popup
- Bug fix: Crash on Media Slider navigation
Expand Down
41 changes: 40 additions & 1 deletion gamevault/Helper/CacheHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ internal static async Task CreateOfflineCacheAsync(Game game)
catch { }
}

internal static async Task HandleImageCacheAsync(int identifier, int imageId, string cachePath, ImageCache cacheType, System.Windows.Controls.Image img)
internal static async Task LoadImageCacheToUIAsync(int identifier, int imageId, string cachePath, ImageCache cacheType, System.Windows.Controls.Image img)
{
string cacheFile = $"{cachePath}/{identifier}.{imageId}";
try
Expand Down Expand Up @@ -111,6 +111,45 @@ internal static async Task HandleImageCacheAsync(int identifier, int imageId, st
img.Source = GetReplacementImage(cacheType);
}
}
internal static async Task EnsureImageCacheForGame(Game game)
{
try
{
if (LoginManager.Instance.IsLoggedIn())
{
if (TaskQueue.Instance.IsAlreadyInProcess(game.Metadata.Background.ID))
{
await TaskQueue.Instance.WaitForProcessToFinish(game.Metadata.Background.ID);
}
if (TaskQueue.Instance.IsAlreadyInProcess(game.Metadata.Cover.ID))
{
await TaskQueue.Instance.WaitForProcessToFinish(game.Metadata.Cover.ID);
}

string backGroundCacheFile = $"{AppFilePath.ImageCache}/gbg/{game.ID}.{game.Metadata.Background.ID}";
string boxArtCacheFile = $"{AppFilePath.ImageCache}/gbox/{game.ID}.{game.Metadata.Cover.ID}";
if (!Directory.Exists($"{AppFilePath.ImageCache}/gbg"))
{
Directory.CreateDirectory($"{AppFilePath.ImageCache}/gbg");
}
if (!Directory.Exists($"{AppFilePath.ImageCache}/gbox"))
{
Directory.CreateDirectory($"{AppFilePath.ImageCache}/gbox");
}

if (!File.Exists(backGroundCacheFile))
{
//Not in que because its not loading images for the UI
await WebHelper.DownloadImageFromUrlAsync($"{SettingsViewModel.Instance.ServerUrl}/api/media/{game.Metadata.Background.ID}", backGroundCacheFile);
}
if (!File.Exists(boxArtCacheFile))
{
await WebHelper.DownloadImageFromUrlAsync($"{SettingsViewModel.Instance.ServerUrl}/api/media/{game.Metadata.Cover.ID}", boxArtCacheFile);
}
}
}
catch { }
}
internal static BitmapImage GetReplacementImage(ImageCache cacheType)
{
switch (cacheType)
Expand Down
7 changes: 4 additions & 3 deletions gamevault/Helper/GameTimeTracker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ public async Task Start()
private void TimerCallback(object sender, ElapsedEventArgs e)
{
Task.Run(() =>
{
string installationPath = Path.Combine(SettingsViewModel.Instance.RootPath, "GameVault\\Installations");
{
string installationPath = Path.Combine(SettingsViewModel.Instance.RootPath, "GameVault\\Installations");

if (!Directory.Exists(installationPath))
return;
Expand Down Expand Up @@ -93,6 +93,7 @@ private void TimerCallback(object sender, ElapsedEventArgs e)
{
WebHelper.Put(@$"{SettingsViewModel.Instance.ServerUrl}/api/progresses/user/{LoginManager.Instance.GetCurrentUser().ID}/game/{gameid}/increment", string.Empty);
}
DiscordHelper.Instance.SyncGameWithDiscordPresence(gamesToCountUp, foundGames);
}
catch (Exception ex)
{
Expand Down Expand Up @@ -165,7 +166,7 @@ private string[] GetAllOfflineCacheKeys()
return keys.ToArray();
}
return new string[0];
}
}
private bool ContainsValueFromIgnoreList(string value)
{
return SettingsViewModel.Instance.IgnoreList.Any(x => x.Contains(value, StringComparison.OrdinalIgnoreCase));
Expand Down
77 changes: 77 additions & 0 deletions gamevault/Helper/Integrations/DiscordHelper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
using DiscordRPC;
using DiscordRPC.Logging;
using gamevault.Models;
using gamevault.ViewModels;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace gamevault.Helper
{
internal class DiscordHelper
{
#region Singleton
private static DiscordHelper instance = null;
private static readonly object padlock = new object();

public static DiscordHelper Instance
{
get
{
lock (padlock)
{
if (instance == null)
{
instance = new DiscordHelper();
}
return instance;
}
}
}
#endregion
private DiscordRpcClient client;
private int currentGameId = -1;
internal DiscordHelper()
{
try
{
client = new DiscordRpcClient(Encoding.UTF8.GetString(Convert.FromBase64String("MTMxMzIyNTUxNjUwOTMwMjgxNQ==")));
}
catch { }
}
internal void SyncGameWithDiscordPresence(List<int> trackedGameIds, Dictionary<int, string> installedGames)
{
try
{
if (!SettingsViewModel.Instance.SyncDiscordPresence || !SettingsViewModel.Instance.License.IsActive())
return;

if (!client.IsInitialized)
{
client.Initialize();
}
if (currentGameId != -1 && !trackedGameIds.Contains(currentGameId))
{
client.ClearPresence();
currentGameId = -1;
}
if (trackedGameIds.Count <= 0)
return;

KeyValuePair<int, string> firstGame = installedGames.First(g => g.Key == trackedGameIds.First());
string gameTitle = Path.GetFileName(firstGame.Value.TrimEnd(Path.DirectorySeparatorChar));
gameTitle = gameTitle.Replace($"({firstGame.Key})", "");
client.SetPresence(new RichPresence()
{
Details = gameTitle,
Buttons = new Button[] { new Button() { Label = "What's GameVault?", Url = "https://gamevau.lt/" } }
});
currentGameId = firstGame.Key;
}
catch { }
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -228,65 +228,69 @@ public static uint Compute(string input, uint seed = DefaultSeed)
}
#endregion

internal static void SyncGamesWithSteamShortcuts(Dictionary<Game, string> games)
internal static async Task SyncGamesWithSteamShortcuts(Dictionary<Game, string> games)
{
try
await Task.Run(async () =>
{
if (!SettingsViewModel.Instance.License.IsActive())
return;

string shortcutsDirectory = GetMostRecentUserUserDirectory();
if (!Directory.Exists(shortcutsDirectory))
return;

EnsureBackup(shortcutsDirectory);//In case something went wrong.
VdfMap shortcutFileMap = null;
string shortcutFile = Path.Combine(shortcutsDirectory, "shortcuts.vdf");
List<string> steamGridImageIDsToRemove = null;
if (File.Exists(shortcutFile))
try
{
try
if (!SettingsViewModel.Instance.License.IsActive())
return;

string shortcutsDirectory = GetMostRecentUserUserDirectory();
if (!Directory.Exists(shortcutsDirectory))
return;

EnsureBackup(shortcutsDirectory);//In case something went wrong.
VdfMap shortcutFileMap = null;
string shortcutFile = Path.Combine(shortcutsDirectory, "shortcuts.vdf");
List<string> steamGridImageIDsToRemove = null;
if (File.Exists(shortcutFile))
{
//Try catch here, because if something goes wrong, the file is corrupted and will be overwritten in a clean way by the new games
shortcutFileMap = VdfUtilities.ReadVdf(File.ReadAllBytes(Path.Combine(shortcutsDirectory, "shortcuts.vdf")));
shortcutFileMap = TrimFirstOffsetEntry(shortcutFileMap);//Making sure to have a clean game list
shortcutFileMap = RemoveUninstalledGames(shortcutFileMap, games, out steamGridImageIDsToRemove);//Checks for gamevault games and compares them to the current installed game list. Extracts also the steam grid image ids for clean removal later on
games = TrimExistingGames(games, shortcutFileMap);//No need for double entries. So they will be removed
try
{
//Try catch here, because if something goes wrong, the file is corrupted and will be overwritten in a clean way by the new games
shortcutFileMap = VdfUtilities.ReadVdf(File.ReadAllBytes(Path.Combine(shortcutsDirectory, "shortcuts.vdf")));
shortcutFileMap = TrimFirstOffsetEntry(shortcutFileMap);//Making sure to have a clean game list
shortcutFileMap = RemoveUninstalledGames(shortcutFileMap, games, out steamGridImageIDsToRemove);//Checks for gamevault games and compares them to the current installed game list. Extracts also the steam grid image ids for clean removal later on
games = TrimExistingGames(games, shortcutFileMap);//No need for double entries. So they will be removed
}
catch { }
}
catch { }
}
else
{
File.Create(shortcutFile).Close();
}
shortcutFileMap = AddGamesToShortcuts(shortcutFileMap, games);
else
{
File.Create(shortcutFile).Close();
}
shortcutFileMap = AddGamesToShortcuts(shortcutFileMap, games);

VdfMap root = new VdfMap();
root.Add("shortcuts", shortcutFileMap);//Adds back the first offset entry a vdf file needs to work
VdfMap root = new VdfMap();
root.Add("shortcuts", shortcutFileMap);//Adds back the first offset entry a vdf file needs to work

File.WriteAllBytes(shortcutFile, VdfUtilities.WriteVdf(root));
//The steam grid image is only processed after it has been written.Because if it did not work to manipulate the VDF file, there is no point in changing anything in the images
string steamGridDirectory = Path.Combine(shortcutsDirectory, "grid");
if (!Directory.Exists(steamGridDirectory))
{
Directory.CreateDirectory(steamGridDirectory);
}
if (steamGridImageIDsToRemove?.Count > 0)
{
foreach (string steamGridImageToRemove in steamGridImageIDsToRemove)
File.WriteAllBytes(shortcutFile, VdfUtilities.WriteVdf(root));
//The steam grid image is only processed after it has been written.Because if it did not work to manipulate the VDF file, there is no point in changing anything in the images
string steamGridDirectory = Path.Combine(shortcutsDirectory, "grid");
if (!Directory.Exists(steamGridDirectory))
{
RemoveSteamGridImages(steamGridDirectory, steamGridImageToRemove);
Directory.CreateDirectory(steamGridDirectory);
}
if (steamGridImageIDsToRemove?.Count > 0)
{
foreach (string steamGridImageToRemove in steamGridImageIDsToRemove)
{
RemoveSteamGridImages(steamGridDirectory, steamGridImageToRemove);
}
}
foreach (KeyValuePair<Game, string> steamGridGame in games)
{
uint steamGridID = (uint)((VdfMap)shortcutFileMap.First(x => ((VdfMap)x.Value).ElementAt(1).Value.ToString() == steamGridGame.Key.Title).Value).ElementAt(0).Value;
await CacheHelper.EnsureImageCacheForGame(steamGridGame.Key);
SetSteamGridImages(steamGridDirectory, steamGridGame.Key, steamGridID);
}
}
foreach (KeyValuePair<Game, string> steamGridGame in games)
catch (Exception e)
{
uint steamGridID = (uint)((VdfMap)shortcutFileMap.First(x => ((VdfMap)x.Value).ElementAt(1).Value.ToString() == steamGridGame.Key.Title).Value).ElementAt(0).Value;
SetSteamGridImages(steamGridDirectory, steamGridGame.Key, steamGridID);
}
}
catch (Exception e)
{
}
});
}
internal static void RemoveGameVaultGamesFromSteamShortcuts()
{
Expand Down
3 changes: 2 additions & 1 deletion gamevault/Models/AppInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ public enum AppConfigKey
SendAnonymousAnalytics,
LastCommunitySortBy,
ForcedInstallationType,
SyncSteamShortcuts
SyncSteamShortcuts,
SyncDiscordPresence
}
public static class AppFilePath
{
Expand Down
2 changes: 1 addition & 1 deletion gamevault/Models/PhalcodeProduct.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public class PhalcodeProduct
public bool? CancelAtPeriodEnd { get; set; }
public string? UserName { get; set; }
public bool IsActive()
{
{
return (CurrentPeriodEnd != null && CurrentPeriodEnd > DateTime.UtcNow);
}
}
Expand Down
9 changes: 9 additions & 0 deletions gamevault/Resources/Assets/Icons.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,15 @@
<Geometry x:Key="IconGameSettingsGoogle">
M6 12a6 6 0 0 0 11.659 2H12v-4h9.805v4H21.8c-.927 4.564-4.962 8-9.8 8c-5.523 0-10-4.477-10-10S6.477 2 12 2a9.99 9.99 0 0 1 8.282 4.393l-3.278 2.295A6 6 0 0 0 6 12
</Geometry>
<Geometry x:Key="IconGameSettingsSteam">
M.329 10.333A8.01 8.01 0 0 0 7.99 16C12.414 16 16 12.418 16 8s-3.586-8-8.009-8A8.006 8.006 0 0 0 0 7.468l.003.006l4.304 1.769A2.2 2.2 0 0 1 5.62 8.88l1.96-2.844l-.001-.04a3.046 3.046 0 0 1 3.042-3.043a3.046 3.046 0 0 1 3.042 3.043a3.047 3.047 0 0 1-3.111 3.044l-2.804 2a2.223 2.223 0 0 1-3.075 2.11a2.22 2.22 0 0 1-1.312-1.568L.33 10.333Z M4.868 12.683a1.715 1.715 0 0 0 1.318-3.165a1.7 1.7 0 0 0-1.263-.02l1.023.424a1.261 1.261 0 1 1-.97 2.33l-.99-.41a1.7 1.7 0 0 0 .882.84Zm3.726-6.687a2.03 2.03 0 0 0 2.027 2.029a2.03 2.03 0 0 0 2.027-2.029a2.03 2.03 0 0 0-2.027-2.027a2.03 2.03 0 0 0-2.027 2.027m2.03-1.527a1.524 1.524 0 1 1-.002 3.048a1.524 1.524 0 0 1 .002-3.048
</Geometry>
<Geometry x:Key="IconGameSettingsDiscord">
M13.545 2.907a13.2 13.2 0 0 0-3.257-1.011a.05.05 0 0 0-.052.025c-.141.25-.297.577-.406.833a12.2 12.2 0 0 0-3.658 0a8 8 0 0 0-.412-.833a.05.05 0 0 0-.052-.025c-1.125.194-2.22.534-3.257 1.011a.04.04 0 0 0-.021.018C.356 6.024-.213 9.047.066 12.032q.003.022.021.037a13.3 13.3 0 0 0 3.995 2.02a.05.05 0 0 0 .056-.019q.463-.63.818-1.329a.05.05 0 0 0-.01-.059l-.018-.011a9 9 0 0 1-1.248-.595a.05.05 0 0 1-.02-.066l.015-.019q.127-.095.248-.195a.05.05 0 0 1 .051-.007c2.619 1.196 5.454 1.196 8.041 0a.05.05 0 0 1 .053.007q.121.1.248.195a.05.05 0 0 1-.004.085a8 8 0 0 1-1.249.594a.05.05 0 0 0-.03.03a.05.05 0 0 0 .003.041c.24.465.515.909.817 1.329a.05.05 0 0 0 .056.019a13.2 13.2 0 0 0 4.001-2.02a.05.05 0 0 0 .021-.037c.334-3.451-.559-6.449-2.366-9.106a.03.03 0 0 0-.02-.019m-8.198 7.307c-.789 0-1.438-.724-1.438-1.612s.637-1.613 1.438-1.613c.807 0 1.45.73 1.438 1.613c0 .888-.637 1.612-1.438 1.612m5.316 0c-.788 0-1.438-.724-1.438-1.612s.637-1.613 1.438-1.613c.807 0 1.451.73 1.438 1.613c0 .888-.631 1.612-1.438 1.612
</Geometry>
<Geometry x:Key="IconGameSettingsPlaynite">
m10.667 6.134l-.502-.355A4.24 4.24 0 0 0 7.715 5h-.612c-.405 0-.813.025-1.194.16c-2.383.846-4.022 3.935-3.903 10.943c.024 1.412.354 2.972 1.628 3.581A3.2 3.2 0 0 0 5.027 20a2.74 2.74 0 0 0 1.53-.437c.41-.268.77-.616 1.13-.964c.444-.43.888-.86 1.424-1.138a4.1 4.1 0 0 1 1.89-.461H13c.658 0 1.306.158 1.89.46c.536.279.98.709 1.425 1.139c.36.348.72.696 1.128.964c.39.256.895.437 1.531.437a3.2 3.2 0 0 0 1.393-.316c1.274-.609 1.604-2.17 1.628-3.581c.119-7.008-1.52-10.097-3.903-10.942C17.71 5.025 17.3 5 16.897 5h-.612a4.24 4.24 0 0 0-2.45.78l-.502.354a2.31 2.31 0 0 1-2.666 0M16.75 9a.75.75 0 1 1 0 1.5a.75.75 0 0 1 0-1.5m-9.25.25a.75.75 0 0 1 .75.75v.75H9a.75.75 0 0 1 0 1.5h-.75V13a.75.75 0 0 1-1.5 0v-.75H6a.75.75 0 0 1 0-1.5h.75V10a.75.75 0 0 1 .75-.75m11.5 2a.75.75 0 1 1-1.5 0a.75.75 0 0 1 1.5 0m-3.75.75a.75.75 0 1 0 0-1.5a.75.75 0 0 0 0 1.5m2.25.75a.75.75 0 1 0-1.5 0a.75.75 0 0 0 1.5 0
</Geometry>
<Geometry x:Key="IconGameSettingsLinked">
M7 17q-2.075 0-3.537-1.463T2 12t1.463-3.537T7 7h3q.425 0 .713.288T11 8t-.288.713T10 9H7q-1.25 0-2.125.875T4 12t.875 2.125T7 15h3q.425 0 .713.288T11 16t-.288.713T10 17zm2-4q-.425 0-.712-.288T8 12t.288-.712T9 11h6q.425 0 .713.288T16 12t-.288.713T15 13zm5 4q-.425 0-.712-.288T13 16t.288-.712T14 15h3q1.25 0 2.125-.875T20 12t-.875-2.125T17 9h-3q-.425 0-.712-.288T13 8t.288-.712T14 7h3q2.075 0 3.538 1.463T22 12t-1.463 3.538T17 17z
</Geometry>
Expand Down
2 changes: 1 addition & 1 deletion gamevault/UserControls/GeneralControls/CacheImage.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ private async Task DataChanged(object newData)
}
else
{
await CacheHelper.HandleImageCacheAsync(media.Identifier, imageId, cachePath, ImageCacheType, uiImg);
await CacheHelper.LoadImageCacheToUIAsync(media.Identifier, imageId, cachePath, ImageCacheType, uiImg);
}
}
public ImageSource GetImageSource()
Expand Down
4 changes: 2 additions & 2 deletions gamevault/UserControls/InstallUserControl.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -419,12 +419,12 @@ private void Rows_ValueChanged(object sender, RoutedPropertyChangedEventArgs<dou
catch { }
}

private void Collection_Updated(object sender, RoutedPropertyChangedEventArgs<double?> e)
private async void Collection_Updated(object sender, RoutedPropertyChangedEventArgs<double?> e)
{
Rows_ValueChanged(null, null);
if (e.OldValue != null && e.OldValue != e.NewValue && SettingsViewModel.Instance.SyncSteamShortcuts)//Make sure that a game has really been added or removed
{
SteamHelper.SyncGamesWithSteamShortcuts(InstallViewModel.Instance.InstalledGames.ToDictionary(pair => pair.Key, pair => pair.Value));
await SteamHelper.SyncGamesWithSteamShortcuts(InstallViewModel.Instance.InstalledGames.ToDictionary(pair => pair.Key, pair => pair.Value));
}
}
}
Expand Down
Loading

0 comments on commit 01ea89c

Please sign in to comment.