mirror of
https://github.com/goatcorp/Dalamud.git
synced 2025-12-12 18:27:23 +01:00
Implement Print methods that work with Lumina SeString/ReadOnlySeString (#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
This commit is contained in:
parent
bf7ef00ec0
commit
3a3d6b6e6a
3 changed files with 118 additions and 45 deletions
|
|
@ -8,9 +8,11 @@ using Dalamud.Game.Text;
|
|||
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;
|
||||
|
||||
|
|
@ -20,6 +22,8 @@ using FFXIVClientStructs.FFXIV.Client.UI;
|
|||
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;
|
||||
|
|
@ -107,6 +111,8 @@ internal sealed unsafe class ChatGui : IInternalDisposableService, IChatGui
|
|||
this.handleLinkClickHook.Dispose();
|
||||
}
|
||||
|
||||
#region DalamudSeString
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Print(XivChatEntry chat)
|
||||
{
|
||||
|
|
@ -137,6 +143,24 @@ internal sealed unsafe class ChatGui : IInternalDisposableService, IChatGui
|
|||
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>
|
||||
|
|
@ -145,30 +169,35 @@ internal sealed unsafe class ChatGui : IInternalDisposableService, IChatGui
|
|||
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));
|
||||
|
|
@ -228,29 +257,6 @@ internal sealed unsafe class ChatGui : IInternalDisposableService, IChatGui
|
|||
}
|
||||
}
|
||||
|
||||
private void PrintTagged(string message, XivChatType channel, string? tag, ushort? color)
|
||||
{
|
||||
var builder = new SeStringBuilder();
|
||||
|
||||
if (!tag.IsNullOrEmpty())
|
||||
{
|
||||
if (color is not null)
|
||||
{
|
||||
builder.AddUiForeground($"[{tag}] ", color.Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.AddText($"[{tag}] ");
|
||||
}
|
||||
}
|
||||
|
||||
this.Print(new XivChatEntry
|
||||
{
|
||||
Message = builder.AddText(message).Build(),
|
||||
Type = channel,
|
||||
});
|
||||
}
|
||||
|
||||
private void PrintTagged(SeString message, XivChatType channel, string? tag, ushort? color)
|
||||
{
|
||||
var builder = new SeStringBuilder();
|
||||
|
|
@ -274,6 +280,31 @@ internal sealed unsafe class ChatGui : IInternalDisposableService, IChatGui
|
|||
});
|
||||
}
|
||||
|
||||
private void PrintTagged(ReadOnlySpan<byte> message, XivChatType channel, string? tag, ushort? color)
|
||||
{
|
||||
var builder = new LuminaSeStringBuilder();
|
||||
|
||||
if (!tag.IsNullOrEmpty())
|
||||
{
|
||||
if (color is not null)
|
||||
{
|
||||
builder.PushColorType(color.Value);
|
||||
builder.Append($"[{tag}] ");
|
||||
builder.PopColorType();
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.Append($"[{tag}] ");
|
||||
}
|
||||
}
|
||||
|
||||
this.Print(new XivChatEntry
|
||||
{
|
||||
MessageBytes = builder.Append((ReadOnlySeStringSpan)message).ToArray(),
|
||||
Type = channel,
|
||||
});
|
||||
}
|
||||
|
||||
private void InventoryItemCopyDetour(InventoryItem* thisPtr, InventoryItem* otherPtr)
|
||||
{
|
||||
this.inventoryItemCopyHook.Original(thisPtr, otherPtr);
|
||||
|
|
@ -505,6 +536,14 @@ internal class ChatGuiPluginScoped : IInternalDisposableService, IChatGui
|
|||
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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue