Five sub-changes:
1. Windows-1252 codec switch (global). Every Encoding.ASCII call site
in src/AcDream.Core.Net/Messages/ -> Encoding.GetEncoding(1252).
Touched HearSpeech, ChatRequests, GameEvents, AppraiseInfoParser,
CharacterList, CreateObject, PlayerDescriptionParser, SocialActions.
New Encodings.cs module-init registers CodePagesEncodingProvider
(System.Text.Encoding.CodePages ships with .NET 10 SDK but isn't
auto-registered). Matches retail + holtburger; accented names
no longer round-trip-broken.
2. New parsers (opcodes confirmed against holtburger opcodes.rs):
- EmoteText (0x01E0) { u32 senderGuid, string16 senderName, string16 text }
- SoulEmote (0x01E2) same wire layout as EmoteText
- ServerMessage (0xF7E0) { string16 message, u32 chatType }
- PlayerKilled (0x019E) { string16 deathMessage, u32 victimGuid, u32 killerGuid }
Shared StringReader.cs has the CP1252 String16L primitive.
3. WorldSession dispatch. ProcessDatagram adds branches for the four
new top-level opcodes + fires session-level events (EmoteHeard,
SoulEmoteHeard, ServerMessageReceived, PlayerKilledReceived).
0x0295 SetTurbineChatChannels stubbed with TODO for parallel I.6.
4. GameEventWiring routes WeenieError + WeenieErrorWithString
(parsers existed but were unrouted) -> chat.OnWeenieError.
5. ChatLog adapters: Emote / SoulEmote ChatKind values, OnEmote,
OnSoulEmote, OnPlayerKilled, OnWeenieError. OnLocalSpeech now
substitutes empty sender -> "You" per holtburger client/messages.rs.
ChatVM.FormatEntry handles new kinds (asterisk + sender + text).
22 new tests covering parser round-trips + reject-bad-opcode +
ChatLog adapter coverage + Win-1252 round-trip with non-ASCII chars.
Solution total: 881 green (210->225 in Core.Net.Tests, 606->613 in Core.Tests).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
51 lines
2 KiB
C#
51 lines
2 KiB
C#
using System.Runtime.CompilerServices;
|
|
using System.Text;
|
|
|
|
namespace AcDream.Core.Net.Messages;
|
|
|
|
/// <summary>
|
|
/// Shared text encodings for the AC wire protocol.
|
|
///
|
|
/// <para>
|
|
/// Retail and holtburger both use Windows-1252 (CP1252) for every
|
|
/// String16L on the wire — chat, character names, item names,
|
|
/// system messages, popup text. Before Phase I.5 we used ASCII,
|
|
/// which was wrong: any non-ASCII byte (e.g. é = 0xE9, name accents
|
|
/// or chat punctuation from Latin-1 locales) decoded to '?'. Switching
|
|
/// to CP1252 matches what the server actually sends.
|
|
/// </para>
|
|
///
|
|
/// <para>
|
|
/// .NET 6+ ships only ASCII / Latin-1 / UTF8/16/32 in the base library;
|
|
/// Windows-1252 lives in <c>System.Text.Encoding.CodePages</c>. The
|
|
/// module initializer below registers that provider on first load of
|
|
/// any type in <c>AcDream.Core.Net.Messages</c>, so call sites can use
|
|
/// <see cref="Encoding.GetEncoding(int)"/> with id <c>1252</c> directly.
|
|
/// </para>
|
|
/// </summary>
|
|
public static class Encodings
|
|
{
|
|
/// <summary>
|
|
/// Module initializer — guaranteed by ECMA-335 to run before any
|
|
/// other code in this module (in particular, before any static
|
|
/// field initializer in user code that follows). Registers
|
|
/// <c>CodePagesEncodingProvider</c> so
|
|
/// <see cref="Encoding.GetEncoding(int)"/> with id 1252 returns a
|
|
/// real encoding instance instead of throwing
|
|
/// <see cref="System.NotSupportedException"/>.
|
|
/// </summary>
|
|
#pragma warning disable CA2255 // Library-internal: registers CP1252 once
|
|
// before any wire-string parser is invoked.
|
|
[ModuleInitializer]
|
|
internal static void Register()
|
|
{
|
|
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
|
|
}
|
|
#pragma warning restore CA2255
|
|
|
|
/// <summary>
|
|
/// CP1252 (Windows-1252). Cached so callers don't look it up each frame.
|
|
/// Initialized after <see cref="Register"/> via field-init ordering.
|
|
/// </summary>
|
|
public static readonly Encoding Windows1252 = Encoding.GetEncoding(1252);
|
|
}
|