acdream/tests/AcDream.Core.Tests/Chat/ChatChannelInfoTests.cs
Erik ca968fc766 feat(net+chat): #19 TurbineChat (0xF7DE) codec + ChatChannelInfo + SetTurbineChatChannels parser
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>
2026-04-25 19:44:56 +02:00

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());
}
}