LANCommander/LANCommander.Launcher.Services/GameService.cs
2026-06-27 11:51:55 -05:00

160 lines
No EOL
5.7 KiB
C#

using LANCommander.Launcher.Data;
using LANCommander.Launcher.Data.Models;
using LANCommander.Launcher.Models;
using LANCommander.SDK;
using LANCommander.SDK.Extensions;
using LANCommander.SDK.Helpers;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.DependencyInjection;
using System.Diagnostics;
using LANCommander.SDK.Services;
namespace LANCommander.Launcher.Services
{
public class GameService(
DatabaseContext dbContext,
ILogger<GameService> logger,
AuthenticationService authenticationService,
PlaySessionService playSessionService,
ProfileClient profileClient,
GameClient gameClient,
ToolService toolService,
ToolClient toolClient,
IConnectionClient connectionClient,
IServiceProvider serviceProvider) : BaseDatabaseService<Game>(dbContext, logger)
{
public Dictionary<Guid, Process> RunningProcesses = new Dictionary<Guid, Process>();
public async Task<Dictionary<Guid, DateTime>> GetImportedOnMapAsync(IEnumerable<Guid> ids)
{
var idSet = ids.ToHashSet();
return await Context.Set<Game>()
.Where(g => idSet.Contains(g.Id))
.Select(g => new { g.Id, g.ImportedOn })
.ToDictionaryAsync(g => g.Id, g => g.ImportedOn);
}
public delegate Task OnUninstallCompleteHandler(Game game);
public event OnUninstallCompleteHandler OnUninstallComplete;
public delegate Task OnUninstallHandler(Game game);
public event OnUninstallHandler OnUninstall;
public async Task UninstallAsync(Game game)
{
using (var operation = Logger.BeginOperation("Uninstalling game {GameTitle} ({GameId})", game.Title, game.Id))
{
try
{
OnUninstall?.Invoke(game);
var installService = serviceProvider.GetService<InstallService>();
installService?.ClearCompleted(game.Id);
await gameClient.UninstallAsync(game.InstallDirectory, game.Id);
if (game.BaseGameId.HasValue)
{
var libraryService = serviceProvider.GetService<LibraryService>();
var isInstalled = await libraryService!.IsInstalledAsync(game.BaseGameId.Value);
if (!isInstalled)
{
var baseGame = await GetAsync(game.BaseGameId.Value);
await gameClient.UninstallAsync(game.InstallDirectory, baseGame?.Id ?? game.BaseGameId.Value);
ClearGameState(baseGame!, skipAddons: true);
}
}
// Uninstall any tools that were installed for this game. Tools are installed
// into the game's own directory and tracked per game, so uninstalling this
// game only removes its copy and leaves the tool intact for other games that
// share it. This must run before ClearGameState clears the install directory.
var installedTools = await toolService.GetInstalledToolsForGameAsync(game.Id);
foreach (var gameTool in installedTools)
{
try
{
await toolClient.UninstallAsync(game.InstallDirectory, gameTool.ToolId);
await toolService.SetToolUninstalledAsync(game.Id, gameTool.ToolId);
}
catch (Exception ex)
{
Logger?.LogError(ex, "Could not uninstall tool {ToolId} from game {GameId}", gameTool.ToolId, game.Id);
}
}
ClearGameState(game);
await UpdateAsync(game);
OnUninstallComplete?.Invoke(game);
operation.Complete();
}
catch (Exception ex)
{
Logger?.LogError(ex, "Game {GameTitle} ({GameId}) could not be uninstalled", game.Title, game.Id);
}
}
}
public async Task Run(Game game, SDK.Models.Manifest.Action action)
{
Guid userId;
if (connectionClient.IsConnected())
{
var profile = await profileClient.GetAsync();
userId = profile.Id;
}
else
{
userId = authenticationService.GetUserId();
}
try
{
var latestSession = await playSessionService.GetLatestSession(game.Id, userId);
await playSessionService.StartSession(game.Id, userId);
await gameClient.RunAsync(game.InstallDirectory, game.Id, action, latestSession?.End);
}
catch (Exception ex)
{
Logger?.LogError(ex, "Game failed to run");
throw;
}
finally
{
await playSessionService.EndSession(game.Id, userId);
}
}
protected void ClearGameState(Game game, bool skipAddons = false)
{
if (game == null)
return;
game.InstallDirectory = null;
game.Installed = false;
game.InstalledOn = null;
game.InstalledVersion = null;
if (!skipAddons)
{
foreach (var addon in (game.DependentGames ?? []))
{
ClearGameState(addon);
}
}
}
}
}