Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
de7c48b
Merge pull request #9 from ClassicCounter/main
koolych Dec 1, 2025
e133074
AvaloniaUI Init
koolych Dec 1, 2025
22b6a6a
GUI Build enhance
koolych Dec 1, 2025
f403d60
wauncher layout
koolych Dec 3, 2025
5e2125c
Last moments of ReactiveUI usage
koolych Dec 3, 2025
a6e0e8f
Bye bye old project
koolych Dec 3, 2025
d86ade2
New CommunityToolkit AvaloniaUI App Init
koolych Dec 3, 2025
372253c
Remove old project
koolych Dec 3, 2025
e829310
Update Wauncher.csproj
koolych Dec 3, 2025
802c33e
Merge pull request #1 from PookTechnologiesUnited/gui
koolych Dec 3, 2025
87681cb
Init Major Changes
koolych Dec 30, 2025
eacf416
GUI Layout - Merge pull request #10 from PookTechnologiesUnited/main
koolych Dec 30, 2025
8bb5746
Merge pull request #11 from ClassicCounter/main
koolych Dec 30, 2025
3cf6746
Info Window Layout
koolych Jan 4, 2026
a8eb64b
.gitignore update
koolych Jan 4, 2026
84a1aae
Discord data changes
koolych Jan 4, 2026
b8162b1
not good
koolych Jan 5, 2026
7930f82
fixes aaaaa
koolych Jan 24, 2026
e0875ef
Protocol Handling :Thumbsup:
koolych Feb 14, 2026
8c22469
Merge branch 'gui' of https://github.com/ClassicCounter/launcher into…
koolych Feb 14, 2026
d45d32e
Mutex implement
koolych Feb 14, 2026
3971b52
Update .gitignore
koolych Feb 14, 2026
78e4a1a
Remove launchSettings.json
koolych Feb 14, 2026
d34fe84
Better publishing
koolych Feb 14, 2026
4c374e2
ts doesn't work
koolych Feb 14, 2026
b41e9ba
Trimmed Code publishing for Wauncher
koolych Feb 14, 2026
060925a
Revert "Trimmed Code publishing for Wauncher"
koolych Feb 14, 2026
37e3687
Missing csgo.exe error
frytt Feb 15, 2026
c697e47
error handling for gamestate file as well
koolych Feb 15, 2026
67933e2
Merge pull request #12 from frytt/gui
koolych Feb 15, 2026
b9d50c8
Launcher Patch fix + Hash Multi-Thread: 4
koolych Feb 17, 2026
54d1be6
Delete Wauncher/Assets/avalonia-logo.ico
koolych Feb 17, 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
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -451,5 +451,7 @@ $RECYCLE.BIN/
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json

## Additions
Wauncher/Properties/launchSettings.json
6 changes: 6 additions & 0 deletions Directory.Build.props
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<Project>
<PropertyGroup>
<Nullable>enable</Nullable>
<AvaloniaVersion>11.1.0</AvaloniaVersion>
</PropertyGroup>
</Project>
6 changes: 6 additions & 0 deletions Launcher.sln
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Launcher", "Launcher\Launch
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{8EC462FD-D22E-90A8-E5CE-7E832BA40C5D}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Wauncher", "Wauncher\Wauncher.csproj", "{6F2F5730-8F26-498D-BB71-F0068EB6870B}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand All @@ -17,6 +19,10 @@ Global
{C52CA2D0-3034-4F68-A523-BD7AED0A7479}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C52CA2D0-3034-4F68-A523-BD7AED0A7479}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C52CA2D0-3034-4F68-A523-BD7AED0A7479}.Release|Any CPU.Build.0 = Release|Any CPU
{6F2F5730-8F26-498D-BB71-F0068EB6870B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6F2F5730-8F26-498D-BB71-F0068EB6870B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6F2F5730-8F26-498D-BB71-F0068EB6870B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6F2F5730-8F26-498D-BB71-F0068EB6870B}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
5 changes: 5 additions & 0 deletions Launcher/Launcher.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,17 @@
<PublishSingleFile>true</PublishSingleFile>
<SelfContained>false</SelfContained>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<ApplicationIcon>assets\Launcher.ico</ApplicationIcon>
</PropertyGroup>

<ItemGroup>
<None Remove="Assets\steamhappy.txt" />
</ItemGroup>

<ItemGroup>
<Content Include="assets\Launcher.ico" />
</ItemGroup>

