Full port of holtburger's TurbineChat sidecar wire path: - TurbineChat.cs: 0xF7DE codec with three payload variants (EventSendToRoom S->C, RequestSendToRoomById C->S, Response). 10-field outer header (size_first/blob_type/dispatch_type/ target_type/target_id/transport_type/transport_id/cookie/ size_second + payload). - UTF-16LE turbine string codec with 1-or-2 byte variable-length prefix (high bit on first byte signals 2-byte form). Mirrors holtburger's read_turbine_string / write_turbine_string at references/holtburger/.../messages/chat/turbine.rs:502-544. - SetTurbineChatChannels.cs: 0x0295 GameEvent sub-opcode parser (10 x u32 channel ids). Wired through GameEventDispatcher in WorldSession ctor; routes to GameEventWiring + TurbineChatState. - ChatChannelInfo.cs (Core): unified record union with Legacy (channel id + name) and Turbine (room id + chat type + dispatch type + name) variants, plus IsSelfEchoChannel predicate (Tells = false, channels = true so optimistic echo is suppressed where the server will echo). - TurbineChatState.cs (Core): Enabled flag + 10 cached room ids + NextContextId() cookie counter starting at 1. - WorldSession adds TurbineChatReceived + TurbineChannelsReceived events; SendTurbineChatTo outbound builds RequestSendToRoomById + sends through SendGameAction. ProcessDatagram dispatches 0xF7DE at the top level. - GameWindow constructs TurbineChatState, subscribes inbound EventSendToRoom -> ChatLog.OnChannelBroadcast; extends I.3's SendChatCmd handler to route Turbine kinds (General/Trade/Lfg/ Roleplay/Society/Olthoi) through TurbineChat first, fall back to legacy ChatChannel send when state.Enabled == false. Round-trip golden fixtures from holtburger source verified for all three payload variants + UTF-16LE strings (short + long prefix + non-ASCII Cafe + empty) + SetTurbineChatChannels. 26 new tests: - TurbineChatTests, SetTurbineChatChannelsTests in Core.Net.Tests - ChatChannelInfoTests, TurbineChatStateTests in Core.Tests Solution total: 960 green (243 Core.Net + 625 Core + 92 UI). ACE doesn't run a TurbineChat server, so codec is "ready when needed" for retail-server-emulating setups. Legacy ChatChannel fallback continues to work for current ACE-against-acdream play. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
61 lines
2.2 KiB
C#
61 lines
2.2 KiB
C#
using AcDream.Core.Chat;
|
|
using Xunit;
|
|
|
|
namespace AcDream.Core.Tests.Chat;
|
|
|
|
/// <summary>
|
|
/// Phase I.6: <see cref="ChatChannelInfo"/> + <see cref="ChatChannelSource"/>
|
|
/// behaviour. Mirrors holtburger's <c>is_self_echo_channel</c> predicate
|
|
/// (chat.rs:492-507) — only the legacy fellowship + allegiance-tree
|
|
/// channels echo the sender's own messages back with empty sender.
|
|
/// </summary>
|
|
public sealed class ChatChannelInfoTests
|
|
{
|
|
[Fact]
|
|
public void Legacy_FellowshipBitflag_IsSelfEcho()
|
|
{
|
|
var c = new ChatChannelInfo.Legacy(0x00000800u, "Fellowship");
|
|
Assert.Equal(ChatChannelSource.Legacy, c.Source);
|
|
Assert.Equal("Fellowship", c.DisplayName);
|
|
Assert.True(c.IsSelfEchoChannel());
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(0x00001000u)] // Vassals
|
|
[InlineData(0x00002000u)] // Patron
|
|
[InlineData(0x00004000u)] // Monarch
|
|
[InlineData(0x01000000u)] // CoVassals
|
|
public void Legacy_AllegianceTreeBitflags_AreSelfEcho(uint id)
|
|
{
|
|
var c = new ChatChannelInfo.Legacy(id, "Allegiance");
|
|
Assert.True(c.IsSelfEchoChannel(), $"channel 0x{id:X8} should self-echo");
|
|
}
|
|
|
|
[Fact]
|
|
public void Legacy_AllegianceBroadcastBitflag_IsNOTSelfEcho()
|
|
{
|
|
// 0x02000000 AllegianceBroadcast is the read-only motd channel —
|
|
// server does not echo client messages back on it (per holtburger
|
|
// chat.rs:492-507 predicate, only Fellow+Vassals+Patron+Monarch+CoVassals).
|
|
var c = new ChatChannelInfo.Legacy(0x02000000u, "AllegianceBroadcast");
|
|
Assert.False(c.IsSelfEchoChannel());
|
|
}
|
|
|
|
[Fact]
|
|
public void Turbine_IsNeverSelfEcho()
|
|
{
|
|
// Turbine rooms don't echo — caller must emit local optimistic echo.
|
|
var t = new ChatChannelInfo.Turbine(
|
|
RoomId: 0x12345678u, ChatType: 2u, DispatchType: 2u, DisplayName: "General");
|
|
Assert.Equal(ChatChannelSource.Turbine, t.Source);
|
|
Assert.Equal("General", t.DisplayName);
|
|
Assert.False(t.IsSelfEchoChannel());
|
|
}
|
|
|
|
[Fact]
|
|
public void Legacy_UnknownBitflag_IsNotSelfEcho()
|
|
{
|
|
var c = new ChatChannelInfo.Legacy(0xCAFEF00Du, "WatCha");
|
|
Assert.False(c.IsSelfEchoChannel());
|
|
}
|
|
}
|