- tweak: fix resolving think

This commit is contained in:
2025-01-07 17:01:00 +03:00
parent 8619e248fd
commit 99312d38a8
12 changed files with 286 additions and 105 deletions

View File

@@ -4,6 +4,7 @@ using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Data.Core; using Avalonia.Data.Core;
using Avalonia.Data.Core.Plugins; using Avalonia.Data.Core.Plugins;
using System.Linq; using System.Linq;
using System.Reflection;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Nebula.Launcher.ViewModels; using Nebula.Launcher.ViewModels;

View File

@@ -27,6 +27,7 @@
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.2.1" /> <PackageReference Include="CommunityToolkit.Mvvm" Version="8.2.1" />
<PackageReference Include="JetBrains.Annotations" Version="2024.3.0" /> <PackageReference Include="JetBrains.Annotations" Version="2024.3.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.0" /> <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.0" />
<PackageReference Include="libsodium" Version="1.0.20" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
@@ -35,6 +36,13 @@
<SubType>Code</SubType> <SubType>Code</SubType>
</Compile> </Compile>
</ItemGroup> </ItemGroup>
<Target Name="BuildCheck" AfterTargets="AfterBuild">
<Copy SourceFiles="..\Nebula.Runner\bin\$(Configuration)\$(TargetFramework)\Nebula.Runner.dll" DestinationFolder="$(OutDir)"/>
<Copy SourceFiles="..\Nebula.Runner\bin\$(Configuration)\$(TargetFramework)\Nebula.Runner.pdb" DestinationFolder="$(OutDir)"/>
<Copy SourceFiles="..\Nebula.Runner\bin\$(Configuration)\$(TargetFramework)\Nebula.Runner.deps.json" DestinationFolder="$(OutDir)"/>
<Copy SourceFiles="..\Nebula.Runner\bin\$(Configuration)\$(TargetFramework)\Nebula.Runner.runtimeconfig.json" DestinationFolder="$(OutDir)"/>
</Target>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Nebula.Shared\Nebula.Shared.csproj" /> <ProjectReference Include="..\Nebula.Shared\Nebula.Shared.csproj" />

View File