<ItemGroup>
<EmbeddedResource Include="Assets\steamhappy.txt" />
</ItemGroup>
Expand Down
17 changes: 15 additions & 2 deletions Launcher/Utils/Argument.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ public static class Argument
"--patch-only",
"--gc",
"--disable-rpc",
"--install-dependencies"
"--install-dependencies",
"--protocol-command"
};

private static List<string> _additionalArguments = new();
Expand Down Expand Up @@ -39,7 +40,19 @@ public static List<string> GenerateGameArguments(bool passLauncherArguments = fa
List<string> gameArguments = new();

foreach (string arg in launcherArguments)
if ((passLauncherArguments || !_launcherArguments.Contains(arg.ToLowerInvariant()))
if (arg.StartsWith("cc://"))
{
string protocolArgument = arg.Replace("cc://", "");
string[] protocolArguments = protocolArgument.Split('/');
switch (protocolArguments[0])
{
case "connect":
gameArguments.Add("+" + protocolArguments[0]);
gameArguments.Add(protocolArguments[1]);
break;
}
}
else if ((passLauncherArguments || !_launcherArguments.Contains(arg.ToLowerInvariant()))
&& !arg.EndsWith(".exe"))
gameArguments.Add(arg.ToLowerInvariant());

Expand Down
13 changes: 13 additions & 0 deletions Launcher/Utils/Console.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,26 @@ public static class ConsoleManager
[DllImport("user32.dll")]
private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);

[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern int MessageBox(IntPtr hWnd, string text, string caption, uint type);

private const int SW_HIDE = 0;
private const uint MB_ICONERROR = 0x00000010;

private static IntPtr ConsoleHandle = GetConsoleWindow();

public static void HideConsole()
{
ShowWindow(ConsoleHandle, SW_HIDE);
}

public static void ShowError(string message)
{
if (OperatingSystem.IsWindows())
{
MessageBox(IntPtr.Zero, message, "ClassicCounter Error", MB_ICONERROR);
}
}
}
}

10 changes: 10 additions & 0 deletions Launcher/Utils/Discord.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using DiscordRPC;
using DiscordRPC.Logging;
using DiscordRPC.Message;
using System;

namespace Launcher.Utils
{
Expand All @@ -10,6 +11,8 @@ public static class Discord
private static DiscordRpcClient _client = new DiscordRpcClient(_appId);
private static RichPresence _presence = new RichPresence();
public static string? CurrentUserId { get; private set; } // ! DEPRECATED ! for whitelist check
public static string? CurrentUserAvatar { get; private set; }
public static string? CurrentUserUsername { get; private set; }

public static void Init()
{
Expand Down Expand Up @@ -58,9 +61,16 @@ public static void SetSmallArtwork(string? key)
private static void OnReady(object sender, ReadyMessage e)
{
CurrentUserId = e.User.ID.ToString(); // ! DEPRECATED ! for passing current uid to api
CurrentUserAvatar = e.User.GetAvatarURL(User.AvatarFormat.PNG);
CurrentUserUsername = e.User.Username;
OnAvatarUpdate?.Invoke(CurrentUserAvatar);
OnUsernameUpdate?.Invoke(CurrentUserUsername);

if (Debug.Enabled())
Terminal.Debug($"Discord RPC: User is ready => @{e.User.Username} ({e.User.ID})");
}

public static event Action<string?>? OnAvatarUpdate;
public static event Action<string?>? OnUsernameUpdate;
}
}
14 changes: 14 additions & 0 deletions Launcher/Utils/Game.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ public static async Task<bool> Launch()
_listener.NewGameState += OnNewGameState;
_listener.Start();

