LANCommander/LANCommander.SDK/Clients/SaveClient.cs

421 lines
17 KiB
C#
Raw Permalink Normal View History

using LANCommander.SDK.Extensions;
using LANCommander.SDK.Helpers;
using LANCommander.SDK.Models;
using LANCommander.SDK.PowerShell;
using Microsoft.Extensions.Logging;
2023-04-07 19:09:00 -05:00
using SharpCompress.Common;
using SharpCompress.Readers;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using LANCommander.SDK.Abstractions;
Implement SDK using dependency injection - API requests are now made through the ApiRequestBuilder with DI supplied via the ApiRequestFactory singleton - Reliance on RestSharp and WebClient has been removed in favor of HttpClient - Auth token is now being tracked by the ITokenProvider - Network information (MAC address, broadcast addresses, IP address) is now supplied with the INetworkInformationProvider singleton - Connection state is now being maintained by the ConnectionService, with a reliance on RPC (SignalR websocket) reporting actual connection state without relying on pings - All download streams are now provided as a TrackableStream - Singleton Client class has been removed. All usage of the SDK should happen via Dependency Injection This is just a base, non-functional refactor of the SDK to use DI. The following changes to the rest of the codebase need to be made: - Usage of the SDK client in the launcher needs to be replaced in favor of injecting SDK services - PowerShell cmdlets need to be able to have services injected. Most likely a separate scope will have to be opened up per PS runtime? - Usage of the SDK client in the server needs to be replaced in favor of injecting SDK services. This should be minimal and should actually provide benefit when it comes to executing client-like features (scripts mostly) without needing a fully configured client that maintains connection state. - Configuration of the client needs to be implemented using ILANCommanderConfiguration
2025-09-22 00:29:51 -05:00
using LANCommander.SDK.Factories;
2025-03-18 20:30:04 -05:00
using LANCommander.SDK.Utilities;
Implement SDK using dependency injection - API requests are now made through the ApiRequestBuilder with DI supplied via the ApiRequestFactory singleton - Reliance on RestSharp and WebClient has been removed in favor of HttpClient - Auth token is now being tracked by the ITokenProvider - Network information (MAC address, broadcast addresses, IP address) is now supplied with the INetworkInformationProvider singleton - Connection state is now being maintained by the ConnectionService, with a reliance on RPC (SignalR websocket) reporting actual connection state without relying on pings - All download streams are now provided as a TrackableStream - Singleton Client class has been removed. All usage of the SDK should happen via Dependency Injection This is just a base, non-functional refactor of the SDK to use DI. The following changes to the rest of the codebase need to be made: - Usage of the SDK client in the launcher needs to be replaced in favor of injecting SDK services - PowerShell cmdlets need to be able to have services injected. Most likely a separate scope will have to be opened up per PS runtime? - Usage of the SDK client in the server needs to be replaced in favor of injecting SDK services. This should be minimal and should actually provide benefit when it comes to executing client-like features (scripts mostly) without needing a fully configured client that maintains connection state. - Configuration of the client needs to be implemented using ILANCommanderConfiguration
2025-09-22 00:29:51 -05:00
using Action = System.Action;
2025-08-18 03:03:33 -05:00
// Some terms for this file since they're probably going to be needed in the future:
// Local path - The full path of the file/directory on the local disk. No variables used, just the raw path for current machine
// Actual path - The path where the entries should be extracted to, before expanding environemnt variables.
// Archive path - The path of where the entries are located in the ZIP
//
// Other notes:
// - Entries in the ZIP are separated by save path ID to avoid collision
namespace LANCommander.SDK.Services
{
public class SaveClient(
Implement SDK using dependency injection - API requests are now made through the ApiRequestBuilder with DI supplied via the ApiRequestFactory singleton - Reliance on RestSharp and WebClient has been removed in favor of HttpClient - Auth token is now being tracked by the ITokenProvider - Network information (MAC address, broadcast addresses, IP address) is now supplied with the INetworkInformationProvider singleton - Connection state is now being maintained by the ConnectionService, with a reliance on RPC (SignalR websocket) reporting actual connection state without relying on pings - All download streams are now provided as a TrackableStream - Singleton Client class has been removed. All usage of the SDK should happen via Dependency Injection This is just a base, non-functional refactor of the SDK to use DI. The following changes to the rest of the codebase need to be made: - Usage of the SDK client in the launcher needs to be replaced in favor of injecting SDK services - PowerShell cmdlets need to be able to have services injected. Most likely a separate scope will have to be opened up per PS runtime? - Usage of the SDK client in the server needs to be replaced in favor of injecting SDK services. This should be minimal and should actually provide benefit when it comes to executing client-like features (scripts mostly) without needing a fully configured client that maintains connection state. - Configuration of the client needs to be implemented using ILANCommanderConfiguration
2025-09-22 00:29:51 -05:00
ApiRequestFactory apiRequestFactory,
ISettingsProvider settingsProvider,
PowerShellScriptFactory powerShellScriptFactory,
ILogger<SaveClient> logger)
{
public delegate void OnDownloadProgressHandler(DownloadProgressChangedEventArgs e);
public event OnDownloadProgressHandler OnDownloadProgress;
Implement SDK using dependency injection - API requests are now made through the ApiRequestBuilder with DI supplied via the ApiRequestFactory singleton - Reliance on RestSharp and WebClient has been removed in favor of HttpClient - Auth token is now being tracked by the ITokenProvider - Network information (MAC address, broadcast addresses, IP address) is now supplied with the INetworkInformationProvider singleton - Connection state is now being maintained by the ConnectionService, with a reliance on RPC (SignalR websocket) reporting actual connection state without relying on pings - All download streams are now provided as a TrackableStream - Singleton Client class has been removed. All usage of the SDK should happen via Dependency Injection This is just a base, non-functional refactor of the SDK to use DI. The following changes to the rest of the codebase need to be made: - Usage of the SDK client in the launcher needs to be replaced in favor of injecting SDK services - PowerShell cmdlets need to be able to have services injected. Most likely a separate scope will have to be opened up per PS runtime? - Usage of the SDK client in the server needs to be replaced in favor of injecting SDK services. This should be minimal and should actually provide benefit when it comes to executing client-like features (scripts mostly) without needing a fully configured client that maintains connection state. - Configuration of the client needs to be implemented using ILANCommanderConfiguration
2025-09-22 00:29:51 -05:00
public delegate void OnDownloadCompleteHandler();
public event OnDownloadCompleteHandler OnDownloadComplete;
Implement SDK using dependency injection - API requests are now made through the ApiRequestBuilder with DI supplied via the ApiRequestFactory singleton - Reliance on RestSharp and WebClient has been removed in favor of HttpClient - Auth token is now being tracked by the ITokenProvider - Network information (MAC address, broadcast addresses, IP address) is now supplied with the INetworkInformationProvider singleton - Connection state is now being maintained by the ConnectionService, with a reliance on RPC (SignalR websocket) reporting actual connection state without relying on pings - All download streams are now provided as a TrackableStream - Singleton Client class has been removed. All usage of the SDK should happen via Dependency Injection This is just a base, non-functional refactor of the SDK to use DI. The following changes to the rest of the codebase need to be made: - Usage of the SDK client in the launcher needs to be replaced in favor of injecting SDK services - PowerShell cmdlets need to be able to have services injected. Most likely a separate scope will have to be opened up per PS runtime? - Usage of the SDK client in the server needs to be replaced in favor of injecting SDK services. This should be minimal and should actually provide benefit when it comes to executing client-like features (scripts mostly) without needing a fully configured client that maintains connection state. - Configuration of the client needs to be implemented using ILANCommanderConfiguration
2025-09-22 00:29:51 -05:00
private async Task<FileInfo> DownloadAsync(Guid id, Action<DownloadProgressChangedEventArgs> progressHandler, Action completeHandler)
{
Implement SDK using dependency injection - API requests are now made through the ApiRequestBuilder with DI supplied via the ApiRequestFactory singleton - Reliance on RestSharp and WebClient has been removed in favor of HttpClient - Auth token is now being tracked by the ITokenProvider - Network information (MAC address, broadcast addresses, IP address) is now supplied with the INetworkInformationProvider singleton - Connection state is now being maintained by the ConnectionService, with a reliance on RPC (SignalR websocket) reporting actual connection state without relying on pings - All download streams are now provided as a TrackableStream - Singleton Client class has been removed. All usage of the SDK should happen via Dependency Injection This is just a base, non-functional refactor of the SDK to use DI. The following changes to the rest of the codebase need to be made: - Usage of the SDK client in the launcher needs to be replaced in favor of injecting SDK services - PowerShell cmdlets need to be able to have services injected. Most likely a separate scope will have to be opened up per PS runtime? - Usage of the SDK client in the server needs to be replaced in favor of injecting SDK services. This should be minimal and should actually provide benefit when it comes to executing client-like features (scripts mostly) without needing a fully configured client that maintains connection state. - Configuration of the client needs to be implemented using ILANCommanderConfiguration
2025-09-22 00:29:51 -05:00
var destination = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
return await apiRequestFactory
.Create()
.UseAuthenticationToken()
.UseVersioning()
.UseRoute($"/api/Saves/{id}/Download")
.OnProgress(progressHandler)
.OnComplete(completeHandler)
.DownloadAsync(destination);
}
Implement SDK using dependency injection - API requests are now made through the ApiRequestBuilder with DI supplied via the ApiRequestFactory singleton - Reliance on RestSharp and WebClient has been removed in favor of HttpClient - Auth token is now being tracked by the ITokenProvider - Network information (MAC address, broadcast addresses, IP address) is now supplied with the INetworkInformationProvider singleton - Connection state is now being maintained by the ConnectionService, with a reliance on RPC (SignalR websocket) reporting actual connection state without relying on pings - All download streams are now provided as a TrackableStream - Singleton Client class has been removed. All usage of the SDK should happen via Dependency Injection This is just a base, non-functional refactor of the SDK to use DI. The following changes to the rest of the codebase need to be made: - Usage of the SDK client in the launcher needs to be replaced in favor of injecting SDK services - PowerShell cmdlets need to be able to have services injected. Most likely a separate scope will have to be opened up per PS runtime? - Usage of the SDK client in the server needs to be replaced in favor of injecting SDK services. This should be minimal and should actually provide benefit when it comes to executing client-like features (scripts mostly) without needing a fully configured client that maintains connection state. - Configuration of the client needs to be implemented using ILANCommanderConfiguration
2025-09-22 00:29:51 -05:00
public async Task<FileInfo> DownloadLatestAsync(Guid gameId, Action<DownloadProgressChangedEventArgs> progressHandler, Action completeHandler)
{
Implement SDK using dependency injection - API requests are now made through the ApiRequestBuilder with DI supplied via the ApiRequestFactory singleton - Reliance on RestSharp and WebClient has been removed in favor of HttpClient - Auth token is now being tracked by the ITokenProvider - Network information (MAC address, broadcast addresses, IP address) is now supplied with the INetworkInformationProvider singleton - Connection state is now being maintained by the ConnectionService, with a reliance on RPC (SignalR websocket) reporting actual connection state without relying on pings - All download streams are now provided as a TrackableStream - Singleton Client class has been removed. All usage of the SDK should happen via Dependency Injection This is just a base, non-functional refactor of the SDK to use DI. The following changes to the rest of the codebase need to be made: - Usage of the SDK client in the launcher needs to be replaced in favor of injecting SDK services - PowerShell cmdlets need to be able to have services injected. Most likely a separate scope will have to be opened up per PS runtime? - Usage of the SDK client in the server needs to be replaced in favor of injecting SDK services. This should be minimal and should actually provide benefit when it comes to executing client-like features (scripts mostly) without needing a fully configured client that maintains connection state. - Configuration of the client needs to be implemented using ILANCommanderConfiguration
2025-09-22 00:29:51 -05:00
var destination = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
return await apiRequestFactory
.Create()
.UseAuthenticationToken()
.UseVersioning()
.UseRoute($"/api/Saves/Game/{gameId}/Latest/Download")
.OnProgress(progressHandler)
.OnComplete(completeHandler)
.DownloadAsync(destination);
2024-02-17 17:30:14 -06:00
}
2024-10-23 18:17:01 -05:00
public async Task<IEnumerable<GameSave>> GetAsync(Guid gameId)
{
Implement SDK using dependency injection - API requests are now made through the ApiRequestBuilder with DI supplied via the ApiRequestFactory singleton - Reliance on RestSharp and WebClient has been removed in favor of HttpClient - Auth token is now being tracked by the ITokenProvider - Network information (MAC address, broadcast addresses, IP address) is now supplied with the INetworkInformationProvider singleton - Connection state is now being maintained by the ConnectionService, with a reliance on RPC (SignalR websocket) reporting actual connection state without relying on pings - All download streams are now provided as a TrackableStream - Singleton Client class has been removed. All usage of the SDK should happen via Dependency Injection This is just a base, non-functional refactor of the SDK to use DI. The following changes to the rest of the codebase need to be made: - Usage of the SDK client in the launcher needs to be replaced in favor of injecting SDK services - PowerShell cmdlets need to be able to have services injected. Most likely a separate scope will have to be opened up per PS runtime? - Usage of the SDK client in the server needs to be replaced in favor of injecting SDK services. This should be minimal and should actually provide benefit when it comes to executing client-like features (scripts mostly) without needing a fully configured client that maintains connection state. - Configuration of the client needs to be implemented using ILANCommanderConfiguration
2025-09-22 00:29:51 -05:00
return await apiRequestFactory
.Create()
.UseAuthenticationToken()
.UseVersioning()
.UseRoute($"/api/Saves/Game/{gameId}")
.GetAsync<IEnumerable<GameSave>>();
}
Implement SDK using dependency injection - API requests are now made through the ApiRequestBuilder with DI supplied via the ApiRequestFactory singleton - Reliance on RestSharp and WebClient has been removed in favor of HttpClient - Auth token is now being tracked by the ITokenProvider - Network information (MAC address, broadcast addresses, IP address) is now supplied with the INetworkInformationProvider singleton - Connection state is now being maintained by the ConnectionService, with a reliance on RPC (SignalR websocket) reporting actual connection state without relying on pings - All download streams are now provided as a TrackableStream - Singleton Client class has been removed. All usage of the SDK should happen via Dependency Injection This is just a base, non-functional refactor of the SDK to use DI. The following changes to the rest of the codebase need to be made: - Usage of the SDK client in the launcher needs to be replaced in favor of injecting SDK services - PowerShell cmdlets need to be able to have services injected. Most likely a separate scope will have to be opened up per PS runtime? - Usage of the SDK client in the server needs to be replaced in favor of injecting SDK services. This should be minimal and should actually provide benefit when it comes to executing client-like features (scripts mostly) without needing a fully configured client that maintains connection state. - Configuration of the client needs to be implemented using ILANCommanderConfiguration
2025-09-22 00:29:51 -05:00
public async Task<GameSave> GetLatestAsync(Guid gameId)
{
Implement SDK using dependency injection - API requests are now made through the ApiRequestBuilder with DI supplied via the ApiRequestFactory singleton - Reliance on RestSharp and WebClient has been removed in favor of HttpClient - Auth token is now being tracked by the ITokenProvider - Network information (MAC address, broadcast addresses, IP address) is now supplied with the INetworkInformationProvider singleton - Connection state is now being maintained by the ConnectionService, with a reliance on RPC (SignalR websocket) reporting actual connection state without relying on pings - All download streams are now provided as a TrackableStream - Singleton Client class has been removed. All usage of the SDK should happen via Dependency Injection This is just a base, non-functional refactor of the SDK to use DI. The following changes to the rest of the codebase need to be made: - Usage of the SDK client in the launcher needs to be replaced in favor of injecting SDK services - PowerShell cmdlets need to be able to have services injected. Most likely a separate scope will have to be opened up per PS runtime? - Usage of the SDK client in the server needs to be replaced in favor of injecting SDK services. This should be minimal and should actually provide benefit when it comes to executing client-like features (scripts mostly) without needing a fully configured client that maintains connection state. - Configuration of the client needs to be implemented using ILANCommanderConfiguration
2025-09-22 00:29:51 -05:00
return await apiRequestFactory
.Create()
.UseAuthenticationToken()
.UseVersioning()
.UseRoute($"/api/Saves/Game/{gameId}/Latest")
.GetAsync<GameSave>();
}
2024-08-07 01:14:13 -05:00
public async Task DownloadAsync(string installDirectory, Guid gameId, Guid? saveId = null)
{
var manifest = await ManifestHelper.ReadAsync<SDK.Models.Manifest.Game>(installDirectory, gameId);
2025-08-18 03:03:28 -05:00
string tempFile;
string tempLocation = string.Empty;
if (manifest != null)
{
Implement SDK using dependency injection - API requests are now made through the ApiRequestBuilder with DI supplied via the ApiRequestFactory singleton - Reliance on RestSharp and WebClient has been removed in favor of HttpClient - Auth token is now being tracked by the ITokenProvider - Network information (MAC address, broadcast addresses, IP address) is now supplied with the INetworkInformationProvider singleton - Connection state is now being maintained by the ConnectionService, with a reliance on RPC (SignalR websocket) reporting actual connection state without relying on pings - All download streams are now provided as a TrackableStream - Singleton Client class has been removed. All usage of the SDK should happen via Dependency Injection This is just a base, non-functional refactor of the SDK to use DI. The following changes to the rest of the codebase need to be made: - Usage of the SDK client in the launcher needs to be replaced in favor of injecting SDK services - PowerShell cmdlets need to be able to have services injected. Most likely a separate scope will have to be opened up per PS runtime? - Usage of the SDK client in the server needs to be replaced in favor of injecting SDK services. This should be minimal and should actually provide benefit when it comes to executing client-like features (scripts mostly) without needing a fully configured client that maintains connection state. - Configuration of the client needs to be implemented using ILANCommanderConfiguration
2025-09-22 00:29:51 -05:00
FileInfo destination;
2024-02-17 17:30:14 -06:00
if (!saveId.HasValue)
{
2024-08-07 01:14:13 -05:00
destination = await DownloadLatestAsync(manifest.Id, (changed) =>
2024-02-17 17:30:14 -06:00
{
OnDownloadProgress?.Invoke(changed);
Implement SDK using dependency injection - API requests are now made through the ApiRequestBuilder with DI supplied via the ApiRequestFactory singleton - Reliance on RestSharp and WebClient has been removed in favor of HttpClient - Auth token is now being tracked by the ITokenProvider - Network information (MAC address, broadcast addresses, IP address) is now supplied with the INetworkInformationProvider singleton - Connection state is now being maintained by the ConnectionService, with a reliance on RPC (SignalR websocket) reporting actual connection state without relying on pings - All download streams are now provided as a TrackableStream - Singleton Client class has been removed. All usage of the SDK should happen via Dependency Injection This is just a base, non-functional refactor of the SDK to use DI. The following changes to the rest of the codebase need to be made: - Usage of the SDK client in the launcher needs to be replaced in favor of injecting SDK services - PowerShell cmdlets need to be able to have services injected. Most likely a separate scope will have to be opened up per PS runtime? - Usage of the SDK client in the server needs to be replaced in favor of injecting SDK services. This should be minimal and should actually provide benefit when it comes to executing client-like features (scripts mostly) without needing a fully configured client that maintains connection state. - Configuration of the client needs to be implemented using ILANCommanderConfiguration
2025-09-22 00:29:51 -05:00
}, () =>
2024-02-17 17:30:14 -06:00
{
Implement SDK using dependency injection - API requests are now made through the ApiRequestBuilder with DI supplied via the ApiRequestFactory singleton - Reliance on RestSharp and WebClient has been removed in favor of HttpClient - Auth token is now being tracked by the ITokenProvider - Network information (MAC address, broadcast addresses, IP address) is now supplied with the INetworkInformationProvider singleton - Connection state is now being maintained by the ConnectionService, with a reliance on RPC (SignalR websocket) reporting actual connection state without relying on pings - All download streams are now provided as a TrackableStream - Singleton Client class has been removed. All usage of the SDK should happen via Dependency Injection This is just a base, non-functional refactor of the SDK to use DI. The following changes to the rest of the codebase need to be made: - Usage of the SDK client in the launcher needs to be replaced in favor of injecting SDK services - PowerShell cmdlets need to be able to have services injected. Most likely a separate scope will have to be opened up per PS runtime? - Usage of the SDK client in the server needs to be replaced in favor of injecting SDK services. This should be minimal and should actually provide benefit when it comes to executing client-like features (scripts mostly) without needing a fully configured client that maintains connection state. - Configuration of the client needs to be implemented using ILANCommanderConfiguration
2025-09-22 00:29:51 -05:00
OnDownloadComplete?.Invoke();
2024-02-17 17:30:14 -06:00
});
}
else
{
2024-08-07 01:14:13 -05:00
destination = await DownloadAsync(saveId.Value, (changed) =>
2024-02-17 17:30:14 -06:00
{
OnDownloadProgress?.Invoke(changed);
Implement SDK using dependency injection - API requests are now made through the ApiRequestBuilder with DI supplied via the ApiRequestFactory singleton - Reliance on RestSharp and WebClient has been removed in favor of HttpClient - Auth token is now being tracked by the ITokenProvider - Network information (MAC address, broadcast addresses, IP address) is now supplied with the INetworkInformationProvider singleton - Connection state is now being maintained by the ConnectionService, with a reliance on RPC (SignalR websocket) reporting actual connection state without relying on pings - All download streams are now provided as a TrackableStream - Singleton Client class has been removed. All usage of the SDK should happen via Dependency Injection This is just a base, non-functional refactor of the SDK to use DI. The following changes to the rest of the codebase need to be made: - Usage of the SDK client in the launcher needs to be replaced in favor of injecting SDK services - PowerShell cmdlets need to be able to have services injected. Most likely a separate scope will have to be opened up per PS runtime? - Usage of the SDK client in the server needs to be replaced in favor of injecting SDK services. This should be minimal and should actually provide benefit when it comes to executing client-like features (scripts mostly) without needing a fully configured client that maintains connection state. - Configuration of the client needs to be implemented using ILANCommanderConfiguration
2025-09-22 00:29:51 -05:00
}, () =>
2024-02-17 17:30:14 -06:00
{
Implement SDK using dependency injection - API requests are now made through the ApiRequestBuilder with DI supplied via the ApiRequestFactory singleton - Reliance on RestSharp and WebClient has been removed in favor of HttpClient - Auth token is now being tracked by the ITokenProvider - Network information (MAC address, broadcast addresses, IP address) is now supplied with the INetworkInformationProvider singleton - Connection state is now being maintained by the ConnectionService, with a reliance on RPC (SignalR websocket) reporting actual connection state without relying on pings - All download streams are now provided as a TrackableStream - Singleton Client class has been removed. All usage of the SDK should happen via Dependency Injection This is just a base, non-functional refactor of the SDK to use DI. The following changes to the rest of the codebase need to be made: - Usage of the SDK client in the launcher needs to be replaced in favor of injecting SDK services - PowerShell cmdlets need to be able to have services injected. Most likely a separate scope will have to be opened up per PS runtime? - Usage of the SDK client in the server needs to be replaced in favor of injecting SDK services. This should be minimal and should actually provide benefit when it comes to executing client-like features (scripts mostly) without needing a fully configured client that maintains connection state. - Configuration of the client needs to be implemented using ILANCommanderConfiguration
2025-09-22 00:29:51 -05:00
OnDownloadComplete?.Invoke();
2024-02-17 17:30:14 -06:00
});
}
Implement SDK using dependency injection - API requests are now made through the ApiRequestBuilder with DI supplied via the ApiRequestFactory singleton - Reliance on RestSharp and WebClient has been removed in favor of HttpClient - Auth token is now being tracked by the ITokenProvider - Network information (MAC address, broadcast addresses, IP address) is now supplied with the INetworkInformationProvider singleton - Connection state is now being maintained by the ConnectionService, with a reliance on RPC (SignalR websocket) reporting actual connection state without relying on pings - All download streams are now provided as a TrackableStream - Singleton Client class has been removed. All usage of the SDK should happen via Dependency Injection This is just a base, non-functional refactor of the SDK to use DI. The following changes to the rest of the codebase need to be made: - Usage of the SDK client in the launcher needs to be replaced in favor of injecting SDK services - PowerShell cmdlets need to be able to have services injected. Most likely a separate scope will have to be opened up per PS runtime? - Usage of the SDK client in the server needs to be replaced in favor of injecting SDK services. This should be minimal and should actually provide benefit when it comes to executing client-like features (scripts mostly) without needing a fully configured client that maintains connection state. - Configuration of the client needs to be implemented using ILANCommanderConfiguration
2025-09-22 00:29:51 -05:00
if (!destination.Exists)
2026-05-25 04:16:19 -05:00
{
logger?.LogWarning("Save archive was not downloaded for game {GameId}", gameId);
2023-12-27 17:22:36 -06:00
return;
2026-05-25 04:16:19 -05:00
}
2023-12-27 17:22:36 -06:00
2026-05-25 04:16:19 -05:00
logger?.LogDebug("Save archive downloaded to {SaveTempLocation} for game {GameId}", destination, gameId);
Implement SDK using dependency injection - API requests are now made through the ApiRequestBuilder with DI supplied via the ApiRequestFactory singleton - Reliance on RestSharp and WebClient has been removed in favor of HttpClient - Auth token is now being tracked by the ITokenProvider - Network information (MAC address, broadcast addresses, IP address) is now supplied with the INetworkInformationProvider singleton - Connection state is now being maintained by the ConnectionService, with a reliance on RPC (SignalR websocket) reporting actual connection state without relying on pings - All download streams are now provided as a TrackableStream - Singleton Client class has been removed. All usage of the SDK should happen via Dependency Injection This is just a base, non-functional refactor of the SDK to use DI. The following changes to the rest of the codebase need to be made: - Usage of the SDK client in the launcher needs to be replaced in favor of injecting SDK services - PowerShell cmdlets need to be able to have services injected. Most likely a separate scope will have to be opened up per PS runtime? - Usage of the SDK client in the server needs to be replaced in favor of injecting SDK services. This should be minimal and should actually provide benefit when it comes to executing client-like features (scripts mostly) without needing a fully configured client that maintains connection state. - Configuration of the client needs to be implemented using ILANCommanderConfiguration
2025-09-22 00:29:51 -05:00
tempFile = destination.FullName;
// Go into the archive and extract the files to the correct locations
try
{
tempLocation = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
Directory.CreateDirectory(tempLocation);
bool success = RetryHelper.RetryOnException(10, TimeSpan.FromMilliseconds(200), false, () =>
{
2026-05-25 04:16:19 -05:00
logger?.LogTrace("Extracting save archive to temporary location {TempPath}", tempLocation);
ExtractFilesFromZip(tempFile, tempLocation);
return true;
});
if (!success)
throw new ExtractionException("Could not extract the save archive. Is the file locked?");
manifest = await ManifestHelper.ReadAsync<SDK.Models.Manifest.Game>(tempLocation);
#region Move files
var tempLocationFilePath = "Files";
// Legacy support
if (!Directory.Exists(Path.Combine(tempLocation, tempLocationFilePath)))
tempLocationFilePath = "Saves";
foreach (var savePath in manifest.SavePaths.Where(sp => sp.Type == Enums.SavePathType.File && EnvironmentHelper.SupportsCurrentRuntime(sp.Platforms)))
{
Implement SDK using dependency injection - API requests are now made through the ApiRequestBuilder with DI supplied via the ApiRequestFactory singleton - Reliance on RestSharp and WebClient has been removed in favor of HttpClient - Auth token is now being tracked by the ITokenProvider - Network information (MAC address, broadcast addresses, IP address) is now supplied with the INetworkInformationProvider singleton - Connection state is now being maintained by the ConnectionService, with a reliance on RPC (SignalR websocket) reporting actual connection state without relying on pings - All download streams are now provided as a TrackableStream - Singleton Client class has been removed. All usage of the SDK should happen via Dependency Injection This is just a base, non-functional refactor of the SDK to use DI. The following changes to the rest of the codebase need to be made: - Usage of the SDK client in the launcher needs to be replaced in favor of injecting SDK services - PowerShell cmdlets need to be able to have services injected. Most likely a separate scope will have to be opened up per PS runtime? - Usage of the SDK client in the server needs to be replaced in favor of injecting SDK services. This should be minimal and should actually provide benefit when it comes to executing client-like features (scripts mostly) without needing a fully configured client that maintains connection state. - Configuration of the client needs to be implemented using ILANCommanderConfiguration
2025-09-22 00:29:51 -05:00
var entries = GetFileSavePathEntries(savePath, installDirectory) ?? [];
2026-05-25 04:16:19 -05:00
logger?.LogTrace("Processing save path {SavePathId} with {EntryCount} entries", savePath.Id, entries.Count());
foreach (var entry in entries)
{
var entryPath = Path.Combine(tempLocation, tempLocationFilePath, savePath.Id.ToString(), entry.ArchivePath.Replace('/', Path.DirectorySeparatorChar));
var destinationPath = entry.ActualPath.ExpandEnvironmentVariables(installDirectory);
if (File.Exists(entryPath))
{
var destinationDirectory = Path.GetDirectoryName(destinationPath);
Directory.CreateDirectory(destinationDirectory);
// Handle individual files that were saved as an entry in the path
if (File.Exists(destinationPath))
File.Delete(destinationPath);
File.Move(entryPath, destinationPath);
2026-05-25 04:16:19 -05:00
logger?.LogTrace("Restored save file {ArchivePath} to {DestinationPath}", entry.ArchivePath, destinationPath);
}
else if (Directory.Exists(entryPath))
{
// Handle directories that were saved as an entry in the path
var entryFiles = Directory.GetFiles(entryPath, "*", SearchOption.AllDirectories);
foreach (var entryFile in entryFiles)
{
var fileDestination = entryFile.Replace(entryPath, destinationPath);
2026-05-25 04:16:19 -05:00
var destinationDirectory = Path.GetDirectoryName(fileDestination);
Directory.CreateDirectory(destinationDirectory);
if (File.Exists(fileDestination))
File.Delete(fileDestination);
File.Move(entryFile, fileDestination);
}
2026-05-25 04:16:19 -05:00
logger?.LogTrace("Restored save directory {ArchivePath} ({FileCount} files) to {DestinationPath}", entry.ArchivePath, entryFiles.Length, destinationPath);
}
else
{
logger?.LogWarning("Save entry {ArchivePath} not found in archive at {EntryPath}", entry.ArchivePath, entryPath);
}
}
}
#endregion
#region Handle registry importing
var registryImportFilePaths = Directory.GetFiles(tempLocation, "_registry*.reg");
var importer = new RegistryImportUtility();
foreach (var registryImportFilePath in registryImportFilePaths)
{
var registryImportFileContents = File.ReadAllText(registryImportFilePath);
var script = powerShellScriptFactory.Create(Enums.ScriptType.SaveDownload);
string adminArgument = string.Empty;
if (registryImportFileContents.Contains("HKEY_LOCAL_MACHINE"))
{
script.AsAdmin();
adminArgument = " -Verb RunAs";
}
script.UseInline($"Start-Process regedit.exe {adminArgument} -ArgumentList \"/s\", \"{registryImportFilePath}\"");
if (settingsProvider.CurrentValue.Debug.EnableScriptDebugging)
{
2024-08-16 18:36:17 -05:00
script.EnableDebug();
}
2024-08-16 18:36:17 -05:00
2024-09-20 00:27:20 -05:00
await script.ExecuteAsync<int>();
}
#endregion
// Clean up temp files
Directory.Delete(tempLocation, true);
}
catch (Exception ex)
{
2026-05-25 04:16:19 -05:00
logger?.LogError(ex, "Failed to extract save files for game {GameId}", gameId);
}
finally
{
if (Directory.Exists(tempLocation))
Directory.Delete(tempLocation, true);
}
}
}
public async Task<Stream> PackAsync(string installDirectory, SDK.Models.Manifest.Game manifest)
{
2025-03-18 20:30:04 -05:00
using (var savePacker = new SavePacker(installDirectory))
{
2025-03-18 20:30:04 -05:00
if (manifest?.SavePaths.Any() ?? false)
savePacker.AddPaths(manifest.SavePaths.Where(sp => EnvironmentHelper.SupportsCurrentRuntime(sp.Platforms)));
2025-03-18 20:30:04 -05:00
await savePacker.AddManifestAsync(manifest);
2025-03-18 20:30:04 -05:00
return await savePacker.PackAsync();
}
}
public async Task<GameSave> UploadAsync(Stream stream, SDK.Models.Manifest.Game manifest)
{
Implement SDK using dependency injection - API requests are now made through the ApiRequestBuilder with DI supplied via the ApiRequestFactory singleton - Reliance on RestSharp and WebClient has been removed in favor of HttpClient - Auth token is now being tracked by the ITokenProvider - Network information (MAC address, broadcast addresses, IP address) is now supplied with the INetworkInformationProvider singleton - Connection state is now being maintained by the ConnectionService, with a reliance on RPC (SignalR websocket) reporting actual connection state without relying on pings - All download streams are now provided as a TrackableStream - Singleton Client class has been removed. All usage of the SDK should happen via Dependency Injection This is just a base, non-functional refactor of the SDK to use DI. The following changes to the rest of the codebase need to be made: - Usage of the SDK client in the launcher needs to be replaced in favor of injecting SDK services - PowerShell cmdlets need to be able to have services injected. Most likely a separate scope will have to be opened up per PS runtime? - Usage of the SDK client in the server needs to be replaced in favor of injecting SDK services. This should be minimal and should actually provide benefit when it comes to executing client-like features (scripts mostly) without needing a fully configured client that maintains connection state. - Configuration of the client needs to be implemented using ILANCommanderConfiguration
2025-09-22 00:29:51 -05:00
return await apiRequestFactory
.Create()
.UseAuthenticationToken()
.UseVersioning()
.UseRoute($"/api/Saves/Game/{manifest.Id}/Upload?platform={EnvironmentHelper.GetCurrentRuntime()}")
.UploadAsync<GameSave>($"game-{manifest.Id}-save", stream);
}
2025-08-18 03:03:28 -05:00
public async Task UploadAsync(string installDirectory, Guid gameId)
2025-03-18 20:30:04 -05:00
{
using (var savePacker = new SavePacker(installDirectory))
{
var manifest = await ManifestHelper.ReadAsync<SDK.Models.Manifest.Game>(installDirectory, gameId);
if (manifest?.SavePaths?.Any() ?? false)
savePacker.AddPaths(manifest.SavePaths.Where(sp => EnvironmentHelper.SupportsCurrentRuntime(sp.Platforms)));
if (savePacker.HasEntries())
{
2026-05-25 04:16:19 -05:00
logger?.LogDebug("Packing {EntryCount} save entries for game {GameId}", savePacker.EntryCount, gameId);
await savePacker.AddManifestAsync(manifest);
var stream = await savePacker.PackAsync();
2025-08-18 03:03:28 -05:00
await UploadAsync(stream, manifest);
2026-05-25 04:16:19 -05:00
logger?.LogDebug("Save uploaded for game {GameId} ({Size} bytes)", gameId, stream.Length);
}
else
{
logger?.LogDebug("No save files found to upload for game {GameId}", gameId);
}
}
}
2024-10-23 18:17:01 -05:00
public async Task DeleteAsync(Guid id)
2024-02-17 17:30:14 -06:00
{
Implement SDK using dependency injection - API requests are now made through the ApiRequestBuilder with DI supplied via the ApiRequestFactory singleton - Reliance on RestSharp and WebClient has been removed in favor of HttpClient - Auth token is now being tracked by the ITokenProvider - Network information (MAC address, broadcast addresses, IP address) is now supplied with the INetworkInformationProvider singleton - Connection state is now being maintained by the ConnectionService, with a reliance on RPC (SignalR websocket) reporting actual connection state without relying on pings - All download streams are now provided as a TrackableStream - Singleton Client class has been removed. All usage of the SDK should happen via Dependency Injection This is just a base, non-functional refactor of the SDK to use DI. The following changes to the rest of the codebase need to be made: - Usage of the SDK client in the launcher needs to be replaced in favor of injecting SDK services - PowerShell cmdlets need to be able to have services injected. Most likely a separate scope will have to be opened up per PS runtime? - Usage of the SDK client in the server needs to be replaced in favor of injecting SDK services. This should be minimal and should actually provide benefit when it comes to executing client-like features (scripts mostly) without needing a fully configured client that maintains connection state. - Configuration of the client needs to be implemented using ILANCommanderConfiguration
2025-09-22 00:29:51 -05:00
await apiRequestFactory
.Create()
.UseAuthenticationToken()
.UseVersioning()
.UseRoute($"/api/Saves/{id}")
.DeleteAsync<bool>();
2024-02-17 17:30:14 -06:00
}
public IEnumerable<SavePathEntry> GetFileSavePathEntries(SDK.Models.Manifest.SavePath savePath, string installDirectory)
{
IEnumerable<string> localPaths;
if (savePath.IsRegex)
{
var workingDirectory = GetLocalPath(savePath.WorkingDirectory, installDirectory);
var pattern = savePath.Path;
if (string.IsNullOrWhiteSpace(workingDirectory))
workingDirectory = installDirectory;
var regex = new Regex(pattern);
localPaths = Directory.GetFiles(workingDirectory, "*", SearchOption.AllDirectories)
.Where(p =>
{
var relativePath = p.Substring(workingDirectory.Length)
.TrimStart(Path.DirectorySeparatorChar)
.Replace('\\', '/');
return regex.IsMatch(relativePath);
})
.ToList();
}
else
{
var workingDirectory = GetLocalPath(savePath.WorkingDirectory, installDirectory);
var localPath = Path.Combine(workingDirectory, GetLocalPath(savePath.Path, installDirectory));
2025-08-18 03:03:28 -05:00
localPaths = new[] { localPath };
}
var entries = new List<SavePathEntry>();
foreach (var localPath in localPaths)
{
var actualPath = localPath.DeflateEnvironmentVariables(installDirectory);
var workingDirectory = savePath.WorkingDirectory.DeflateEnvironmentVariables(installDirectory);
var archivePath = actualPath.Replace(workingDirectory, "").TrimStart(Path.DirectorySeparatorChar);
entries.Add(new SavePathEntry
{
ArchivePath = archivePath.Replace(Path.DirectorySeparatorChar, '/'),
ActualPath = actualPath.Replace(Path.DirectorySeparatorChar, '/')
});
savePath.Entries = entries;
}
return entries;
}
public string GetLocalPath(string path, string installDirectory)
{
var localPath = path.ExpandEnvironmentVariables(installDirectory);
if (Path.DirectorySeparatorChar == '/')
localPath = localPath.Replace('\\', Path.DirectorySeparatorChar);
else
localPath = localPath.Replace('/', Path.DirectorySeparatorChar);
return localPath;
}
public string GetActualPath(string path, string installDirectory)
{
var actualPath = path.DeflateEnvironmentVariables(installDirectory);
if (Path.DirectorySeparatorChar == '\\')
actualPath = path.Replace('/', Path.DirectorySeparatorChar);
return actualPath;
}
public string GetArchivePath(string path, string workingDirectory, string installDirectory)
{
path = GetLocalPath(path, installDirectory);
workingDirectory = GetLocalPath(workingDirectory, installDirectory);
var archivePath = path.Replace(workingDirectory, "").Trim(Path.DirectorySeparatorChar);
if (Path.DirectorySeparatorChar == '\\')
archivePath = archivePath.Replace(Path.DirectorySeparatorChar, '/');
return archivePath;
}
private void ExtractFilesFromZip(string zipPath, string destination)
{
2023-04-07 19:09:00 -05:00
using (var fs = File.OpenRead(zipPath))
using (var ts = new TrackableStream(fs, fs.Length))
using (var reader = ReaderFactory.OpenReader(ts, new ReaderOptions()))
{
2023-04-07 19:09:00 -05:00
reader.WriteAllToDirectory(destination, new ExtractionOptions()
{
2023-04-07 19:09:00 -05:00
ExtractFullPath = true,
Overwrite = true
});
}
}
}
}