2024-12-21 15:15:04 +03:00
|
|
|
using System;
|
2024-12-26 09:49:01 +03:00
|
|
|
using System.Collections.Generic;
|
2024-12-22 16:38:47 +03:00
|
|
|
using System.Collections.ObjectModel;
|
2024-12-27 08:22:17 +03:00
|
|
|
using System.Linq;
|
2025-06-22 10:40:42 +03:00
|
|
|
using System.Threading;
|
2024-12-22 16:38:47 +03:00
|
|
|
using CommunityToolkit.Mvvm.ComponentModel;
|
2025-06-22 22:11:27 +03:00
|
|
|
using JetBrains.Annotations;
|
2025-06-14 22:33:03 +03:00
|
|
|
using Microsoft.Extensions.DependencyInjection;
|
2025-05-02 20:06:33 +03:00
|
|
|
using Nebula.Launcher.Controls;
|
2025-06-14 22:33:03 +03:00
|
|
|
using Nebula.Launcher.Models;
|
|
|
|
|
using Nebula.Launcher.ServerListProviders;
|
2025-01-14 22:10:16 +03:00
|
|
|
using Nebula.Launcher.Services;
|
2024-12-21 15:15:04 +03:00
|
|
|
using Nebula.Launcher.Views.Pages;
|
2025-06-14 22:33:03 +03:00
|
|
|
using Nebula.Shared;
|
2025-01-05 17:05:23 +03:00
|
|
|
using Nebula.Shared.Models;
|
|
|
|
|
using Nebula.Shared.Services;
|
2025-07-02 21:32:51 +03:00
|
|
|
using Nebula.Shared.ViewHelper;
|
2024-12-21 15:15:04 +03:00
|
|
|
|
2025-01-14 22:10:16 +03:00
|
|
|
namespace Nebula.Launcher.ViewModels.Pages;
|
2024-12-21 15:15:04 +03:00
|
|
|
|
2025-06-14 22:33:03 +03:00
|
|
|
[ViewModelRegister(typeof(ServerOverviewView))]
|
2025-01-14 22:10:16 +03:00
|
|
|
[ConstructGenerator]
|
2025-06-14 22:33:03 +03:00
|
|
|
public partial class ServerOverviewModel : ViewModelBase
|
2024-12-21 15:15:04 +03:00
|
|
|
{
|
2025-01-14 22:10:16 +03:00
|
|
|
[ObservableProperty] private string _searchText = string.Empty;
|
2025-03-14 19:42:55 +03:00
|
|
|
[ObservableProperty] private bool _isFilterVisible;
|
|
|
|
|
|
2025-06-22 22:11:27 +03:00
|
|
|
public readonly ServerFilter CurrentFilter = new();
|
2025-06-14 22:33:03 +03:00
|
|
|
[GenerateProperty] private IServiceProvider ServiceProvider { get; }
|
|
|
|
|
[GenerateProperty] private ConfigurationService ConfigurationService { get; }
|
|
|
|
|
[GenerateProperty] private FavoriteServerListProvider FavoriteServerListProvider { get; }
|
|
|
|
|
public ObservableCollection<ServerListTabTemplate> Items { get; private set; }
|
|
|
|
|
[ObservableProperty] private ServerListTabTemplate _selectedItem;
|
2025-06-22 22:11:27 +03:00
|
|
|
[GenerateProperty, DesignConstruct] private ServerViewContainer ServerViewContainer { get; }
|
2025-12-11 21:47:54 +03:00
|
|
|
[GenerateProperty, DesignConstruct] public ServerListViewModel CurrentServerList { get; }
|
2025-01-30 10:11:54 +03:00
|
|
|
|
2025-01-14 22:10:16 +03:00
|
|
|
|
2024-12-22 21:38:19 +03:00
|
|
|
//Design think
|
2025-01-14 22:10:16 +03:00
|
|
|
protected override void InitialiseInDesignMode()
|
2024-12-22 16:38:47 +03:00
|
|
|
{
|
2025-06-14 22:33:03 +03:00
|
|
|
Items = new ObservableCollection<ServerListTabTemplate>([
|
|
|
|
|
new ServerListTabTemplate(new TestServerList(), "Test think"),
|
|
|
|
|
new ServerListTabTemplate(new TestServerList(), "Test think2")
|
|
|
|
|
]);
|
|
|
|
|
SelectedItem = Items[0];
|
2024-12-22 16:38:47 +03:00
|
|
|
}
|
2025-01-14 22:10:16 +03:00
|
|
|
|
2024-12-22 21:38:19 +03:00
|
|
|
//real think
|
2025-01-14 22:10:16 +03:00
|
|
|
protected override void Initialise()
|
2025-06-19 21:12:42 +03:00
|
|
|
{
|
|
|
|
|
ConfigurationService.SubscribeVarChanged(LauncherConVar.Hub, OnHubListChanged, true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void OnHubListChanged(ServerHubRecord[]? value)
|
2024-12-22 16:38:47 +03:00
|
|
|
{
|
2025-06-14 22:33:03 +03:00
|
|
|
var tempItems = new List<ServerListTabTemplate>();
|
2025-06-19 21:12:42 +03:00
|
|
|
|
|
|
|
|
foreach (var record in value ?? [])
|
2025-06-14 22:33:03 +03:00
|
|
|
{
|
|
|
|
|
tempItems.Add(new ServerListTabTemplate(ServiceProvider.GetService<HubServerListProvider>()!.With(record.MainUrl), record.Name));
|
|
|
|
|
}
|
2025-01-30 10:11:54 +03:00
|
|
|
|
2025-12-06 23:25:25 +03:00
|
|
|
tempItems.Add(new ServerListTabTemplate(FavoriteServerListProvider, LocalizationService.GetString("tab-favorite")));
|
2025-06-14 22:33:03 +03:00
|
|
|
|
|
|
|
|
Items = new ObservableCollection<ServerListTabTemplate>(tempItems);
|
|
|
|
|
|
|
|
|
|
SelectedItem = Items[0];
|
2024-12-27 08:22:17 +03:00
|
|
|
}
|
2025-06-15 14:04:29 +03:00
|
|
|
|
2025-06-28 14:05:19 +03:00
|
|
|
partial void OnSearchTextChanged(string value)
|
2025-06-15 14:04:29 +03:00
|
|
|
{
|
2025-06-28 14:05:19 +03:00
|
|
|
CurrentFilter.SearchText = value;
|
2025-06-15 14:04:29 +03:00
|
|
|
ApplyFilter();
|
|
|
|
|
}
|
|
|
|
|
|
2025-05-02 20:06:33 +03:00
|
|
|
public void ApplyFilter()
|
2025-03-14 19:42:55 +03:00
|
|
|
{
|
2025-05-02 20:06:33 +03:00
|
|
|
foreach (var entry in ServerViewContainer.Items)
|
2025-03-14 19:42:55 +03:00
|
|
|
{
|
2025-06-22 10:40:42 +03:00
|
|
|
if(entry is IFilterConsumer filterConsumer)
|
|
|
|
|
filterConsumer.ProcessFilter(CurrentFilter);
|
2025-03-14 19:42:55 +03:00
|
|
|
}
|
2025-05-02 20:06:33 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void OnFilterChanged(FilterBoxChangedEventArgs args)
|
|
|
|
|
{
|
|
|
|
|
if (args.Checked)
|
|
|
|
|
CurrentFilter.Tags.Add(args.Tag);
|
2025-03-14 19:42:55 +03:00
|
|
|
else
|
2025-05-02 20:06:33 +03:00
|
|
|
CurrentFilter.Tags.Remove(args.Tag);
|
|
|
|
|
ApplyFilter();
|
2025-03-14 19:42:55 +03:00
|
|
|
}
|
2025-06-14 22:33:03 +03:00
|
|
|
|
|
|
|
|
public void FilterRequired()
|
2025-01-30 10:11:54 +03:00
|
|
|
{
|
2025-06-14 22:33:03 +03:00
|
|
|
IsFilterVisible = !IsFilterVisible;
|
2025-01-30 10:11:54 +03:00
|
|
|
}
|
|
|
|
|
|
2025-06-14 22:33:03 +03:00
|
|
|
public void UpdateRequired()
|
2024-12-27 08:22:17 +03:00
|
|
|
{
|
2025-06-22 10:40:42 +03:00
|
|
|
ServerViewContainer.Clear();
|
2025-06-14 22:33:03 +03:00
|
|
|
CurrentServerList.RefreshFromProvider();
|
2025-06-17 21:07:32 +03:00
|
|
|
CurrentServerList.ApplyFilter(CurrentFilter);
|
2024-12-22 16:38:47 +03:00
|
|
|
}
|
|
|
|
|
|
2025-06-14 22:33:03 +03:00
|
|
|
partial void OnSelectedItemChanged(ServerListTabTemplate value)
|
2024-12-21 15:15:04 +03:00
|
|
|
{
|
2025-12-11 21:47:54 +03:00
|
|
|
CurrentServerList.Provider = value.ServerListProvider;
|
2025-06-22 22:11:27 +03:00
|
|
|
ApplyFilter();
|
2024-12-27 08:22:17 +03:00
|
|
|
}
|
2025-06-14 22:33:03 +03:00
|
|
|
|
|
|
|
|
}
|
2024-12-27 08:22:17 +03:00
|
|
|
|
2025-06-14 22:33:03 +03:00
|
|
|
[ServiceRegister]
|
2025-12-11 21:47:54 +03:00
|
|
|
public sealed class ServerViewContainer
|
2025-06-14 22:33:03 +03:00
|
|
|
{
|
|
|
|
|
private readonly ViewHelperService _viewHelperService;
|
2025-06-22 22:11:27 +03:00
|
|
|
private readonly List<string> _favorites = [];
|
|
|
|
|
private readonly Dictionary<string, string> _customNames = [];
|
2025-01-29 12:32:42 +03:00
|
|
|
|
2025-12-12 22:23:45 +03:00
|
|
|
private readonly Dictionary<string, WeakReference<IListEntryModelView>> _entries = new();
|
|
|
|
|
|
|
|
|
|
public ICollection<IListEntryModelView> Items =>
|
|
|
|
|
_entries.Values
|
|
|
|
|
.Select(wr => wr.TryGetTarget(out var target) ? target : null)
|
|
|
|
|
.Where(t => t != null)
|
|
|
|
|
.ToList()!;
|
|
|
|
|
|
2025-06-14 22:33:03 +03:00
|
|
|
public ServerViewContainer()
|
2024-12-27 19:15:33 +03:00
|
|
|
{
|
2025-06-14 22:33:03 +03:00
|
|
|
_viewHelperService = new ViewHelperService();
|
2024-12-27 19:15:33 +03:00
|
|
|
}
|
2025-01-29 12:32:42 +03:00
|
|
|
|
2025-06-22 22:11:27 +03:00
|
|
|
[UsedImplicitly]
|
2025-06-19 21:12:42 +03:00
|
|
|
public ServerViewContainer(ViewHelperService viewHelperService, ConfigurationService configurationService)
|
2025-01-29 12:32:42 +03:00
|
|
|
{
|
2025-06-14 22:33:03 +03:00
|
|
|
_viewHelperService = viewHelperService;
|
2025-06-19 21:12:42 +03:00
|
|
|
configurationService.SubscribeVarChanged(LauncherConVar.Favorites, OnFavoritesChange, true);
|
2025-06-22 22:11:27 +03:00
|
|
|
configurationService.SubscribeVarChanged(LauncherConVar.ServerCustomNames, OnCustomNamesChanged, true);
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-12 22:23:45 +03:00
|
|
|
public void Clear()
|
2025-06-22 22:11:27 +03:00
|
|
|
{
|
2025-12-12 22:23:45 +03:00
|
|
|
foreach (var (_, weakRef) in _entries)
|
2025-06-22 22:11:27 +03:00
|
|
|
{
|
2025-12-12 22:23:45 +03:00
|
|
|
if (weakRef.TryGetTarget(out var value))
|
|
|
|
|
value.Dispose();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_entries.Clear();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public IListEntryModelView Get(RobustUrl url, ServerStatus? serverStatus = null)
|
|
|
|
|
{
|
|
|
|
|
var key = url.ToString();
|
|
|
|
|
IListEntryModelView? entry;
|
|
|
|
|
|
|
|
|
|
lock (_entries)
|
|
|
|
|
{
|
|
|
|
|
_customNames.TryGetValue(key, out var customName);
|
|
|
|
|
|
|
|
|
|
if (_entries.TryGetValue(key, out var weakEntry)
|
|
|
|
|
&& weakEntry.TryGetTarget(out entry))
|
2025-06-22 22:11:27 +03:00
|
|
|
{
|
2025-12-12 22:23:45 +03:00
|
|
|
return entry;
|
|
|
|
|
}
|
2025-06-22 22:11:27 +03:00
|
|
|
|
2025-12-12 22:23:45 +03:00
|
|
|
if (serverStatus is not null)
|
|
|
|
|
{
|
|
|
|
|
entry = _viewHelperService
|
|
|
|
|
.GetViewModel<ServerEntryModelView>()
|
|
|
|
|
.WithData(url, customName, serverStatus);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
entry = _viewHelperService
|
|
|
|
|
.GetViewModel<ServerCompoundEntryViewModel>()
|
|
|
|
|
.LoadServerEntry(url, customName, CancellationToken.None);
|
2025-06-22 22:11:27 +03:00
|
|
|
}
|
|
|
|
|
|
2025-12-12 22:23:45 +03:00
|
|
|
if (_favorites.Contains(key)
|
|
|
|
|
&& entry is IFavoriteEntryModelView fav)
|
2025-06-22 22:11:27 +03:00
|
|
|
{
|
2025-12-12 22:23:45 +03:00
|
|
|
fav.IsFavorite = true;
|
2025-06-22 22:11:27 +03:00
|
|
|
}
|
2025-12-12 22:23:45 +03:00
|
|
|
|
|
|
|
|
_entries[key] = new WeakReference<IListEntryModelView>(entry);
|
2025-06-22 22:11:27 +03:00
|
|
|
}
|
2025-12-12 22:23:45 +03:00
|
|
|
|
|
|
|
|
return entry;
|
2025-01-29 12:32:42 +03:00
|
|
|
}
|
2025-06-19 21:12:42 +03:00
|
|
|
|
|
|
|
|
private void OnFavoritesChange(string[]? value)
|
|
|
|
|
{
|
2025-06-22 22:11:27 +03:00
|
|
|
_favorites.Clear();
|
2025-12-12 22:23:45 +03:00
|
|
|
if (value == null) return;
|
|
|
|
|
|
2025-06-22 22:11:27 +03:00
|
|
|
foreach (var favorite in value)
|
2025-06-19 21:12:42 +03:00
|
|
|
{
|
2025-06-22 22:11:27 +03:00
|
|
|
_favorites.Add(favorite);
|
2025-12-12 22:23:45 +03:00
|
|
|
if (_entries.TryGetValue(favorite, out var weak)
|
|
|
|
|
&& weak.TryGetTarget(out var entry)
|
|
|
|
|
&& entry is IFavoriteEntryModelView fav)
|
2025-06-19 21:12:42 +03:00
|
|
|
{
|
2025-12-12 22:23:45 +03:00
|
|
|
fav.IsFavorite = true;
|
2025-06-19 21:12:42 +03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-12 22:23:45 +03:00
|
|
|
private void OnCustomNamesChanged(Dictionary<string, string>? value)
|
2025-01-30 20:18:40 +03:00
|
|
|
{
|
2025-12-12 22:23:45 +03:00
|
|
|
var oldNames = _customNames.ToDictionary(x => x.Key, x => x.Value);
|
|
|
|
|
_customNames.Clear();
|
|
|
|
|
|
|
|
|
|
if (value == null)
|
2025-12-11 21:47:54 +03:00
|
|
|
{
|
2025-12-12 22:23:45 +03:00
|
|
|
foreach (var (ip, _) in oldNames)
|
|
|
|
|
{
|
|
|
|
|
ResetName(ip);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return;
|
2025-12-11 21:47:54 +03:00
|
|
|
}
|
2025-01-30 20:18:40 +03:00
|
|
|
|
2025-12-12 22:23:45 +03:00
|
|
|
foreach (var (oldIp, oldName) in oldNames)
|
2025-01-30 10:11:54 +03:00
|
|
|
{
|
2025-12-12 22:23:45 +03:00
|
|
|
if (value.TryGetValue(oldIp, out var newName))
|
2025-01-31 22:55:01 +03:00
|
|
|
{
|
2025-12-12 22:23:45 +03:00
|
|
|
if (oldName == newName)
|
|
|
|
|
value.Remove(newName);
|
|
|
|
|
|
|
|
|
|
continue;
|
2025-01-31 22:55:01 +03:00
|
|
|
}
|
2025-01-30 10:11:54 +03:00
|
|
|
|
2025-12-12 22:23:45 +03:00
|
|
|
ResetName(oldIp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
foreach (var (ip, name) in value)
|
|
|
|
|
{
|
|
|
|
|
_customNames.Add(ip, name);
|
|
|
|
|
|
|
|
|
|
if (_entries.TryGetValue(ip, out var weak)
|
|
|
|
|
&& weak.TryGetTarget(out var entry)
|
|
|
|
|
&& entry is IEntryNameHolder holder)
|
|
|
|
|
{
|
|
|
|
|
holder.Name = name;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void ResetName(string ip)
|
|
|
|
|
{
|
|
|
|
|
if (_entries.TryGetValue(ip, out var weak)
|
|
|
|
|
&& weak.TryGetTarget(out var entry)
|
|
|
|
|
&& entry is IEntryNameHolder holder)
|
|
|
|
|
{
|
|
|
|
|
holder.Name = null;
|
2025-01-30 20:18:40 +03:00
|
|
|
}
|
2025-01-30 10:11:54 +03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-11 21:47:54 +03:00
|
|
|
public interface IListEntryModelView : IDisposable
|
2025-06-22 10:40:42 +03:00
|
|
|
{
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public interface IFavoriteEntryModelView
|
|
|
|
|
{
|
|
|
|
|
public bool IsFavorite { get; set; }
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-22 22:11:27 +03:00
|
|
|
public interface IEntryNameHolder
|
|
|
|
|
{
|
|
|
|
|
public string? Name { get; set; }
|
|
|
|
|
}
|
|
|
|
|
|
2025-01-28 19:59:35 +03:00
|
|
|
public class ServerComparer : IComparer<ServerHubInfo>, IComparer<ServerStatus>, IComparer<(RobustUrl,ServerStatus)>
|
2024-12-26 09:49:01 +03:00
|
|
|
{
|
|
|
|
|
public int Compare(ServerHubInfo? x, ServerHubInfo? y)
|
|
|
|
|
{
|
|
|
|
|
if (ReferenceEquals(x, y))
|
|
|
|
|
return 0;
|
|
|
|
|
if (ReferenceEquals(null, y))
|
|
|
|
|
return 1;
|
|
|
|
|
if (ReferenceEquals(null, x))
|
|
|
|
|
return -1;
|
|
|
|
|
|
2025-01-28 19:59:35 +03:00
|
|
|
return Compare(x.StatusData, y.StatusData);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public int Compare(ServerStatus? x, ServerStatus? y)
|
|
|
|
|
{
|
|
|
|
|
if (ReferenceEquals(x, y))
|
|
|
|
|
return 0;
|
|
|
|
|
if (ReferenceEquals(null, y))
|
|
|
|
|
return 1;
|
|
|
|
|
if (ReferenceEquals(null, x))
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
return y.Players.CompareTo(x.Players);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public int Compare((RobustUrl, ServerStatus) x, (RobustUrl, ServerStatus) y)
|
|
|
|
|
{
|
|
|
|
|
return Compare(x.Item2, y.Item2);
|
2024-12-21 15:15:04 +03:00
|
|
|
}
|
2025-05-02 20:06:33 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public sealed class ServerFilter
|
|
|
|
|
{
|
|
|
|
|
public string SearchText { get; set; } = "";
|
|
|
|
|
public HashSet<string> Tags { get; } = new();
|
|
|
|
|
public bool IsMatchByName(string name)
|
|
|
|
|
{
|
|
|
|
|
if (string.IsNullOrWhiteSpace(SearchText))
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
return name.Contains(SearchText, StringComparison.OrdinalIgnoreCase);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool IsMatchByTags(IEnumerable<string> itemTags)
|
|
|
|
|
{
|
|
|
|
|
if (Tags.Count == 0)
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
var itemTagSet = new HashSet<string>(itemTags);
|
|
|
|
|
return Tags.All(tag => itemTagSet.Contains(tag));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool IsMatch(string name, IEnumerable<string> itemTags)
|
|
|
|
|
{
|
|
|
|
|
return IsMatchByName(name) && IsMatchByTags(itemTags);
|
|
|
|
|
}
|
2025-07-02 21:32:51 +03:00
|
|
|
}
|