try {
await File.WriteAllTextAsync(gameStatePath,
@"""ClassicCounter""
{
Expand All @@ -61,6 +62,11 @@ await File.WriteAllTextAsync(gameStatePath,
}
}"
);
}
catch
{
Terminal.Error($"(!) \"/csgo/cfg/gamestate_integration_cc.cfg\" not found in the current directory!");
}
}
else if (File.Exists(gameStatePath)) File.Delete(gameStatePath);

Expand All @@ -74,7 +80,15 @@ await File.WriteAllTextAsync(gameStatePath,
_process.StartInfo.FileName = $"{directory}\\{gameExe}";
_process.StartInfo.Arguments = string.Join(" ", arguments);

if (!File.Exists(_process.StartInfo.FileName))
{
Terminal.Error($"(!) {gameExe} not found in the current directory!");
ConsoleManager.ShowError($"{gameExe} not found in the current directory!\n\nPlease make sure the launcher and game files are in the same folder.");
return false;
}

return _process.Start();

}

public static async Task Monitor()
Expand Down
34 changes: 23 additions & 11 deletions Launcher/Utils/Patch.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.Collections.Concurrent;
using System.Security.Cryptography;

namespace Launcher.Utils
Expand Down Expand Up @@ -152,23 +153,31 @@ public static async Task<Patches> ValidatePatches(bool validateAll = false)
needPak01Update = true;
}

if (needPak01Update)
if (!needPak01Update)
{
patches.Remove(dirPatch);
}
}

foreach (Patch patch in patches)
var concurrentMissing = new ConcurrentBag<Patch>();
var concurrentOutdated = new ConcurrentBag<Patch>();

var parallelOptions = new ParallelOptions
{
MaxDegreeOfParallelism = 4
};

await Parallel.ForEachAsync(patches, parallelOptions, async (patch, cancellationToken) =>
{
string originalFileName = GetOriginalFileName(patch.File);

// skip dir file (we already checked it)
if (originalFileName.Contains("pak01_dir.vpk"))
continue;
return;

// are you a pak01 file?
bool isPak01File = originalFileName.Contains("pak01_");
string path = $"{Directory.GetCurrentDirectory()}/{originalFileName}";
string path = Path.Combine(Directory.GetCurrentDirectory(), originalFileName);

if (isPak01File && !needPak01Update && !Argument.Exists("--validate-all"))
{
Expand All @@ -177,23 +186,23 @@ public static async Task<Patches> ValidatePatches(bool validateAll = false)
if (Debug.Enabled())
Terminal.Debug($"Missing: {originalFileName}");

missing.Add(patch);
continue;
concurrentMissing.Add(patch);
return;
}

if (Debug.Enabled())
Terminal.Debug($"Skipping hash check for: {originalFileName} (pak01_dir.vpk up to date)");

continue;
return;
}

if (!File.Exists(path))
{
if (Debug.Enabled())
Terminal.Debug($"Missing: {originalFileName}");

missing.Add(patch);
continue;
concurrentMissing.Add(patch);
return;
}

if (Debug.Enabled())
Expand All @@ -206,9 +215,12 @@ public static async Task<Patches> ValidatePatches(bool validateAll = false)
Terminal.Debug($"Outdated: {originalFileName}");

File.Delete(path);
outdated.Add(patch);
concurrentOutdated.Add(patch);
}
}
});

missing.AddRange(concurrentMissing);
outdated.AddRange(concurrentOutdated);

// if pak01_dir.vpk needs update, move it to end of lists
if (needPak01Update && dirPatch != null)
Expand Down
Binary file added Launcher/assets/Launcher.ico
Binary file not shown.
32 changes: 32 additions & 0 deletions Wauncher/App.axaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<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"
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>

<Application.Styles>
<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>
75 changes: 75 additions & 0 deletions Wauncher/App.axaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
using Avalonia;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Data.Core.Plugins;
using Avalonia.Markup.Xaml;
using CommunityToolkit.Mvvm.Input;
using Launcher.Utils;
using Wauncher.Utils;
using Wauncher.ViewModels;
using Wauncher.Views;

namespace Wauncher
{
public partial class App : Application
{
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();
desktop.MainWindow = new MainWindow
{
DataContext = new MainWindowViewModel(),
};
}

base.OnFrameworkInitializationCompleted();
}

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

// remove each entry found
foreach (var plugin in dataValidationPluginsToRemove)
{
BindingPlugins.DataValidators.Remove(plugin);
}
}


[RelayCommand]
public void TrayIconClicked()
{
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop && desktop.MainWindow != null)
{
desktop.MainWindow.Show();
desktop.MainWindow.Activate();
}
}

public void ExitApplication_Click(object? sender, System.EventArgs e)
{
switch (ApplicationLifetime)
{
case IClassicDesktopStyleApplicationLifetime desktopLifetime:
desktopLifetime.TryShutdown();
break;
case IControlledApplicationLifetime controlledLifetime:
controlledLifetime.Shutdown();
break;
}
}
}
}
3 changes: 3 additions & 0 deletions Wauncher/Assets/CS-logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading