Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ Lightweight server picker for CS2 and Deadlock with cross-platform support for *
### [Releases](https://github.com/FN-FAL113/server-picker-x/releases)

## 📷 Screenshot
![ServerPickerX](https://github.com/user-attachments/assets/195b9553-4ca3-48b4-9d21-1aca8990d623)
![ServerPickerX](https://github.com/user-attachments/assets/6feac783-5d18-4900-bf6a-0a07b9d665a9)
<details>
<summary>Windows Short Demo</summary>

Expand Down
110 changes: 110 additions & 0 deletions ServerPickerX/Comparers/NaturalStringComparer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
using System;
using System.Collections.Generic;

namespace ServerPickerX.Comparers
{
public sealed class NaturalStringComparer : IComparer<string>
{
public static NaturalStringComparer OrdinalIgnoreCase { get; } = new(StringComparison.OrdinalIgnoreCase);

private readonly StringComparison _stringComparison;

public NaturalStringComparer(StringComparison stringComparison)
{
_stringComparison = stringComparison;
}

public int Compare(string? left, string? right)
{
if (ReferenceEquals(left, right))
{
return 0;
}

if (left == null)
{
return -1;
}

if (right == null)
{
return 1;
}

int leftIndex = 0;
int rightIndex = 0;

while (leftIndex < left.Length && rightIndex < right.Length)
{
if (char.IsDigit(left[leftIndex]) && char.IsDigit(right[rightIndex]))
{
int leftNumberStart = leftIndex;
int rightNumberStart = rightIndex;

while (leftIndex < left.Length && char.IsDigit(left[leftIndex]))
{
leftIndex++;
}

while (rightIndex < right.Length && char.IsDigit(right[rightIndex]))
{
rightIndex++;
}

ReadOnlySpan<char> leftDigits = left.AsSpan(leftNumberStart, leftIndex - leftNumberStart);
ReadOnlySpan<char> rightDigits = right.AsSpan(rightNumberStart, rightIndex - rightNumberStart);

int leftTrimmedStart = 0;
while (leftTrimmedStart < leftDigits.Length - 1 && leftDigits[leftTrimmedStart] == '0')
{
leftTrimmedStart++;
}

int rightTrimmedStart = 0;
while (rightTrimmedStart < rightDigits.Length - 1 && rightDigits[rightTrimmedStart] == '0')
{
rightTrimmedStart++;
}

ReadOnlySpan<char> leftTrimmedDigits = leftDigits[leftTrimmedStart..];
ReadOnlySpan<char> rightTrimmedDigits = rightDigits[rightTrimmedStart..];

if (leftTrimmedDigits.Length != rightTrimmedDigits.Length)
{
return leftTrimmedDigits.Length.CompareTo(rightTrimmedDigits.Length);
}

int digitComparison = leftTrimmedDigits.CompareTo(rightTrimmedDigits, StringComparison.Ordinal);

if (digitComparison != 0)
{
return digitComparison;
}

if (leftDigits.Length != rightDigits.Length)
{
return leftDigits.Length.CompareTo(rightDigits.Length);
}

continue;
}

int characterComparison = string.Compare(
left[leftIndex].ToString(),
right[rightIndex].ToString(),
_stringComparison
);

if (characterComparison != 0)
{
return characterComparison;
}

leftIndex++;
rightIndex++;
}

return left.Length.CompareTo(right.Length);
}
}
}
26 changes: 24 additions & 2 deletions ServerPickerX/Locales/Locale_de-de.axaml
Original file line number Diff line number Diff line change
@@ -1,28 +1,50 @@
<ResourceDictionary xmlns="https://github.com/avaloniaui"
<ResourceDictionary xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<x:String x:Key="LanguageCode">de-de</x:String>
<x:String x:Key="ClusterServers">Server gruppieren</x:String>
<x:String x:Key="UnclusterServers">Server entgruppieren</x:String>
<x:String x:Key="Refresh">Aktualisieren</x:String>
<x:String x:Key="SearchForServers">Nach Servern suchen...</x:String>
<x:String x:Key="NoPresets">Keine Presets</x:String>
<x:String x:Key="BlockAll">Alle blockieren</x:String>
<x:String x:Key="BlockSelected">Ausgewählte blockieren</x:String>
<x:String x:Key="UnblockAll">Alle freigeben</x:String>
<x:String x:Key="UnblockSelected">Ausgewählte freigeben</x:String>
<x:String x:Key="SaveButtonText">Speichern</x:String>
<x:String x:Key="CancelButtonText">Abbrechen</x:String>
<x:String x:Key="CheckForNewVersion">Bei Start nach neuen Versionen suchen</x:String>
<x:String x:Key="ResetFirewall">Brandmauer zurücksetzen</x:String>
<x:String x:Key="GameModeToolTip">Spielmodus auswählen</x:String>
<x:String x:Key="PresetToolTip">Preset auswählen</x:String>
<x:String x:Key="ClusterUnclusterToolTip">Server gruppieren oder entgruppieren</x:String>
<x:String x:Key="RefreshToolTip">Ping aller Server aktualisieren</x:String>
<x:String x:Key="SwapGameModeUnblockAllConflict">Diese Aktion wird zuerst alle Server freigeben, um Firewall-Konflikte zu vermeiden.</x:String>
<x:String x:Key="LanguageComboBoxToolTip">Sprache auswählen</x:String>
<x:String x:Key="ResetFirewallToolTip">Firewall-Regeln zurücksetzen</x:String>
<x:String x:Key="MessageBoxInfoTitle">Info</x:String>
<x:String x:Key="SyncServersUnblockAllDialogue">Die Serverdaten wurden soeben von Valve aktualisiert! Alle gesperrten Server werden entsperrt, um die neuen Serverdaten zu synchronisieren.</x:String>
<x:String x:Key="SyncServersUnblockAllDialogue">Die Serverdaten wurden gerade von Valve aktualisiert! Alle blockierten Server werden entsperrt, um die neuen Serverdaten zu synchronisieren. Preset-Einträge für Server, die nicht mehr existieren, werden entfernt, und das zuletzt ausgewählte Preset wird erneut angewendet, falls verfügbar.</x:String>
<x:String x:Key="NewVersionDialogue">Neue Version verfügbar! Zu den Veröffentlichungen?</x:String>
<x:String x:Key="SelectOneServerToBlockDialogue">Hey! Bitte wähle mindestens einen Server zum Blockieren aus.</x:String>
<x:String x:Key="SelectOneServerToUnblockDialogue">Bitte wählen Sie mindestens einen Server aus, um die Entsperrung aufzuheben.</x:String>
<x:String x:Key="PendingOperationDialogue">Moment! Ein Vorgang läuft bereits. Bitte warten Sie...</x:String>
<x:String x:Key="FirewallResetConfirmDialogue">Dadurch wird versucht, die Firewall auf den Standardzustand zurückzusetzen. Aktion bestätigen?</x:String>
<x:String x:Key="FirewallResetSuccessDialogue">Firewall erfolgreich zurückgesetzt!</x:String>
<x:String x:Key="Presets">Voreinstellungen</x:String>
<x:String x:Key="PresetsToolTip">Voreinstellungen verwalten</x:String>
<x:String x:Key="AddPreset">Hinzufügen</x:String>
<x:String x:Key="NewPresetName">Neues Preset</x:String>
<x:String x:Key="AddPresetToolTip">Neues Preset erstellen</x:String>
<x:String x:Key="DeletePreset">Löschen</x:String>
<x:String x:Key="DeletePresetToolTip">Ausgewählte Presets löschen</x:String>
<x:String x:Key="ApplyPreset">Anwenden</x:String>
<x:String x:Key="ApplyPresetToolTip">Ausgewähltes Preset anwenden</x:String>
<x:String x:Key="PresetsWindowTitle">Voreinstellungen</x:String>
<x:String x:Key="PresetServersHeader">Blockierte Server</x:String>
<x:String x:Key="BlockedColumnHeader">Blockiert</x:String>
<x:String x:Key="PresetListHeader">Voreinstellung</x:String>
<x:String x:Key="PresetDeleteConfirmDialogue">Preset '{0}' löschen?</x:String>
<x:String x:Key="PresetDeleteSelectedConfirmDialogue">Die {0} ausgewählten Presets löschen?</x:String>
<x:String x:Key="PresetOverwriteConfirmDialogue">Ein Preset mit dem Namen '{0}' existiert für dieses Spiel bereits. Überschreiben?</x:String>
<x:String x:Key="PresetChangeViewModeConfirmDialogue">Das Wechseln zwischen gruppierter und entgruppierter Ansicht löscht die blockierten Einträge dieses Presets. Fortfahren?</x:String>
<x:String x:Key="PresetNameRequiredDialogue">Der Preset-Name darf nicht leer sein.</x:String>
</ResourceDictionary>
24 changes: 23 additions & 1 deletion ServerPickerX/Locales/Locale_en-us.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,46 @@
<x:String x:Key="UnclusterServers">Uncluster Servers</x:String>
<x:String x:Key="Refresh">Refresh</x:String>
<x:String x:Key="SearchForServers">Search for servers...</x:String>
<x:String x:Key="NoPresets">No presets</x:String>
<x:String x:Key="BlockAll">Block All</x:String>
<x:String x:Key="BlockSelected">Block Selected</x:String>
<x:String x:Key="UnblockAll">Unblock All</x:String>
<x:String x:Key="UnblockSelected">Unblock Selected</x:String>
<x:String x:Key="SaveButtonText">Save</x:String>
<x:String x:Key="CancelButtonText">Cancel</x:String>
<x:String x:Key="CheckForNewVersion">Check for new version on startup</x:String>
<x:String x:Key="ResetFirewall">Reset Firewall</x:String>
<x:String x:Key="GameModeToolTip">Select game mode</x:String>
<x:String x:Key="PresetToolTip">Select preset</x:String>
<x:String x:Key="ClusterUnclusterToolTip">Group or ungroup servers</x:String>
<x:String x:Key="RefreshToolTip">Refresh all server ping</x:String>
<x:String x:Key="SwapGameModeUnblockAllConflict">This action will unblock all servers first to prevent firewall conflicts.</x:String>
<x:String x:Key="LanguageComboBoxToolTip">Select language</x:String>
<x:String x:Key="ResetFirewallToolTip">Reset firewall rules</x:String>
<x:String x:Key="MessageBoxInfoTitle">Info</x:String>
<x:String x:Key="SyncServersUnblockAllDialogue">Server data just got updated by Valve! All blocked servers will be unblocked in order to synchronize new server data</x:String>
<x:String x:Key="SyncServersUnblockAllDialogue">Server data just got updated by Valve! All blocked servers will be unblocked in order to synchronize new server data. Preset entries for servers that no longer exist will be removed, and the last selected preset will be reapplied if available.</x:String>
<x:String x:Key="NewVersionDialogue">New version available! Go to releases?</x:String>
<x:String x:Key="SelectOneServerToBlockDialogue">Hey! Please select at least one server to block</x:String>
<x:String x:Key="SelectOneServerToUnblockDialogue">Hey! Please select at least one server to unblock</x:String>
<x:String x:Key="PendingOperationDialogue">Whoa! There's already a pending operation. Please wait...</x:String>
<x:String x:Key="FirewallResetConfirmDialogue">This will attempt to reset firewall to its default state. Confirm action?</x:String>
<x:String x:Key="FirewallResetSuccessDialogue">Firewall has been successfully reset!</x:String>
<x:String x:Key="Presets">Presets</x:String>
<x:String x:Key="PresetsToolTip">Manage presets</x:String>
<x:String x:Key="AddPreset">Add</x:String>
<x:String x:Key="NewPresetName">New Preset</x:String>
<x:String x:Key="AddPresetToolTip">Create a new preset</x:String>
<x:String x:Key="DeletePreset">Delete</x:String>
<x:String x:Key="DeletePresetToolTip">Delete selected presets</x:String>
<x:String x:Key="ApplyPreset">Apply</x:String>
<x:String x:Key="ApplyPresetToolTip">Apply the selected preset</x:String>
<x:String x:Key="PresetsWindowTitle">Presets</x:String>
<x:String x:Key="PresetServersHeader">Blocked Servers</x:String>
<x:String x:Key="BlockedColumnHeader">Blocked</x:String>
<x:String x:Key="PresetListHeader">Preset</x:String>
<x:String x:Key="PresetDeleteConfirmDialogue">Delete the preset '{0}'?</x:String>
<x:String x:Key="PresetDeleteSelectedConfirmDialogue">Delete the {0} selected presets?</x:String>
<x:String x:Key="PresetOverwriteConfirmDialogue">A preset named '{0}' already exists for this game. Overwrite it?</x:String>
<x:String x:Key="PresetChangeViewModeConfirmDialogue">Changing between clustered and unclustered view will clear this preset's blocked entries. Continue?</x:String>
<x:String x:Key="PresetNameRequiredDialogue">Preset name cannot be empty.</x:String>
</ResourceDictionary>
26 changes: 24 additions & 2 deletions ServerPickerX/Locales/Locale_es-es.axaml
Original file line number Diff line number Diff line change
@@ -1,28 +1,50 @@
<ResourceDictionary xmlns="https://github.com/avaloniaui"
<ResourceDictionary xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<x:String x:Key="LanguageCode">es-es</x:String>
<x:String x:Key="ClusterServers">Servidores de clúster</x:String>
<x:String x:Key="UnclusterServers">Servidores Uncluster</x:String>
<x:String x:Key="Refresh">Refrescar</x:String>
<x:String x:Key="SearchForServers">Buscar servidores...</x:String>
<x:String x:Key="NoPresets">No hay presets</x:String>
<x:String x:Key="BlockAll">Bloquear Todo</x:String>
<x:String x:Key="BlockSelected">Bloque Seleccionado</x:String>
<x:String x:Key="UnblockAll">Desbloquear Todo</x:String>
<x:String x:Key="UnblockSelected">Desbloquear Seleccionado</x:String>
<x:String x:Key="SaveButtonText">Guardar</x:String>
<x:String x:Key="CancelButtonText">Cancelar</x:String>
<x:String x:Key="CheckForNewVersion">Comprobar si hay nueva versión al iniciar</x:String>
<x:String x:Key="ResetFirewall">Restablecer el cortafuegos</x:String>
<x:String x:Key="GameModeToolTip">Seleccionar modo de juego</x:String>
<x:String x:Key="PresetToolTip">Seleccionar preset</x:String>
<x:String x:Key="ClusterUnclusterToolTip">Agrupar o desagrupar servidores</x:String>
<x:String x:Key="RefreshToolTip">Actualizar el ping de todos los servidores</x:String>
<x:String x:Key="SwapGameModeUnblockAllConflict">Esta acción desbloqueará primero todos los servidores para evitar conflictos de firewall.</x:String>
<x:String x:Key="LanguageComboBoxToolTip">Seleccionar idioma</x:String>
<x:String x:Key="ResetFirewallToolTip">Restablecer reglas del firewall</x:String>
<x:String x:Key="MessageBoxInfoTitle">Información</x:String>
<x:String x:Key="SyncServersUnblockAllDialogue">¡Valve acaba de actualizar los datos del servidor! Todos los servidores bloqueados serán desbloqueados para sincronizar los nuevos datos.</x:String>
<x:String x:Key="SyncServersUnblockAllDialogue">Valve acaba de actualizar los datos del servidor. Todos los servidores bloqueados se desbloquearán para sincronizar los nuevos datos. Se eliminarán las entradas del preset correspondientes a servidores que ya no existen y, si está disponible, se volverá a aplicar el último preset seleccionado.</x:String>
<x:String x:Key="NewVersionDialogue">¡Nueva versión disponible! ¿Ir a lanzamientos?</x:String>
<x:String x:Key="SelectOneServerToBlockDialogue">Por favor, selecciona al menos un servidor para bloquear.</x:String>
<x:String x:Key="SelectOneServerToUnblockDialogue">Seleccione al menos un servidor para desbloquear.</x:String>
<x:String x:Key="PendingOperationDialogue">¡Vaya! Ya hay una operación pendiente. Por favor, espere...</x:String>
<x:String x:Key="FirewallResetConfirmDialogue">Esto intentará restablecer el cortafuegos a su estado predeterminado. ¿Confirma la acción?</x:String>
<x:String x:Key="FirewallResetSuccessDialogue">¡El cortafuegos se ha restablecido correctamente a su estado predeterminado!</x:String>
<x:String x:Key="Presets">Preajustes</x:String>
<x:String x:Key="PresetsToolTip">Administrar preajustes</x:String>
<x:String x:Key="AddPreset">Añadir</x:String>
<x:String x:Key="NewPresetName">Nuevo preajuste</x:String>
<x:String x:Key="AddPresetToolTip">Crear un nuevo preajuste</x:String>
<x:String x:Key="DeletePreset">Eliminar</x:String>
<x:String x:Key="DeletePresetToolTip">Eliminar los preajustes seleccionados</x:String>
<x:String x:Key="ApplyPreset">Aplicar</x:String>
<x:String x:Key="ApplyPresetToolTip">Aplicar el preajuste seleccionado</x:String>
<x:String x:Key="PresetsWindowTitle">Preajustes</x:String>
<x:String x:Key="PresetServersHeader">Servidores bloqueados</x:String>
<x:String x:Key="BlockedColumnHeader">Bloqueado</x:String>
<x:String x:Key="PresetListHeader">Preajuste</x:String>
<x:String x:Key="PresetDeleteConfirmDialogue">¿Eliminar el preset '{0}'?</x:String>
<x:String x:Key="PresetDeleteSelectedConfirmDialogue">¿Eliminar los {0} preajustes seleccionados?</x:String>
<x:String x:Key="PresetOverwriteConfirmDialogue">Ya existe un preset llamado '{0}' para este juego. ¿Sobrescribirlo?</x:String>
<x:String x:Key="PresetChangeViewModeConfirmDialogue">Cambiar entre la vista agrupada y no agrupada borrará las entradas bloqueadas de este preajuste. ¿Continuar?</x:String>
<x:String x:Key="PresetNameRequiredDialogue">El nombre del preset no puede estar vacío.</x:String>
</ResourceDictionary>
Loading
Loading