feat(runtime): share chat commands and run login sequence
This commit is contained in:
parent
5535d0adac
commit
41b15efd4d
57 changed files with 1739 additions and 345 deletions
|
|
@ -1,6 +1,7 @@
|
|||
using System.Net;
|
||||
using AcDream.Core.Net;
|
||||
using AcDream.Core.Net.Messages;
|
||||
using AcDream.Runtime.Chat;
|
||||
using AcDream.Runtime.Session;
|
||||
|
||||
namespace AcDream.Runtime.Tests.Session;
|
||||
|
|
@ -74,6 +75,37 @@ public sealed class LiveSessionHostTests
|
|||
Assert.False(host.IsInWorld);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void LoginSequenceStartsAfterTheEnteredWorldObservation()
|
||||
{
|
||||
var calls = new List<string>();
|
||||
var operations = new TestOperations(calls);
|
||||
var controller = new LiveSessionController(operations);
|
||||
var bus = new LiveCommandBus();
|
||||
bus.Register<SendChatCmd>(command => calls.Add($"login:{command.Text}"));
|
||||
var loginCommands = new LoginCommandSequence(
|
||||
["ready"],
|
||||
TimeSpan.Zero,
|
||||
new TestFeedback(),
|
||||
bus);
|
||||
LiveSessionHost host = CreateHost(
|
||||
controller,
|
||||
calls,
|
||||
_ => new TestEventRouting(calls),
|
||||
_ => new TestCommandRouting(calls),
|
||||
loginCommands);
|
||||
|
||||
Assert.Equal(
|
||||
LiveSessionStartStatus.Connected,
|
||||
host.Start(LiveOptions()).Status);
|
||||
|
||||
int entered = calls.IndexOf("character-entered:1342177282");
|
||||
int login = calls.IndexOf("login:ready");
|
||||
Assert.True(entered >= 0);
|
||||
Assert.Equal(entered + 1, login);
|
||||
controller.Dispose();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CommandFactoryFailureRollsBackTheAlreadyAttachedEventRoute()
|
||||
{
|
||||
|
|
@ -219,7 +251,8 @@ public sealed class LiveSessionHostTests
|
|||
LiveSessionController controller,
|
||||
List<string> calls,
|
||||
Func<WorldSession, ILiveSessionEventRouting> createEvents,
|
||||
Func<WorldSession, ILiveSessionCommandRouting> createCommands) =>
|
||||
Func<WorldSession, ILiveSessionCommandRouting> createCommands,
|
||||
LoginCommandSequence? loginCommands = null) =>
|
||||
new(controller, new LiveSessionHostBindings(
|
||||
Routing: new(createEvents, createCommands),
|
||||
Reset: _ => calls.Add("reset"),
|
||||
|
|
@ -240,7 +273,8 @@ public sealed class LiveSessionHostTests
|
|||
Connected: () => calls.Add("connected"),
|
||||
Roster: roster => calls.Add($"roster:{roster.AccountName}"),
|
||||
CharacterEntered: selection =>
|
||||
calls.Add($"character-entered:{selection.CharacterId}")));
|
||||
calls.Add($"character-entered:{selection.CharacterId}"),
|
||||
LoginCommands: loginCommands));
|
||||
|
||||
private static LiveSessionConnectOptions LiveOptions(
|
||||
bool live = true,
|
||||
|
|
@ -291,6 +325,14 @@ public sealed class LiveSessionHostTests
|
|||
public void Dispose() => calls.Add("dispose-commands");
|
||||
}
|
||||
|
||||
private sealed class TestFeedback : IChatCommandFeedback
|
||||
{
|
||||
public string? LastIncomingTellSender => null;
|
||||
public string? LastOutgoingTellTarget => null;
|
||||
public void ShowInterfaceText(string text) { }
|
||||
public void ShowSystemMessage(string text) { }
|
||||
}
|
||||
|
||||
private sealed class TestOperations(List<string> calls) : ILiveSessionOperations
|
||||
{
|
||||
public List<WorldSession> Sessions { get; } = [];
|
||||
|
|
|
|||
|
|
@ -32,11 +32,12 @@ public sealed class SessionStatusWriterTests
|
|||
writer.EnteredWorld("s1", 0x50000001u, "Ready");
|
||||
writer.PluginLoaded("s1", "acdream.good");
|
||||
writer.PluginFailed("s1", "acdream.bad", "enable failed");
|
||||
writer.LoginCommandFailed("s1", 2, "/version", "unsupported headless command");
|
||||
writer.Disconnected("s1", "stopped");
|
||||
writer.Exited("s1", 0, "disposed");
|
||||
|
||||
string[] lines = File.ReadAllLines(file.Path);
|
||||
Assert.Equal(8, lines.Length);
|
||||
Assert.Equal(9, lines.Length);
|
||||
|
||||
JsonElement started = Parse(lines[0]);
|
||||
Assert.Equal(1, started.GetProperty("v").GetInt32());
|
||||
|
|
@ -73,11 +74,25 @@ public sealed class SessionStatusWriterTests
|
|||
Assert.Equal("acdream.bad", pluginFailed.GetProperty("plugin").GetString());
|
||||
Assert.Equal("enable failed", pluginFailed.GetProperty("error").GetString());
|
||||
|
||||
JsonElement disconnected = Parse(lines[6]);
|
||||
JsonElement loginCommandFailed = Parse(lines[6]);
|
||||
Assert.Equal(1, loginCommandFailed.GetProperty("v").GetInt32());
|
||||
Assert.Equal("loginCommandFailed", loginCommandFailed.GetProperty("e").GetString());
|
||||
Assert.Equal("s1", loginCommandFailed.GetProperty("sessionId").GetString());
|
||||
Assert.Equal(2, loginCommandFailed.GetProperty("commandIndex").GetInt32());
|
||||
Assert.Equal("/version", loginCommandFailed.GetProperty("command").GetString());
|
||||
Assert.Equal(
|
||||
"unsupported headless command",
|
||||
loginCommandFailed.GetProperty("error").GetString());
|
||||
Assert.Equal(
|
||||
["v", "e", "t", "sessionId", "commandIndex", "command", "error"],
|
||||
loginCommandFailed.EnumerateObject()
|
||||
.Select(static property => property.Name));
|
||||
|
||||
JsonElement disconnected = Parse(lines[7]);
|
||||
Assert.Equal("disconnected", disconnected.GetProperty("e").GetString());
|
||||
Assert.Equal("stopped", disconnected.GetProperty("reason").GetString());
|
||||
|
||||
JsonElement exited = Parse(lines[7]);
|
||||
JsonElement exited = Parse(lines[8]);
|
||||
Assert.Equal("exited", exited.GetProperty("e").GetString());
|
||||
Assert.Equal(0, exited.GetProperty("code").GetInt32());
|
||||
Assert.Equal("disposed", exited.GetProperty("reason").GetString());
|
||||
|
|
@ -93,6 +108,7 @@ public sealed class SessionStatusWriterTests
|
|||
writer.Connected("s1");
|
||||
writer.PluginLoaded("s1", "acdream.good");
|
||||
writer.PluginFailed("s1", "acdream.bad", "failed");
|
||||
writer.LoginCommandFailed("s1", 0, "", "unknown command");
|
||||
writer.Disconnected("s1", "stopped");
|
||||
writer.Exited("s1", 0, "disposed");
|
||||
|
||||
|
|
@ -199,11 +215,12 @@ public sealed class SessionStatusWriterTests
|
|||
writer.EnteredWorld("bot", 0x50000001u, "Ready");
|
||||
writer.PluginLoaded("bot", "acdream.good");
|
||||
writer.PluginFailed("bot", "acdream.bad", "enable failed");
|
||||
writer.LoginCommandFailed("bot", 1, "/version", "unsupported");
|
||||
writer.Disconnected("bot", "stopped");
|
||||
writer.Exited("bot", 0, "disposed");
|
||||
|
||||
string[] lines = File.ReadAllLines(file.Path);
|
||||
Assert.Equal(8, lines.Length);
|
||||
Assert.Equal(9, lines.Length);
|
||||
|
||||
AssertExactProperties(lines[0], "v", "e", "t", "sessionId");
|
||||
AssertExactProperties(lines[1], "v", "e", "t", "sessionId");
|
||||
|
|
@ -215,8 +232,11 @@ public sealed class SessionStatusWriterTests
|
|||
AssertExactProperties(lines[4], "v", "e", "t", "sessionId", "plugin");
|
||||
AssertExactProperties(
|
||||
lines[5], "v", "e", "t", "sessionId", "plugin", "error");
|
||||
AssertExactProperties(lines[6], "v", "e", "t", "sessionId", "reason");
|
||||
AssertExactProperties(lines[7], "v", "e", "t", "sessionId", "code", "reason");
|
||||
AssertExactProperties(
|
||||
lines[6],
|
||||
"v", "e", "t", "sessionId", "commandIndex", "command", "error");
|
||||
AssertExactProperties(lines[7], "v", "e", "t", "sessionId", "reason");
|
||||
AssertExactProperties(lines[8], "v", "e", "t", "sessionId", "code", "reason");
|
||||
|
||||
// The nested characters[] entries are exact too — the exact shape a
|
||||
// password could otherwise be smuggled through.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue