- fix: remove cloning server entry in list

This commit is contained in:
2025-03-14 17:08:35 +03:00
parent 5384e97640
commit 6ae50486d5
8 changed files with 169 additions and 61 deletions

View File

@@ -7,6 +7,7 @@ using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Data.Core.Plugins; using Avalonia.Data.Core.Plugins;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Nebula.Launcher.ViewModels.ContentView;
using Nebula.Launcher.Views; using Nebula.Launcher.Views;
using Nebula.Shared; using Nebula.Shared;
@@ -40,6 +41,7 @@ public class App : Application
services.AddAvaloniaServices(); services.AddAvaloniaServices();
services.AddServices(); services.AddServices();
services.AddViews(); services.AddViews();
services.AddTransient<DecompilerContentView>();
var serviceProvider = services.BuildServiceProvider(); var serviceProvider = services.BuildServiceProvider();

View File

@@ -19,4 +19,6 @@ public static class LauncherConVar
]); ]);
public static readonly ConVar<string> CurrentLang = ConVarBuilder.Build<string>("launcher.language", "en-US"); public static readonly ConVar<string> CurrentLang = ConVarBuilder.Build<string>("launcher.language", "en-US");
public static readonly ConVar<string> ILSpyUrl = ConVarBuilder.Build<string>("decompiler.url",
"https://github.com/icsharpcode/ILSpy/releases/download/v9.0/ILSpy_binaries_9.0.0.7889-x64.zip");
} }

View File

@@ -0,0 +1,54 @@
using System.Diagnostics;
using System.IO;
using System.IO.Compression;
using System.Net.Http;
using System.Threading.Tasks;
using Nebula.Launcher.ViewModels.Popup;
using Nebula.Shared;
using Nebula.Shared.FileApis;
using Nebula.Shared.FileApis.Interfaces;
using Nebula.Shared.Services;
namespace Nebula.Launcher.Services;
[ConstructGenerator, ServiceRegister]
public sealed partial class DecompilerService
{
[GenerateProperty] private ConfigurationService ConfigurationService { get; }
[GenerateProperty] private PopupMessageService PopupMessageService {get;}
[GenerateProperty] private ViewHelperService ViewHelperService {get;}
private HttpClient _httpClient = new HttpClient();
private static string fullPath = Path.Join(FileService.RootPath,"ILSpy");
private static string executePath = Path.Join(fullPath, "ILSpy.exe");
public async void OpenDecompiler(string path){
await EnsureILSpy();
var startInfo = new ProcessStartInfo(){
FileName = executePath,
Arguments = path
};
Process.Start(startInfo);
}
private void Initialise(){}
private void InitialiseInDesignMode(){}
private async Task EnsureILSpy(){
if(!Directory.Exists(fullPath))
await Download();
}
private async Task Download(){
using var loading = ViewHelperService.GetViewModel<LoadingContextViewModel>();
loading.LoadingName = "Download ILSpy";
loading.SetJobsCount(1);
PopupMessageService.Popup(loading);
using var response = await _httpClient.GetAsync(ConfigurationService.GetConfigValue(LauncherConVar.ILSpyUrl));
using var zipArchive = new ZipArchive(await response.Content.ReadAsStreamAsync());
Directory.CreateDirectory(fullPath);
zipArchive.ExtractToDirectory(fullPath);
}
}

View File

@@ -0,0 +1,32 @@
using System.IO;
using Nebula.Launcher.Services;
using Nebula.Launcher.ViewModels.Pages;
namespace Nebula.Launcher.ViewModels.ContentView;
[ConstructGenerator]
public sealed partial class DecompilerContentView: ContentViewBase
{
[GenerateProperty] private DecompilerService decompilerService {get;}
public override void InitialiseWithData(ContentPath path, Stream stream)
{
base.InitialiseWithData(path, stream);
var myTempFile = Path.Combine(Path.GetTempPath(), "tempie.dll");
var sw = new FileStream(myTempFile, FileMode.Create, FileAccess.Write, FileShare.None);
stream.CopyTo(sw);
sw.Dispose();
stream.Dispose();
decompilerService.OpenDecompiler(myTempFile);
}
protected override void Initialise()
{
}
protected override void InitialiseInDesignMode()
{
}
}

View File

