浏览代码

error handling refactor

Veloe 2 年之前
父节点
当前提交
91df70f697

+ 11 - 68
VeloeMinecraftLauncher/MinecraftLauncher/Downloader.cs

@@ -540,39 +540,7 @@ internal static class Downloader
         {
             IsControlsEnabled(true);
             DownloadingFileName($"Error occured while downloading {SelectedModpack.Name}.");        
-            var message = ex.Message;
-            var stackTrace = ex.StackTrace;
-            Settings.logger.Error(ex.Message);
-            if (ex.StackTrace is not null)
-                Settings.logger.Error(ex.StackTrace);
-            Exception? innerException = ex.InnerException;
-            while (innerException is not null)
-            {
-                message += "\n" + innerException.Message;
-                stackTrace += "\n" + innerException.StackTrace;
-                Settings.logger.Error(innerException.Message);
-                if (innerException.StackTrace is not null)
-                    Settings.logger.Error(innerException.StackTrace);
-                innerException = innerException.InnerException;
-            }
-
-#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
-            Dispatcher.UIThread.InvokeAsync(() =>
-            {
-            if (Avalonia.Application.Current is not null && Avalonia.Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
-                {
-                    Dispatcher.UIThread.InvokeAsync(() =>
-                    {
-                        var ErrorMessageViewModel = new ErrorWindowViewModel(message, stackTrace);
-                        var ErrorMessage = new ErrorWindow()
-                        {
-                            DataContext = ErrorMessageViewModel
-                        };
-                        ErrorMessage.ShowDialog(desktop.MainWindow);
-                    });
-                }
-            });
-#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
+            ViewModelBase.OpenErrorWindow(ex);
             return TaskStatus.Faulted;
         }
         return TaskStatus.RanToCompletion;
@@ -585,10 +553,16 @@ internal static class Downloader
             var jsonData = string.Empty;
             try
             {
-                Settings.logger.Debug($"Downloading: {url.Split('/').Last()}");
-                jsonData = webClient.GetStringAsync(url).Result;
+                try
+                {
+                    Settings.logger.Debug($"Downloading: {url.Split('/').Last()}");
+                    jsonData = webClient.GetStringAsync(url).Result;
+                }
+                catch (Exception ex)
+                {
+                    throw new WebException($"An error occured while downloading {url}. Check your internet connection.",ex);
+                }
             
-
                 if (string.IsNullOrEmpty(jsonData))
                 {
                     Settings.logger.Warning("Empty string!");
@@ -615,38 +589,7 @@ internal static class Downloader
             }
             catch (Exception ex)
             {
-                var message = ex.Message;
-                var stackTrace = ex.StackTrace;
-                Settings.logger.Error(ex.Message);
-                if (ex.StackTrace is not null)
-                    Settings.logger.Error(ex.StackTrace);
-                Exception? innerException = ex.InnerException;
-                while (innerException is not null)
-                {
-                    message += "\n" + innerException.Message;
-                    stackTrace += "\n" + innerException.StackTrace;
-                    Settings.logger.Error(innerException.Message);
-                    if (innerException.StackTrace is not null)
-                        Settings.logger.Error(innerException.StackTrace);
-                    innerException = innerException.InnerException;
-                }
-
-                Dispatcher.UIThread.InvokeAsync(() =>
-                {
-                    if (Avalonia.Application.Current is not null && Avalonia.Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
-                    {
-                        Dispatcher.UIThread.InvokeAsync(() =>
-                        {
-                            var ErrorMessageViewModel = new ErrorWindowViewModel(message, stackTrace);
-                            var ErrorMessage = new ErrorWindow()
-                            {
-                                DataContext = ErrorMessageViewModel
-                            };
-                            ErrorMessage.ShowDialog(desktop.MainWindow);
-                        });
-                    }
-                });
-                return new T();
+                throw;
             }
         }
     }

+ 17 - 0
VeloeMinecraftLauncher/MinecraftLauncher/JavaProcessException.cs

@@ -0,0 +1,17 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace VeloeMinecraftLauncher.MinecraftLauncher
+{
+    internal class JavaProcessException : Exception
+    {
+        public JavaProcessException(string message, string logpath) : base(message)
+        {
+            LogPath = logpath;
+        }
+        public string LogPath { get; set; }
+    }
+}

+ 1 - 5
VeloeMinecraftLauncher/Program.cs

@@ -31,11 +31,7 @@ namespace VeloeMinecraftLauncher
             Trace.Listeners.Add(listener);
             
             return AppBuilder.Configure<App>()
-                  .UsePlatformDetect()
-                  .With(new Win32PlatformOptions
-                  {
-                      UseWindowsUIComposition = true
-                  })
+                  .UsePlatformDetect()                 
                   .LogToTrace()
                   .UseReactiveUI();
         }

+ 85 - 3
VeloeMinecraftLauncher/ViewModels/ErrorWindowViewModel.cs

@@ -1,17 +1,55 @@
-using ReactiveUI;
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Controls.ApplicationLifetimes;
+using Avalonia.Layout;
+using ReactiveUI;
+using System.IO;
+using System.Security.Principal;
 
 namespace VeloeMinecraftLauncher.ViewModels;
 
 public class ErrorWindowViewModel : ViewModelBase
-{
-    public ErrorWindowViewModel(string errorMessage, string errorTrace)
+{ 
+    public ErrorWindowViewModel()
+    {
+        ErrorMessage = "Error";
+        ErrorTrace = "ErrorTrace\n1\n2\n3\n4\n5";
+        IsErrorTraceVisible = false;
+        ButtonText = "Open log";
+        CenterMessage = true;
+        _logfile = "";
+    }
+
+    public ErrorWindowViewModel(string errorMessage, string errorTrace = "", bool centerMessage = false, string logfile = "")
     {
         ErrorMessage = errorMessage;
         ErrorTrace = errorTrace;
+        if (string.IsNullOrEmpty(errorTrace))
+        {
+            IsErrorTraceVisible = false;
+            CenterMessage = true;
+        }
+        else
+        {
+            IsErrorTraceVisible = true;
+            CenterMessage = false;
+        }
+        //IsErrorTraceVisible = isErrorTraceVisible;
+        //CenterMessage = centerMessage;
+        if (!string.IsNullOrEmpty(logfile))
+        {
+            _logfile = logfile;
+            ButtonText = "Open folder";
+            this.RaisePropertyChanged(nameof(IsLogButtonVisible));
+        }
+
     }
 
     private string _errorMessage;
     private string _errorTrace;
+    private bool _isErrorTraceVisible;
+    private bool _centerMessage;
+    private string _logfile;
 
     public string ErrorTrace
     {
@@ -24,4 +62,48 @@ public class ErrorWindowViewModel : ViewModelBase
         get => _errorMessage;
         set => this.RaiseAndSetIfChanged(ref _errorMessage, value);
     }
+
+    public bool CenterMessage
+    {
+        get => _centerMessage;
+        set => this.RaiseAndSetIfChanged(ref _centerMessage, value);
+    }
+
+    public bool IsErrorTraceVisible
+    {
+        get => _isErrorTraceVisible;
+        set => this.RaiseAndSetIfChanged(ref _isErrorTraceVisible, value);
+    }
+
+    public HorizontalAlignment HorizontalAlignment
+    {
+        get => CenterMessage ? HorizontalAlignment.Center : HorizontalAlignment.Stretch;
+    }
+
+    public VerticalAlignment VerticalAlignment
+    {
+        get => CenterMessage ? VerticalAlignment.Center : VerticalAlignment.Stretch;
+    }
+
+    public string ButtonText { get; set; } = string.Empty;
+
+    public bool IsLogButtonVisible
+    {
+        get => !string.IsNullOrEmpty(_logfile);
+    }
+
+    public void OpenLogFile()
+    {
+        if (Path.Exists(_logfile))
+        {
+            System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo() { FileName = _logfile, UseShellExecute = true });
+        }
+    }
+
+    public void CloseWindow(Window window)
+    {
+        window?.Close();
+    }
+
+
 }

+ 17 - 36
VeloeMinecraftLauncher/ViewModels/MainWindowViewModel.cs

@@ -74,7 +74,7 @@ public class MainWindowViewModel : ViewModelBase
                 //check launcher update
                 _logger.Information("Checking launcher versions updates.");
                 _latestLauncherInfo = Downloader.DownloadAndDeserializeJsonData<LatestLauncherVersion>("https://files.veloe.link/launcher/update/versions.json");
-                if (_latestLauncherInfo is not null)
+                if (_latestLauncherInfo is not null && _latestLauncherInfo.Latest is not null)
                 {
                     _logger.Information("Launcher version on server: {0}", _latestLauncherInfo.Latest);
                     _logger.Information("Launcher version: {0}", Assembly.GetExecutingAssembly().GetName().Version);
@@ -84,8 +84,9 @@ public class MainWindowViewModel : ViewModelBase
                         _logger.Debug("Update available!");
                         IsUpdateAvailable = true;
                     }
