Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions FASTER/Models/Logger.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using System;
using System.IO;

namespace FASTER.Models
{
public static class Logger
{
private static readonly string LogPath = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
"FASTER", "faster.log");

public static bool IsEnabled => Properties.Settings.Default.enableDebugLog;

public static string LogFilePath => LogPath;

public static void Log(string message)
{
if (!IsEnabled) return;
try
{
Directory.CreateDirectory(Path.GetDirectoryName(LogPath)!);
File.AppendAllText(LogPath, $"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] {message}{Environment.NewLine}");
}
catch { }

Check warning on line 24 in FASTER/Models/Logger.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Either remove or fill this block of code.

See more on https://sonarcloud.io/project/issues?id=Foxlider_FASTER&issues=AZ4slKjg_UM1u-u3fyQ5&open=AZ4slKjg_UM1u-u3fyQ5&pullRequest=262

Check warning on line 24 in FASTER/Models/Logger.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Handle the exception or explain in a comment why it can be ignored.

See more on https://sonarcloud.io/project/issues?id=Foxlider_FASTER&issues=AZ4slKjg_UM1u-u3fyQ4&open=AZ4slKjg_UM1u-u3fyQ4&pullRequest=262
}
}
}
12 changes: 12 additions & 0 deletions FASTER/Properties/Settings.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions FASTER/Properties/Settings.settings
Original file line number Diff line number Diff line change
Expand Up @@ -95,5 +95,8 @@
<Setting Name="usingEFDlc" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">False</Value>
</Setting>
<Setting Name="enableDebugLog" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">False</Value>
</Setting>
</Settings>
</SettingsFile>
45 changes: 42 additions & 3 deletions FASTER/ViewModel/DeploymentViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -108,12 +108,25 @@ public void DeployAll()
{"Name", Settings.Default.steamUserName}
});

Logger.Log($"DeployAll: installPath={Deployment.InstallPath}, mods={Deployment.DeployMods.Count}");

if (!Directory.Exists(Deployment.InstallPath))
{
Logger.Log("DeployAll: install path not found, aborting.");
DisplayMessage("Arma Install Path is empty.\nMake sure you have entered a valid path before deploying mods.");
return;
}

foreach (var mod in Deployment.DeployMods)
{
var linkPath = Path.Combine(Deployment.InstallPath, $"@{Functions.SafeName(mod.Name)}");
mod.Marked = true;
Logger.Log($" Linking {mod.Name}: {mod.Path} -> {linkPath}");
LinkMod(mod, linkPath);
}
Settings.Default.Deployments = Deployment;
Settings.Default.Save();
Logger.Log("DeployAll: done.");
}

/// <summary>
Expand Down Expand Up @@ -204,15 +217,36 @@ public void OpenModPage(DeploymentMod mod)
/// <param name="linkPath"></param>
private void LinkMod(DeploymentMod mod, string linkPath)
{
Logger.Log($"LinkMod: {mod.Name} ({mod.WorkshopId}) -> {linkPath}");
try
{
if(Directory.Exists(linkPath))
Directory.Delete(linkPath, true);
{
if (new DirectoryInfo(linkPath).Attributes.HasFlag(FileAttributes.ReparsePoint))
{
Logger.Log($" Removing existing symlink: {linkPath}");
Directory.Delete(linkPath);
}
else
{
Logger.Log($" Removing existing real dir: {linkPath}");
Directory.Delete(linkPath, true);
}
}

Directory.CreateSymbolicLink(linkPath ?? throw new ArgumentNullException(nameof(linkPath)), mod.Path);
Logger.Log($" Symlink created OK.");
}
catch (UnauthorizedAccessException)
{
Logger.Log($" ERROR: UnauthorizedAccessException creating symlink.");
DisplayMessage("Could not create symlink: Access denied.\n\nTo deploy mods, enable Windows Developer Mode in Settings → Update & Security → For Developers, or run FASTER as Administrator.");
}
catch (Exception ex)
{ DisplayMessage("An exception occurred: \n\n" + ex.Message); }
{
Logger.Log($" ERROR: {ex.Message}");
DisplayMessage("An exception occurred: \n\n" + ex.Message);
}
}

/// <summary>
Expand All @@ -224,7 +258,12 @@ private void DeleteLink(string linkPath)
try
{
if (Directory.Exists(linkPath))
Directory.Delete(linkPath, true);
{
if (new DirectoryInfo(linkPath).Attributes.HasFlag(FileAttributes.ReparsePoint))
Directory.Delete(linkPath);
else
Directory.Delete(linkPath, true);
}
}
catch (Exception ex)
{ DisplayMessage("An exception occurred: \n\n" + ex.Message); }
Expand Down
121 changes: 118 additions & 3 deletions FASTER/ViewModel/ModsViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -230,10 +230,16 @@ public void OpenModFolder(ArmaMod mod)

Process.Start(startInfo);
}
public void CheckForUpdates()
public async Task CheckForUpdates()
{
Logger.Log("CheckForUpdates started.");
foreach (ArmaMod mod in ModsCollection.ArmaMods)
{ Task.Run(() => mod.UpdateInfos()); }
{
Logger.Log($" Checking mod {mod.WorkshopId} ({mod.Name})...");
await Task.Run(() => mod.UpdateInfos());
await Task.Delay(300);
}
Logger.Log("CheckForUpdates finished.");
}

public async Task UpdateSelectedMods()
Expand All @@ -253,8 +259,117 @@ public async Task UpdateAll()

MainWindow.Instance.NavigateToConsole();
var ans = await MainWindow.Instance.SteamUpdaterViewModel.RunModsUpdater(ModsCollection.ArmaMods);
if(ans == UpdateState.LoginFailed)
if(ans == UpdateState.LoginFailed)
DisplayMessage("Steam Login Failed");
}

public void PurgeAndReinstallMod(ArmaMod mod)
{
if (mod == null) return;

Logger.Log($"PurgeAndReinstallMod: {mod.WorkshopId} ({mod.Name}) path={mod.Path}");
try
{
if (Directory.Exists(mod.Path))
{
Directory.Delete(mod.Path, true);
Logger.Log($" Deleted folder: {mod.Path}");
}
else
Logger.Log($" Folder not found, skipping delete: {mod.Path}");
}
catch (Exception ex)
{
Logger.Log($" ERROR deleting folder: {ex.Message}");
DisplayMessage($"Could not delete folder for mod {mod.WorkshopId}");
}

mod.Status = ArmaModStatus.UpdateRequired;
mod.LocalLastUpdated = 0;
mod.Size = 0;
Properties.Settings.Default.Save();
}

public void PurgeAndReinstallSelectedMods()
{
var selectedMods = new List<ArmaMod>(ModsCollection.ArmaMods.Where(m => m.IsSelected && !m.IsLocal));
foreach (var mod in selectedMods)
PurgeAndReinstallMod(mod);
}

public async Task PurgeAndReinstallAll()
{
var answer = await DialogCoordinator.ShowInputAsync(this, "Are you sure you want to purge all mods?", "Write \"yes\" and press OK to delete all folders in the Mod Staging Directory and re-download everything.");

if (string.IsNullOrEmpty(answer) || !answer.Equals("yes"))
return;

Analytics.TrackEvent("Mods - Clicked PurgeAndReinstallAll", new Dictionary<string, string>
{
{"Name", Properties.Settings.Default.steamUserName}
});

var stagingDir = Properties.Settings.Default.modStagingDirectory;
Logger.Log($"PurgeAndReinstallAll: staging dir={stagingDir}");
if (Directory.Exists(stagingDir))
{
foreach (var dir in Directory.GetDirectories(stagingDir))
{
try
{
Directory.Delete(dir, true);
Logger.Log($" Deleted: {dir}");
}
catch (Exception ex)
{
Logger.Log($" ERROR deleting {dir}: {ex.Message}");
DisplayMessage($"Could not delete folder: {dir}");
}
}
}
else
Logger.Log(" Staging dir does not exist, nothing deleted.");

foreach (var mod in ModsCollection.ArmaMods.Where(m => !m.IsLocal).ToList())
{
mod.Status = ArmaModStatus.UpdateRequired;
mod.LocalLastUpdated = 0;
mod.Size = 0;
Logger.Log($" Reset mod {mod.WorkshopId} ({mod.Name})");
}
Properties.Settings.Default.Save();

Logger.Log("PurgeAndReinstallAll: launching UpdateAll...");
MainWindow.Instance.NavigateToConsole();
var ans = await MainWindow.Instance.SteamUpdaterViewModel.RunModsUpdater(ModsCollection.ArmaMods);
if (ans == UpdateState.LoginFailed)
DisplayMessage("Steam Login Failed");
}

public async Task PurgeUnusedMods()
{
var usedIds = Properties.Settings.Default.Profiles
.SelectMany(p => p.ProfileMods ?? Enumerable.Empty<ProfileMod>())
.Select(m => m.Id)
.ToHashSet();

var unusedMods = ModsCollection.ArmaMods
.Where(m => !m.IsLocal && !usedIds.Contains(m.WorkshopId))
.ToList();

if (unusedMods.Count == 0)
{
DisplayMessage("No unused mods found.");
return;
}

var result = await DialogCoordinator.ShowInputAsync(this,
"Purge Unused Mods",
$"Found {unusedMods.Count} unused mod(s). Type \"yes\" to confirm deletion.");
if (result?.ToLower() != "yes") return;

foreach (var mod in unusedMods)
DeleteMod(mod);
}
}
}
Loading