@@ -3,31 +3,106 @@ using System.Diagnostics;
using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.ComponentModel;
using Nebula.Launcher.ViewHelper; using Nebula.Launcher.ViewHelper;
using Nebula.Shared.Models; using Nebula.Shared.Models;
using Nebula.Shared.Services;
namespace Nebula.Launcher.ViewModels; namespace Nebula.Launcher.ViewModels;
[ViewModelRegister(isSingleton:false)] [ViewModelRegister(isSingleton:false)]
public partial class ServerEntryModelView : ViewModelBase public partial class ServerEntryModelView : ViewModelBase
{ {
private readonly AuthService _authService = default!;
private readonly ContentService _contentService = default!;
private readonly CancellationService _cancellationService = default!;
private readonly DebugService _debugService = default!;
private readonly RunnerService _runnerService;
[ObservableProperty] private bool _runVisible = true; [ObservableProperty] private bool _runVisible = true;
public ServerHubInfo ServerHubInfo { get; set; } public ServerHubInfo ServerHubInfo { get; set; } = default!;
public ServerEntryModelView() : base() public ServerEntryModelView() : base()
{ {
} }
public ServerEntryModelView(IServiceProvider serviceProvider) : base(serviceProvider) public ServerEntryModelView(
IServiceProvider serviceProvider,
AuthService authService,
ContentService contentService,
CancellationService cancellationService,
DebugService debugService,
RunnerService runnerService
) : base(serviceProvider)
{ {
_authService = authService;
_contentService = contentService;
_cancellationService = cancellationService;
_debugService = debugService;
_runnerService = runnerService;
} }
public void RunInstance() private Process? _process;
public async void RunInstance()
{ {
var p = Process.Start("./Nebula.Runner", "a b c"); var authProv = _authService.SelectedAuth;
p.BeginOutputReadLine();
p.BeginErrorReadLine(); var buildInfo = await _contentService.GetBuildInfo(new RobustUrl(ServerHubInfo.Address), _cancellationService.Token);
await _runnerService.PrepareRun(buildInfo, _cancellationService.Token);
_process = Process.Start(new ProcessStartInfo()
{
FileName = "dotnet.exe",
Arguments = "./Nebula.Runner.dll",
Environment = {
{ "ROBUST_AUTH_USERID", authProv?.UserId.ToString() } ,
{ "ROBUST_AUTH_TOKEN", authProv?.Token.Token } ,
{ "ROBUST_AUTH_SERVER", authProv?.AuthLoginPassword.AuthServer } ,
{ "ROBUST_AUTH_PUBKEY", buildInfo.BuildInfo.Auth.PublicKey } ,
{ "GAME_URL", ServerHubInfo.Address } ,
{ "AUTH_LOGIN", authProv?.AuthLoginPassword.Login } ,
},
CreateNoWindow = true, UseShellExecute = false
});
if (_process is null)
{
return;
}
_process.OutputDataReceived += OnOutputDataReceived;
_process.ErrorDataReceived += OnErrorDataReceived;
_process.Exited += OnExited;
} }
private void OnExited(object? sender, EventArgs e)
{
if (_process is null)
{
return;
}
_process.OutputDataReceived -= OnOutputDataReceived;
_process.ErrorDataReceived -= OnErrorDataReceived;
_process.Exited -= OnExited;
_debugService.Log("PROCESS EXIT WITH CODE " + _process.ExitCode);
_process.Dispose();
_process = null;
}
private void OnErrorDataReceived(object sender, DataReceivedEventArgs e)
{
if (e.Data != null) _debugService.Error(e.Data);
}
private void OnOutputDataReceived(object sender, DataReceivedEventArgs e)
{
if (e.Data != null) _debugService.Log(e.Data);
}
public void ReadLog() public void ReadLog()
{ {
@@ -36,6 +111,25 @@ public partial class ServerEntryModelView : ViewModelBase
public void StopInstance() public void StopInstance()
{ {
_process?.Close();
}
static string FindDotnetPath()
{
var pathEnv = Environment.GetEnvironmentVariable("PATH");
var paths = pathEnv?.Split(System.IO.Path.PathSeparator);
if (paths != null)
{
foreach (var path in paths)
{
var dotnetPath = System.IO.Path.Combine(path, "dotnet");
if (System.IO.File.Exists(dotnetPath))
{
return dotnetPath;
}
}
}
throw new Exception("Dotnet not found!");
} }
} }

View File

@@ -1,14 +1,57 @@
using Nebula.Shared; using Nebula.Shared;
using Nebula.Shared.Models;
using Nebula.Shared.Services; using Nebula.Shared.Services;
using Nebula.Shared.Utils;
using Robust.LoaderApi;
namespace Nebula.Runner; namespace Nebula.Runner;
[ServiceRegister] [ServiceRegister]
public class App(DebugService debugService) public sealed class App(DebugService debugService, RunnerService runnerService, ContentService contentService) : IRedialApi
{ {
public async Task Run(string[] args1)
public void Run(string[] args)
{ {
debugService.Log("HELLO!!! " + string.Join(" ",args)); debugService.Log("HELLO!!! ");
var login = Environment.GetEnvironmentVariable("AUTH_LOGIN") ?? "Alexandra";
var urlraw = Environment.GetEnvironmentVariable("GAME_URL") ?? "ss14://localhost";
var url = urlraw.ToRobustUrl();
using var cancelTokenSource = new CancellationTokenSource();
var buildInfo = await contentService.GetBuildInfo(url, cancelTokenSource.Token);
var args = new List<string>
{
// Pass username to launched client.
// We don't load username from client_config.toml when launched via launcher.
"--username", login,
// Tell game we are launcher
"--cvar", "launch.launcher=true"
};
var connectionString = url.ToString();
if (!string.IsNullOrEmpty(buildInfo.BuildInfo.ConnectAddress))
connectionString = buildInfo.BuildInfo.ConnectAddress;
// We are using the launcher. Don't show main menu etc..
// Note: --launcher also implied --connect.
// For this reason, content bundles do not set --launcher.
args.Add("--launcher");
args.Add("--connect-address");
args.Add(connectionString);
args.Add("--ss14-address");
args.Add(url.ToString());
await runnerService.Run(args.ToArray(), buildInfo, this, cancelTokenSource.Token);
}
public void Redial(Uri uri, string text = "")
{
} }
} }

View File

@@ -1,6 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
@@ -12,6 +13,6 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.0" /> <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.0" />
<PackageReference Include="SharpZstd.Interop" Version="1.5.5-beta1"/>
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@@ -1,3 +1,4 @@
using System.Reflection;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Nebula.Shared; using Nebula.Shared;
@@ -11,6 +12,7 @@ public static class Program
services.AddServices(); services.AddServices();
var serviceProvider = services.BuildServiceProvider(); var serviceProvider = services.BuildServiceProvider();
serviceProvider.GetService<App>()!.Run(args); var task = serviceProvider.GetService<App>()!.Run(args);
task.Wait();
} }
} }

