Skip to content

Commit

Permalink
Implement Print methods that work with Lumina SeString/ReadOnlySeStri…
Browse files Browse the repository at this point in the history
…ng (#2106)

* Implement Print methods that work with Lumina SeString/ReadOnlySeString

* null terminate before passing to Utf8String

* Rename XivChatEntryReadOnly to XivChatEntryRaw

* Fix error from wrong conversion method

* Follow Rider suggestion

* Switch from AppendMacroString to BeginMacro for optimization

* More optimization suggested by kizer

* More kizer suggested optimizations

* Fix small mistake

* Use XivChatEntry and read/write to Byte fields accordingly
  • Loading branch information
Infiziert90 authored Nov 19, 2024
1 parent bf7ef00 commit 3a3d6b6
Show file tree
Hide file tree
Showing 3 changed files with 102 additions and 29 deletions.
85 changes: 62 additions & 23 deletions Dalamud/Game/Gui/ChatGui.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@
using Dalamud.Game.Text.SeStringHandling;
using Dalamud.Game.Text.SeStringHandling.Payloads;
using Dalamud.Hooking;
using Dalamud.Interface.ImGuiSeStringRenderer.Internal.TextProcessing;
using Dalamud.IoC;
using Dalamud.IoC.Internal;
using Dalamud.Logging.Internal;
using Dalamud.Memory;
using Dalamud.Plugin.Services;
using Dalamud.Utility;

Expand All @@ -20,6 +22,8 @@
using FFXIVClientStructs.FFXIV.Client.UI.Misc;
using FFXIVClientStructs.FFXIV.Component.GUI;

using Lumina.Text.Payloads;

using LinkMacroPayloadType = Lumina.Text.Payloads.LinkMacroPayloadType;
using LuminaSeStringBuilder = Lumina.Text.SeStringBuilder;
using ReadOnlySePayloadType = Lumina.Text.ReadOnly.ReadOnlySePayloadType;
Expand Down Expand Up @@ -107,6 +111,8 @@ void IInternalDisposableService.DisposeService()
this.handleLinkClickHook.Dispose();
}

#region DalamudSeString

/// <inheritdoc/>
public void Print(XivChatEntry chat)
{
Expand Down Expand Up @@ -137,6 +143,24 @@ public void PrintError(SeString message, string? messageTag = null, ushort? tagC
this.PrintTagged(message, XivChatType.Urgent, messageTag, tagColor);
}

#endregion

#region LuminaSeString

/// <inheritdoc/>
public void Print(ReadOnlySpan<byte> message, string? messageTag = null, ushort? tagColor = null)
{
this.PrintTagged(message, this.configuration.GeneralChatType, messageTag, tagColor);
}

/// <inheritdoc/>
public void PrintError(ReadOnlySpan<byte> message, string? messageTag = null, ushort? tagColor = null)
{
this.PrintTagged(message, XivChatType.Urgent, messageTag, tagColor);
}

#endregion

/// <summary>
/// Process a chat queue.
/// </summary>
Expand All @@ -145,30 +169,35 @@ public void UpdateQueue()
while (this.chatQueue.Count > 0)
{
var chat = this.chatQueue.Dequeue();
var replacedMessage = new SeStringBuilder();
var sb = LuminaSeStringBuilder.SharedPool.Get();
var rosss = (ReadOnlySeStringSpan)chat.MessageBytes;

// Normalize Unicode NBSP to the built-in one, as the former won't renderl
foreach (var payload in chat.Message.Payloads)
foreach (var payload in rosss)
{
if (payload is TextPayload { Text: not null } textPayload)
if (payload.Type == ReadOnlySePayloadType.Invalid)
continue;

if (payload.Type != ReadOnlySePayloadType.Text)
{
var split = textPayload.Text.Split("\u202f"); // NARROW NO-BREAK SPACE
for (var i = 0; i < split.Length; i++)
{
replacedMessage.AddText(split[i]);
if (i + 1 < split.Length)
replacedMessage.Add(new RawPayload([0x02, (byte)Lumina.Text.Payloads.PayloadType.Indent, 0x01, 0x03]));
}
sb.Append(payload);
continue;
}
else

foreach (var c in UtfEnumerator.From(payload.Body, UtfEnumeratorFlags.Default))
{
replacedMessage.Add(payload);
if (c.Value.IntValue == 0x202F)
sb.BeginMacro(MacroCode.NonBreakingSpace).EndMacro();
else
sb.Append(c.EffectiveChar);
}
}

var sender = Utf8String.FromSequence(chat.Name.Encode());
var message = Utf8String.FromSequence(replacedMessage.BuiltString.Encode());

var output = sb.ToArray();
LuminaSeStringBuilder.SharedPool.Return(sb);

var sender = Utf8String.FromSequence(chat.NameBytes.NullTerminate());
var message = Utf8String.FromSequence(output.NullTerminate());

var targetChannel = chat.Type ?? this.configuration.GeneralChatType;

this.HandlePrintMessageDetour(RaptureLogModule.Instance(), targetChannel, sender, message, chat.Timestamp, (byte)(chat.Silent ? 1 : 0));
Expand Down Expand Up @@ -228,7 +257,7 @@ internal void RemoveChatLinkHandler(string pluginName, uint commandId)
}
}

