Skip to content
Merged
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
17 changes: 11 additions & 6 deletions Knossos.NET/Classes/FsoBuild.cs
Original file line number Diff line number Diff line change
Expand Up @@ -320,7 +320,7 @@ public async Task<FsoResult> RunFSO(FsoExecType executableType, string cmdline,
/// Get FSO flags structure of this build using the JSON format V1
/// </summary>
/// <returns>A FlagsJsonV1 structure or null if failed</returns>
public FlagsJsonV1? GetFlagsV1()
public async Task<FlagsJsonV1?> GetFlagsV1Async()
{
var executable = GetExecutable(FsoExecType.Flags);
var fullpath = GetExecutablePath(executable);
Expand Down Expand Up @@ -374,10 +374,15 @@ public async Task<FsoResult> RunFSO(FsoExecType executableType, string cmdline,
}

cmd.Start();
string result = cmd.StandardOutput.ReadToEnd();
stderr = cmd.StandardError.ReadToEnd();
// Read stdout and stderr concurrently to prevent pipe deadlock.
// Sequential reads deadlock if stderr output exceeds the pipe buffer (~4 KB).
var stdoutTask = cmd.StandardOutput.ReadToEndAsync();
var stderrTask = cmd.StandardError.ReadToEndAsync();
await Task.WhenAll(stdoutTask, stderrTask).ConfigureAwait(false);
string result = stdoutTask.Result;
stderr = stderrTask.Result;
output = result;
cmd.WaitForExit();
await cmd.WaitForExitAsync().ConfigureAwait(false);

if (KnUtils.IsLinux && !string.IsNullOrEmpty(stderr))
{
Expand All @@ -395,7 +400,7 @@ public async Task<FsoResult> RunFSO(FsoExecType executableType, string cmdline,
if (!_flagErrorOneWarn)
{
_flagErrorOneWarn = true;
Dispatcher.UIThread.Invoke(async () => { await MessageBox.Show(MainWindow.instance, libfuseError, "Unable to run FSO", MessageBox.MessageBoxButtons.OK); });
await Dispatcher.UIThread.InvokeAsync(async () => { await MessageBox.Show(MainWindow.instance, libfuseError, "Unable to run FSO", MessageBox.MessageBoxButtons.OK); });
}
}
else
Expand All @@ -404,7 +409,7 @@ public async Task<FsoResult> RunFSO(FsoExecType executableType, string cmdline,
if (!_flagErrorOneWarn)
{
_flagErrorOneWarn = true;
Dispatcher.UIThread.Invoke(async ()=> { await MessageBox.Show(MainWindow.instance, stderr, "Unable to run FSO", MessageBox.MessageBoxButtons.OK); });
await Dispatcher.UIThread.InvokeAsync(async () => { await MessageBox.Show(MainWindow.instance, stderr, "Unable to run FSO", MessageBox.MessageBoxButtons.OK); });
}
}
return null;
Expand Down
23 changes: 13 additions & 10 deletions Knossos.NET/ViewModels/GlobalSettingsViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -588,10 +588,13 @@ public void CommitPendingChanges()
/// Loads data from the GlobalSettings.cs class into this one to display it in the UI
/// Also loads flag data from a FSO build, if one is installed
/// </summary>
public void LoadData()
public async Task LoadDataAsync()
{
var old_path = KnUtils.GetFSODataFolderPath();
var flagData = GetFlagData();
// Fetch flag data on a background thread so the UI thread stays responsive.
// await resumes on Avalonia's UI synchronization context, so all ObservableProperty
// assignments below are safe.
var flagData = await Task.Run(GetFlagDataAsync);

// reset the ini info if we have gotten an updated preferred path from FSO.
if (old_path != KnUtils.GetFSODataFolderPath()){
Expand Down Expand Up @@ -1090,7 +1093,7 @@ public void LoadData()
UnCommitedChanges = false;
}

private FlagsJsonV1? GetFlagData()
private async Task<FlagsJsonV1?> GetFlagDataAsync()
{
FlagDataLoaded = false;
var builds = Knossos.GetInstalledBuildsList();
Expand All @@ -1103,7 +1106,7 @@ public void LoadData()
stables.Sort(FsoBuild.CompareVersion);
foreach (var stable in stables)
{
var flags = stable.GetFlagsV1();
var flags = await stable.GetFlagsV1Async().ConfigureAwait(false);
if (flags != null)
{
FlagDataLoaded = true;
Expand All @@ -1120,7 +1123,7 @@ public void LoadData()
{
foreach (var other in others)
{
var flags = other.GetFlagsV1();
var flags = await other.GetFlagsV1Async().ConfigureAwait(false);
if (flags != null)
{
FlagDataLoaded = true;
Expand Down Expand Up @@ -1168,7 +1171,7 @@ internal async void BrowseFolderCommand()
Knossos.globalSettings.basePath = result[0].Path.LocalPath.ToString();
Knossos.globalSettings.Save();
Knossos.ResetBasePath();
LoadData();
await LoadDataAsync();
}
}
catch (Exception ex)
Expand All @@ -1184,12 +1187,12 @@ await Dispatcher.UIThread.Invoke(async () => {
/// <summary>
/// Reload data from json
/// </summary>
internal void ResetCommand()
internal async void ResetCommand()
{
var pxoUser = Knossos.globalSettings.pxoLogin;
var pxoPassword = Knossos.globalSettings.pxoPassword;
Knossos.globalSettings = new GlobalSettings();
LoadData();
await LoadDataAsync();
Knossos.globalSettings.pxoPassword = pxoPassword;
Knossos.globalSettings.pxoLogin = pxoUser;
SaveCommand();
Expand Down Expand Up @@ -1465,10 +1468,10 @@ internal void GlobalCmdHelp()
/// <summary>
/// Reloads configuration and FSO flag data
/// </summary>
internal void ReloadFlagData()
internal async void ReloadFlagData()
{
Knossos.globalSettings.Load();
LoadData();
await LoadDataAsync();
}

/// <summary>
Expand Down
11 changes: 6 additions & 5 deletions Knossos.NET/ViewModels/Templates/DevModFsoSettingsViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using Knossos.NET.Models;
using Knossos.NET.Views;
using System.Linq;
using System.Threading.Tasks;

namespace Knossos.NET.ViewModels
{
Expand Down Expand Up @@ -70,7 +71,7 @@ private void LoadFsoPicker()
}
}

internal void ConfigureBuild()
internal async void ConfigureBuild()
{
if (editor == null || FsoPicker == null)
return;
Expand Down Expand Up @@ -102,24 +103,24 @@ internal void ConfigureBuild()

if(fsoBuild != null)
{
var flagsV1=fsoBuild.GetFlagsV1();
var flagsV1 = await Task.Run(fsoBuild.GetFlagsV1Async);
if(flagsV1 != null)
{
FsoFlags = new FsoFlagsViewModel(flagsV1, CmdLine);
}
else
{
Dispatcher.UIThread.InvokeAsync(async () =>
await Dispatcher.UIThread.InvokeAsync(async () =>
{
await MessageBox.Show(MainWindow.instance!, "Unable to get flag data from build " + fsoBuild + " It might be below the minimal version supported (3.8.1) or some other error ocurred.", "Invalid flag data", MessageBox.MessageBoxButtons.OK);
});

}
}
else
{
/* No valid build found, send message */
Dispatcher.UIThread.InvokeAsync(async () =>
await Dispatcher.UIThread.InvokeAsync(async () =>
{
await MessageBox.Show(MainWindow.instance!, "Unable to resolve FSO build dependency, download the correct one or manually select a FSO version. ", "Not engine build found", MessageBox.MessageBoxButtons.OK);
});
Expand Down
4 changes: 2 additions & 2 deletions Knossos.NET/ViewModels/Windows/MainWindowViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -354,7 +354,7 @@ partial void OnSelectedMenuItemChanged(MainViewMenuItem? value)
GlobalSettingsView?.CheckDisplaySettingsWarning();
}
Knossos.globalSettings.Load();
GlobalSettingsView?.LoadData();
_ = GlobalSettingsView?.LoadDataAsync();
GlobalSettingsView?.UpdateImgCacheSize();
}

Expand Down Expand Up @@ -498,7 +498,7 @@ public void RemoveInstalledModVersion(Mod mod)
/// </summary>
public void GlobalSettingsLoadData()
{
GlobalSettingsView?.LoadData();
_ = GlobalSettingsView?.LoadDataAsync();
}

internal void ApplySettings()
Expand Down
18 changes: 9 additions & 9 deletions Knossos.NET/ViewModels/Windows/ModSettingsViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -377,17 +377,17 @@ internal void SaveSettingsCommand()
}
}

internal void ConfigureBuildCommand()
internal async void ConfigureBuildCommand()
{
ConfigureBuild(false);
await ConfigureBuild(false);
}

/// <summary>
/// Display Fso Build Flag list
/// Also allows to edit the mod cmdline
/// </summary>
/// <param name="ignoreUserSettings"></param>
private void ConfigureBuild(bool ignoreUserSettings)
private async Task ConfigureBuild(bool ignoreUserSettings)
{
if (modJson == null)
return;
Expand All @@ -411,24 +411,24 @@ private void ConfigureBuild(bool ignoreUserSettings)

if(fsoBuild != null)
{
var flagsV1=fsoBuild.GetFlagsV1();
var flagsV1 = await Task.Run(fsoBuild.GetFlagsV1Async);
if(flagsV1 != null)
{
FsoFlags = new FsoFlagsViewModel(flagsV1,modJson.GetModCmdLine(ignoreUserSettings));
}
else
{
Dispatcher.UIThread.InvokeAsync(async () =>
await Dispatcher.UIThread.InvokeAsync(async () =>
{
await MessageBox.Show(MainWindow.instance!, "Unable to get flag data from build " + fsoBuild + " It might be below the minimal version supported (3.8.1) or some other error ocurred.", "Invalid flag data", MessageBox.MessageBoxButtons.OK);
});

}
}
else
{
/* No valid build found, send message */
Dispatcher.UIThread.InvokeAsync(async () =>
await Dispatcher.UIThread.InvokeAsync(async () =>
{
await MessageBox.Show(MainWindow.instance!, "Unable to resolve FSO build dependency, download the correct one or manually select a FSO version. ", "Not engine build found", MessageBox.MessageBoxButtons.OK);
});
Expand All @@ -438,13 +438,13 @@ private void ConfigureBuild(bool ignoreUserSettings)
/// <summary>
/// Reset settings to DEFAULT on UI
/// </summary>
internal void ResetSettingsCommand()
internal async void ResetSettingsCommand()
{
CustomDependencies = false;
BuildMissingWarning = string.Empty;
FsoPicker = new FsoBuildPickerViewModel(null, window);
FsoFlags = null;
ConfigureBuild(true);
await ConfigureBuild(true);
DepItems.Clear();
CreateDependencyItems(true);
IgnoreGlobalCmd = false;
Expand Down