View File

@@ -8,12 +8,10 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<EmbeddedResource Include="Utils\runtime.json" /> <EmbeddedResource Include="Utils\runtime.json">
</ItemGroup> <LogicalName>Utility.runtime.json</LogicalName>
</EmbeddedResource>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.0" /> <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.0" />
<PackageReference Include="libsodium" Version="1.0.20" />
<PackageReference Include="Robust.Natives" Version="0.1.1" /> <PackageReference Include="Robust.Natives" Version="0.1.1" />
<PackageReference Include="SharpZstd.Interop" Version="1.5.6" /> <PackageReference Include="SharpZstd.Interop" Version="1.5.6" />
</ItemGroup> </ItemGroup>

View File

@@ -7,8 +7,17 @@ public static class ServiceExt
{ {
public static void AddServices(this IServiceCollection services) public static void AddServices(this IServiceCollection services)
{ {
foreach (var (type, inference) in GetServicesWithHelpAttribute(Assembly.GetExecutingAssembly())) foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
{ {
AddServices(services, assembly);
}
}
public static void AddServices(this IServiceCollection services, Assembly assembly)
{
foreach (var (type, inference) in GetServicesWithHelpAttribute(assembly))
{
Console.WriteLine("[ServiceMng] ADD SERVICE " + type);
if (inference is null) if (inference is null)
{ {
services.AddSingleton(type); services.AddSingleton(type);

View File

@@ -10,20 +10,36 @@ namespace Nebula.Shared.Services;
[ServiceRegister] [ServiceRegister]
public class AssemblyService public class AssemblyService
{ {
private readonly Dictionary<string,Assembly> _assemblies = new(); private readonly List<Assembly> _assemblies = new();
private readonly DebugService _debugService; private readonly DebugService _debugService;
public AssemblyService(DebugService debugService) public AssemblyService(DebugService debugService)
{ {
_debugService = debugService; _debugService = debugService;
SharpZstd.Interop.ZstdImportResolver.ResolveLibrary += (name, assembly1, path) =>
{
if (name.Equals("SharpZstd.Native"))
{
_debugService.Debug("RESOLVING SHARPZSTD THINK: " + name + " " + path);
GetRuntimeInfo(out string platform, out string architecture, out string extension);
string fileName = GetDllName(platform, architecture, extension);
if (NativeLibrary.TryLoad(fileName, assembly1, path, out var nativeLibrary))
{
return nativeLibrary;
}
}
return IntPtr.Zero;
};
} }
//public IReadOnlyList<Assembly> Assemblies => _assemblies; public IReadOnlyList<Assembly> Assemblies => _assemblies;
public AssemblyApi Mount(IFileApi fileApi, string apiName = "") public AssemblyApi Mount(IFileApi fileApi)
{ {
var asmApi = new AssemblyApi(fileApi); var asmApi = new AssemblyApi(fileApi);
AssemblyLoadContext.Default.Resolving += (context, name) => OnAssemblyResolving(context, name, asmApi, apiName); AssemblyLoadContext.Default.Resolving += (context, name) => OnAssemblyResolving(context, name, asmApi);
AssemblyLoadContext.Default.ResolvingUnmanagedDll += LoadContextOnResolvingUnmanaged; AssemblyLoadContext.Default.ResolvingUnmanagedDll += LoadContextOnResolvingUnmanaged;
return asmApi; return asmApi;
@@ -53,11 +69,6 @@ public class AssemblyService
public bool TryOpenAssembly(string name, AssemblyApi assemblyApi, [NotNullWhen(true)] out Assembly? assembly) public bool TryOpenAssembly(string name, AssemblyApi assemblyApi, [NotNullWhen(true)] out Assembly? assembly)
{ {
if (_assemblies.TryGetValue(name, out assembly))
{
return true;
}
if (!TryOpenAssemblyStream(name, assemblyApi, out var asm, out var pdb)) if (!TryOpenAssemblyStream(name, assemblyApi, out var asm, out var pdb))
{ {
assembly = null; assembly = null;
@@ -66,8 +77,9 @@ public class AssemblyService
assembly = AssemblyLoadContext.Default.LoadFromStream(asm, pdb); assembly = AssemblyLoadContext.Default.LoadFromStream(asm, pdb);
_debugService.Log("LOADED ASSEMBLY " + name); _debugService.Log("LOADED ASSEMBLY " + name);
_assemblies.Add(name, assembly);
if (!_assemblies.Contains(assembly)) _assemblies.Add(assembly);
asm.Dispose(); asm.Dispose();
pdb?.Dispose(); pdb?.Dispose();
@@ -86,13 +98,27 @@ public class AssemblyService
assemblyApi.TryOpen($"{name}.pdb", out pdb); assemblyApi.TryOpen($"{name}.pdb", out pdb);
return true; return true;
} }
private readonly HashSet<string> _resolvingAssemblies = new HashSet<string>();
private Assembly? OnAssemblyResolving(AssemblyLoadContext context, AssemblyName name, AssemblyApi assemblyApi, private Assembly? OnAssemblyResolving(AssemblyLoadContext context, AssemblyName name, AssemblyApi assemblyApi)
string apiName)
{ {
if (_resolvingAssemblies.Contains(name.FullName))
_debugService.Debug($"Resolving assembly from {apiName}: {name.Name}"); {
return TryOpenAssembly(name.Name!, assemblyApi, out var assembly) ? assembly : null; _debugService.Debug($"Already resolving {name.Name}, skipping.");
return null; // Prevent recursive resolution
}
try
{
_resolvingAssemblies.Add(name.FullName);
_debugService.Debug($"Resolving assembly from FileAPI: {name.Name}");
return TryOpenAssembly(name.Name!, assemblyApi, out var assembly) ? assembly : null;
}
finally
{
_resolvingAssemblies.Remove(name.FullName);
}
} }
private IntPtr LoadContextOnResolvingUnmanaged(Assembly assembly, string unmanaged) private IntPtr LoadContextOnResolvingUnmanaged(Assembly assembly, string unmanaged)
@@ -104,7 +130,66 @@ public class AssemblyService
if (NativeLibrary.TryLoad(a, out var handle)) if (NativeLibrary.TryLoad(a, out var handle))
return handle; return handle;
_debugService.Error("Loading dll error! Not found");
return IntPtr.Zero; return IntPtr.Zero;
} }
public static string GetDllName(
string platform,
string architecture,
string extension)
{
string name = $"SharpZstd.Native.{extension}";
return name;
}
public static void GetRuntimeInfo(
out string platform,
out string architecture,
out string extension)
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
platform = "win";
extension = "dll";
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
platform = "linux";
extension = "so";
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
platform = "osx";
extension = "dylib";
}
else
{
platform = "linux";
extension = "so";
}
if (RuntimeInformation.ProcessArchitecture == Architecture.X64)
{
architecture = "x64";
}
else if (RuntimeInformation.ProcessArchitecture == Architecture.X86)
{
architecture = "x86";
}
else if (RuntimeInformation.ProcessArchitecture == Architecture.Arm)
{
architecture = "arm";
}
else if (RuntimeInformation.ProcessArchitecture == Architecture.Arm64)
{
architecture = "arm64";
}
else
{
throw new PlatformNotSupportedException("Unsupported process architecture.");
}
}
} }

View File

@@ -45,6 +45,12 @@ public partial class AuthService(
SelectedAuth = null; SelectedAuth = null;
} }
public void SetAuth(Guid guid, string token, string login, string authServer)
{
SelectedAuth = new CurrentAuthInfo(guid, new LoginToken(token, DateTimeOffset.Now),
new AuthLoginPassword(login, "", authServer));
}
public async Task<bool> EnsureToken() public async Task<bool> EnsureToken()
{ {
if (SelectedAuth is null) return false; if (SelectedAuth is null) return false;

View File

@@ -81,7 +81,7 @@ public sealed class EngineService
try try
{ {
return _assemblyService.Mount(_fileService.OpenZip(version, _fileService.EngineFileApi),$"Engine.Ensure-{version}"); return _assemblyService.Mount(_fileService.OpenZip(version, _fileService.EngineFileApi));
} }
catch (Exception e) catch (Exception e)
{ {
@@ -161,7 +161,7 @@ public sealed class EngineService
try try
{ {
return _assemblyService.Mount(_fileService.OpenZip(fileName, _fileService.EngineFileApi),"Engine.EnsureModule"); return _assemblyService.Mount(_fileService.OpenZip(fileName, _fileService.EngineFileApi));
} }
catch (Exception e) catch (Exception e)
{ {

View File

@@ -10,18 +10,8 @@ public sealed class RunnerService(
ConfigurationService varService, ConfigurationService varService,
FileService fileService, FileService fileService,
EngineService engineService, EngineService engineService,
AssemblyService assemblyService, AssemblyService assemblyService)
AuthService authService,
PopupMessageService popupMessageService,
CancellationService cancellationService)
: IRedialApi
{ {
public async Task PrepareRun(RobustUrl url)
{
var buildInfo = await contentService.GetBuildInfo(url, cancellationService.Token);
await PrepareRun(buildInfo, cancellationService.Token);
}
public async Task PrepareRun(RobustBuildInfo buildInfo, CancellationToken cancellationToken) public async Task PrepareRun(RobustBuildInfo buildInfo, CancellationToken cancellationToken)
{ {
debugService.Log("Prepare Content!"); debugService.Log("Prepare Content!");
@@ -67,60 +57,4 @@ public sealed class RunnerService(
await Task.Run(() => loader.Main(args), cancellationToken); await Task.Run(() => loader.Main(args), cancellationToken);
} }
public async Task RunGame(string urlraw)
{
var url = new RobustUrl(urlraw);
using var cancelTokenSource = new CancellationTokenSource();
var buildInfo = await contentService.GetBuildInfo(url, cancelTokenSource.Token);
var account = authService.SelectedAuth;
if (account is null)
{
popupMessageService.Popup("Error! Auth is required!");
return;
}
if (buildInfo.BuildInfo.Auth.Mode != "Disabled")
{
Environment.SetEnvironmentVariable("ROBUST_AUTH_TOKEN", account.Token.Token);
Environment.SetEnvironmentVariable("ROBUST_AUTH_USERID", account.UserId.ToString());
Environment.SetEnvironmentVariable("ROBUST_AUTH_PUBKEY", buildInfo.BuildInfo.Auth.PublicKey);
Environment.SetEnvironmentVariable("ROBUST_AUTH_SERVER", account.AuthLoginPassword.AuthServer);
}
var args = new List<string>
{
// Pass username to launched client.
// We don't load username from client_config.toml when launched via launcher.
"--username", account.AuthLoginPassword.Login,
// Tell game we are launcher
"--cvar", "launch.launcher=true"
};
var connectionString = url.ToString();
if (!string.IsNullOrEmpty(buildInfo.BuildInfo.ConnectAddress))
connectionString = buildInfo.BuildInfo.ConnectAddress;
// We are using the launcher. Don't show main menu etc..
// Note: --launcher also implied --connect.
// For this reason, content bundles do not set --launcher.
args.Add("--launcher");
args.Add("--connect-address");
args.Add(connectionString);
args.Add("--ss14-address");
args.Add(url.ToString());
debugService.Debug("Connect to " + url.ToString() + " " + account.AuthLoginPassword.AuthServer);
await Run(args.ToArray(), buildInfo, this, cancelTokenSource.Token);
}
public async void Redial(Uri uri, string text = "")
{
//await RunGame(uri.ToString());
}
} }