private void PrintTagged(string message, XivChatType channel, string? tag, ushort? color)
private void PrintTagged(SeString message, XivChatType channel, string? tag, ushort? color)
{
var builder = new SeStringBuilder();

Expand All @@ -246,30 +275,32 @@ private void PrintTagged(string message, XivChatType channel, string? tag, ushor

this.Print(new XivChatEntry
{
Message = builder.AddText(message).Build(),
Message = builder.Build().Append(message),
Type = channel,
});
}

private void PrintTagged(SeString message, XivChatType channel, string? tag, ushort? color)
private void PrintTagged(ReadOnlySpan<byte> message, XivChatType channel, string? tag, ushort? color)
{
var builder = new SeStringBuilder();
var builder = new LuminaSeStringBuilder();

if (!tag.IsNullOrEmpty())
{
if (color is not null)
{
builder.AddUiForeground($"[{tag}] ", color.Value);
builder.PushColorType(color.Value);
builder.Append($"[{tag}] ");
builder.PopColorType();
}
else
{
builder.AddText($"[{tag}] ");
builder.Append($"[{tag}] ");
}
}

this.Print(new XivChatEntry
{
Message = builder.Build().Append(message),
MessageBytes = builder.Append((ReadOnlySeStringSpan)message).ToArray(),
Type = channel,
});
}
Expand Down Expand Up @@ -505,6 +536,14 @@ public void PrintError(string message, string? messageTag = null, ushort? tagCol
public void PrintError(SeString message, string? messageTag = null, ushort? tagColor = null)
=> this.chatGuiService.PrintError(message, messageTag, tagColor);

/// <inheritdoc/>
public void Print(ReadOnlySpan<byte> message, string? messageTag = null, ushort? tagColor = null)
=> this.chatGuiService.Print(message, messageTag, tagColor);

/// <inheritdoc/>
public void PrintError(ReadOnlySpan<byte> message, string? messageTag = null, ushort? tagColor = null)
=> this.chatGuiService.PrintError(message, messageTag, tagColor);

private void OnMessageForward(XivChatType type, int timestamp, ref SeString sender, ref SeString message, ref bool isHandled)
=> this.ChatMessage?.Invoke(type, timestamp, ref sender, ref message, ref isHandled);

Expand Down
22 changes: 20 additions & 2 deletions Dalamud/Game/Text/XivChatEntry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,30 @@ public sealed class XivChatEntry
/// <summary>
/// Gets or sets the sender name.
/// </summary>
public SeString Name { get; set; } = string.Empty;
public SeString Name
{
get => SeString.Parse(this.NameBytes);
set => this.NameBytes = value.Encode();
}

/// <summary>
/// Gets or sets the message.
/// </summary>
public SeString Message { get; set; } = string.Empty;
public SeString Message
{
get => SeString.Parse(this.MessageBytes);
set => this.MessageBytes = value.Encode();
}

/// <summary>
/// Gets or Sets the name payloads
/// </summary>
public byte[] NameBytes { get; set; } = [];

/// <summary>
/// Gets or Sets the message payloads.
/// </summary>
public byte[] MessageBytes { get; set; } = [];

/// <summary>
/// Gets or sets a value indicating whether new message sounds should be silenced or not.
Expand Down
24 changes: 20 additions & 4 deletions Dalamud/Plugin/Services/IChatGui.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public interface IChatGui
/// <param name="sender">The sender name.</param>
/// <param name="message">The message sent.</param>
public delegate void OnMessageUnhandledDelegate(XivChatType type, int timestamp, SeString sender, SeString message);

/// <summary>
/// Event that will be fired when a chat message is sent to chat by the game.
/// </summary>
Expand All @@ -68,17 +68,17 @@ public interface IChatGui
/// Event that will be fired when a chat message is not handled by Dalamud or a Plugin.
/// </summary>
public event OnMessageUnhandledDelegate ChatMessageUnhandled;

/// <summary>
/// Gets the ID of the last linked item.
/// </summary>
public uint LastLinkedItemId { get; }

/// <summary>
/// Gets the flags of the last linked item.
/// </summary>
public byte LastLinkedItemFlags { get; }

/// <summary>
/// Gets the dictionary of Dalamud Link Handlers.
/// </summary>
Expand Down Expand Up @@ -121,4 +121,20 @@ public interface IChatGui
/// <param name="messageTag">String to prepend message with "[messageTag] ".</param>
/// <param name="tagColor">Color to display the message tag with.</param>
public void PrintError(SeString message, string? messageTag = null, ushort? tagColor = null);

/// <summary>
/// Queue a chat message. Dalamud will send queued messages on the next framework event.
/// </summary>
/// <param name="message">A message to send.</param>
/// <param name="messageTag">String to prepend message with "[messageTag] ".</param>
/// <param name="tagColor">Color to display the message tag with.</param>
public void Print(ReadOnlySpan<byte> message, string? messageTag = null, ushort? tagColor = null);

/// <summary>
/// Queue a chat message. Dalamud will send queued messages on the next framework event.
/// </summary>
/// <param name="message">A message to send.</param>
/// <param name="messageTag">String to prepend message with "[messageTag] ".</param>
/// <param name="tagColor">Color to display the message tag with.</param>
public void PrintError(ReadOnlySpan<byte> message, string? messageTag = null, ushort? tagColor = null);
}

0 comments on commit 3a3d6b6

Please sign in to comment.