163 lines
6.1 KiB
C#
163 lines
6.1 KiB
C#
namespace AcDream.Core.Chat;
|
|
|
|
/// <summary>
|
|
/// Runtime state for retail's TurbineChat (0xF7DE) global chat rooms —
|
|
/// the General / Trade / LFG / Roleplay / Society / Olthoi pipeline.
|
|
///
|
|
/// <para>
|
|
/// Lifecycle:
|
|
/// <list type="bullet">
|
|
/// <item>Pre-login: <see cref="Enabled"/> false, all room ids 0, context counter 1.</item>
|
|
/// <item>Server fires <c>SetTurbineChatChannels (0x0295)</c> shortly after EnterWorld
|
|
/// → <see cref="OnChannelsReceived"/> populates the room ids and flips
|
|
/// <see cref="Enabled"/> on.</item>
|
|
/// <item>Outbound chat: caller asks for a fresh context id via
|
|
/// <see cref="NextContextId"/>, looks up the room id by
|
|
/// <see cref="ChatType"/>, and feeds the pair to
|
|
/// <see cref="WorldSession.SendTurbineChatTo"/>.</item>
|
|
/// </list>
|
|
/// </para>
|
|
///
|
|
/// <para>
|
|
/// Mirrors holtburger's <c>TurbineChatState</c>
|
|
/// (<c>references/holtburger/crates/holtburger-core/src/client/types.rs</c>
|
|
/// lines 657-672). Cookie counter starts at 1 (not 0) per holtburger:
|
|
/// <c>next_context_id.wrapping_add(1).max(1)</c> keeps 0 reserved for
|
|
/// "no context" / response cookies.
|
|
/// </para>
|
|
/// </summary>
|
|
public sealed class TurbineChatState
|
|
{
|
|
/// <summary>True after the first <c>SetTurbineChatChannels</c> arrives.</summary>
|
|
public bool Enabled { get; private set; }
|
|
|
|
/// <summary>Allegiance Turbine room (0 if player has no allegiance).</summary>
|
|
public uint AllegianceRoom { get; private set; }
|
|
public uint GeneralRoom { get; private set; }
|
|
public uint TradeRoom { get; private set; }
|
|
public uint LfgRoom { get; private set; }
|
|
public uint RoleplayRoom { get; private set; }
|
|
public uint OlthoiRoom { get; private set; }
|
|
/// <summary>Top-level Society room (0 if player has no society).</summary>
|
|
public uint SocietyRoom { get; private set; }
|
|
public uint SocietyCelestialHandRoom { get; private set; }
|
|
public uint SocietyEldrytchWebRoom { get; private set; }
|
|
public uint SocietyRadiantBloodRoom { get; private set; }
|
|
|
|
private uint _nextContextId = 1;
|
|
|
|
/// <summary>
|
|
/// Get-and-increment the per-session context cookie. Wraps to 1 (not
|
|
/// 0) so 0 stays reserved for "no context" / Response cookies.
|
|
/// Mirrors holtburger's <c>wrapping_add(1).max(1)</c>.
|
|
/// </summary>
|
|
public uint NextContextId()
|
|
{
|
|
uint cookie = _nextContextId;
|
|
unchecked
|
|
{
|
|
_nextContextId += 1;
|
|
if (_nextContextId == 0) _nextContextId = 1;
|
|
}
|
|
return cookie;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Absorb a parsed <c>SetTurbineChatChannels</c> payload — flips
|
|
/// <see cref="Enabled"/> on and fills in the per-channel room ids.
|
|
/// Takes raw u32s (rather than the parser's struct directly) so
|
|
/// AcDream.Core stays free of an AcDream.Core.Net dependency.
|
|
/// </summary>
|
|
public void OnChannelsReceived(
|
|
uint allegianceRoom,
|
|
uint generalRoom,
|
|
uint tradeRoom,
|
|
uint lfgRoom,
|
|
uint roleplayRoom,
|
|
uint olthoiRoom,
|
|
uint societyRoom,
|
|
uint societyCelestialHandRoom,
|
|
uint societyEldrytchWebRoom,
|
|
uint societyRadiantBloodRoom)
|
|
{
|
|
Enabled = true;
|
|
AllegianceRoom = allegianceRoom;
|
|
GeneralRoom = generalRoom;
|
|
TradeRoom = tradeRoom;
|
|
LfgRoom = lfgRoom;
|
|
RoleplayRoom = roleplayRoom;
|
|
OlthoiRoom = olthoiRoom;
|
|
SocietyRoom = societyRoom;
|
|
SocietyCelestialHandRoom = societyCelestialHandRoom;
|
|
SocietyEldrytchWebRoom = societyEldrytchWebRoom;
|
|
SocietyRadiantBloodRoom = societyRadiantBloodRoom;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Return the server-assigned room table and outbound context sequence to
|
|
/// their pre-login values. Room ids and cookies belong to one negotiated
|
|
/// character session and must never cross a reconnect boundary.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// Retail <c>CCommunicationSystem</c> construction at <c>0x005566C0</c>
|
|
/// starts Turbine chat disabled; SetTurbineChatChannels (0x0295) supplies
|
|
/// rooms per character. Cookie 1 is the Holtburger protocol-reference
|
|
/// initial value, with zero reserved for no context.
|
|
/// </remarks>
|
|
public void Reset()
|
|
{
|
|
Enabled = false;
|
|
AllegianceRoom = 0u;
|
|
GeneralRoom = 0u;
|
|
TradeRoom = 0u;
|
|
LfgRoom = 0u;
|
|
RoleplayRoom = 0u;
|
|
OlthoiRoom = 0u;
|
|
SocietyRoom = 0u;
|
|
SocietyCelestialHandRoom = 0u;
|
|
SocietyEldrytchWebRoom = 0u;
|
|
SocietyRadiantBloodRoom = 0u;
|
|
_nextContextId = 1u;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Look up the runtime room id for a Turbine
|
|
/// <see cref="ChatChannelKindLite"/>. Returns 0 if the channel is not
|
|
/// available (server hasn't populated it / player not in society / etc).
|
|
/// </summary>
|
|
public uint RoomFor(ChatChannelKindLite kind) => kind switch
|
|
{
|
|
ChatChannelKindLite.Allegiance => AllegianceRoom,
|
|
ChatChannelKindLite.General => GeneralRoom,
|
|
ChatChannelKindLite.Trade => TradeRoom,
|
|
ChatChannelKindLite.Lfg => LfgRoom,
|
|
ChatChannelKindLite.Roleplay => RoleplayRoom,
|
|
ChatChannelKindLite.Olthoi => OlthoiRoom,
|
|
ChatChannelKindLite.Society => SocietyRoom,
|
|
ChatChannelKindLite.SocietyCelestialHand => SocietyCelestialHandRoom,
|
|
ChatChannelKindLite.SocietyEldrytchWeb => SocietyEldrytchWebRoom,
|
|
ChatChannelKindLite.SocietyRadiantBlood => SocietyRadiantBloodRoom,
|
|
_ => 0u,
|
|
};
|
|
}
|
|
|
|
/// <summary>
|
|
/// Coarse Turbine channel selector usable from
|
|
/// <see cref="TurbineChatState.RoomFor"/> without dragging in the
|
|
/// AcDream.UI.Abstractions <c>ChatChannelKind</c> (which lives in a
|
|
/// different layer). Maps 1:1 onto the chat-type-id values from
|
|
/// holtburger turbine.rs (TurbineChatType, lines 31-45).
|
|
/// </summary>
|
|
public enum ChatChannelKindLite
|
|
{
|
|
Allegiance,
|
|
General,
|
|
Trade,
|
|
Lfg,
|
|
Roleplay,
|
|
Society,
|
|
SocietyCelestialHand,
|
|
SocietyEldrytchWeb,
|
|
SocietyRadiantBlood,
|
|
Olthoi,
|
|
}
|