-
                 }
+                else
+                    _logger.Warning("Can't get latest verion info! Skipping...");
             }
             catch (Exception ex)
             {
@@ -204,6 +205,7 @@ public class MainWindowViewModel : ViewModelBase
 
     LatestLauncherVersion _latestLauncherInfo;
     DownloadedVersion _downloadedVersion;
+    DownloadedVersion _startedVersion;
 
     ObservableCollection<DownloadedVersion> _downloadedVersions;
 
@@ -350,7 +352,6 @@ public class MainWindowViewModel : ViewModelBase
                     return;
                 }
 
-
                 int version = 0;
 
                 using (StreamReader reader = new StreamReader(DownloadedVersion.path))
@@ -466,6 +467,7 @@ public class MainWindowViewModel : ViewModelBase
                         minecraft.EnableRaisingEvents = true;
 
                         IsNoGameRunning = false;
+                        _startedVersion = DownloadedVersion;
                         minecraft.Start();
 
                         minecraft.Exited += ProcessExited;
@@ -546,13 +548,22 @@ public class MainWindowViewModel : ViewModelBase
     void ProcessExited(object? sendingProcess, EventArgs e)
     {
         ((Process)sendingProcess).Exited -= ProcessExited;
+        ((Process)sendingProcess).OutputDataReceived -= OutputHandler;
+        ((Process)sendingProcess).ErrorDataReceived -= OutputHandler;
+        ((Process)sendingProcess).CancelOutputRead();
+        ((Process)sendingProcess).CancelErrorRead();
 
         _logger.Debug("Exit code: {0}", ((Process)sendingProcess).ExitCode);
-
+       
         StartButtonOutput = "";
         IsNoGameRunning = true;
-        if (((Process)sendingProcess).ExitCode != 0)
-            OpenErrorWindow(new Exception("Minecraft process exited with an error.\nCheck log in game folder or in launcher console."));
+        if (((Process)sendingProcess).ExitCode is not (0 or 2))
+            OpenErrorWindow(new JavaProcessException($"Minecraft process exited with an error code {((Process)sendingProcess).ExitCode}.\nCheck log in game folder or launcher console.", $"{Settings.minecraftForlderPath}versions/{_startedVersion.version}/crash-reports"));
+        else if (((Process)sendingProcess).ExitCode is 2)
+        {
+            OpenErrorWindow(new Exception("JVM exited on the startup (Exit code 2). Check your Java installation. Get more info in the laucher console."));
+        }
+            
 
         ((Process)sendingProcess).Dispose();         
     }
@@ -805,34 +816,4 @@ public class MainWindowViewModel : ViewModelBase
 
         return panel;
     }
-
-    private void OpenErrorWindow(Exception ex)
-    {
-        var message = ex.Message;
-        var stackTrace = ex.StackTrace;
-        Settings.logger.Error(ex.Message);
-        Settings.logger.Error(ex.StackTrace);
-        Exception innerException = ex.InnerException;
-        while (innerException is not null)
-        {
-            message += "\n" + innerException.Message;
-            stackTrace += "\n" + innerException.StackTrace;
-            Settings.logger.Error(innerException.Message);
-            Settings.logger.Error(innerException.StackTrace);
-            innerException = innerException.InnerException;
-        }
-
-        Dispatcher.UIThread.InvokeAsync(() =>
-        {
-            if (Avalonia.Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
-            {
-                var ErrorMessageViewModel = new ErrorWindowViewModel(message, stackTrace);
-                var ErrorMessage = new ErrorWindow()
-                {
-                    DataContext = ErrorMessageViewModel
-                };
-                ErrorMessage.ShowDialog(desktop.MainWindow);
-            }
-        });
-    }
 }

+ 3 - 75
VeloeMinecraftLauncher/ViewModels/VersionsDownloaderViewModel.cs

@@ -66,32 +66,7 @@ public class VersionsDownloaderViewModel : ViewModelBase
         }
         catch (Exception ex)
         {
-            var message = ex.Message;
-            var stackTrace = ex.StackTrace;
-            Settings.logger.Error(ex.Message);
-            Settings.logger.Error(ex.StackTrace);
-            Exception innerException = ex.InnerException;
-            while (innerException is not null)
-            {
-                message += "\n" + innerException.Message;
-                stackTrace += "\n" + innerException.StackTrace;
-                Settings.logger.Error(innerException.Message);
-                Settings.logger.Error(innerException.StackTrace);
-                innerException = innerException.InnerException;
-            }
-
-
-            if (Avalonia.Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
-            {
-                var ErrorMessageViewModel = new ErrorWindowViewModel(message, stackTrace);
-                var ErrorMessage = new ErrorWindow()
-                {
-                    DataContext = ErrorMessageViewModel
-                };
-                //var versionsDownloaderTask =
-                ErrorMessage.ShowDialog(desktop.MainWindow);
-                //versionsDownloaderTask.Wait();              
-            }
+            OpenErrorWindow(ex);
         }
     }
 
