diff --git a/Wauncher/App.axaml.cs b/Wauncher/App.axaml.cs index c68f90b..336687e 100644 --- a/Wauncher/App.axaml.cs +++ b/Wauncher/App.axaml.cs @@ -70,6 +70,7 @@ public override async void OnFrameworkInitializationCompleted() } catch (Exception ex) { + ErrorLogger.LogError("App.OnFrameworkInitializationCompleted", ex, "Application startup validation failed"); ConsoleManager.ShowError($"Startup error: {ex.Message}"); desktop.Shutdown(); return; @@ -104,8 +105,9 @@ private static bool IsSteamRunning() { return Process.GetProcessesByName("steam").Length > 0; } - catch + catch (Exception ex) { + ErrorLogger.LogError("App.IsSteamRunning", ex, "Failed to check if Steam is running"); return false; } } @@ -150,6 +152,7 @@ private void SetupTrayIcon() } catch (Exception ex) { + ErrorLogger.LogError("App.SetupTrayIcon", ex, "Failed to load tray icon"); // Tray icon is optional, log but don't fail System.Diagnostics.Debug.WriteLine($"Failed to load tray icon: {ex.Message}"); } diff --git a/Wauncher/Program.cs b/Wauncher/Program.cs index 78bd1e2..40819aa 100644 --- a/Wauncher/Program.cs +++ b/Wauncher/Program.cs @@ -34,15 +34,7 @@ public static void Main(string[] args) } catch (Exception ex) { - try - { - var logPath = Path.Combine(Path.GetDirectoryName(System.Environment.ProcessPath) ?? ".", "wauncher_error.log"); - File.WriteAllText(logPath, $"{DateTime.Now:yyyy-MM-dd HH:mm:ss}\n{ex}"); - } - catch - { - } - + ErrorLogger.LogError("Program.Main", ex, "Application startup failed"); throw; } finally @@ -83,15 +75,7 @@ private static bool OnStartup(string[]? Args) } catch (Exception ex) { - try - { - var logPath = Path.Combine(Path.GetDirectoryName(System.Environment.ProcessPath) ?? ".", "wauncher_startup_error.log"); - File.WriteAllText(logPath, $"{DateTime.Now:yyyy-MM-dd HH:mm:ss}\nOnStartup Error:\n{ex}"); - } - catch - { - } - + ErrorLogger.LogError("Program.OnStartup", ex, "Startup validation failed"); return true; // Allow app to continue anyway } } @@ -136,8 +120,9 @@ private static bool IsHardwareAccelerationDisabled() return string.Equals(value, "true", StringComparison.OrdinalIgnoreCase); } } - catch + catch (Exception ex) { + ErrorLogger.LogError("Program.IsHardwareAccelerationDisabled", ex, "Failed to check hardware acceleration setting"); } return false; diff --git a/Wauncher/Services/CarouselService.cs b/Wauncher/Services/CarouselService.cs index 95d56d0..03afd93 100644 --- a/Wauncher/Services/CarouselService.cs +++ b/Wauncher/Services/CarouselService.cs @@ -66,9 +66,10 @@ public async Task SetupCarouselAsync() return urls.Count == 0 ? null : urls; } - catch - { - return null; + catch (Exception ex) + { + ErrorLogger.LogError("CarouselService.LoadCarouselUrlsFromGitHubAsync", ex, "Failed to load carousel URLs from GitHub"); + return null; } } @@ -95,8 +96,9 @@ private static int GetCarouselSortIndex(string name) return await File.ReadAllBytesAsync(path); } - catch + catch (Exception ex) { + ErrorLogger.LogError("CarouselService.TryGetCachedCarouselBytesAsync", ex, $"Failed to get cached carousel bytes for URL: {url}"); return null; } } @@ -111,8 +113,9 @@ private static async Task TryWriteCarouselCacheAsync(string url, byte[] bytes) await File.WriteAllBytesAsync(tempPath, bytes); File.Move(tempPath, path, overwrite: true); } - catch + catch (Exception ex) { + ErrorLogger.LogError("CarouselService.TryWriteCarouselCacheAsync", ex, $"Failed to write carousel cache for URL: {url}"); // Best-effort cache only. } } diff --git a/Wauncher/Services/DiscordService.cs b/Wauncher/Services/DiscordService.cs index 9b29b42..90b061e 100644 --- a/Wauncher/Services/DiscordService.cs +++ b/Wauncher/Services/DiscordService.cs @@ -9,7 +9,14 @@ public async Task InitializeAsync() { await Task.Run(() => { - Discord.Init(); + try + { + Discord.Init(); + } + catch + { + // Discord integration is optional. + } }); } @@ -17,7 +24,14 @@ public async Task SetDetailsAsync(string details) { await Task.Run(() => { - Discord.SetDetails(details); + try + { + Discord.SetDetails(details); + } + catch + { + // Discord integration is optional. + } }); } @@ -25,7 +39,14 @@ public async Task UpdateAsync() { await Task.Run(() => { - Discord.Update(); + try + { + Discord.Update(); + } + catch + { + // Discord integration is optional. + } }); } @@ -33,7 +54,14 @@ public async Task ShutdownAsync() { await Task.Run(() => { - Discord.Deinitialize(); + try + { + Discord.Deinitialize(); + } + catch + { + // Discord integration is optional. + } }); } } diff --git a/Wauncher/Services/FriendsService.cs b/Wauncher/Services/FriendsService.cs index d21ebf8..b0179a2 100644 --- a/Wauncher/Services/FriendsService.cs +++ b/Wauncher/Services/FriendsService.cs @@ -144,8 +144,9 @@ public async Task LoadSelfProfileAsync() CurrentUserUsername = self.Username; }); } - catch + catch (Exception ex) { + ErrorLogger.LogError("FriendsService.LoadSelfProfileAsync", ex, "Failed to load self profile"); // Best-effort profile load; keep defaults on failure. } } @@ -206,8 +207,9 @@ public async Task RefreshFriendsAsync() { rawFriendsJson = await Api.Eddies.GetFriends(Steam.recentSteamID64 ?? string.Empty); } - catch + catch (Exception ex) { + ErrorLogger.LogError("FriendsService.RefreshFriendsAsync.GetFriends", ex, "Failed to get friends via SteamID64, trying SteamID2 fallback"); rawFriendsJson = await Api.Eddies.GetFriendsBySteamId2(Steam.recentSteamID2 ?? string.Empty); } var apiFriends = Api.ParseFriendsPayload(rawFriendsJson) @@ -239,8 +241,9 @@ public async Task RefreshFriendsAsync() FriendsStatus = Friends.Count == 0 ? "No friends found." : ""; }); } - catch + catch (Exception ex) { + ErrorLogger.LogError("FriendsService.RefreshFriendsAsync", ex, "Failed to refresh friends"); if (TryShowCachedFriends(Steam.recentSteamID2 ?? string.Empty, forceOfflineStatus: true)) return; diff --git a/Wauncher/Services/GameService.cs b/Wauncher/Services/GameService.cs index 2e9606c..d29b4b7 100644 --- a/Wauncher/Services/GameService.cs +++ b/Wauncher/Services/GameService.cs @@ -30,12 +30,31 @@ public async Task LaunchAsync(string? connectTarget = null, string? launch QueueDeferredConnect(resolvedTarget); } - return await Task.Run(() => Game.Launch()); + return await Task.Run(() => + { + try + { + return Game.Launch(); + } + catch (Exception ex) + { + ErrorLogger.LogError("GameService.LaunchAsync", ex, $"Connect target: {connectTarget}, Launch options: {launchOptions}"); + throw; + } + }); } public async Task MonitorAsync() { - await Game.Monitor(); + try + { + await Game.Monitor(); + } + catch (Exception ex) + { + ErrorLogger.LogError("GameService.MonitorAsync", ex, "Game monitoring failed"); + throw; + } } public bool IsRunning() @@ -106,8 +125,9 @@ private static async Task ResolveConnectTarget(string ipPort) var address = addresses.FirstOrDefault(); return address == null ? ipPort.Trim() : $"{address}:{parts[1]}"; } - catch + catch (Exception ex) { + ErrorLogger.LogError("GameService.ResolveConnectTarget", ex, $"Failed to resolve connect target: {ipPort}"); return ipPort.Trim(); } } diff --git a/Wauncher/Services/UpdateService.cs b/Wauncher/Services/UpdateService.cs index 39fd3e9..99e053d 100644 --- a/Wauncher/Services/UpdateService.cs +++ b/Wauncher/Services/UpdateService.cs @@ -73,6 +73,11 @@ public async Task CheckForUpdatesAsync() return needsUpdate; } + catch (Exception ex) + { + ErrorLogger.LogError("UpdateService.CheckForUpdatesAsync", ex, "Failed to check for updates"); + return false; + } finally { IsCheckingUpdates = false; @@ -139,6 +144,7 @@ await DownloadManager.InstallFullGame( } catch (Exception ex) { + ErrorLogger.LogError("UpdateService.InstallGameFromCdnAsync", ex, "Failed to install game from CDN"); DownloadManager.Cleanup7zFiles(); UpdateStatusFile = $"Install error: {ex.Message}"; UpdateStatusSpeed = ""; @@ -228,6 +234,7 @@ await DownloadManager.DownloadPatch( } catch (Exception ex) { + ErrorLogger.LogError("UpdateService.ValidateGameFilesAsync", ex, "Failed to validate game files"); UpdateStatusFile = $"Error: {ex.Message}"; UpdateStatusSpeed = ""; return false; @@ -249,8 +256,9 @@ await DownloadManager.DownloadPatch( _cachedPatches = patches; return patches; } - catch + catch (Exception ex) { + ErrorLogger.LogError("UpdateService.GetPatchesAsync", ex, "Failed to get patches"); return null; } } diff --git a/Wauncher/Utils/ErrorLogger.cs b/Wauncher/Utils/ErrorLogger.cs new file mode 100644 index 0000000..1142410 --- /dev/null +++ b/Wauncher/Utils/ErrorLogger.cs @@ -0,0 +1,124 @@ +using System; +using System.IO; +using System.Threading.Tasks; + +namespace Wauncher.Utils +{ + public static class ErrorLogger + { + private static readonly string LogFilePath = Path.Combine( + Path.GetDirectoryName(System.Environment.ProcessPath) ?? ".", + "WauncherLog.txt" + ); + + private static readonly object _lock = new object(); + + public static void LogError(string componentName, Exception exception, string? additionalContext = null) + { + try + { + var logEntry = FormatLogEntry(componentName, exception, additionalContext); + + lock (_lock) + { + File.AppendAllText(LogFilePath, logEntry); + } + } + catch + { + // If logging fails, don't throw an exception to avoid infinite loops + } + } + + public static void LogError(string componentName, string errorMessage, string? additionalContext = null) + { + try + { + var logEntry = FormatLogEntry(componentName, errorMessage, additionalContext); + + lock (_lock) + { + File.AppendAllText(LogFilePath, logEntry); + } + } + catch + { + // If logging fails, don't throw an exception to avoid infinite loops + } + } + + public static async Task LogErrorAsync(string componentName, Exception exception, string? additionalContext = null) + { + try + { + var logEntry = FormatLogEntry(componentName, exception, additionalContext); + + await Task.Run(() => + { + lock (_lock) + { + File.AppendAllText(LogFilePath, logEntry); + } + }); + } + catch + { + // If logging fails, don't throw an exception to avoid infinite loops + } + } + + public static async Task LogErrorAsync(string componentName, string errorMessage, string? additionalContext = null) + { + try + { + var logEntry = FormatLogEntry(componentName, errorMessage, additionalContext); + + await Task.Run(() => + { + lock (_lock) + { + File.AppendAllText(LogFilePath, logEntry); + } + }); + } + catch + { + // If logging fails, don't throw an exception to avoid infinite loops + } + } + + private static string FormatLogEntry(string componentName, Exception exception, string? additionalContext) + { + var timestamp = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"); + var logEntry = $"[{timestamp}] ERROR in {componentName}:\n"; + logEntry += $"Message: {exception.Message}\n"; + + if (!string.IsNullOrEmpty(additionalContext)) + { + logEntry += $"Context: {additionalContext}\n"; + } + + logEntry += $"Exception Type: {exception.GetType().Name}\n"; + logEntry += $"Stack Trace: {exception.StackTrace}\n"; + logEntry += new string('-', 80) + "\n"; + + return logEntry; + } + + private static string FormatLogEntry(string componentName, string errorMessage, string? additionalContext) + { + var timestamp = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"); + var logEntry = $"[{timestamp}] ERROR in {componentName}:\n"; + logEntry += $"Message: {errorMessage}\n"; + + if (!string.IsNullOrEmpty(additionalContext)) + { + logEntry += $"Context: {additionalContext}\n"; + } + + logEntry += new string('-', 80) + "\n"; + + return logEntry; + } + } +} diff --git a/Wauncher/Utils/Game.cs b/Wauncher/Utils/Game.cs index 4510ceb..7281742 100644 --- a/Wauncher/Utils/Game.cs +++ b/Wauncher/Utils/Game.cs @@ -84,8 +84,9 @@ public static async Task Launch() """; await File.WriteAllTextAsync(gameStatePath, gameStateContents); } - catch + catch (Exception ex) { + ErrorLogger.LogError("Game.Launch", ex, "Failed to write gamestate integration config"); Terminal.Error("(!) \"/csgo/cfg/gamestate_integration_cc.cfg\" not found in the current directory!"); } } @@ -131,8 +132,9 @@ public static async Task Monitor() { trackedProcessRunning = _process != null && !_process.HasExited; } - catch + catch (Exception ex) { + ErrorLogger.LogError("Game.Monitor", ex, "Failed to check tracked process status"); trackedProcessRunning = false; } diff --git a/Wauncher/Utils/Steam.cs b/Wauncher/Utils/Steam.cs index 396b8e9..e7b654f 100644 --- a/Wauncher/Utils/Steam.cs +++ b/Wauncher/Utils/Steam.cs @@ -41,8 +41,16 @@ public class Steam public static bool IsInstalled() { - var path = GetSteamInstallPath(); - return !string.IsNullOrWhiteSpace(path) && Directory.Exists(path); + try + { + var path = GetSteamInstallPath(); + return !string.IsNullOrWhiteSpace(path) && Directory.Exists(path); + } + catch (Exception ex) + { + ErrorLogger.LogError("Steam.IsInstalled", ex, "Failed to check if Steam is installed"); + return false; + } } public static async Task GetRecentLoggedInSteamID() @@ -119,10 +127,11 @@ public static async Task GetRecentLoggedInSteamID(bool exitOnMissing) break; } } - catch (RuntimeBinderException) + catch (RuntimeBinderException ex) { if (Debug.Enabled()) Terminal.Debug($"Skipping malformed Steam loginusers entry for {steamId64 ?? "unknown user"}."); + ErrorLogger.LogError("Steam.GetRecentLoggedInSteamID", ex, $"Malformed Steam loginusers entry for {steamId64 ?? "unknown user"}"); } } diff --git a/Wauncher/patchnotes.md b/Wauncher/patchnotes.md index 2c26368..b00f6f4 100644 --- a/Wauncher/patchnotes.md +++ b/Wauncher/patchnotes.md @@ -13,6 +13,21 @@ - Server information is now pulled from the live ClassicCounter server list repository - Various backend and stability improvements +# Hotfix +03/18/2026 +## What's Changed +- Fixed context menu when page is not in initial position (is scrolled down) +- Changed how Newest and Oldest sorting works, and added two new sorting methods based on item ID +- Revamped trading code, so now traded items are properly tracked which allows us in the future to create an accurate trade history +- Improved the code for validation of items in a trade offer, should fix some bugs +- StatTrak kills get now reset down to 0 when they get traded away +- General updates of a weapon no longer update the timestamp used for sorting in the inventory, most commonly seen with StatTrak weapons by getting kills +- Changed how deleting items works, they are no longer expunged from the database and just remain hidden +- Made randomness in multiple places on the website more cryptographically safe +- Fixed traded-up items having incorrect wear name +- In-game item announcer now works again +- Trading is no longer under maintainance and is now again available to the public + # Hotfix 03/08/2026 ## What's Changed