feat(launcher): Campaign LA add Avalonia desktop shell
This commit is contained in:
parent
6c4cd2bbc6
commit
d0a9c65d85
31 changed files with 4831 additions and 16 deletions
25
tests/AcDream.Launcher.Tests/AcDream.Launcher.Tests.csproj
Normal file
25
tests/AcDream.Launcher.Tests/AcDream.Launcher.Tests.csproj
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<IsPackable>false</IsPackable>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="coverlet.collector" Version="6.0.4" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
|
||||
<PackageReference Include="xunit" Version="2.9.3" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.4" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Using Include="Xunit" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\AcDream.Launcher\AcDream.Launcher.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
77
tests/AcDream.Launcher.Tests/LauncherProjectBoundaryTests.cs
Normal file
77
tests/AcDream.Launcher.Tests/LauncherProjectBoundaryTests.cs
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
using System.Xml.Linq;
|
||||
|
||||
namespace AcDream.Launcher.Tests;
|
||||
|
||||
public sealed class LauncherProjectBoundaryTests
|
||||
{
|
||||
private static readonly string[] ExpectedAvaloniaPackages =
|
||||
[
|
||||
"Avalonia",
|
||||
"Avalonia.Desktop",
|
||||
"Avalonia.Themes.Fluent",
|
||||
];
|
||||
|
||||
[Fact]
|
||||
public void LauncherReferencesOnlyLauncherCoreAndPinsOneAvaloniaVersion()
|
||||
{
|
||||
string root = FindRepositoryRoot();
|
||||
string projectPath = Path.Combine(
|
||||
root,
|
||||
"src",
|
||||
"AcDream.Launcher",
|
||||
"AcDream.Launcher.csproj");
|
||||
XDocument project = XDocument.Load(projectPath);
|
||||
|
||||
string projectReference = Assert.Single(
|
||||
project.Descendants("ProjectReference")
|
||||
.Select(element => element.Attribute("Include")?.Value ?? string.Empty));
|
||||
Assert.EndsWith(
|
||||
"AcDream.Launcher.Core\\AcDream.Launcher.Core.csproj",
|
||||
projectReference,
|
||||
StringComparison.Ordinal);
|
||||
|
||||
(string Name, string Version)[] packages = project
|
||||
.Descendants("PackageReference")
|
||||
.Select(element => (
|
||||
element.Attribute("Include")?.Value ?? string.Empty,
|
||||
element.Attribute("Version")?.Value ?? string.Empty))
|
||||
.OrderBy(package => package.Item1, StringComparer.Ordinal)
|
||||
.ToArray();
|
||||
|
||||
Assert.Equal(ExpectedAvaloniaPackages, packages.Select(package => package.Name));
|
||||
Assert.All(packages, package => Assert.Equal("12.1.1", package.Version));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void LauncherAndItsTestsAreSolutionMembers()
|
||||
{
|
||||
string root = FindRepositoryRoot();
|
||||
XDocument solution = XDocument.Load(Path.Combine(root, "AcDream.slnx"));
|
||||
string[] paths = solution.Descendants("Project")
|
||||
.Select(element => (element.Attribute("Path")?.Value ?? string.Empty)
|
||||
.Replace('\\', '/'))
|
||||
.ToArray();
|
||||
|
||||
Assert.Contains("src/AcDream.Launcher/AcDream.Launcher.csproj", paths);
|
||||
Assert.Contains("tests/AcDream.Launcher.Tests/AcDream.Launcher.Tests.csproj", paths);
|
||||
}
|
||||
|
||||
private static string FindRepositoryRoot()
|
||||
{
|
||||
foreach (string start in new[] { AppContext.BaseDirectory, Environment.CurrentDirectory })
|
||||
{
|
||||
DirectoryInfo? directory = new(start);
|
||||
while (directory is not null)
|
||||
{
|
||||
if (File.Exists(Path.Combine(directory.FullName, "AcDream.slnx")))
|
||||
{
|
||||
return directory.FullName;
|
||||
}
|
||||
|
||||
directory = directory.Parent;
|
||||
}
|
||||
}
|
||||
|
||||
throw new DirectoryNotFoundException("Could not find AcDream.slnx.");
|
||||
}
|
||||
}
|
||||
508
tests/AcDream.Launcher.Tests/LauncherWindowViewModelTests.cs
Normal file
508
tests/AcDream.Launcher.Tests/LauncherWindowViewModelTests.cs
Normal file
|
|
@ -0,0 +1,508 @@
|
|||
using AcDream.Launcher.Core.Orchestration;
|
||||
using AcDream.Launcher.Core.Profiles;
|
||||
using AcDream.Launcher.Core.Launching;
|
||||
using AcDream.Launcher.ViewModels;
|
||||
|
||||
namespace AcDream.Launcher.Tests;
|
||||
|
||||
public sealed class LauncherWindowViewModelTests
|
||||
{
|
||||
[Fact]
|
||||
public void InitializeProjectsHierarchySessionsAndFutureWorkflowShells()
|
||||
{
|
||||
using var orchestrator = new FakeLauncherOrchestrator();
|
||||
using var viewModel = new LauncherWindowViewModel(
|
||||
orchestrator,
|
||||
new ImmediateUiDispatcher());
|
||||
|
||||
viewModel.Initialize();
|
||||
|
||||
Assert.True(orchestrator.LoadCalled);
|
||||
LauncherTreeNodeViewModel server = Assert.Single(viewModel.Servers);
|
||||
Assert.Equal(LauncherTreeNodeKind.Server, server.Kind);
|
||||
LauncherTreeNodeViewModel account = Assert.Single(server.Children);
|
||||
Assert.Equal(LauncherTreeNodeKind.Account, account.Kind);
|
||||
LauncherTreeNodeViewModel character = Assert.Single(account.Children);
|
||||
Assert.Equal("+Acdream", character.DisplayName);
|
||||
Assert.Same(server, viewModel.SelectedNode);
|
||||
|
||||
LauncherSessionRowViewModel session = Assert.Single(viewModel.Sessions);
|
||||
Assert.Equal("Gui", session.Mode);
|
||||
Assert.Equal("Connected", session.State);
|
||||
Assert.True(session.IsActive);
|
||||
|
||||
Assert.True(viewModel.IsFirstRunRequired);
|
||||
Assert.Contains("LA9", viewModel.FirstRunWizardShell.Body, StringComparison.Ordinal);
|
||||
Assert.Contains("LA10", viewModel.UpdatePromptShell.Body, StringComparison.Ordinal);
|
||||
viewModel.FirstRunWizardShell.OpenCommand.Execute(null);
|
||||
Assert.True(viewModel.FirstRunWizardShell.IsOpen);
|
||||
viewModel.FirstRunWizardShell.CloseCommand.Execute(null);
|
||||
Assert.False(viewModel.FirstRunWizardShell.IsOpen);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProfileCommandsExposeServerAccountCharacterCrudDialogsAndClearPasswords()
|
||||
{
|
||||
using var orchestrator = new FakeLauncherOrchestrator();
|
||||
using var viewModel = CreateInitialized(orchestrator);
|
||||
|
||||
viewModel.AddServerCommand.Execute(null);
|
||||
Assert.Equal(ProfileEditorKind.AddServer, viewModel.EditorDialog.Kind);
|
||||
viewModel.EditorDialog.Name = "Remote ACE";
|
||||
viewModel.EditorDialog.Host = "ace.example.test";
|
||||
viewModel.EditorDialog.Port = "9001";
|
||||
viewModel.EditorDialog.SubmitCommand.Execute(null);
|
||||
Assert.Equal(("Remote ACE", "ace.example.test", 9001), orchestrator.AddedServer);
|
||||
|
||||
SelectServer(viewModel);
|
||||
viewModel.AddAccountCommand.Execute(null);
|
||||
Assert.Equal(ProfileEditorKind.AddAccount, viewModel.EditorDialog.Kind);
|
||||
viewModel.EditorDialog.Name = "second-account";
|
||||
viewModel.EditorDialog.Password = "one-use-secret";
|
||||
viewModel.EditorDialog.SubmitCommand.Execute(null);
|
||||
Assert.Equal(
|
||||
("Local ACE", "second-account", "one-use-secret"),
|
||||
orchestrator.AddedAccount);
|
||||
Assert.Equal(string.Empty, viewModel.EditorDialog.Password);
|
||||
|
||||
SelectAccount(viewModel);
|
||||
viewModel.AddCharacterCommand.Execute(null);
|
||||
Assert.Equal(ProfileEditorKind.AddCharacter, viewModel.EditorDialog.Kind);
|
||||
viewModel.EditorDialog.Name = "+Second";
|
||||
viewModel.EditorDialog.CharacterId = "0x5000000B";
|
||||
viewModel.EditorDialog.SubmitCommand.Execute(null);
|
||||
Assert.Equal(
|
||||
("Local ACE", "testaccount", "+Second", "0x5000000B"),
|
||||
orchestrator.AddedCharacter);
|
||||
|
||||
SelectCharacter(viewModel);
|
||||
viewModel.EditSelectedCommand.Execute(null);
|
||||
Assert.Equal(ProfileEditorKind.EditCharacter, viewModel.EditorDialog.Kind);
|
||||
viewModel.EditorDialog.Name = "+Renamed";
|
||||
viewModel.EditorDialog.CharacterId = "0x5000000C";
|
||||
viewModel.EditorDialog.SubmitCommand.Execute(null);
|
||||
Assert.Equal(
|
||||
("Local ACE", "testaccount", "+Acdream", "+Renamed", "0x5000000C"),
|
||||
orchestrator.EditedCharacter);
|
||||
|
||||
SelectCharacter(viewModel);
|
||||
viewModel.RemoveSelectedCommand.Execute(null);
|
||||
Assert.Equal(ProfileEditorKind.Remove, viewModel.EditorDialog.Kind);
|
||||
viewModel.EditorDialog.SubmitCommand.Execute(null);
|
||||
Assert.Equal(
|
||||
("Local ACE", "testaccount", "+Acdream"),
|
||||
orchestrator.RemovedCharacter);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ServerAndAccountEditRemoveDialogsRouteEveryMutationThroughCore()
|
||||
{
|
||||
using var orchestrator = new FakeLauncherOrchestrator();
|
||||
using var viewModel = CreateInitialized(orchestrator);
|
||||
|
||||
SelectServer(viewModel);
|
||||
viewModel.EditSelectedCommand.Execute(null);
|
||||
Assert.Equal(ProfileEditorKind.EditServer, viewModel.EditorDialog.Kind);
|
||||
Assert.Equal("127.0.0.1", viewModel.EditorDialog.Host);
|
||||
viewModel.EditorDialog.Name = "Renamed ACE";
|
||||
viewModel.EditorDialog.Host = "renamed.example.test";
|
||||
viewModel.EditorDialog.Port = "9010";
|
||||
viewModel.EditorDialog.SubmitCommand.Execute(null);
|
||||
Assert.Equal(
|
||||
("Local ACE", "Renamed ACE", "renamed.example.test", 9010),
|
||||
orchestrator.EditedServer);
|
||||
|
||||
SelectAccount(viewModel);
|
||||
viewModel.EditSelectedCommand.Execute(null);
|
||||
Assert.Equal(ProfileEditorKind.EditAccount, viewModel.EditorDialog.Kind);
|
||||
viewModel.EditorDialog.Name = "renamed-account";
|
||||
viewModel.EditorDialog.Password = "replacement-secret";
|
||||
viewModel.EditorDialog.SubmitCommand.Execute(null);
|
||||
Assert.Equal(
|
||||
("Local ACE", "testaccount", "renamed-account", "replacement-secret"),
|
||||
orchestrator.EditedAccount);
|
||||
Assert.Equal(string.Empty, viewModel.EditorDialog.Password);
|
||||
|
||||
SelectAccount(viewModel);
|
||||
viewModel.RemoveSelectedCommand.Execute(null);
|
||||
viewModel.EditorDialog.SubmitCommand.Execute(null);
|
||||
Assert.Equal(("Local ACE", "testaccount"), orchestrator.RemovedAccount);
|
||||
|
||||
SelectServer(viewModel);
|
||||
viewModel.RemoveSelectedCommand.Execute(null);
|
||||
viewModel.EditorDialog.SubmitCommand.Execute(null);
|
||||
Assert.Equal("Local ACE", orchestrator.RemovedServer);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CharacterSettingsAndLaunchActionsPreserveTheirTypedSemantics()
|
||||
{
|
||||
using var orchestrator = new FakeLauncherOrchestrator();
|
||||
using var viewModel = CreateInitialized(orchestrator);
|
||||
SelectCharacter(viewModel);
|
||||
|
||||
viewModel.CharacterLaunchMode = LaunchMode.Headless;
|
||||
viewModel.CharacterPluginsText = "Plugin.One\nPlugin.Two\nPlugin.One";
|
||||
viewModel.CharacterLoginCommandsText = " /tell someone, hi \n/vt start\n/tell someone, hi";
|
||||
viewModel.SaveCharacterSettingsCommand.Execute(null);
|
||||
|
||||
Assert.NotNull(orchestrator.SettingsUpdate);
|
||||
Assert.Equal(LaunchMode.Headless, orchestrator.SettingsUpdate.Value.Mode);
|
||||
Assert.Equal(["Plugin.One", "Plugin.Two"], orchestrator.SettingsUpdate.Value.Plugins);
|
||||
Assert.Equal(
|
||||
["/tell someone, hi", "/vt start", "/tell someone, hi"],
|
||||
orchestrator.SettingsUpdate.Value.Commands);
|
||||
|
||||
await viewModel.LaunchHeadlessCommand.ExecuteAsync();
|
||||
Assert.Equal(
|
||||
("Local ACE", "testaccount", "+Acdream", LaunchMode.Headless),
|
||||
orchestrator.LaunchRequest);
|
||||
Assert.Equal("Headless session started for +Acdream.", viewModel.OperationStatus);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ProbeUsesTheSelectedAccountAndRunningAccountDisablesIt()
|
||||
{
|
||||
using var orchestrator = new FakeLauncherOrchestrator();
|
||||
using var viewModel = CreateInitialized(orchestrator);
|
||||
SelectAccount(viewModel);
|
||||
|
||||
Assert.True(viewModel.CanProbe);
|
||||
await viewModel.RefreshCharactersCommand.ExecuteAsync();
|
||||
Assert.Equal(("Local ACE", "testaccount"), orchestrator.ProbeRequest);
|
||||
|
||||
orchestrator.ProbeCapability = LauncherCapability.Unavailable(
|
||||
"Stop the active session before refreshing this account.");
|
||||
orchestrator.RaiseStateChanged();
|
||||
|
||||
SelectAccount(viewModel);
|
||||
Assert.False(viewModel.CanProbe);
|
||||
Assert.Contains("Stop", viewModel.ProbeDisabledReason, StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void LinuxKeepsLauncherAndHeadlessAvailableButExplainsDisabledGuiModes()
|
||||
{
|
||||
using var orchestrator = new FakeLauncherOrchestrator
|
||||
{
|
||||
Platform = LinuxPlatform(),
|
||||
};
|
||||
using var viewModel = CreateInitialized(orchestrator);
|
||||
SelectCharacter(viewModel);
|
||||
|
||||
Assert.True(viewModel.ShowLinuxGraphicalNotice);
|
||||
Assert.False(viewModel.CanLaunchGui);
|
||||
Assert.False(viewModel.CanLaunchGuiSelect);
|
||||
Assert.True(viewModel.CanLaunchHeadless);
|
||||
Assert.Equal(
|
||||
LauncherPlatformCapabilities.LinuxGraphicalLaunchDisabledReason,
|
||||
viewModel.LinuxGraphicalNotice);
|
||||
Assert.Contains("Slice L", viewModel.GuiLaunchDisabledReason, StringComparison.Ordinal);
|
||||
Assert.Contains("parked at L1", viewModel.GuiLaunchDisabledReason, StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task LaunchErrorAndCancellationBecomeSafeVisibleState()
|
||||
{
|
||||
using var orchestrator = new FakeLauncherOrchestrator
|
||||
{
|
||||
LaunchHandler = _ => throw new LauncherOperationException("spawn failed safely"),
|
||||
};
|
||||
using var viewModel = CreateInitialized(orchestrator);
|
||||
SelectCharacter(viewModel);
|
||||
|
||||
await viewModel.LaunchGuiCommand.ExecuteAsync();
|
||||
|
||||
Assert.Equal("spawn failed safely", viewModel.LastError);
|
||||
Assert.Equal("Operation failed.", viewModel.OperationStatus);
|
||||
Assert.False(viewModel.IsBusy);
|
||||
|
||||
orchestrator.LaunchHandler = async cancellationToken =>
|
||||
{
|
||||
await Task.Delay(Timeout.InfiniteTimeSpan, cancellationToken);
|
||||
return FakeLauncherOrchestrator.CreateSession();
|
||||
};
|
||||
Task launch = viewModel.LaunchGuiCommand.ExecuteAsync();
|
||||
Assert.True(viewModel.IsBusy);
|
||||
Assert.True(viewModel.CancelOperationCommand.CanExecute(null));
|
||||
viewModel.CancelOperationCommand.Execute(null);
|
||||
await launch;
|
||||
|
||||
Assert.Equal("Operation cancelled.", viewModel.OperationStatus);
|
||||
Assert.False(viewModel.HasError);
|
||||
Assert.False(viewModel.IsBusy);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProfileDialogRedactsARejectedCredentialAndClearsItOnClose()
|
||||
{
|
||||
var dialog = new ProfileEditorDialogViewModel();
|
||||
dialog.Open(
|
||||
ProfileEditorKind.AddAccount,
|
||||
"Add account",
|
||||
candidate => throw new InvalidOperationException(
|
||||
$"rejected {candidate.Password}"));
|
||||
dialog.Password = "do-not-display";
|
||||
|
||||
dialog.SubmitCommand.Execute(null);
|
||||
|
||||
Assert.True(dialog.IsOpen);
|
||||
Assert.DoesNotContain("do-not-display", dialog.Error ?? string.Empty, StringComparison.Ordinal);
|
||||
Assert.Contains("[redacted]", dialog.Error ?? string.Empty, StringComparison.Ordinal);
|
||||
dialog.CancelCommand.Execute(null);
|
||||
Assert.Equal(string.Empty, dialog.Password);
|
||||
Assert.False(dialog.IsOpen);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ActiveSessionStopAndFinishedSessionClearUseCoreOwnership()
|
||||
{
|
||||
using var orchestrator = new FakeLauncherOrchestrator();
|
||||
using var viewModel = CreateInitialized(orchestrator);
|
||||
|
||||
LauncherSessionRowViewModel session = Assert.Single(viewModel.Sessions);
|
||||
await session.StopCommand.ExecuteAsync();
|
||||
Assert.Equal("session-1", orchestrator.StoppedSessionId);
|
||||
|
||||
orchestrator.Session = FakeLauncherOrchestrator.CreateSession(
|
||||
LauncherActivityState.Exited,
|
||||
"Exited cleanly.");
|
||||
orchestrator.RaiseStateChanged();
|
||||
Assert.True(viewModel.ClearFinishedSessionsCommand.CanExecute(null));
|
||||
viewModel.ClearFinishedSessionsCommand.Execute(null);
|
||||
Assert.True(orchestrator.ClearCalled);
|
||||
}
|
||||
|
||||
private static LauncherWindowViewModel CreateInitialized(
|
||||
FakeLauncherOrchestrator orchestrator)
|
||||
{
|
||||
var viewModel = new LauncherWindowViewModel(
|
||||
orchestrator,
|
||||
new ImmediateUiDispatcher());
|
||||
viewModel.Initialize();
|
||||
return viewModel;
|
||||
}
|
||||
|
||||
private static void SelectServer(LauncherWindowViewModel viewModel) =>
|
||||
viewModel.SelectedNode = Assert.Single(viewModel.Servers);
|
||||
|
||||
private static void SelectAccount(LauncherWindowViewModel viewModel) =>
|
||||
viewModel.SelectedNode = Assert.Single(Assert.Single(viewModel.Servers).Children);
|
||||
|
||||
private static void SelectCharacter(LauncherWindowViewModel viewModel) =>
|
||||
viewModel.SelectedNode = Assert.Single(
|
||||
Assert.Single(Assert.Single(viewModel.Servers).Children).Children);
|
||||
|
||||
private static LauncherPlatformCapabilities LinuxPlatform() => new(
|
||||
IsWindows: false,
|
||||
IsLinux: true,
|
||||
CanRunHeadless: true,
|
||||
CanLaunchGraphicalClient: false,
|
||||
PlatformName: "Linux",
|
||||
GraphicalLaunchDisabledReason:
|
||||
LauncherPlatformCapabilities.LinuxGraphicalLaunchDisabledReason);
|
||||
|
||||
private sealed class FakeLauncherOrchestrator : ILauncherOrchestrator
|
||||
{
|
||||
public event EventHandler? StateChanged;
|
||||
|
||||
public bool LoadCalled { get; private set; }
|
||||
|
||||
public bool ClearCalled { get; private set; }
|
||||
|
||||
public LauncherPlatformCapabilities Platform { get; init; } = new(
|
||||
IsWindows: true,
|
||||
IsLinux: false,
|
||||
CanRunHeadless: true,
|
||||
CanLaunchGraphicalClient: true,
|
||||
PlatformName: "Windows",
|
||||
GraphicalLaunchDisabledReason: null);
|
||||
|
||||
public LauncherCapability ProbeCapability { get; set; } = LauncherCapability.Available;
|
||||
|
||||
public LauncherSessionSnapshot Session { get; set; } = CreateSession();
|
||||
|
||||
public Func<CancellationToken, Task<LauncherSessionSnapshot>>? LaunchHandler { get; set; }
|
||||
|
||||
public (string Name, string Host, int Port)? AddedServer { get; private set; }
|
||||
|
||||
public (string Name, string NewName, string NewHost, int NewPort)? EditedServer { get; private set; }
|
||||
|
||||
public string? RemovedServer { get; private set; }
|
||||
|
||||
public (string Server, string Account, string Password)? AddedAccount { get; private set; }
|
||||
|
||||
public (string Server, string Account, string NewAccount, string? Password)? EditedAccount { get; private set; }
|
||||
|
||||
public (string Server, string Account)? RemovedAccount { get; private set; }
|
||||
|
||||
public (string Server, string Account, string Character, string? Id)? AddedCharacter { get; private set; }
|
||||
|
||||
public (string Server, string Account, string Character, string NewName, string? Id)? EditedCharacter { get; private set; }
|
||||
|
||||
public (string Server, string Account, string Character)? RemovedCharacter { get; private set; }
|
||||
|
||||
public (LaunchMode Mode, IReadOnlyList<string> Plugins, IReadOnlyList<string> Commands)? SettingsUpdate { get; private set; }
|
||||
|
||||
public (string Server, string Account, string Character, LaunchMode Mode)? LaunchRequest { get; private set; }
|
||||
|
||||
public (string Server, string Account)? ProbeRequest { get; private set; }
|
||||
|
||||
public string? StoppedSessionId { get; private set; }
|
||||
|
||||
public void LoadProfiles() => LoadCalled = true;
|
||||
|
||||
public LauncherStateSnapshot GetSnapshot() => new(
|
||||
[CreateServerSnapshot()],
|
||||
[Session],
|
||||
Platform,
|
||||
IsInstallationReady: false,
|
||||
InstallationStatus: "No installed client is configured.");
|
||||
|
||||
public LauncherCapability GetLaunchCapability(LaunchMode mode) =>
|
||||
Platform.ForLaunchMode(mode);
|
||||
|
||||
public LauncherCapability GetProbeCapability(string serverName, string accountName) =>
|
||||
ProbeCapability;
|
||||
|
||||
public void SetInstallRecord(LauncherInstallRecord? installRecord)
|
||||
{
|
||||
}
|
||||
|
||||
public void AddServer(string name, string host, int port) =>
|
||||
AddedServer = (name, host, port);
|
||||
|
||||
public void EditServer(string name, string newName, string newHost, int newPort) =>
|
||||
EditedServer = (name, newName, newHost, newPort);
|
||||
|
||||
public void RemoveServer(string name) => RemovedServer = name;
|
||||
|
||||
public void AddAccount(string serverName, string accountName, string password) =>
|
||||
AddedAccount = (serverName, accountName, password);
|
||||
|
||||
public void EditAccount(
|
||||
string serverName,
|
||||
string accountName,
|
||||
string newAccountName,
|
||||
string? newPassword) =>
|
||||
EditedAccount = (serverName, accountName, newAccountName, newPassword);
|
||||
|
||||
public void RemoveAccount(string serverName, string accountName) =>
|
||||
RemovedAccount = (serverName, accountName);
|
||||
|
||||
public void AddCharacter(
|
||||
string serverName,
|
||||
string accountName,
|
||||
string characterName,
|
||||
string? characterId) =>
|
||||
AddedCharacter = (serverName, accountName, characterName, characterId);
|
||||
|
||||
public void EditCharacterIdentity(
|
||||
string serverName,
|
||||
string accountName,
|
||||
string characterName,
|
||||
string newCharacterName,
|
||||
string? newCharacterId) =>
|
||||
EditedCharacter = (
|
||||
serverName,
|
||||
accountName,
|
||||
characterName,
|
||||
newCharacterName,
|
||||
newCharacterId);
|
||||
|
||||
public void UpdateCharacterSettings(
|
||||
string serverName,
|
||||
string accountName,
|
||||
string characterName,
|
||||
LaunchMode launchMode,
|
||||
IReadOnlyList<string> plugins,
|
||||
IReadOnlyList<string> loginCommands) =>
|
||||
SettingsUpdate = (launchMode, plugins, loginCommands);
|
||||
|
||||
public void RemoveCharacter(
|
||||
string serverName,
|
||||
string accountName,
|
||||
string characterName) =>
|
||||
RemovedCharacter = (serverName, accountName, characterName);
|
||||
|
||||
public Task<LauncherSessionSnapshot> LaunchAsync(
|
||||
string serverName,
|
||||
string accountName,
|
||||
string characterName,
|
||||
LaunchMode mode,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
LaunchRequest = (serverName, accountName, characterName, mode);
|
||||
return LaunchHandler?.Invoke(cancellationToken) ?? Task.FromResult(Session);
|
||||
}
|
||||
|
||||
public Task<LauncherSessionSnapshot> ProbeAsync(
|
||||
string serverName,
|
||||
string accountName,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
ProbeRequest = (serverName, accountName);
|
||||
return Task.FromResult(Session with { Kind = LauncherActivityKind.Probe });
|
||||
}
|
||||
|
||||
public Task StopSessionAsync(
|
||||
string sessionId,
|
||||
TimeSpan timeout,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
StoppedSessionId = sessionId;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public void PollStatus()
|
||||
{
|
||||
}
|
||||
|
||||
public void ClearFinishedSessions() => ClearCalled = true;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
|
||||
public void RaiseStateChanged() => StateChanged?.Invoke(this, EventArgs.Empty);
|
||||
|
||||
public static LauncherSessionSnapshot CreateSession(
|
||||
LauncherActivityState state = LauncherActivityState.Connected,
|
||||
string status = "Connected.") => new(
|
||||
"session-1",
|
||||
LauncherActivityKind.Play,
|
||||
"Local ACE",
|
||||
"testaccount",
|
||||
"+Acdream",
|
||||
LaunchMode.Gui,
|
||||
state,
|
||||
status,
|
||||
ExitCode: state == LauncherActivityState.Exited ? 0 : null,
|
||||
Error: null,
|
||||
CreatedAt: DateTimeOffset.UnixEpoch);
|
||||
|
||||
private static LauncherServerSnapshot CreateServerSnapshot() => new(
|
||||
"Local ACE",
|
||||
"127.0.0.1",
|
||||
9000,
|
||||
[
|
||||
new LauncherAccountSnapshot(
|
||||
"Local ACE",
|
||||
"testaccount",
|
||||
[
|
||||
new LauncherCharacterSnapshot(
|
||||
"Local ACE",
|
||||
"testaccount",
|
||||
"+Acdream",
|
||||
"0x5000000A",
|
||||
LaunchMode.GuiSelect,
|
||||
["Existing.Plugin"],
|
||||
["/tell someone, ready"],
|
||||
HasRunningSession: true,
|
||||
SessionStatus: "Connected."),
|
||||
],
|
||||
HasRunningActivity: true,
|
||||
ActivityStatus: "Connected."),
|
||||
]);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue