diff --git a/UnitystationLauncher/Models/ConfigFile/Preferences.cs b/UnitystationLauncher/Models/ConfigFile/Preferences.cs index 570363e..5b0fc33 100644 --- a/UnitystationLauncher/Models/ConfigFile/Preferences.cs +++ b/UnitystationLauncher/Models/ConfigFile/Preferences.cs @@ -8,6 +8,7 @@ public class Preferences : ReactiveObject private int _ignoreVersionUpdate; private string _installationPath = string.Empty; private bool? _TTSEnabled = null; + private bool? _enableCodeScan = true; public bool AutoRemove { @@ -32,4 +33,10 @@ public bool? TTSEnabled get => _TTSEnabled; set => this.RaiseAndSetIfChanged(ref _TTSEnabled, value); } + + public bool? EnableCodeScan + { + get => _enableCodeScan; + set => this.RaiseAndSetIfChanged(ref _enableCodeScan, value); + } } diff --git a/UnitystationLauncher/Services/CodeScanConfigService.cs b/UnitystationLauncher/Services/CodeScanConfigService.cs index 8078c3e..d234507 100644 --- a/UnitystationLauncher/Services/CodeScanConfigService.cs +++ b/UnitystationLauncher/Services/CodeScanConfigService.cs @@ -46,7 +46,7 @@ public CodeScanConfigService(HttpClient httpClient, IPreferencesService preferen } string pathBase = _preferencesService.GetPreferences().InstallationPath; - string folderName = GetFolderName(version); + string folderName = GetFolderName(version, _environmentService); string versionPath = Path.Combine(pathBase, "nonbuild", version, folderName); if (Directory.Exists(versionPath) == false) @@ -194,9 +194,9 @@ private string GetZipFolderName() } } - private string GetFolderName(string version) + public static string GetFolderName(string version, IEnvironmentService environmentService) { - CurrentEnvironment os = _environmentService.GetCurrentEnvironment(); + CurrentEnvironment os = environmentService.GetCurrentEnvironment(); switch (os) { case CurrentEnvironment.WindowsStandalone: diff --git a/UnitystationLauncher/Services/InstallationService.cs b/UnitystationLauncher/Services/InstallationService.cs index befb863..13cbe41 100644 --- a/UnitystationLauncher/Services/InstallationService.cs +++ b/UnitystationLauncher/Services/InstallationService.cs @@ -95,16 +95,22 @@ public List GetInstallations() return (null!, failureReason); } - bool result = await _codeScanConfigService.ValidGoodFilesVersionAsync(server.GoodFileVersion); + if (_preferencesService.GetPreferences().EnableCodeScan is true) + { + bool result = await _codeScanConfigService.ValidGoodFilesVersionAsync(server.GoodFileVersion); - if (result == false) + if (result == false) + { + const string failureReason = "server does not have a valid ServerGoodFileVersion "; + Log.Warning(failureReason + $" ServerName: {server.ServerName} ServerGoodFileVersion : {server.GoodFileVersion}"); + return (null!, failureReason); + } + } + else { - const string failureReason = "server does not have a valid ServerGoodFileVersion "; - Log.Warning(failureReason + $" ServerName: {server.ServerName} ServerGoodFileVersion : {server.GoodFileVersion}"); - return (null!, failureReason); + Log.Information("Code scan is disabled, skipping..."); } - Download? download = GetInProgressDownload(server.ForkName, server.BuildVersion); if (download != null) @@ -168,6 +174,7 @@ public List GetInstallations() { const string failureReason = "Couldn't find executable to start."; Log.Warning(failureReason + $" Installation Path: {installation.InstallationPath ?? "null"}"); + _ = ShowErrorWhenNoServerExecutable(installationId, server); return (false, failureReason); } @@ -195,6 +202,30 @@ public List GetInstallations() return (true, string.Empty); } + private async Task ShowErrorWhenNoServerExecutable(Guid installationId, string? server) + { + if (server is "") return; + IMsBox msgBox = MessageBoxBuilder.CreateMessageBox( + MessageBoxButtons.YesNo, + "Executable Not Found", + "The executable for this installation could not be found. Would you like to re-download it?" + ); + string response = await msgBox.ShowAsync(); + if (response.Equals(MessageBoxResults.Yes)) + { + DeleteInstallation(installationId); + Server? serverDetails = await _serverService.GetServerByIpAddress(server); + if (serverDetails != null) + { + _ = DownloadInstallationAsync(serverDetails); + } + } + else + { + DeleteInstallation(installationId); + } + } + public (bool, string) DeleteInstallation(Guid installationId) { Installation? installation = GetInstallationById(installationId); @@ -460,7 +491,14 @@ private async Task StartDownloadAsync(Download download) // ExtractAndScan() must be run in a separate thread, but we want this one to wait for that one to finish // Without this download progress will not work properly - await Task.Run(() => ExtractAndScan(download, progressStream)); + if (_preferencesService.GetPreferences().EnableCodeScan is false) + { + await Task.Run(() => Extract(download, progressStream)); + } + else + { + await Task.Run(() => ExtractAndScan(download, progressStream)); + } } catch (Exception e) { @@ -474,6 +512,22 @@ private async Task StartDownloadAsync(Download download) } } + private async Task Extract(Download download, ProgressStream progressStream) + { + try + { + ZipArchive archive = new(progressStream); + Log.Information($"Extracting without scan to: {download.InstallPath}"); + archive.ExtractToDirectory(download.InstallPath, true); + InstallationDone(download); + } + catch (Exception e) + { + Log.Information($"Extracting stopped with {e}"); + download.DownloadState = DownloadState.Failed; + } + } + private async Task ExtractAndScan(Download download, ProgressStream progressStream) { List scanLogs = []; @@ -498,26 +552,13 @@ void ScanLogs(ScanLog log) scanLogs.Add(log); } - + download.DownloadState = DownloadState.Scanning; bool scanTask = await _codeScanService.OnScanAsync(archive, download.InstallPath, download.GoodFileVersion, ScanLogs); if (scanTask) { - Log.Information("Download completed"); - - _installations.Add(new() - { - BuildVersion = download.BuildVersion, - ForkName = download.ForkName, - InstallationId = Guid.NewGuid(), - InstallationPath = download.InstallPath, - LastPlayedDate = DateTime.Now - }); - - WriteInstallations(); - EnsureExecutableFlagOnUnixSystems(download.InstallPath); - download.DownloadState = DownloadState.Installed; + InstallationDone(download); } else { @@ -553,6 +594,24 @@ void ScanLogs(ScanLog log) } } + private void InstallationDone(Download download) + { + Log.Information("Download completed"); + + _installations.Add(new() + { + BuildVersion = download.BuildVersion, + ForkName = download.ForkName, + InstallationId = Guid.NewGuid(), + InstallationPath = download.InstallPath, + LastPlayedDate = DateTime.Now + }); + + WriteInstallations(); + EnsureExecutableFlagOnUnixSystems(download.InstallPath); + download.DownloadState = DownloadState.Installed; + } + private static async Task ShowScanFailPopUp(string message, string logFolder) { IMsBox msgBox = MessageBoxBuilder.CreateMessageBox( diff --git a/UnitystationLauncher/Services/Interface/IServerService.cs b/UnitystationLauncher/Services/Interface/IServerService.cs index 6fe87f0..8ad3f1d 100644 --- a/UnitystationLauncher/Services/Interface/IServerService.cs +++ b/UnitystationLauncher/Services/Interface/IServerService.cs @@ -1,4 +1,6 @@ +using System; using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; using UnitystationLauncher.Models; using UnitystationLauncher.Models.Api; @@ -19,4 +21,10 @@ public interface IServerService /// Installation to check /// true if at least one server is using this installation. false otherwise public bool IsInstallationInUse(Installation installation); + + public async Task GetServerByIpAddress(string ipAddress) + { + var results = await GetServersAsync(); + return results.Count == 0 ? null : results.Find(x => x.ServerIp == ipAddress); + } } \ No newline at end of file diff --git a/UnitystationLauncher/ViewModels/PreferencesPanelViewModel.cs b/UnitystationLauncher/ViewModels/PreferencesPanelViewModel.cs index af71c43..9c95e07 100644 --- a/UnitystationLauncher/ViewModels/PreferencesPanelViewModel.cs +++ b/UnitystationLauncher/ViewModels/PreferencesPanelViewModel.cs @@ -48,11 +48,20 @@ public bool? TTSEnabled get => _TTSEnabled; set => this.RaiseAndSetIfChanged(ref _TTSEnabled, value); } + + private bool? _enableCodeScan = true; + + public bool? EnableCodeScan + { + get => _enableCodeScan; + set => this.RaiseAndSetIfChanged(ref _enableCodeScan, value); + } private readonly IPreferencesService _preferencesService; private readonly IInstallationService _installationService; private readonly IEnvironmentService _environmentService; private readonly ITTSService _ttsService; + public PreferencesPanelViewModel( IPreferencesService preferencesService, @@ -69,6 +78,7 @@ public PreferencesPanelViewModel( _installationPath = preferences.InstallationPath; _autoRemove = preferences.AutoRemove; _TTSEnabled = preferences.TTSEnabled; + _enableCodeScan = preferences.EnableCodeScan; this.WhenAnyValue(p => p.AutoRemove) .Select(_ => Observable.FromAsync(OnAutoRemoveChangedAsync)) .Concat() @@ -78,6 +88,11 @@ public PreferencesPanelViewModel( .Select(_ => Observable.FromAsync(OnTTSChangedAsync)) .Concat() .Subscribe(); + + this.WhenAnyValue(p => p.EnableCodeScan) + .Select(_ => Observable.FromAsync(OnAllowCodeScanChangedAsync)) + .Concat() + .Subscribe(); } public async Task OnAutoRemoveChangedAsync() @@ -160,6 +175,26 @@ await MessageBoxBuilder.CreateMessageBox(MessageBoxButtons.Ok, "Error moving ins await MessageBoxBuilder.CreateMessageBox(MessageBoxButtons.Ok, "Invalid installation path", invalidReason).ShowAsync(); } } + + public async Task OnAllowCodeScanChangedAsync() + { + if (EnableCodeScan == false) + { + IMsBox msgBox = MessageBoxBuilder.CreateMessageBox( + MessageBoxButtons.YesNo, + "Warning", + "For security reasons, we recommend you never disable the codescan feature unless you understand what you are doing and you trust the server you are trying to play on.\n Are you sure you want to proceed?" + ); + + string response = await msgBox.ShowAsync(); + if (response.Equals(MessageBoxResults.No)) + { + EnableCodeScan = true; // Revert the change + } + } + + _preferencesService.GetPreferences().EnableCodeScan = EnableCodeScan; + } public override void Refresh() { diff --git a/UnitystationLauncher/Views/PreferencesPanelView.xaml b/UnitystationLauncher/Views/PreferencesPanelView.xaml index 26b557e..0bcae26 100644 --- a/UnitystationLauncher/Views/PreferencesPanelView.xaml +++ b/UnitystationLauncher/Views/PreferencesPanelView.xaml @@ -69,6 +69,11 @@ Enable the Text-To-Speech System + + + Enable Code Scan Feature + +