diff --git a/MelonLoader.Installer/App.axaml b/MelonLoader.Installer/App.axaml
index ed21c5e..61619ab 100644
--- a/MelonLoader.Installer/App.axaml
+++ b/MelonLoader.Installer/App.axaml
@@ -18,5 +18,8 @@
+
\ No newline at end of file
diff --git a/MelonLoader.Installer/MLManager.cs b/MelonLoader.Installer/MLManager.cs
index d8c5499..2788b32 100644
--- a/MelonLoader.Installer/MLManager.cs
+++ b/MelonLoader.Installer/MLManager.cs
@@ -168,12 +168,11 @@ private static async Task GetVersionsAsync(List versions)
return true;
}
- public static bool Uninstall(string gameDir, bool removeUserFiles, [NotNullWhen(false)] out string? errorMessage)
+ public static string? Uninstall(string gameDir, bool removeUserFiles)
{
if (!Directory.Exists(gameDir))
{
- errorMessage = "The provided directory does not exist.";
- return false;
+ return "The provided directory does not exist.";
}
foreach (var proxy in proxyNames)
@@ -194,8 +193,7 @@ public static bool Uninstall(string gameDir, bool removeUserFiles, [NotNullWhen(
}
catch
{
- errorMessage = "Failed to uninstall MelonLoader. Ensure that the game is fully closed before trying again.";
- return false;
+ return "Failed to uninstall MelonLoader. Ensure that the game is fully closed before trying again.";
}
}
@@ -208,8 +206,7 @@ public static bool Uninstall(string gameDir, bool removeUserFiles, [NotNullWhen(
}
catch
{
- errorMessage = "Failed to uninstall MelonLoader. Ensure that the game is fully closed before trying again.";
- return false;
+ return "Failed to uninstall MelonLoader. Ensure that the game is fully closed before trying again.";
}
}
@@ -222,8 +219,7 @@ public static bool Uninstall(string gameDir, bool removeUserFiles, [NotNullWhen(
}
catch
{
- errorMessage = $"Failed to fully uninstall MelonLoader: Failed to remove dobby.";
- return false;
+ return "Failed to fully uninstall MelonLoader: Failed to remove dobby.";
}
}
@@ -236,8 +232,7 @@ public static bool Uninstall(string gameDir, bool removeUserFiles, [NotNullWhen(
}
catch
{
- errorMessage = $"Failed to fully uninstall MelonLoader: Failed to remove 'NOTICE.txt'.";
- return false;
+ return "Failed to fully uninstall MelonLoader: Failed to remove 'NOTICE.txt'.";
}
}
@@ -252,8 +247,7 @@ public static bool Uninstall(string gameDir, bool removeUserFiles, [NotNullWhen(
}
catch
{
- errorMessage = $"Failed to fully uninstall MelonLoader: Failed to remove the Mods folder.";
- return false;
+ return "Failed to fully uninstall MelonLoader: Failed to remove the Mods folder.";
}
}
@@ -266,8 +260,7 @@ public static bool Uninstall(string gameDir, bool removeUserFiles, [NotNullWhen(
}
catch
{
- errorMessage = $"Failed to fully uninstall MelonLoader: Failed to remove the Plugins folder.";
- return false;
+ return "Failed to fully uninstall MelonLoader: Failed to remove the Plugins folder.";
}
}
@@ -280,8 +273,7 @@ public static bool Uninstall(string gameDir, bool removeUserFiles, [NotNullWhen(
}
catch
{
- errorMessage = $"Failed to fully uninstall MelonLoader: Failed to remove the UserData folder.";
- return false;
+ return "Failed to fully uninstall MelonLoader: Failed to remove the UserData folder.";
}
}
@@ -294,14 +286,12 @@ public static bool Uninstall(string gameDir, bool removeUserFiles, [NotNullWhen(
}
catch
{
- errorMessage = $"Failed to fully uninstall MelonLoader: Failed to remove the UserLibs folder.";
- return false;
+ return "Failed to fully uninstall MelonLoader: Failed to remove the UserLibs folder.";
}
}
}
- errorMessage = null;
- return true;
+ return null;
}
public static void SetLocalZip(string zipPath, InstallProgressEventHandler? onProgress, InstallFinishedEventHandler? onFinished)
@@ -372,9 +362,10 @@ public static async Task InstallAsync(string gameDir, bool removeUserFiles, MLVe
onProgress?.Invoke(0, "Uninstalling previous versions");
- if (!Uninstall(gameDir, removeUserFiles, out var error))
+ var unErr = Uninstall(gameDir, removeUserFiles);
+ if (unErr != null)
{
- onFinished?.Invoke(error);
+ onFinished?.Invoke(unErr);
return;
}
diff --git a/MelonLoader.Installer/Program.cs b/MelonLoader.Installer/Program.cs
index cdd2c98..6990b2f 100644
--- a/MelonLoader.Installer/Program.cs
+++ b/MelonLoader.Installer/Program.cs
@@ -21,8 +21,6 @@ internal static class Program
[STAThread]
private static void Main(string[] args)
{
- SetupCrashLogging();
-
if (!Directory.Exists(Config.CacheDir))
Directory.CreateDirectory(Config.CacheDir);
@@ -53,7 +51,14 @@ private static void Main(string[] args)
Updater.UpdateIfPossible();
- BuildAvaloniaApp().StartWithClassicDesktopLifetime(args);
+ try
+ {
+ BuildAvaloniaApp().StartWithClassicDesktopLifetime(args);
+ }
+ catch (Exception ex)
+ {
+ LogCrashException(ex);
+ }
Exiting?.Invoke();
@@ -61,20 +66,6 @@ private static void Main(string[] args)
File.Delete(processLockPath);
}
- private static void SetupCrashLogging()
- {
- AppDomain.CurrentDomain.UnhandledException += OnUnhandledException;
- }
-
- private static void OnUnhandledException(object sender, UnhandledExceptionEventArgs e)
- {
- if (!e.IsTerminating)
- return;
-
- if (e.ExceptionObject is Exception ex and not TaskCanceledException)
- LogCrashException(ex);
- }
-
public static void LogCrashException(Exception ex)
{
try
diff --git a/MelonLoader.Installer/ViewModels/DetailsViewModel.cs b/MelonLoader.Installer/ViewModels/DetailsViewModel.cs
index 0520f4b..33a5a13 100644
--- a/MelonLoader.Installer/ViewModels/DetailsViewModel.cs
+++ b/MelonLoader.Installer/ViewModels/DetailsViewModel.cs
@@ -4,6 +4,7 @@ public class DetailsViewModel(GameModel game) : ViewModelBase
{
private bool _installing;
private bool _offline;
+ private bool _linuxInstructions;
public GameModel Game => game;
@@ -29,5 +30,15 @@ public bool Offline
}
}
+ public bool LinuxInstructions
+ {
+ get => _linuxInstructions;
+ set
+ {
+ _linuxInstructions = value;
+ OnPropertyChanged();
+ }
+ }
+
public bool EnableSettings => !Offline && !Installing;
}
diff --git a/MelonLoader.Installer/ViewModels/GameModel.cs b/MelonLoader.Installer/ViewModels/GameModel.cs
index 7726b02..1bfd0b3 100644
--- a/MelonLoader.Installer/ViewModels/GameModel.cs
+++ b/MelonLoader.Installer/ViewModels/GameModel.cs
@@ -16,6 +16,7 @@ public class GameModel(string path, string name, bool is32Bit, bool isLinux, Gam
public string MLStatusText => mlVersion == null ? "Not Installed" : "Installed " + MLVersionText;
public bool MLInstalled => mlVersion != null;
public bool IsProtected => isProtected;
+ public string Dir { get; } = System.IO.Path.GetDirectoryName(path)!;
public SemVersion? MLVersion
{
@@ -46,7 +47,7 @@ public bool ValidateGame()
return false;
}
- var newMlVersion = Installer.MLVersion.GetMelonLoaderVersion(System.IO.Path.GetDirectoryName(path)!, out var ml86, out var mlLinux);
+ var newMlVersion = Installer.MLVersion.GetMelonLoaderVersion(Dir, out var ml86, out var mlLinux);
if (newMlVersion != null && (ml86 != Is32Bit || mlLinux != IsLinux))
newMlVersion = null;
diff --git a/MelonLoader.Installer/Views/DetailsView.axaml b/MelonLoader.Installer/Views/DetailsView.axaml
index 1d799e3..9ca50bb 100644
--- a/MelonLoader.Installer/Views/DetailsView.axaml
+++ b/MelonLoader.Installer/Views/DetailsView.axaml
@@ -6,17 +6,19 @@
mc:Ignorable="d" d:DesignWidth="450" d:DesignHeight="650"
x:Class="MelonLoader.Installer.Views.DetailsView"
x:DataType="vm:DetailsViewModel">
-
+
+ FontSize="20" Background="Transparent" Content="←" FontWeight="Bold" Foreground="Gray"
+ HorizontalContentAlignment="Center" />
+
+
-
@@ -56,5 +58,26 @@
VerticalAlignment="Center" Opacity="0.7" FontSize="14" />
+
+
+ How do I start MelonLoader?
+
+
+
+ Linux Launch Instructions
+
+ In order to start MelonLoader under Wine, you'll need to export the following variable:
+ WINEDLLOVERRIDES="version=n,b"
+ On Steam, you can set the launch options to:
+ WINEDLLOVERRIDES="version=n,b" %command%"
+
+
+ In order to start MelonLoader, you'll need to export the following variables:
+
+ LD_PRELOAD="libversion.so"
+ On Steam, you can set the launch options to:
+
+
+
\ No newline at end of file
diff --git a/MelonLoader.Installer/Views/DetailsView.axaml.cs b/MelonLoader.Installer/Views/DetailsView.axaml.cs
index 52cd525..2112d3a 100644
--- a/MelonLoader.Installer/Views/DetailsView.axaml.cs
+++ b/MelonLoader.Installer/Views/DetailsView.axaml.cs
@@ -41,6 +41,16 @@ protected override void OnDataContextChanged(EventArgs e)
if (Model == null)
return;
+
+#if LINUX
+ if (Model.Game.IsLinux)
+ {
+ LdLibPathVar.Text = $"LD_LIBRARY_PATH=\"{Model.Game.Dir}:$LD_LIBRARY_PATH\"";
+ SteamLaunchOptions.Text = $"{LdLibPathVar.Text} {LdPreloadVar.Text} %command%";
+ }
+
+ ShowLinuxInstructions.IsVisible = Model.Game.MLInstalled;
+#endif
Model.Game.PropertyChanged += PropertyChangedHandler;
@@ -73,7 +83,16 @@ public void UpdateVersionList()
private void BackClickHandler(object sender, RoutedEventArgs args)
{
- if (Model is { Installing: true })
+ if (Model == null)
+ return;
+
+ if (Model.LinuxInstructions)
+ {
+ Model.LinuxInstructions = false;
+ return;
+ }
+
+ if (Model.Installing)
return;
MainWindow.Instance.ShowMainView();
@@ -116,6 +135,7 @@ private void InstallHandler(object sender, RoutedEventArgs args)
}
Model.Installing = true;
+ ShowLinuxInstructions.IsVisible = false;
_ = MLManager.InstallAsync(Path.GetDirectoryName(Model.Game.Path)!, Model.Game.MLInstalled && !KeepFilesCheck.IsChecked!.Value,
(MLVersion)VersionCombobox.SelectedItem!, Model.Game.IsLinux, Model.Game.Is32Bit,
@@ -139,10 +159,12 @@ private void OnInstallFinished(string? errorMessage)
var wasReinstall = Model.Game.MLInstalled;
Model.Game.ValidateGame();
+
+#if LINUX
+ ShowLinuxInstructions.IsVisible = Model.Game.MLInstalled;
+#endif
Model.Installing = false;
- NightlyCheck.IsEnabled = true;
- VersionCombobox.IsEnabled = true;
if (errorMessage != null)
{
@@ -150,7 +172,7 @@ private void OnInstallFinished(string? errorMessage)
return;
}
- DialogBox.ShowNotice("Success!", $"{(wasReinstall ? "Reinstall" : "Install")} was Successful!");
+ DialogBox.ShowNotice("Success!", $"Successfully {(Model.Game.MLInstalled ? (wasReinstall ? "reinstalled" : "installed") : "uninstalled")} MelonLoader!");
}
private void OpenDirHandler(object sender, RoutedEventArgs args)
@@ -158,7 +180,7 @@ private void OpenDirHandler(object sender, RoutedEventArgs args)
if (Model == null)
return;
- TopLevel.GetTopLevel(this)!.Launcher.LaunchDirectoryInfoAsync(new(Path.GetDirectoryName(Model.Game.Path)!));
+ TopLevel.GetTopLevel(this)!.Launcher.LaunchDirectoryInfoAsync(new(Model.Game.Dir));
}
private void UninstallHandler(object sender, RoutedEventArgs args)
@@ -172,15 +194,9 @@ private void UninstallHandler(object sender, RoutedEventArgs args)
if (!Model.Game.MLInstalled)
return;
- if (!MLManager.Uninstall(Path.GetDirectoryName(Model.Game.Path)!, !KeepFilesCheck.IsChecked!.Value, out var error))
- {
- DialogBox.ShowError(error);
- Model.Game.ValidateGame();
- return;
- }
-
- Model.Game.ValidateGame();
- DialogBox.ShowNotice("Success!", "Uninstall was Successful!");
+ var error = MLManager.Uninstall(Model.Game.Dir, !KeepFilesCheck.IsChecked!.Value);
+
+ OnInstallFinished(error);
}
private async void SelectZipHandler(object sender, TappedEventArgs args)
@@ -208,6 +224,7 @@ private async void SelectZipHandler(object sender, TappedEventArgs args)
var path = files[0].Path.LocalPath;
Model.Installing = true;
+ ShowLinuxInstructions.IsVisible = false;
_ = Task.Run(() => MLManager.SetLocalZip(path,
(progress, newStatus) => Dispatcher.UIThread.Post(() => OnInstallProgress(progress, newStatus)),
@@ -226,4 +243,12 @@ private async void SelectZipHandler(object sender, TappedEventArgs args)
UpdateVersionList();
})));
}
+
+ private void ShowLinuxInstructionsHandler(object sender, TappedEventArgs args)
+ {
+ if (Model == null)
+ return;
+
+ Model.LinuxInstructions = true;
+ }
}
\ No newline at end of file
diff --git a/MelonLoader.Installer/Views/MainView.axaml.cs b/MelonLoader.Installer/Views/MainView.axaml.cs
index c33029e..bcd30af 100644
--- a/MelonLoader.Installer/Views/MainView.axaml.cs
+++ b/MelonLoader.Installer/Views/MainView.axaml.cs
@@ -22,6 +22,8 @@ protected override void OnDataContextChanged(EventArgs e)
if (Model == null)
return;
+ Model.Ready = false;
+
Task.Run(InitServicesAsync);
}