@@ -7,14 +7,12 @@ using System.Diagnostics.CodeAnalysis;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Avalonia.Media;
using Avalonia.Media.Imaging;
using Avalonia.Platform;
using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.ComponentModel;
using Nebula.Launcher.Services; using Nebula.Launcher.Services;
using Nebula.Launcher.ViewModels.ContentView; using Nebula.Launcher.ViewModels.ContentView;
using Nebula.Launcher.ViewModels.Popup; using Nebula.Launcher.ViewModels.Popup;
using Nebula.Launcher.Views.Pages; using Nebula.Launcher.Views.Pages;
using Nebula.Shared.FileApis;
using Nebula.Shared.Models; using Nebula.Shared.Models;
using Nebula.Shared.Services; using Nebula.Shared.Services;
using Nebula.Shared.Utils; using Nebula.Shared.Utils;
@@ -44,6 +42,7 @@ public sealed partial class ContentBrowserViewModel : ViewModelBase , IViewModel
[GenerateProperty] private DebugService DebugService { get; } = default!; [GenerateProperty] private DebugService DebugService { get; } = default!;
[GenerateProperty] private PopupMessageService PopupService { get; } = default!; [GenerateProperty] private PopupMessageService PopupService { get; } = default!;
[GenerateProperty] private HubService HubService { get; } = default!; [GenerateProperty] private HubService HubService { get; } = default!;
[GenerateProperty] private IServiceProvider ServiceProvider {get;}
[GenerateProperty, DesignConstruct] private ViewHelperService ViewHelperService { get; } = default!; [GenerateProperty, DesignConstruct] private ViewHelperService ViewHelperService { get; } = default!;
public ObservableCollection<ContentEntry> Entries { get; } = new(); public ObservableCollection<ContentEntry> Entries { get; } = new();
@@ -55,27 +54,31 @@ public sealed partial class ContentBrowserViewModel : ViewModelBase , IViewModel
get => _selectedEntry; get => _selectedEntry;
set set
{ {
var oldSearchText = SearchText;
SearchText = value?.GetPath().ToString() ?? ""; SearchText = value?.GetPath().ToString() ?? "";
ContentView = null;
if (value is { Item: not null }) ContentView = null;
{ Entries.Clear();
if (FileService.ContentFileApi.TryOpen(value.Item.Value.Hash, out var stream)) _selectedEntry = value;
{
var ext = Path.GetExtension(value.Item.Value.Path); if (value == null) return;
if(value.TryOpen(out var stream, out var item)){
var ext = Path.GetExtension(item.Value.Path);
var myTempFile = Path.Combine(Path.GetTempPath(), "tempie" + ext);
if(TryGetContentViewer(ext, out var contentViewBase)){ if(TryGetContentViewer(ext, out var contentViewBase)){
DebugService.Debug($"Opening custom context:{item.Value.Path}");
contentViewBase.InitialiseWithData(value.GetPath(), stream); contentViewBase.InitialiseWithData(value.GetPath(), stream);
ContentView = contentViewBase; ContentView = contentViewBase;
return; return;
} }
var myTempFile = Path.Combine(Path.GetTempPath(), "tempie" + ext); var sw = new FileStream(myTempFile, FileMode.Create, FileAccess.Write, FileShare.None);
using (var sw = new FileStream(myTempFile, FileMode.Create, FileAccess.Write, FileShare.None))
{
stream.CopyTo(sw); stream.CopyTo(sw);
}
sw.Dispose();
stream.Dispose(); stream.Dispose();
var startInfo = new ProcessStartInfo(myTempFile) var startInfo = new ProcessStartInfo(myTempFile)
@@ -83,16 +86,15 @@ public sealed partial class ContentBrowserViewModel : ViewModelBase , IViewModel
UseShellExecute = true UseShellExecute = true
}; };
DebugService.Log("Opening " + myTempFile);
Process.Start(startInfo); Process.Start(startInfo);
}
return; return;
} }
Entries.Clear(); if(SearchText.Length > oldSearchText.Length)
_selectedEntry = value; AppendHistory(oldSearchText);
if (value == null) return;
foreach (var (_, entryCh) in value.Childs) Entries.Add(entryCh); foreach (var (_, entryCh) in value.Childs) Entries.Add(entryCh);
} }
@@ -104,16 +106,15 @@ public sealed partial class ContentBrowserViewModel : ViewModelBase , IViewModel
!contentViewType.IsAssignableTo(typeof(ContentViewBase))) !contentViewType.IsAssignableTo(typeof(ContentViewBase)))
return false; return false;
contentViewBase = (ContentViewBase)ServiceProvider.GetService(contentViewType)!;
contentViewBase = (ContentViewBase)Activator.CreateInstance(contentViewType)!;
return true; return true;
} }
protected override void InitialiseInDesignMode() protected override void InitialiseInDesignMode()
{ {
var a = new ContentEntry(this, "A:", "A", ""); var a = new ContentEntry(this, "A:", "A", "", default!);
var b = new ContentEntry(this, "B", "B", ""); var b = new ContentEntry(this, "B", "B", "", default!);
a.TryAddChild(b); a.TryAddChild(b);
Entries.Add(a); Entries.Add(a);
} }
@@ -126,6 +127,8 @@ public sealed partial class ContentBrowserViewModel : ViewModelBase , IViewModel
HubService.HubServerLoaded += GoHome; HubService.HubServerLoaded += GoHome;
if (!HubService.IsUpdating) GoHome(); if (!HubService.IsUpdating) GoHome();
_contentContainers.Add(".dll",typeof(DecompilerContentView));
} }
private void GoHome() private void GoHome()
@@ -142,10 +145,10 @@ public sealed partial class ContentBrowserViewModel : ViewModelBase , IViewModel
private void FillRoot(IEnumerable<ServerHubInfo> infos) private void FillRoot(IEnumerable<ServerHubInfo> infos)
{ {
foreach (var info in infos) _root.Add(new ContentEntry(this, info.StatusData.Name, info.Address, info.Address)); foreach (var info in infos) _root.Add(new ContentEntry(this, info.StatusData.Name, info.Address, info.Address, default!));
} }
public async void Go(ContentPath path, bool appendHistory = true) public async void Go(ContentPath path)
{ {
if (path.Pathes.Count > 0 && (path.Pathes[0].StartsWith("ss14://") || path.Pathes[0].StartsWith("ss14s://"))) if (path.Pathes.Count > 0 && (path.Pathes[0].StartsWith("ss14://") || path.Pathes[0].StartsWith("ss14s://")))
{ {
@@ -181,7 +184,7 @@ public sealed partial class ContentBrowserViewModel : ViewModelBase , IViewModel
public void OnBackEnter() public void OnBackEnter()
{ {
Go(new ContentPath(GetHistory()), false); Go(new ContentPath(GetHistory()));
} }
public void OnGoEnter() public void OnGoEnter()
@@ -200,12 +203,12 @@ public sealed partial class ContentBrowserViewModel : ViewModelBase , IViewModel
var hashApi = await ContentService.EnsureItems(info.RobustManifestInfo, loading, var hashApi = await ContentService.EnsureItems(info.RobustManifestInfo, loading,
CancellationService.Token); CancellationService.Token);
var rootEntry = new ContentEntry(this, "", "", serverUrl); var rootEntry = new ContentEntry(this, "", "", serverUrl, default!);
foreach (var item in hashApi.Manifest.Values) foreach (var item in hashApi.Manifest.Values)
{ {
var path = new ContentPath(item.Path); var path = new ContentPath(item.Path);
rootEntry.CreateItem(path, item); rootEntry.CreateItem(path, item, hashApi);
} }
loading.Dispose(); loading.Dispose();
@@ -236,15 +239,17 @@ public class ContentEntry
{ {
private readonly Dictionary<string, ContentEntry> _childs = new(); private readonly Dictionary<string, ContentEntry> _childs = new();
private readonly ContentBrowserViewModel _viewModel; private readonly ContentBrowserViewModel _viewModel;
private HashApi _fileApi;
public RobustManifestItem? Item; public RobustManifestItem? Item { get; private set; }
internal ContentEntry(ContentBrowserViewModel viewModel, string name, string pathName, string serverName) internal ContentEntry(ContentBrowserViewModel viewModel, string name, string pathName, string serverName, HashApi fileApi)
{ {
Name = name; Name = name;
ServerName = serverName; ServerName = serverName;
PathName = pathName; PathName = pathName;
_viewModel = viewModel; _viewModel = viewModel;
_fileApi = fileApi;
} }
public bool IsDirectory => Item == null; public bool IsDirectory => Item == null;
@@ -259,6 +264,22 @@ public class ContentEntry
public IReadOnlyDictionary<string, ContentEntry> Childs => _childs.ToFrozenDictionary(); public IReadOnlyDictionary<string, ContentEntry> Childs => _childs.ToFrozenDictionary();
public Stream Open()
{
_fileApi.TryOpen(Item!.Value, out var stream);
return stream!;
}
public bool TryOpen([NotNullWhen(true)] out Stream? stream,[NotNullWhen(true)] out RobustManifestItem? item){
stream = null;
item = null;
if(Item is null || !_fileApi.TryOpen(Item.Value, out stream))
return false;
item = Item;
return true;
}
public bool TryGetChild(string name, [NotNullWhen(true)] out ContentEntry? child) public bool TryGetChild(string name, [NotNullWhen(true)] out ContentEntry? child)
{ {
return _childs.TryGetValue(name, out child); return _childs.TryGetValue(name, out child);
@@ -295,7 +316,7 @@ public class ContentEntry
if (!TryGetChild(fName, out var child)) if (!TryGetChild(fName, out var child))
{ {
child = new ContentEntry(_viewModel, fName, fName, ServerName); child = new ContentEntry(_viewModel, fName, fName, ServerName, _fileApi);
TryAddChild(child); TryAddChild(child);
} }
@@ -308,13 +329,13 @@ public class ContentEntry
return Parent.GetRoot(); return Parent.GetRoot();
} }
public ContentEntry CreateItem(ContentPath path, RobustManifestItem item) public ContentEntry CreateItem(ContentPath path, RobustManifestItem item, HashApi fileApi)
{ {
var dir = path.GetDirectory(); var dir = path.GetDirectory();
var dirEntry = GetOrCreateDirectory(dir); var dirEntry = GetOrCreateDirectory(dir);
var name = path.GetName(); var name = path.GetName();
var entry = new ContentEntry(_viewModel, name, name, ServerName) var entry = new ContentEntry(_viewModel, name, name, ServerName, fileApi)
{ {
Item = item Item = item
}; };

View File

@@ -1,10 +1,5 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Linq; using System.Linq;
using System.Threading;
using CommunityToolkit.Mvvm.ComponentModel;
using Nebula.Shared;
using Nebula.Shared.Models; using Nebula.Shared.Models;
using Nebula.Shared.Services; using Nebula.Shared.Services;
using Nebula.Shared.Utils; using Nebula.Shared.Utils;
@@ -16,11 +11,13 @@ public partial class ServerListViewModel
[GenerateProperty] private ConfigurationService ConfigurationService { get; } [GenerateProperty] private ConfigurationService ConfigurationService { get; }
[GenerateProperty] private RestService RestService { get; } [GenerateProperty] private RestService RestService { get; }
public ObservableCollection<ServerEntryModelView> FavoriteServers { get; } = new(); public ObservableCollection<ServerEntryModelView> FavoriteServers { get; } = [];
private void UpdateFavoriteEntries() private void UpdateFavoriteEntries()
{ {
FavoriteServers.Clear(); foreach(var fav in FavoriteServers.ToList()){
FavoriteServers.Remove(fav);
}
var servers = ConfigurationService.GetConfigValue(LauncherConVar.Favorites); var servers = ConfigurationService.GetConfigValue(LauncherConVar.Favorites);
if (servers is null || servers.Length == 0) if (servers is null || servers.Length == 0)

View File

@@ -67,7 +67,10 @@ public partial class ServerListViewModel : ViewModelBase, IViewModelPage
private void UpdateServerEntries() private void UpdateServerEntries()
{ {
Servers.Clear(); foreach(var fav in Servers.ToList()){
Servers.Remove(fav);
}
Task.Run(() => Task.Run(() =>
{ {
UnsortedServers.Sort(new ServerComparer()); UnsortedServers.Sort(new ServerComparer());
@@ -105,7 +108,6 @@ public partial class ServerListViewModel : ViewModelBase, IViewModelPage
{ {
UnsortedServers.Clear(); UnsortedServers.Clear();
ServerViewContainer.Clear(); ServerViewContainer.Clear();
Servers.Clear();
UpdateFavoriteEntries(); UpdateFavoriteEntries();
} }
} }

View File

@@ -52,9 +52,7 @@ public class FileService
var zipArchive = new ZipArchive(zipStream, ZipArchiveMode.Read); var zipArchive = new ZipArchive(zipStream, ZipArchiveMode.Read);
var prefix = ""; return new ZipFileApi(zipArchive, "");
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) prefix = "Space Station 14.app/Contents/Resources/";
return new ZipFileApi(zipArchive, prefix);
} }
catch (Exception) catch (Exception)
{ {