Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
9b0c14c
sync Wauncher standalone structure
ways15xx Mar 10, 2026
f69a440
Proprietary Copyright Licensing Information
edd13s Mar 10, 2026
0764d0d
LICENSE
edd13s Mar 10, 2026
31a3d1f
remove deprecated --ssl-bypass launcher argument
ways15xx Mar 10, 2026
6ca8234
remove insecure ssl bypass from api client
ways15xx Mar 10, 2026
3e65c4e
Update LICENSE
edd13s Mar 10, 2026
6246904
remove carousel local fallback and rely on offline panel
ways15xx Mar 10, 2026
9e6411d
move verify files to launch menu and remove cli-style options
ways15xx Mar 10, 2026
94f042b
Wauncher update
ways15xx Mar 10, 2026
b60f120
show verify progress during file scan
ways15xx Mar 10, 2026
f671e66
show verify progress during file scan
ways15xx Mar 10, 2026
b8aeec2
fix protocol command compile error
ways15xx Mar 10, 2026
5da4031
show verify progress during file scan
ways15xx Mar 10, 2026
35bd63c
make publish.bat wauncher-only
ways15xx Mar 10, 2026
ed36d6f
add wauncher build/publish docs
ways15xx Mar 10, 2026
464d9e3
update readme packages and known issues
ways15xx Mar 10, 2026
c7b1b5d
show verify progress and restore launch options
ways15xx Mar 10, 2026
f4f9233
fix protocol command compile check
ways15xx Mar 10, 2026
c20d890
restore launch options field in settings
ways15xx Mar 10, 2026
b98fe6b
restore launch options and move settings path
ways15xx Mar 10, 2026
d0f8ad8
only recheck updates when skip-updates changes
ways15xx Mar 11, 2026
f6ffee8
only recheck updates when skip-updates changes
ways15xx Mar 11, 2026
4804b29
Only minimize to tray while game is running
ways15xx Mar 11, 2026
0032dcc
Only minimize to tray while game is running
ways15xx Mar 11, 2026
55c5188
Update README for current Wauncher features
ways15xx Mar 11, 2026
97d88c2
Exclude pdb file from publish script
ways15xx Mar 11, 2026
8c92abf
Downscale oversized Steam avatars in cache
ways15xx Mar 11, 2026
74a8382
Removed friends list issue
ways15xx Mar 12, 2026
b573108
Keep rich friend statuses
ways15xx Mar 12, 2026
6c0be2b
Add friend quick join matching
ways15xx Mar 12, 2026
4db5920
Add patch note date headers
ways15xx Mar 12, 2026
d643720
Save launch options live
ways15xx Mar 12, 2026
aa4175a
Refine patch note parsing
ways15xx Mar 12, 2026
0126a52
Polish friends and patch notes UI
ways15xx Mar 12, 2026
94e62ca
Update proprietary Wauncher license
edd13s Mar 13, 2026
6d741e3
PUBLISH WAUNCHER LICENSE
edd13s Mar 13, 2026
168564a
Create Wauncher Proprietary README.md
edd13s Mar 13, 2026
c442616
Update LICENSE hosting conditions
edd13s Mar 13, 2026
ac45661
sync local Wauncher updates
ways15xx Mar 13, 2026
1a1fdbf
remove known issues from readme
ways15xx Mar 13, 2026
f0e6263
simplify in-game friend status text
ways15xx Mar 13, 2026
6cea132
merge main into gui
ways15xx Mar 13, 2026
8d2a966
Fixed an issue where the Install button appeared in green and not in …
NotSimpy Mar 13, 2026
186bfc5
Merge pull request #17 from NotSimpy/gui
ways15xx Mar 13, 2026
0a8ede0
set wauncher branding and release version
ways15xx Mar 13, 2026
fbb853b
sync info window version display
ways15xx Mar 13, 2026
b7e5995
queue protocol server launches through wauncher
ways15xx Mar 13, 2026
7c057d0
fix protocol launches using wrong game directory
ways15xx Mar 13, 2026
9e3933c
remove alpha banner from main window
ways15xx Mar 13, 2026
927644a
Create CONTRIBUTORS.md
edd13s Mar 17, 2026
de27353
release wauncher 3.0.1 hotfixes
ways15xx Mar 18, 2026
9b69da7
fix wauncher gamestate listener crash
ways15xx Mar 18, 2026
54288fc
Fix launcher errors throwing exceptions, add another method of findin…
Mar 18, 2026
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
42 changes: 26 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
<p align="center">
<h2 align="center">ClassicCounter Launcher</h2>
<h2 align="center">ClassicCounter Wauncher</h2>
<p align="center">
Launcher for ClassicCounter with Discord RPC, Auto-Updates and More!
Wauncher for ClassicCounter with Discord RPC, a Server List, Friends List, Auto-Updates and More!
<br/>
Written in C# using .NET 8.
Written in C# using .NET 8 and Avalonia.
</p>
</p>