@@ -144,31 +119,7 @@ public class VersionsDownloaderViewModel : ViewModelBase
             }
             catch (Exception ex)
             {
-                var message = ex.Message;
-                var stackTrace = ex.StackTrace;
-                _logger.Error(ex.Message);
-                _logger.Error(ex.StackTrace);
-                Exception innerException = ex.InnerException;
-                while (innerException is not null)
-                {
-                    message += "\n" + innerException.Message;
-                    stackTrace += "\n" + innerException.StackTrace;
-                    _logger.Error(innerException.Message);
-                    _logger.Error(innerException.StackTrace);
-                    innerException = innerException.InnerException;
-                }
-
-                Dispatcher.UIThread.InvokeAsync(async () => { 
-                if (Avalonia.Application.Current is not null && Avalonia.Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
-                {
-                    var ErrorMessageViewModel = new ErrorWindowViewModel(message, stackTrace);
-                    var ErrorMessage = new ErrorWindow()
-                    {
-                        DataContext = ErrorMessageViewModel
-                    };
-                    ErrorMessage.ShowDialog(desktop.MainWindow);
-                }
-                });
+                OpenErrorWindow(ex);
             }
         }
     }
@@ -369,30 +320,7 @@ public class VersionsDownloaderViewModel : ViewModelBase
         }
         catch (Exception ex)
         {
-            var message = ex.Message;
-            var stackTrace = ex.StackTrace;
-            _logger.Error(ex.Message);
-            _logger.Error(ex.StackTrace);
-            Exception innerException = ex.InnerException;
-            while (innerException is not null)
-            {
-                message += "\n" + innerException.Message;
-                stackTrace += "\n" + innerException.StackTrace;
-                _logger.Error(innerException.Message);
-                _logger.Error(innerException.StackTrace);
-                innerException = innerException.InnerException;
-            }
-
-
-            if (Avalonia.Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
-            {
-                var ErrorMessageViewModel = new ErrorWindowViewModel(message, stackTrace);
-                var ErrorMessage = new ErrorWindow()
-                {
-                    DataContext = ErrorMessageViewModel
-                };
-                ErrorMessage.ShowDialog(desktop.MainWindow);            
-            }
+            OpenErrorWindow(ex);
         }
     }
 }

+ 75 - 0
VeloeMinecraftLauncher/ViewModels/ViewModelBase.cs

@@ -1,8 +1,15 @@
 using Avalonia;
+using Avalonia.Controls.ApplicationLifetimes;
 using Avalonia.Media;
+using Avalonia.Threading;
 using ReactiveUI;
 using ReactiveUI.Validation.Helpers;
+using System;
+using System.ComponentModel;
+using System.IO;
+using System.Net;
 using VeloeMinecraftLauncher.MinecraftLauncher;
+using VeloeMinecraftLauncher.Views;
 
 namespace VeloeMinecraftLauncher.ViewModels;
 
@@ -25,5 +32,73 @@ public class ViewModelBase : ReactiveValidationObject
         get => 0.5F;
         set => this.RaisePropertyChanged(nameof(MaterialOpacity));
     }
