- add: game service
This commit is contained in:
6
Nebula.Launcher/Models/IRunningSignalConsumer.cs
Normal file
6
Nebula.Launcher/Models/IRunningSignalConsumer.cs
Normal file
@@ -0,0 +1,6 @@
|
||||
namespace Nebula.Launcher.Models;
|
||||
|
||||
public interface IRunningSignalConsumer
|
||||
{
|
||||
public void ProcessRunningSignal(bool isRunning);
|
||||
}
|
||||
120
Nebula.Launcher/Services/GameRunnerService.cs
Normal file
120
Nebula.Launcher/Services/GameRunnerService.cs
Normal file
@@ -0,0 +1,120 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Nebula.Launcher.Models;
|
||||
using Nebula.Launcher.ProcessHelper;
|
||||
using Nebula.Launcher.ViewModels;
|
||||
using Nebula.Launcher.ViewModels.Pages;
|
||||
using Nebula.Launcher.ViewModels.Popup;
|
||||
using Nebula.Shared;
|
||||
using Nebula.Shared.Models;
|
||||
using Nebula.Shared.Services;
|
||||
using Nebula.Shared.Services.Logging;
|
||||
|
||||
namespace Nebula.Launcher.Services;
|
||||
|
||||
[ServiceRegister]
|
||||
public class GameRunnerService
|
||||
{
|
||||
private readonly PopupMessageService _popupMessageService;
|
||||
private readonly ViewHelperService _viewHelperService;
|
||||
private readonly GameRunnerPreparer _gameRunnerPreparer;
|
||||
private readonly InstanceRunningContainer _instanceRunningContainer;
|
||||
private readonly AccountInfoViewModel _accountInfoViewModel;
|
||||
private readonly ServerViewContainer _container;
|
||||
private readonly ILogger _logger;
|
||||
|
||||
private readonly Dictionary<InstanceKey, RobustUrl> _robustUrls = new();
|
||||
private readonly Dictionary<RobustUrl, InstanceKey> _robustKeys = new();
|
||||
|
||||
public GameRunnerService(PopupMessageService popupMessageService,
|
||||
DebugService debugService,
|
||||
ViewHelperService viewHelperService,
|
||||
GameRunnerPreparer gameRunnerPreparer,
|
||||
InstanceRunningContainer instanceRunningContainer,
|
||||
AccountInfoViewModel accountInfoViewModel,
|
||||
ServerViewContainer container)
|
||||
{
|
||||
_popupMessageService = popupMessageService;
|
||||
_viewHelperService = viewHelperService;
|
||||
_gameRunnerPreparer = gameRunnerPreparer;
|
||||
_instanceRunningContainer = instanceRunningContainer;
|
||||
_accountInfoViewModel = accountInfoViewModel;
|
||||
_container = container;
|
||||
|
||||
_logger = debugService.GetLogger("GameRunnerService");
|
||||
_instanceRunningContainer.IsRunningChanged += IsRunningChanged;
|
||||
}
|
||||
|
||||
private void IsRunningChanged(InstanceKey key, bool isRunning)
|
||||
{
|
||||
_logger.Debug($"IsRunningChanged {key}: {isRunning}");
|
||||
if (!_robustUrls.TryGetValue(key, out var robustUrl)) return;
|
||||
|
||||
if (_container.Get(robustUrl) is IRunningSignalConsumer signalConsumer)
|
||||
{
|
||||
_logger.Debug($"IsRunningChanged conf {robustUrl}: {isRunning}");
|
||||
signalConsumer.ProcessRunningSignal(isRunning);
|
||||
}
|
||||
|
||||
if (!isRunning)
|
||||
{
|
||||
_robustKeys.Remove(robustUrl);
|
||||
_robustUrls.Remove(key);
|
||||
}
|
||||
}
|
||||
|
||||
public void StopInstance(ServerEntryViewModel serverEntryViewModel)
|
||||
{
|
||||
if (_robustKeys.TryGetValue(serverEntryViewModel.Address, out var instanceKey))
|
||||
{
|
||||
_instanceRunningContainer.Stop(instanceKey);
|
||||
}
|
||||
}
|
||||
|
||||
public void ReadInstanceLog(ServerEntryViewModel serverEntryViewModel)
|
||||
{
|
||||
if (_robustKeys.TryGetValue(serverEntryViewModel.Address, out var instanceKey))
|
||||
{
|
||||
_instanceRunningContainer.Popup(instanceKey);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<InstanceKey?> RunInstanceAsync(ServerEntryViewModel serverEntryViewModel, CancellationToken cancellationToken, bool ignoreLoginCredentials = false)
|
||||
{
|
||||
_logger.Log("Running instance..." + serverEntryViewModel.RealName);
|
||||
if (!ignoreLoginCredentials && _accountInfoViewModel.Credentials.Value is null)
|
||||
{
|
||||
var warningContext = _viewHelperService.GetViewModel<IsLoginCredentialsNullPopupViewModel>()
|
||||
.WithServerEntry(serverEntryViewModel);
|
||||
|
||||
_popupMessageService.Popup(warningContext);
|
||||
return null;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
using var viewModelLoading = _viewHelperService.GetViewModel<LoadingContextViewModel>();
|
||||
viewModelLoading.LoadingName = "Loading instance...";
|
||||
|
||||
_popupMessageService.Popup(viewModelLoading);
|
||||
var currProcessStartProvider =
|
||||
await _gameRunnerPreparer.GetGameProcessStartInfoProvider(serverEntryViewModel.Address, viewModelLoading, cancellationToken);
|
||||
_logger.Log("Preparing instance...");
|
||||
var instanceKey = _instanceRunningContainer.RegisterInstance(currProcessStartProvider);
|
||||
_robustUrls.Add(instanceKey, serverEntryViewModel.Address);
|
||||
_robustKeys.Add(serverEntryViewModel.Address, instanceKey);
|
||||
_instanceRunningContainer.Run(instanceKey);
|
||||
_logger.Log($"Starting instance... {instanceKey.Id} " + serverEntryViewModel.RealName);
|
||||
return instanceKey;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
var error = new Exception("Error while attempt run instance", e);
|
||||
_logger.Error(error);
|
||||
_popupMessageService.Popup(error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,8 +12,7 @@ namespace Nebula.Launcher.Services;
|
||||
[ServiceRegister]
|
||||
public sealed class InstanceRunningContainer(
|
||||
PopupMessageService popupMessageService,
|
||||
DebugService debugService,
|
||||
ServerViewContainer container
|
||||
DebugService debugService
|
||||
)
|
||||
{
|
||||
private readonly InstanceKeyPool _keyPool = new();
|
||||
|
||||
13
Nebula.Launcher/ViewModels/InstanceKey.cs
Normal file
13
Nebula.Launcher/ViewModels/InstanceKey.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
using System;
|
||||
|
||||
namespace Nebula.Launcher.ViewModels;
|
||||
|
||||
public record struct InstanceKey(int Id):
|
||||
IEquatable<int>,
|
||||
IComparable<InstanceKey>
|
||||
{
|
||||
public static implicit operator InstanceKey(int id) => new InstanceKey(id);
|
||||
public static implicit operator int(InstanceKey id) => id.Id;
|
||||
public bool Equals(int other) => Id == other;
|
||||
public int CompareTo(InstanceKey other) => Id.CompareTo(other.Id);
|
||||
};
|
||||
16
Nebula.Launcher/ViewModels/InstanceKeyPool.cs
Normal file
16
Nebula.Launcher/ViewModels/InstanceKeyPool.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
namespace Nebula.Launcher.ViewModels;
|
||||
|
||||
public sealed class InstanceKeyPool
|
||||
{
|
||||
private int _nextId = 1;
|
||||
|
||||
public InstanceKey Take()
|
||||
{
|
||||
return new InstanceKey(_nextId++);
|
||||
}
|
||||
|
||||
public void Free(InstanceKey id)
|
||||
{
|
||||
// TODO: make some free logic later
|
||||
}
|
||||
}
|
||||
@@ -58,7 +58,9 @@ public partial class ServerOverviewModel : ViewModelBase
|
||||
private void OnFavoriteRefreshRequired()
|
||||
{
|
||||
if(CurrentServerList.Provider is FavoriteServerListProvider favoriteServerListProvider)
|
||||
{
|
||||
UpdateRequired();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnHubListChanged(ServerHubRecord[]? value)
|
||||
@@ -105,6 +107,7 @@ public partial class ServerOverviewModel : ViewModelBase
|
||||
public void UpdateRequired()
|
||||
{
|
||||
ServerViewContainer.Clear();
|
||||
CurrentServerList.ClearProvider();
|
||||
CurrentServerList.RefreshFromProvider();
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ using System.Windows.Input;
|
||||
using Avalonia.Controls;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using Nebula.Launcher.Models;
|
||||
using Nebula.Launcher.ProcessHelper;
|
||||
using Nebula.Launcher.ServerListProviders;
|
||||
using Nebula.Launcher.Services;
|
||||
using Nebula.Launcher.ViewModels.Pages;
|
||||
@@ -13,7 +12,6 @@ using Nebula.Launcher.ViewModels.Popup;
|
||||
using Nebula.Launcher.Views;
|
||||
using Nebula.Shared.Models;
|
||||
using Nebula.Shared.Services;
|
||||
using Nebula.Shared.Services.Logging;
|
||||
using Nebula.Shared.Utils;
|
||||
using Nebula.Shared.ViewHelper;
|
||||
|
||||
@@ -21,7 +19,7 @@ namespace Nebula.Launcher.ViewModels;
|
||||
|
||||
[ViewModelRegister(typeof(ServerEntryView), false)]
|
||||
[ConstructGenerator]
|
||||
public sealed partial class ServerEntryViewModel : ViewModelBase, IFilterConsumer, IListEntryModelView, IFavoriteEntryModelView, IEntryNameHolder
|
||||
public sealed partial class ServerEntryViewModel : ViewModelBase, IFilterConsumer, IListEntryModelView, IFavoriteEntryModelView, IEntryNameHolder, IRunningSignalConsumer
|
||||
{
|
||||
[ObservableProperty] private string _description = "Fetching info...";
|
||||
[ObservableProperty] private bool _expandInfo;
|
||||
@@ -36,20 +34,16 @@ public sealed partial class ServerEntryViewModel : ViewModelBase, IFilterConsume
|
||||
set => RealName = value ?? Status.Name;
|
||||
}
|
||||
|
||||
private ILogger _logger;
|
||||
private ServerInfo? _serverInfo;
|
||||
private InstanceKey _instanceKey;
|
||||
|
||||
public RobustUrl Address { get; private set; }
|
||||
[GenerateProperty] private AccountInfoViewModel AccountInfoViewModel { get; }
|
||||
[GenerateProperty] private CancellationService CancellationService { get; } = default!;
|
||||
[GenerateProperty] private DebugService DebugService { get; } = default!;
|
||||
[GenerateProperty] private PopupMessageService PopupMessageService { get; } = default!;
|
||||
[GenerateProperty] private ViewHelperService ViewHelperService { get; } = default!;
|
||||
[GenerateProperty] private RestService RestService { get; } = default!;
|
||||
[GenerateProperty] private MainViewModel MainViewModel { get; } = default!;
|
||||
[GenerateProperty] private FavoriteServerListProvider FavoriteServerListProvider { get; } = default!;
|
||||
[GenerateProperty] private GameRunnerPreparer GameRunnerPreparer { get; } = default!;
|
||||
[GenerateProperty] private InstanceRunningContainer InstanceRunningContainer { get; } = default!;
|
||||
[GenerateProperty] private GameRunnerService GameRunnerService { get; } = default!;
|
||||
|
||||
public ServerStatus Status { get; private set; } =
|
||||
new(
|
||||
@@ -70,16 +64,17 @@ public sealed partial class ServerEntryViewModel : ViewModelBase, IFilterConsume
|
||||
|
||||
public async Task<ServerInfo?> GetServerInfo()
|
||||
{
|
||||
if (_serverInfo == null)
|
||||
try
|
||||
{
|
||||
_serverInfo = await RestService.GetAsync<ServerInfo>(Address.InfoUri, CancellationService.Token);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Description = e.Message;
|
||||
_logger.Error(e);
|
||||
}
|
||||
if (_serverInfo != null)
|
||||
return _serverInfo;
|
||||
|
||||
try
|
||||
{
|
||||
_serverInfo = await RestService.GetAsync<ServerInfo>(Address.InfoUri, CancellationService.Token);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Description = e.Message;
|
||||
}
|
||||
|
||||
return _serverInfo;
|
||||
}
|
||||
@@ -100,16 +95,8 @@ public sealed partial class ServerEntryViewModel : ViewModelBase, IFilterConsume
|
||||
|
||||
protected override void Initialise()
|
||||
{
|
||||
_logger = DebugService.GetLogger(this);
|
||||
InstanceRunningContainer.IsRunningChanged += IsRunningChanged;
|
||||
}
|
||||
|
||||
private void IsRunningChanged(InstanceKey arg1, bool isRunning)
|
||||
{
|
||||
if(arg1.Equals(_instanceKey))
|
||||
RunVisible = !isRunning;
|
||||
}
|
||||
|
||||
|
||||
public void ProcessFilter(ServerFilter? serverFilter)
|
||||
{
|
||||
if (serverFilter == null)
|
||||
@@ -161,55 +148,22 @@ public sealed partial class ServerEntryViewModel : ViewModelBase, IFilterConsume
|
||||
|
||||
public void RunInstance()
|
||||
{
|
||||
Task.Run(async ()=> await RunInstanceAsync());
|
||||
Task.Run(async ()=> await GameRunnerService.RunInstanceAsync(this, CancellationService.Token));
|
||||
}
|
||||
|
||||
public void RunInstanceIgnoreAuth()
|
||||
{
|
||||
Task.Run(async ()=> await RunInstanceAsync(true));
|
||||
}
|
||||
|
||||
private async Task RunInstanceAsync(bool ignoreLoginCredentials = false)
|
||||
{
|
||||
_logger.Log("Running instance..." + RealName);
|
||||
if (!ignoreLoginCredentials && AccountInfoViewModel.Credentials.Value is null)
|
||||
{
|
||||
var warningContext = ViewHelperService.GetViewModel<IsLoginCredentialsNullPopupViewModel>()
|
||||
.WithServerEntry(this);
|
||||
|
||||
PopupMessageService.Popup(warningContext);
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
using var viewModelLoading = ViewHelperService.GetViewModel<LoadingContextViewModel>();
|
||||
viewModelLoading.LoadingName = "Loading instance...";
|
||||
|
||||
PopupMessageService.Popup(viewModelLoading);
|
||||
var currProcessStartProvider =
|
||||
await GameRunnerPreparer.GetGameProcessStartInfoProvider(Address, viewModelLoading, CancellationService.Token);
|
||||
_logger.Log("Preparing instance...");
|
||||
_instanceKey = InstanceRunningContainer.RegisterInstance(currProcessStartProvider);
|
||||
InstanceRunningContainer.Run(_instanceKey);
|
||||
_logger.Log("Starting instance..." + RealName);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
var error = new Exception("Error while attempt run instance", e);
|
||||
_logger.Error(error);
|
||||
PopupMessageService.Popup(error);
|
||||
}
|
||||
Task.Run(async ()=> await GameRunnerService.RunInstanceAsync(this, CancellationService.Token, true));
|
||||
}
|
||||
|
||||
public void StopInstance()
|
||||
{
|
||||
InstanceRunningContainer.Stop(_instanceKey);
|
||||
GameRunnerService.StopInstance(this);
|
||||
}
|
||||
|
||||
public void ReadLog()
|
||||
{
|
||||
InstanceRunningContainer.Popup(_instanceKey);
|
||||
GameRunnerService.ReadInstanceLog(this);
|
||||
}
|
||||
|
||||
public async void ExpandInfoRequired()
|
||||
@@ -226,39 +180,18 @@ public sealed partial class ServerEntryViewModel : ViewModelBase, IFilterConsume
|
||||
if (info.Links is null) return;
|
||||
foreach (var link in info.Links) Links.Add(link);
|
||||
}
|
||||
|
||||
|
||||
public void ProcessRunningSignal(bool isRunning)
|
||||
{
|
||||
RunVisible = !isRunning;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_logger.Dispose();
|
||||
InstanceRunningContainer.IsRunningChanged -= IsRunningChanged;
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class InstanceKeyPool
|
||||
{
|
||||
private int _nextId = 1;
|
||||
|
||||
public InstanceKey Take()
|
||||
{
|
||||
return new InstanceKey(_nextId++);
|
||||
}
|
||||
|
||||
public void Free(InstanceKey id)
|
||||
{
|
||||
// TODO: make some free logic later
|
||||
}
|
||||
}
|
||||
|
||||
public record struct InstanceKey(int Id):
|
||||
IEquatable<int>,
|
||||
IComparable<InstanceKey>
|
||||
{
|
||||
public static implicit operator InstanceKey(int id) => new InstanceKey(id);
|
||||
public static implicit operator int(InstanceKey id) => id.Id;
|
||||
public bool Equals(int other) => Id == other;
|
||||
public int CompareTo(InstanceKey other) => Id.CompareTo(other.Id);
|
||||
};
|
||||
|
||||
public sealed class LinkGoCommand : ICommand
|
||||
{
|
||||
public LinkGoCommand()
|
||||
|
||||
Reference in New Issue
Block a user