Expand All @@ -13,29 +13,39 @@
[![MIT License][license-shield]][license-url]

> [!IMPORTANT]
> .NET Runtime 8 is required to run the launcher. Download it from [**here**](https://dotnet.microsoft.com/en-us/download/dotnet/thank-you/runtime-8.0.11-windows-x64-installer).
> .NET Runtime 8 is required to run the Wauncher. Download it from [**here**](https://dotnet.microsoft.com/en-us/download/dotnet/thank-you/runtime-8.0.11-windows-x64-installer).

## Arguments
- `--debug-mode` - Enables debug mode, prints additional info.
- `--disable-rpc` - Disables Discord RPC.
- `--gc` - Launches the Game with custom Game Coordinator.
- `--install-dependencies` - Launches setup process for required Game dependencies.
- `--patch-only` - Will only check for patches, won't open the game.
- `--skip-updates` - Skips checking for launcher updates.
- `--skip-validating` - Skips validating patches.
- `--validate-all` - Validates all game files.
> [!IMPORTANT]
> Wauncher is still a work in progress. Some features are unfinished and you may run into bugs or changes between builds.

## Settings
- `Minimize to System Tray` keeps the launcher running in the background when in-game.
- `Skip Updates` lets you launch the game even when Wauncher detects available updates.
- `Discord RPC` controls whether Wauncher updates your Discord presence. In some cases, Discord may still show "Counter-Strike: Global Offensive" even when RPC is disabled.
- `Launch Options` lets you pass extra game launch flags such as `-high` or `+fps_max 300`.
- `Verify Game Files` checks your installation and repairs any missing or damaged game files automatically.

> [!CAUTION]
> **Using `--skip-updates` or `--skip-validating` is NOT recommended!**
> **An outdated launcher or patches might cause issues.**
## Build / Publish
- Build: `dotnet build Wauncher/Wauncher.csproj -c Release`
- Publish: `dotnet publish Wauncher/Wauncher.csproj -c Release -r win-x64 --self-contained false`
- Quick publish script: `publish.bat` (builds + hashes + optional copy target)

## Packages Used
- [AsyncImageLoader.Avalonia](https://github.com/AvaloniaUtils/AsyncImageLoader.Avalonia) by [AvaloniaUtils](https://github.com/AvaloniaUtils)
- [Avalonia](https://github.com/AvaloniaUI/Avalonia) by [AvaloniaUI](https://github.com/AvaloniaUI)
- [Avalonia.Desktop](https://github.com/AvaloniaUI/Avalonia) by [AvaloniaUI](https://github.com/AvaloniaUI)
- [Avalonia.Themes.Fluent](https://github.com/AvaloniaUI/Avalonia) by [AvaloniaUI](https://github.com/AvaloniaUI)
- [Avalonia.Fonts.Inter](https://github.com/AvaloniaUI/Avalonia) by [AvaloniaUI](https://github.com/AvaloniaUI)
- [Avalonia.Diagnostics](https://github.com/AvaloniaUI/Avalonia) by [AvaloniaUI](https://github.com/AvaloniaUI)
- [CommunityToolkit.Mvvm](https://github.com/CommunityToolkit/dotnet) by [CommunityToolkit](https://github.com/CommunityToolkit)
- [CSGSI](https://github.com/rakijah/CSGSI) by [rakijah](https://github.com/rakijah)
- [DiscordRichPresence](https://github.com/Lachee/discord-rpc-csharp) by [Lachee](https://github.com/Lachee)
- [Downloader](https://github.com/bezzad/Downloader) by [bezzad](https://github.com/bezzad)
- [Gameloop.Vdf](https://github.com/shravan2x/Gameloop.Vdf) by [shravan2x](https://github.com/shravan2x)
- [Refit](https://github.com/reactiveui/refit) by [ReactiveUI](https://github.com/reactiveui)
- [Refit.Newtonsoft.Json](https://github.com/reactiveui/refit) by [ReactiveUI](https://github.com/reactiveui)
- [Spectre.Console](https://github.com/spectreconsole/spectre.console) by [Spectre Console](https://github.com/spectreconsole)
- [Svg.Controls.Skia.Avalonia](https://github.com/wieslawsoltes/Svg.Skia) by [wieslawsoltes](https://github.com/wieslawsoltes)

[downloads-shield]: https://img.shields.io/github/downloads/classiccounter/launcher/total.svg?style=for-the-badge
[downloads-url]: https://github.com/classiccounter/launcher/releases/latest
Expand Down
68 changes: 42 additions & 26 deletions Wauncher/App.axaml
Original file line number Diff line number Diff line change
@@ -1,32 +1,48 @@
<Application xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="Wauncher.App"
x:CompileBindings="True"
x:DataType="local:App"
x:CompileBindings="True"
x:DataType="local:App"
xmlns:local="using:Wauncher"
RequestedThemeVariant="Default">
<!-- "Default" ThemeVariant follows system theme variant. "Dark" or "Light" are other available options. -->

<Application.DataTemplates>
<local:ViewLocator/>
</Application.DataTemplates>
xmlns:themes="using:Avalonia.Styling"
xmlns:fluent="using:Avalonia.Themes.Fluent">
<Application.DataTemplates>
<local:ViewLocator/>
</Application.DataTemplates>
<Application.Styles>
<FluentTheme />
<StyleInclude Source="/Assets/Styles.axaml"/>
<FluentTheme>
<FluentTheme.Palettes>
<fluent:ColorPaletteResources x:Key="Light" Accent="#22C55E" />
<fluent:ColorPaletteResources x:Key="Dark" Accent="#22C55E" />
</FluentTheme.Palettes>
</FluentTheme>
<StyleInclude Source="/Assets/Styles.axaml"/>
</Application.Styles>

<TrayIcon.Icons>
<TrayIcons>
<TrayIcon Icon="/Assets/Wauncher.ico" ToolTipText="Wauncher" Command="{Binding TrayIconClickedCommand}">
<TrayIcon.Menu>
<NativeMenu>
<NativeMenuItem Header="Discord RPC is running" IsEnabled="False"/>
<NativeMenuItemSeparator />
<NativeMenuItem Header="E_xit" Click="ExitApplication_Click"/>
</NativeMenu>
</TrayIcon.Menu>
</TrayIcon>
</TrayIcons>
</TrayIcon.Icons>
</Application>

<Application.Resources>
<ResourceDictionary>
<SolidColorBrush x:Key="AppTitleBarBrush">#CC3A3A3A</SolidColorBrush>
<SolidColorBrush x:Key="AppPrimaryText">White</SolidColorBrush>
<SolidColorBrush x:Key="AppMutedText">#88FFFFFF</SolidColorBrush>
<SolidColorBrush x:Key="AppSectionLabel">#55FFFFFF</SolidColorBrush>
<SolidColorBrush x:Key="AppBodyText">#CCFFFFFF</SolidColorBrush>
<SolidColorBrush x:Key="AppBulletText">#AAFFFFFF</SolidColorBrush>
<SolidColorBrush x:Key="AppDivider">#22FFFFFF</SolidColorBrush>
<SolidColorBrush x:Key="AppDivider2">#33FFFFFF</SolidColorBrush>
<SolidColorBrush x:Key="AppInputBg">#33FFFFFF</SolidColorBrush>
<SolidColorBrush x:Key="AppServerHover">#22FFFFFF</SolidColorBrush>
<SolidColorBrush x:Key="AppServerPressed">#11FFFFFF</SolidColorBrush>
<SolidColorBrush x:Key="AppIconBtnBg">#44FFFFFF</SolidColorBrush>
<SolidColorBrush x:Key="AppIconBtnHover">#99FFFFFF</SolidColorBrush>
<SolidColorBrush x:Key="AppIconBtnPressed">#33FFFFFF</SolidColorBrush>
<SolidColorBrush x:Key="AppIconBtnBorder">#22FFFFFF</SolidColorBrush>
<SolidColorBrush x:Key="AppRightPanelBg">#223A3A3A</SolidColorBrush>
<SolidColorBrush x:Key="AppServerListBg">#FF3A3A3A</SolidColorBrush>
<SolidColorBrush x:Key="AppGreetingName">White</SolidColorBrush>
<SolidColorBrush x:Key="AppLinkText">#6CB5F5</SolidColorBrush>
</ResourceDictionary>
</Application.Resources>

</Application>
184 changes: 158 additions & 26 deletions Wauncher/App.axaml.cs
Original file line number Diff line number Diff line change
@@ -1,75 +1,207 @@
using Avalonia;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Data.Core.Plugins;
using Avalonia.Markup.Xaml;
using Avalonia.Media.Imaging;
using Avalonia.Platform;
using CommunityToolkit.Mvvm.Input;
using Launcher.Utils;
using Wauncher.Utils;
using System.Diagnostics;
using Wauncher.ViewModels;
using Wauncher.Views;

namespace Wauncher
{
public partial class App : Application
{
private TrayIcon? _trayIcon = null;
private NativeMenuItem? _discordRpcMenuItem = null;

public override void Initialize()
{
AvaloniaXamlLoader.Load(this);
Discord.Init();
ProtocolManager.RegisterURIHandler();
}

public override void OnFrameworkInitializationCompleted()
{
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
// Avoid duplicate validations from both Avalonia and the CommunityToolkit.
// More info: https://docs.avaloniaui.net/docs/guides/development-guides/data-validation#manage-validationplugins
DisableAvaloniaDataAnnotationValidation();
DisableAvaloniaDataAnnotationValidation();

if (!Steam.IsInstalled())
{
ConsoleManager.ShowError(
"Steam is required to use Wauncher.\n\nPlease install Steam and relaunch.");
Environment.Exit(1);
return;
}

if (!IsSteamRunning())
{
ConsoleManager.ShowError(
"Steam must be open before using Wauncher.\n\nPlease open Steam, then relaunch Wauncher.");
Environment.Exit(2);
return;
}

if (Game.IsRunning())
{
ConsoleManager.ShowError(
"ClassicCounter is already running.\n\nPlease close the game before opening Wauncher again.");
Environment.Exit(3);
return;
}

bool hasRecentSteamUser = Steam.GetRecentLoggedInSteamID(false).GetAwaiter().GetResult();
if (!hasRecentSteamUser)
{
ConsoleManager.ShowError(
"Steam is open, but no logged-in Steam account was detected.\n\nPlease sign in to Steam and relaunch Wauncher.");
Environment.Exit(4);
return;
}

// Always init so Discord username/avatar callbacks fire for the greeting.
// Presence is only pushed via Update() when RPC is enabled.
try
{
if (DependencyChecks.IsDiscordInstalled())
Discord.Init();
}
catch
{
// Discord integration is optional.
}

desktop.MainWindow = new MainWindow
{
DataContext = new MainWindowViewModel(),
};
desktop.Exit += (_, _) => _trayIcon?.Dispose();
}

SetupTrayIcon();
base.OnFrameworkInitializationCompleted();
}

private void DisableAvaloniaDataAnnotationValidation()
private static bool IsSteamRunning()
{
// Get an array of plugins to remove
var dataValidationPluginsToRemove =
BindingPlugins.DataValidators.OfType<DataAnnotationsValidationPlugin>().ToArray();

// remove each entry found
foreach (var plugin in dataValidationPluginsToRemove)
try
{
BindingPlugins.DataValidators.Remove(plugin);
return Process.GetProcessesByName("steam").Length > 0;
}
catch
{
return false;
}
}

private void SetupTrayIcon()
{
var settings = SettingsWindowViewModel.LoadGlobal();

[RelayCommand]
public void TrayIconClicked()
_discordRpcMenuItem = new NativeMenuItem
{
Header = settings.DiscordRpc ? "Discord RPC ON" : "Discord RPC OFF"
};
_discordRpcMenuItem.Click += DiscordRpc_Click;

var exitItem = new NativeMenuItem { Header = "Exit" };
exitItem.Click += (_, _) =>
{
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime d)
{
if (d.MainWindow is Views.MainWindow mw)
mw.ForceQuit();
d.TryShutdown();
}
};

var menu = new NativeMenu();
menu.Items.Add(_discordRpcMenuItem);
menu.Items.Add(new NativeMenuItemSeparator());
menu.Items.Add(exitItem);

_trayIcon = new TrayIcon
{
ToolTipText = "ClassicCounter",
Menu = menu,
};

try
{
var uri = new Uri("avares://Wauncher/Assets/Wauncher.ico");
using var stream = AssetLoader.Open(uri);
_trayIcon.Icon = new WindowIcon(stream);
}
catch { }

_trayIcon.Clicked += (_, _) => ShowMainWindow();

// Live sync
SettingsWindowViewModel.DiscordRpcChanged += enabled => ApplyDiscordRpc(enabled);
}

private void ShowMainWindow()
{
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop && desktop.MainWindow != null)
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop
&& desktop.MainWindow != null)
{
desktop.MainWindow.Show();
desktop.MainWindow.WindowState = Avalonia.Controls.WindowState.Normal;
desktop.MainWindow.Activate();
}
}

public void ExitApplication_Click(object? sender, System.EventArgs e)
public void DiscordRpc_Click(object? sender, EventArgs e)
{
var settings = SettingsWindowViewModel.LoadGlobal();
settings.DiscordRpc = !settings.DiscordRpc; // auto-saves via OnDiscordRpcChanged
ApplyDiscordRpc(settings.DiscordRpc);
}

private void ApplyDiscordRpc(bool enabled)
{
switch (ApplicationLifetime)
if (!DependencyChecks.IsDiscordInstalled())
{
case IClassicDesktopStyleApplicationLifetime desktopLifetime:
desktopLifetime.TryShutdown();
break;
case IControlledApplicationLifetime controlledLifetime:
controlledLifetime.Shutdown();
break;
if (_discordRpcMenuItem != null)
_discordRpcMenuItem.Header = "Discord RPC (Discord not installed)";
return;
}

if (enabled)
{
Discord.SetDetails("In Main Menu");
Discord.SetState(null);
Discord.Update();
}
else
{
Discord.Deinitialize();
}

if (_discordRpcMenuItem != null)
_discordRpcMenuItem.Header = enabled ? "Discord RPC ON" : "Discord RPC OFF";
}

[RelayCommand]
public void TrayIconClicked() => ShowMainWindow();

public void ExitApplication_Click(object? sender, EventArgs e)
{
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime d)
d.TryShutdown();
}

private void DisableAvaloniaDataAnnotationValidation()
{
var toRemove = BindingPlugins.DataValidators
.OfType<DataAnnotationsValidationPlugin>().ToArray();
foreach (var plugin in toRemove)
BindingPlugins.DataValidators.Remove(plugin);
}
}
}
}

Loading
Loading