+
+    public static void OpenErrorWindow(Exception ex)
+    {
+        string message = "";
+        string stackTrace = "";
+        string logfile = "";
+        Exception? innerException;
+        switch (ex)
+        {
+            case JavaProcessException:
+            case WebException or Win32Exception:
+                message = ex.Message;
+                Settings.logger.Error(ex.Message);
+
+                if (ex is JavaProcessException)
+                {
+                    if (Path.Exists(((JavaProcessException)ex).LogPath))
+                        logfile = ((JavaProcessException)ex).LogPath;                   
+                }
+
+                if (ex.StackTrace is not null)
+                    Settings.logger.Error(ex.StackTrace);
+                innerException = ex.InnerException;
+                while (innerException is not null)
+                {
+                    Settings.logger.Error(innerException.Message);
+                    if (innerException.StackTrace is not null)
+                        Settings.logger.Error(innerException.StackTrace);
+                    innerException = innerException.InnerException;
+                }
+                break;
+            default:
+                message = ex.Message;
+                stackTrace = ex.StackTrace ?? "";
+                Settings.logger.Error(ex.Message);
+                if (ex.StackTrace is not null)
+                    Settings.logger.Error(ex.StackTrace);
+                innerException = ex.InnerException;
+                while (innerException is not null)
+                {
+                    message += "\n" + innerException.Message;
+                    stackTrace += "\n" + innerException.StackTrace;
+                    Settings.logger.Error(innerException.Message);
+                    if (innerException.StackTrace is not null)
+                        Settings.logger.Error(innerException.StackTrace);
+                    innerException = innerException.InnerException;
+                }
+                break;
+        }       
+
+#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
+        Dispatcher.UIThread.InvokeAsync(() =>
+        {
+        if (Application.Current is not null && Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
+            {
+                Dispatcher.UIThread.InvokeAsync(() =>
+                {
+                    var ErrorMessageViewModel = new ErrorWindowViewModel(message, stackTrace, logfile:logfile);
+                    var ErrorMessage = new ErrorWindow()
+                    {
+                        DataContext = ErrorMessageViewModel
+                    };
+                    ErrorMessage.ShowDialog(desktop.MainWindow);
+                });
+            }
+        });
+#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
+    }
 }
 

+ 51 - 17
VeloeMinecraftLauncher/Views/ErrorWindow.axaml

@@ -3,12 +3,13 @@
 		xmlns:vm="using:VeloeMinecraftLauncher.ViewModels"
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
-        mc:Ignorable="d" d:DesignWidth="400" d:DesignHeight="200"
-		Width = "400" Height ="200"
+        mc:Ignorable="d" d:DesignWidth="400" d:DesignHeight="150"
+		Width = "400" MinHeight="150"
+		SizeToContent="Height"
         x:Class="VeloeMinecraftLauncher.Views.ErrorWindow"
 		xmlns:titlebars="using:VeloeMinecraftLauncher.Views.TitleBar"
         Icon="/Assets/avalonia-logo.ico"
-        Title="ErrorWindow"
+        Title="Error"
 		TransparencyLevelHint="AcrylicBlur"
 		Background="Transparent"
 		CanResize="True"
@@ -38,21 +39,54 @@
 			  TitleText="Error Message"
 			  DockPanel.Dock="Top">
 			</titlebars:TitleBarWindow>
-			<DockPanel 
-				Margin="10">
-			<TextBlock 
-				Text="{Binding ErrorMessage}" 
-				DockPanel.Dock="Top">
-			</TextBlock>
-			<TextBox 
-				Text="{Binding ErrorTrace}" 
-				IsReadOnly="True" 
-				Margin="0 10 0  0" 
+			<Grid
 				DockPanel.Dock="Bottom"
-				HorizontalAlignment="Stretch" 
-				VerticalAlignment="Stretch">
-			</TextBox>
-			</DockPanel>
+				Margin="10">
+				<Grid.RowDefinitions>
+					<RowDefinition Height="Auto"/>
+					<RowDefinition Height="*"/>
+					<RowDefinition Height="35"/>
+				</Grid.RowDefinitions>
+
+					<TextBlock
+						Grid.Row="0"
+						Text="{Binding ErrorMessage}" 
+						TextWrapping="Wrap"
+						HorizontalAlignment="{Binding HorizontalAlignment}"
+						VerticalAlignment="{Binding	VerticalAlignment}"
+					>
+					</TextBlock>		
+					<TextBox 
+						Grid.Row="1"
+						Text="{Binding ErrorTrace}" 
+						IsVisible="{Binding IsErrorTraceVisible}"					
+						IsReadOnly="True" 
+						Margin="0 10 0  10"
+						HorizontalAlignment="Stretch" 
+						VerticalAlignment="Stretch">
+					</TextBox>
+
+				<StackPanel
+					Grid.Row="2"
+					HorizontalAlignment="Right"
+					Orientation="Horizontal">
+					<Button
+						Content="{Binding ButtonText}"
+						IsVisible="{Binding	IsLogButtonVisible}"
+						Command="{Binding OpenLogFile}"
+					>				
+					</Button>
+					<Button
+						Content="Ok"
+						Width="40"
+						Margin="10 0 0 0"
+						Command="{Binding CloseWindow}" 
+						CommandParameter="{Binding $parent[Window]}"
+					>				
+					</Button>
+				</StackPanel>	
+			</Grid>
+										
 		</DockPanel>
 	</Panel>
 </Window>