- Check auto translation for commands and execute them instead of sending

- Plugin commands trigger the command helper window now
- Fix auto translation with empty text appearing
- Switch up all dalamud payload usage to ROSS if possible
- Prepare 7.5 changes
- Cleanup
This commit is contained in:
Infi
2026-04-08 21:15:28 +02:00
parent 9f7a6267f6
commit c424311b24
52 changed files with 614 additions and 423 deletions
+6 -6
View File
@@ -1,6 +1,6 @@
<Project Sdk="Dalamud.NET.Sdk/14.0.1">
<Project Sdk="Dalamud.NET.Sdk/14.0.2">
<PropertyGroup>
<Version>1.34.4</Version>
<Version>1.34.5</Version>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
@@ -53,10 +53,10 @@
<!-- <Copy SourceFiles="@(Files)" DestinationFolder="$(TargetDir)\Frontend\%(RecursiveDir)" />-->
<!-- </Target>-->
<Target Name="NodeJS Compile" BeforeTargets="BeforeCompile" Condition="'$(Configuration)' == 'Debug'">
<Exec Command="npm install" WorkingDirectory="Http\Frontend"/>
<Exec Command="npm run build" WorkingDirectory="Http\Frontend"/>
</Target>
<!-- <Target Name="NodeJS Compile" BeforeTargets="BeforeCompile" Condition="'$(Configuration)' == 'Debug'">-->
<!-- <Exec Command="npm install" WorkingDirectory="Http\Frontend"/>-->
<!-- <Exec Command="npm run build" WorkingDirectory="Http\Frontend"/>-->
<!-- </Target>-->
<Target Name="UnzipBuild" AfterTargets="Build">
<Unzip SourceFiles="websiteBuild.zip" DestinationFolder="$(TargetDir)\Frontend"/>
-1
View File
@@ -1,6 +1,5 @@
namespace ChatTwo.Code;
[System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1028:Enum Storage should be Int32")]
[Flags]
public enum ChatSource : ushort
{
+4 -11
View File
@@ -5,17 +5,10 @@ namespace ChatTwo.Code;
internal static class ChatSourceExt
{
internal const ChatSource All =
ChatSource.Self
| ChatSource.PartyMember
| ChatSource.AllianceMember
| ChatSource.Other
| ChatSource.EngagedEnemy
| ChatSource.UnengagedEnemy
| ChatSource.FriendlyNpc
| ChatSource.SelfPet
| ChatSource.PartyPet
| ChatSource.AlliancePet
| ChatSource.OtherPet;
ChatSource.Self | ChatSource.PartyMember | ChatSource.AllianceMember |
ChatSource.Other | ChatSource.EngagedEnemy | ChatSource.UnengagedEnemy |
ChatSource.FriendlyNpc | ChatSource.SelfPet | ChatSource.PartyPet |
ChatSource.AlliancePet | ChatSource.OtherPet;
internal static string Name(this ChatSource source) => source switch
{
-1
View File
@@ -1,6 +1,5 @@
namespace ChatTwo.Code;
[System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1028:Enum Storage should be Int32")]
public enum ChatType : ushort
{
Debug = 1,
+11 -16
View File
@@ -6,14 +6,9 @@ namespace ChatTwo.Code;
internal static class ChatTypeExt
{
internal static IEnumerable<(string, ChatType[])> SortOrder => new[]
{
(Language.Options_Tabs_ChannelTypes_Special,
internal static IEnumerable<(string, ChatType[])> SortOrder =>
[
ChatType.Debug,
ChatType.Urgent,
ChatType.Notice
]),
(Language.Options_Tabs_ChannelTypes_Special, [ChatType.Debug, ChatType.Urgent, ChatType.Notice]),
(Language.Options_Tabs_ChannelTypes_Chat,
[
@@ -48,8 +43,8 @@ internal static class ChatTypeExt
ChatType.CustomEmote
]),
(Language.Options_Tabs_ChannelTypes_Battle, new[]
{
(Language.Options_Tabs_ChannelTypes_Battle,
[
ChatType.Damage,
ChatType.Miss,
ChatType.Action,
@@ -58,11 +53,11 @@ internal static class ChatTypeExt
ChatType.GainBuff,
ChatType.LoseBuff,
ChatType.GainDebuff,
ChatType.LoseDebuff,
}),
ChatType.LoseDebuff
]),
(Language.Options_Tabs_ChannelTypes_Announcements, new[]
{
(Language.Options_Tabs_ChannelTypes_Announcements,
[
ChatType.System,
ChatType.BattleSystem,
ChatType.GatheringSystem,
@@ -87,11 +82,11 @@ internal static class ChatTypeExt
ChatType.Orchestrion,
ChatType.MessageBook,
ChatType.Alarm,
ChatType.GlamourNotifications,
}),
ChatType.GlamourNotifications
])
// Note: ExtraChat linkshells are handled separately in the tab settings
// UI.
};
];
internal static string Name(this ChatType type)
{
+2 -5
View File
@@ -1,4 +1,3 @@
using Dalamud.Plugin.Services;
using Lumina.Excel.Sheets;
namespace ChatTwo.Code;
@@ -108,11 +107,10 @@ internal static class InputChannelExt
InputChannel.ExtraChatLinkshell6 => "/ecl6",
InputChannel.ExtraChatLinkshell7 => "/ecl7",
InputChannel.ExtraChatLinkshell8 => "/ecl8",
InputChannel.Invalid => "/e",
_ => "/e",
};
public static IEnumerable<TextCommand>? TextCommands(this InputChannel channel, IDataManager data)
public static IEnumerable<TextCommand>? TextCommands(this InputChannel channel)
{
var ids = channel switch
{
@@ -147,8 +145,7 @@ internal static class InputChannelExt
if (ids.Length == 0)
return null;
var cmds = data.GetExcelSheet<TextCommand>();
return ids.Where(id => cmds.HasRow(id)).Select(id => cmds.GetRow(id));
return ids.Where(id => Sheets.TextCommandSheet.HasRow(id)).Select(id => Sheets.TextCommandSheet.GetRow(id));
}
internal static bool IsLinkshell(this InputChannel channel) => channel switch
+3 -8
View File
@@ -4,13 +4,7 @@ namespace ChatTwo;
internal sealed class Commands : IDisposable
{
private Plugin Plugin { get; }
private Dictionary<string, CommandWrapper> Registered { get; } = new();
internal Commands(Plugin plugin)
{
Plugin = plugin;
}
private readonly Dictionary<string, CommandWrapper> Registered = [];
public void Dispose()
{
@@ -47,7 +41,8 @@ internal sealed class Commands : IDisposable
return Registered[name];
}
private void Invoke(string command, string arguments) {
private void Invoke(string command, string arguments)
{
if (!Registered.TryGetValue(command, out var wrapper))
{
Plugin.Log.Warning($"Missing registration for command {command}");
-1
View File
@@ -1,5 +1,4 @@
using System.Collections;
using System.Collections.Concurrent;
using ChatTwo.Code;
using ChatTwo.GameFunctions.Types;
using ChatTwo.Resources;
+8 -14
View File
@@ -478,12 +478,20 @@ internal sealed unsafe class Chat : IDisposable
internal void SendTell(TellReason reason, ulong contentId, string name, ushort homeWorld, byte[] message, string rawText)
{
if (contentId == 0)
{
Plugin.ChatGui.PrintError(Language.Chat_SendTell_Error);
Plugin.Log.Warning("Tried to send a tell with ContentId being 0, sorry this is an internal error.");
return;
}
var uName = Utf8String.FromString(name);
var uMessage = Utf8String.FromSequence(message);
var encoded = Utf8String.FromUtf8String(PronounModule.Instance()->ProcessString(uMessage, true));
var decoded = EncodeMessage(rawText);
AutoTranslate.ReplaceWithPayload(ref decoded);
using var decodedUtf8String = new Utf8String(decoded);
var logModule = RaptureLogModule.Instance();
@@ -493,25 +501,11 @@ internal sealed unsafe class Chat : IDisposable
if (reason == TellReason.Direct)
reason = TellReason.Friend;
if (contentId == 0)
{
encoded->Dtor(true);
uName->Dtor(true);
uMessage->Dtor(true);
Plugin.Log.Warning("Tried to send a tell with content id being 0");
return;
}
var ok = SendTellNative(networkModule, contentId, homeWorld, uName, encoded, (ushort) reason, homeWorld);
if (ok == 1)
{
PrintTellNative(logModule, 33, uName, &decodedUtf8String, 0, contentId, homeWorld, 255, 0, 0);
}
else
{
Plugin.ChatGui.PrintError(Language.Chat_SendTell_Error);
}
encoded->Dtor(true);
uName->Dtor(true);
-1
View File
@@ -4,7 +4,6 @@ using FFXIVClientStructs.FFXIV.Client.UI;
namespace ChatTwo.GameFunctions;
// From: https://git.anna.lgbt/anna/XivCommon/src/branch/main/XivCommon/Functions/Chat.cs
public unsafe class ChatBox
{
public static void SendMessageUnsafe(byte[] message)
+2 -2
View File
@@ -15,7 +15,7 @@ internal sealed unsafe class Context
internal static void TryOn(uint itemId, byte stainId)
{
AgentTryon.TryOn(0xFF, itemId, stainId, 0, 0);
AgentTryon.TryOn(0xFF, itemId, stainId);
}
internal static void LinkItem(uint itemId)
@@ -40,6 +40,6 @@ internal sealed unsafe class Context
internal static void SearchForItem(uint itemId)
{
ItemFinderModule.Instance()->SearchForItem(itemId, true);
ItemFinderModule.Instance()->SearchForItem(itemId);
}
}
+3 -6
View File
@@ -2,7 +2,6 @@ using System.Globalization;
using System.Runtime.InteropServices;
using Dalamud.Game.ClientState.Conditions;
using Dalamud.Game.Text.SeStringHandling;
using Dalamud.Game.Text.SeStringHandling.Payloads;
using Dalamud.Hooking;
using Dalamud.Memory;
using Dalamud.Utility;
@@ -15,7 +14,6 @@ using FFXIVClientStructs.FFXIV.Client.UI.Info;
using FFXIVClientStructs.FFXIV.Component.GUI;
using Lumina.Excel;
using Lumina.Excel.Sheets;
using ValueType = FFXIVClientStructs.FFXIV.Component.GUI.ValueType;
namespace ChatTwo.GameFunctions;
@@ -75,10 +73,9 @@ internal unsafe class GameFunctions : IDisposable
private void ListCommand(string name, ushort world, string commandName)
{
var row = Plugin.DataManager.GetExcelSheet<World>().GetRow(world);
var worldRow = Sheets.WorldSheet.GetRow(world);
var worldName = row.Name.ExtractText();
ReplacementName = $"{name}@{worldName}";
ReplacementName = $"{name}@{worldRow.Name.ToString()}";
ChatBox.SendMessage($"/{commandName} add {Placeholder}");
}
@@ -191,7 +188,7 @@ internal unsafe class GameFunctions : IDisposable
internal static void OpenQuestLog(RowRef<Quest> quest)
{
var splits = quest.Value.Id.ExtractText().Split("_");
var splits = quest.Value.Id.ToString().Split("_");
if (splits.Length != 2)
{
Plugin.ChatGui.Print("QuestId is wrongly formatted");
+1 -1
View File
@@ -17,6 +17,6 @@ internal sealed class TellTarget
public bool IsSet() => Name.Length > 0 && World > 0;
public string ToWorldString() => Sheets.WorldSheet.TryGetRow(World, out var worldRow) ? worldRow.Name.ExtractText() : string.Empty;
public string ToWorldString() => Sheets.WorldSheet.TryGetRow(World, out var worldRow) ? worldRow.Name.ToString() : string.Empty;
public string ToTargetString() => $"{Name}@{ToWorldString()}";
}
+1 -2
View File
@@ -1,5 +1,4 @@
using ChatTwo.Http.MessageProtocol;
using ChatTwo.Util;
using Dalamud.Plugin.Services;
namespace ChatTwo.Http;
@@ -25,7 +24,7 @@ public class ServerCore : IAsyncDisposable
private void FrameworkUpdate(IFramework _)
{
foreach (var (tab, idx) in Plugin.Config.Tabs.WithIndex())
foreach (var (idx, tab) in Plugin.Config.Tabs.Index())
{
if (tab.Unread == tab.LastSendUnread)
continue;
+6 -9
View File
@@ -28,19 +28,16 @@ internal sealed class TypingIpc : IDisposable
private ChatInputState BuildState()
{
var log = Plugin.ChatLogWindow;
var chat = log.Chat ?? string.Empty;
var hasText = !string.IsNullOrWhiteSpace(chat);
var usedChannel = Plugin.CurrentTab?.CurrentChannel;
var inputChannel = usedChannel is not null
? (usedChannel.UseTempChannel ? usedChannel.TempChannel : usedChannel.Channel)
: InputChannel.Invalid;
var usedChannel = Plugin.CurrentTab.CurrentChannel;
var inputChannel = usedChannel.UseTempChannel ? usedChannel.TempChannel : usedChannel.Channel;
var channelType = inputChannel.ToChatType();
return (InputVisible: !log.IsHidden,
InputFocused: log.InputFocused,
HasText: hasText,
IsTyping: log.InputFocused && hasText,
TextLength: chat.Length,
HasText: log.Chat.Length > 0,
IsTyping: log is { InputFocused: true, Chat.Length: > 0 },
TextLength: log.Chat.Length,
ChannelType: channelType);
}
+4 -5
View File
@@ -7,7 +7,6 @@ using System.Text.RegularExpressions;
using Dalamud.Game.Text;
using Dalamud.Utility;
using FFXIVClientStructs.FFXIV.Client.UI.Agent;
using Lumina.Excel.Sheets;
namespace ChatTwo;
@@ -234,7 +233,7 @@ internal partial class Message
AddChunkWithMessage(text.NewWithStyle(chunk.Source, chunk.Link, sentenceBuilder.Append(!wordUsed ? word : "").ToString()));
try
{
AddChunkWithMessage(text.NewWithStyle(chunk.Source, UriPayload.ResolveURI(token.Value), token.Value));
AddChunkWithMessage(text.NewWithStyle(chunk.Source, UriPayload.ResolveUri(token.Value), token.Value));
}
catch (UriFormatException)
{
@@ -326,8 +325,8 @@ internal partial class Message
};
var name = kind != ItemKind.EventItem
? Plugin.DataManager.GetExcelSheet<Item>().GetRow(item.ItemId).Name.ToString()
: Plugin.DataManager.GetExcelSheet<EventItem>().GetRow(item.ItemId).Name.ToString();
? Sheets.ItemSheet.GetRow(item.ItemId).Name.ToString()
: Sheets.EventItemSheet.GetRow(item.ItemId).Name.ToString();
var link = new ItemPayload(item.ItemId, kind, $"{SeIconChar.LinkMarker.ToIconChar()}{name}");
AddChunkWithMessage(text.NewWithStyle(chunk.Source, link, link.DisplayName ?? "Unknown"));
@@ -341,7 +340,7 @@ internal partial class Message
continue;
}
var nameValue = statusRow.Name.ToDalamudString().TextValue;
var nameValue = statusRow.Name.ToString();
var content = statusRow.StatusCategory switch
{
1 => $"{SeIconChar.Buff.ToIconString()}{nameValue}",
+26 -21
View File
@@ -1,5 +1,6 @@
using System.Collections.Concurrent;
using System.Diagnostics;
using System.Text;
using ChatTwo.Code;
using ChatTwo.Resources;
using ChatTwo.Util;
@@ -10,7 +11,9 @@ using Dalamud.Interface.ImGuiNotification;
using Dalamud.Plugin.Services;
using Dalamud.Utility;
using FFXIVClientStructs.FFXIV.Client.UI.Misc;
using Lumina.Excel.Sheets;
using Lumina.Text.Expressions;
using Lumina.Text.Payloads;
using Lumina.Text.ReadOnly;
namespace ChatTwo;
@@ -21,7 +24,7 @@ internal class MessageManager : IAsyncDisposable
private Plugin Plugin { get; }
internal MessageStore Store { get; }
private Dictionary<ChatType, NameFormatting> Formats { get; } = new();
private Dictionary<ChatType, NameFormatting> Formats { get; } = [];
private ulong LastContentId { get; set; }
// Messages go into the PendingSync queue first, which will be consumed one
@@ -31,8 +34,8 @@ internal class MessageManager : IAsyncDisposable
// After that, the message is enqueued in the PendingAsync queue, which will
// be consumed in a separate thread and perform more processing (emotes,
// URLs) as well as inserting the message into the database.
private Queue<PendingMessage> PendingSync { get; } = new();
private ConcurrentQueue<PendingMessage> PendingAsync { get; } = new();
private Queue<PendingMessage> PendingSync { get; } = [];
private ConcurrentQueue<PendingMessage> PendingAsync { get; } = [];
private readonly Thread PendingMessageThread;
private readonly CancellationTokenSource PendingThreadCancellationToken = new();
@@ -298,36 +301,38 @@ internal class MessageManager : IAsyncDisposable
}
}
private NameFormatting? FormatFor(ChatType type)
private NameFormatting FormatFor(ChatType type)
{
if (Formats.TryGetValue(type, out var cached))
return cached;
var logKind = Plugin.DataManager.GetExcelSheet<LogKind>().GetRow((ushort)type);
var format = logKind.Format.ToDalamudString();
static bool IsStringParam(Payload payload, byte num)
var formats = Sheets.LogKindSheet.GetRow((uint)type).Format.ToList();
static bool IsStringParam(ReadOnlySePayload payload, byte num)
{
var data = payload.Encode();
return data.Length >= 5 && data[1] == 0x29 && data[4] == num + 1;
if (payload.MacroCode != MacroCode.String)
return false;
return payload.TryGetExpression(out var expr1)
&& expr1.TryGetParameterExpression(out var expressionType, out var operand)
&& expressionType == (byte)ExpressionType.LocalString
&& operand.TryGetInt(out var lstrIndex)
&& lstrIndex == num;
}
var firstStringParam = format.Payloads.FindIndex(payload => IsStringParam(payload, 1));
var secondStringParam = format.Payloads.FindIndex(payload => IsStringParam(payload, 2));
var firstStringParam = formats.FindIndex(payload => IsStringParam(payload, 1));
var secondStringParam = formats.FindIndex(payload => IsStringParam(payload, 2));
if (firstStringParam == -1 || secondStringParam == -1)
return NameFormatting.Empty();
var before = format.Payloads
var before = formats
.GetRange(0, firstStringParam)
.Where(payload => payload is ITextProvider)
.Cast<ITextProvider>()
.Select(text => text.Text);
var after = format.Payloads
.Where(payload => payload.Type == ReadOnlySePayloadType.Text)
.Select(text => Encoding.UTF8.GetString(text.Body.Span));
var after = formats
.GetRange(firstStringParam + 1, secondStringParam - firstStringParam)
.Where(payload => payload is ITextProvider)
.Cast<ITextProvider>()
.Select(text => text.Text);
.Where(payload => payload.Type == ReadOnlySePayloadType.Text)
.Select(text => Encoding.UTF8.GetString(text.Body.Span)); // Can't use `ToString()` as it defaults to macro
var nameFormatting = NameFormatting.Of(string.Join("", before), string.Join("", after));
Formats[type] = nameFormatting;
+21 -23
View File
@@ -20,7 +20,6 @@ using FFXIVClientStructs.FFXIV.Client.UI;
using FFXIVClientStructs.FFXIV.Component.GUI;
using Dalamud.Bindings.ImGui;
using Lumina.Excel.Sheets;
using Action = System.Action;
using DalamudPartyFinderPayload = Dalamud.Game.Text.SeStringHandling.Payloads.PartyFinderPayload;
using ChatTwoPartyFinderPayload = ChatTwo.Util.PartyFinderPayload;
@@ -39,7 +38,7 @@ public sealed class PayloadHandler
public uint HoverCounter;
public uint LastHoverCounter;
private const uint PopupSfx = 1u;
private const uint PopupSfx = 1;
internal PayloadHandler(ChatLogWindow logWindow)
{
@@ -110,7 +109,7 @@ public sealed class PayloadHandler
var contentId = chunk.Message?.ContentId ?? 0;
var sender = chunk.Message?.Sender.Select(c => c.Link).FirstOrDefault(p => p is PlayerPayload) as PlayerPayload;
using var menu = ImGuiUtil.Menu(Language.Context_Integrations);
using var menu = ImRaii.Menu(Language.Context_Integrations);
if (!menu.Success)
return;
@@ -146,7 +145,7 @@ public sealed class PayloadHandler
//
// It makes it much more convenient in the majority of cases to
// copy the message content without having to open a submenu.
menu = ImGuiUtil.Menu(Plugin.PluginName);
menu = ImRaii.Menu(Plugin.PluginName);
if (!menu.Success)
return;
}
@@ -240,7 +239,7 @@ public sealed class PayloadHandler
DoHover(() => HoverItem(item), hoverSize);
break;
case UriPayload uri:
DoHover(() => HoverURI(uri), hoverSize);
DoHover(() => HoverUri(uri), hoverSize);
break;
}
}
@@ -250,16 +249,14 @@ public sealed class PayloadHandler
ImGui.SetNextWindowSize(new Vector2(width, -1f));
using (ImRaii.Tooltip())
using (ImGuiUtil.TextWrapPos())
using (ImRaii.TextWrapPos(0.0f))
using (ImRaii.PushColor(ImGuiCol.Text, LogWindow.DefaultText))
{
inside();
}
}
public unsafe void MoveTooltip(AddonEvent type, AddonArgs args)
{
// Only move if user has "Next to Cursor" option selected
// Only move if the user has the "Next to Cursor" option selected
if (!Plugin.GameConfig.TryGet(UiControlOption.DetailTrackingType, out uint selected) || selected != 0)
return;
@@ -352,7 +349,7 @@ public sealed class PayloadHandler
InlineIcon(icon);
var builder = new SeStringBuilder();
var nameValue = status.Status.Value.Name.ToDalamudString().TextValue;
var nameValue = status.Status.Value.Name.ToString();
switch (status.Status.Value.StatusCategory)
{
case 1:
@@ -364,14 +361,13 @@ public sealed class PayloadHandler
default:
builder.AddUiForeground(nameValue, 1);
break;
};
}
var name = ChunkUtil.ToChunks(builder.BuiltString, ChunkSource.None, null);
LogWindow.DrawChunks(name.ToList());
ImGui.Separator();
var descString = status.Status.Value.Description.ToDalamudString();
var desc = ChunkUtil.ToChunks(descString, ChunkSource.None, null);
var desc = ChunkUtil.ToChunks(status.Status.Value.Description.ToDalamudString(), ChunkSource.None, null);
LogWindow.DrawChunks(desc.ToList());
}
@@ -383,7 +379,9 @@ public sealed class PayloadHandler
return;
}
item.Item.TryGetValue(out Item resolvedItem);
if (!item.Item.TryGetValue(out Item resolvedItem))
return;
if (Plugin.TextureProvider.GetFromGameIcon(new GameIconLookup(resolvedItem.Icon, item.IsHQ)).GetWrapOrDefault() is { } icon)
InlineIcon(icon);
@@ -413,7 +411,7 @@ public sealed class PayloadHandler
LogWindow.DrawChunks(ChunkUtil.ToChunks(itemHelpRow.Description.ToDalamudString(), ChunkSource.None, null).ToList());
}
private void HoverURI(UriPayload uri)
private void HoverUri(UriPayload uri)
{
ImGui.TextUnformatted(string.Format(Language.Context_URLDomain, uri.Uri.Authority));
ImGuiUtil.WarningText(Language.Context_URLWarning);
@@ -449,7 +447,7 @@ public sealed class PayloadHandler
GameFunctions.GameFunctions.OpenPartyFinder();
break;
case UriPayload uri:
WrapperUtil.TryOpenURI(uri.Uri);
WrapperUtil.TryOpenUri(uri.Uri);
break;
default:
RightClickPayload(chunk, payload);
@@ -552,8 +550,7 @@ public sealed class PayloadHandler
if (Plugin.TextureProvider.GetFromGameIcon(new GameIconLookup(item.Icon)).GetWrapOrDefault() is { } icon)
InlineIcon(icon);
var name = item.Name.ToDalamudString();
LogWindow.DrawChunks(ChunkUtil.ToChunks(name, ChunkSource.None, null).ToList(), false);
LogWindow.DrawChunks(ChunkUtil.ToChunks(item.Name.ToDalamudString(), ChunkSource.None, null).ToList(), false);
ImGui.Separator();
var realItemId = payload.RawItemId;
@@ -561,12 +558,13 @@ public sealed class PayloadHandler
GameFunctions.Context.LinkItem(realItemId);
if (ImGui.Selectable(Language.Context_CopyItemName))
ImGui.SetClipboardText(name.TextValue);
ImGui.SetClipboardText(item.Name.ToString());
}
private void DrawPlayerPopup(Chunk chunk, PlayerPayload player)
{
// Possible that GMs return a null payload
// ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
if (player == null)
return;
@@ -627,7 +625,7 @@ public sealed class PayloadHandler
}
else if (!inInstance)
{
using var menu = ImGuiUtil.Menu(Language.Context_InviteToParty);
using var menu = ImRaii.Menu(Language.Context_InviteToParty);
if (menu.Success)
{
if (ImGui.Selectable(Language.Context_InviteToParty_SameWorld))
@@ -653,7 +651,7 @@ public sealed class PayloadHandler
if (!isFriend && ImGui.Selectable(Language.Context_SendFriendRequest))
LogWindow.Plugin.Functions.SendFriendRequest(player.PlayerName, (ushort) world.RowId);
using (var menuBlockFunctions = ImGuiUtil.Menu(Language.Context_BlockFunctions))
using (var menuBlockFunctions = ImRaii.Menu(Language.Context_BlockFunctions))
{
if (menuBlockFunctions.Success)
{
@@ -718,7 +716,7 @@ public sealed class PayloadHandler
ImGui.Separator();
if (ImGui.Selectable(Language.Context_OpenInBrowser))
WrapperUtil.TryOpenURI(uri.Uri);
WrapperUtil.TryOpenUri(uri.Uri);
if (ImGui.Selectable(Language.Context_CopyLink))
{
@@ -733,7 +731,7 @@ public sealed class PayloadHandler
InlineIcon(icon);
var builder = new SeStringBuilder();
var nameValue = status.Status.Value.Name.ToDalamudString().TextValue;
var nameValue = status.Status.Value.Name.ToString();
switch (status.Status.Value.StatusCategory)
{
case 1:
+4 -6
View File
@@ -7,7 +7,6 @@ using ChatTwo.Resources;
using ChatTwo.Ui;
using ChatTwo.Util;
using Dalamud.Game.ClientState.Conditions;
using Dalamud.Game.ClientState.Objects;
using Dalamud.Interface.Windowing;
using Dalamud.IoC;
using Dalamud.Plugin;
@@ -40,6 +39,7 @@ public sealed class Plugin : IDalamudPlugin
[PluginService] internal static INotificationManager Notification { get; private set; } = null!;
[PluginService] internal static IAddonLifecycle AddonLifecycle { get; private set; } = null!;
[PluginService] internal static IPlayerState PlayerState { get; private set; } = null!;
[PluginService] internal static ISeStringEvaluator Evaluator { get; private set; } = null!;
internal static Configuration Config = null!;
@@ -96,7 +96,7 @@ public sealed class Plugin : IDalamudPlugin
// Functions calls this in its ctor if the player is already logged in
ServerCore = new ServerCore(this);
Commands = new Commands(this);
Commands = new Commands();
Functions = new GameFunctions.GameFunctions(this);
Ipc = new IpcManager();
TypingIpc = new TypingIpc(this);
@@ -202,7 +202,7 @@ public sealed class Plugin : IDalamudPlugin
if (Config.HideInLoadingScreens && Condition[ConditionFlag.BetweenAreas])
{
ChatLogWindow.FinalizeFrame();
TypingIpc?.Update();
TypingIpc.Update();
return;
}
@@ -212,12 +212,10 @@ public sealed class Plugin : IDalamudPlugin
ChatLogWindow.DefaultText = ImGui.GetStyle().Colors[(int) ImGuiCol.Text];
using ((Config.FontsEnabled ? FontManager.RegularFont : FontManager.Axis).Push())
{
WindowSystem.Draw();
}
ChatLogWindow.FinalizeFrame();
TypingIpc?.Update();
TypingIpc.Update();
}
internal void SaveConfig()
+1 -1
View File
@@ -402,7 +402,7 @@ Sie wurden gewarnt.</value>
<value>{0} ist ein Projekt, dessen Ziel es ist, den Spielchat zu verbessern und neu zu gestalten.</value>
</data>
<data name="Options_About_CrowdIn">
<value>Drücken Sie auf den linken Knopf, um bei der Übersetzung von {0} zu helfen.</value>
<value>Helfen Sie bei der Übersetzung: </value>
</data>
<data name="Options_About_Translators">
<value>Übersetzer</value>
+8 -8
View File
@@ -401,7 +401,7 @@
<value>{0} est un projet visant à recréer complètement le chat en jeu et à l'améliorer.</value>
</data>
<data name="Options_About_CrowdIn">
<value>Cliquez sur le bouton à gauche pour nous aider à traduire {0}.</value>
<value>Help to translate: </value>
</data>
<data name="Options_About_Translators">
<value>Traducteurs</value>
@@ -542,7 +542,7 @@
<value>Opacité</value>
</data>
<data name="Options_Tabs_IndependentHide">
<value>Use different hide condition than main window</value>
<value>Utiliser une condition de masquage différente de la fenêtre principale</value>
</data>
<data name="Options_FontsEnabled">
<value>Activer les fontes personnalisées</value>
@@ -680,7 +680,7 @@
<value>Inviter dans l'équipe</value>
</data>
<data name="Context_BlockFunctions">
<value>Block Functions</value>
<value>Fonctions de blocage</value>
</data>
<data name="Context_InviteToParty_SameWorld">
<value>Même monde</value>
@@ -701,10 +701,10 @@
<value>Ajouter à la liste noire</value>
</data>
<data name="Context_AddToMuteList">
<value>Register to Mute List</value>
<value>Ajouter à la Liste Muette</value>
</data>
<data name="Context_AddToTermsFilter">
<value>Add to Term Filters</value>
<value>Ajouter aux filtres des termes</value>
</data>
<data name="Context_InviteToNoviceNetwork">
<value>Inviter dans le réseau des novices</value>
@@ -842,7 +842,7 @@
<value>Notifications d'alarme</value>
</data>
<data name="ChatType_Glamour">
<value>Glamour Notifications</value>
<value>Notification de Glamour</value>
</data>
<data name="ChatType_Echo">
<value>Écho</value>
@@ -965,10 +965,10 @@
<value>Remplace les messages identiques consécutifs par un compteur ajouté à la première instance des dits messages.</value>
</data>
<data name="Options_CollapseDuplicateMsgUniqueLink_Name">
<value>Keep unique links seperate</value>
<value>Garder les liens uniques séparés</value>
</data>
<data name="Options_CollapseDuplicateMsgUniqueLink_Description">
<value>Don't collapse messages if they link to different things with the same text.</value>
<value>Ne pas réduire les messages s'ils sont liés à des choses différentes avec le même texte.</value>
</data>
<data name="Options_Tabs_ExtraChatChannels">
<value>Canaux d'ExtraChat</value>
+3 -3
View File
@@ -827,7 +827,7 @@
<value>回復</value>
</data>
<data name="ChatType_GainBuff">
<value></value>
<value>Beneficial effects granted</value>
</data>
<data name="ChatType_GainDebuff">
<value>Detrimental effects inflicted</value>
@@ -893,10 +893,10 @@
<value>PT募集通知</value>
</data>
<data name="ChatType_Sign">
<value></value>
<value>Sign Messages for PC Targets</value>
</data>
<data name="ChatType_RandomNumber">
<value></value>
<value>Random Number Messages</value>
</data>
<data name="ChatType_NoviceNetworkSystem">
<value>ビギナーチャンネルアナウンス</value>
+1 -1
View File
@@ -401,7 +401,7 @@
<value>{0} is een project om de in-game chat volledig opnieuw te maken en nog beter te maken.</value>
</data>
<data name="Options_About_CrowdIn">
<value>Klik op de knop links om {0} te helpen vertalen.</value>
<value>Help to translate: </value>
</data>
<data name="Options_About_Translators">
<value>Vertalers</value>
+1 -1
View File
@@ -401,7 +401,7 @@
<value>{0} este un proiect care recreează complet chat-ul din joc si îl îmbunătățește.</value>
</data>
<data name="Options_About_CrowdIn">
<value>Apasă butonul din stânga pentru a contribui la traducerea {0}.</value>
<value>Help to translate: </value>
</data>
<data name="Options_About_Translators">
<value>Traducători</value>
+1 -1
View File
@@ -401,7 +401,7 @@
<value>{0} - проект, полностью пересоздаёт внутриигровой чат и делает его еще лучше.</value>
</data>
<data name="Options_About_CrowdIn">
<value>Нажмите кнопку слева, чтобы помочь перевести {0}.</value>
<value>Help to translate: </value>
</data>
<data name="Options_About_Translators">
<value>Переводчики</value>
+2 -2
View File
@@ -395,13 +395,13 @@
<value>Ny flik</value>
</data>
<data name="Options_About_Tab">
<value>Om {0}</value>
<value>Om</value>
</data>
<data name="Options_About_Opening">
<value>{0} är ett projekt gjort för att fullt återskapa spelets ursprungliga chattfönster och göra det ännu bättre.</value>
</data>
<data name="Options_About_CrowdIn">
<value>Klicka på knappen till vänster för att hjälpa med översättningen av {0}.</value>
<value>Help to translate: </value>
</data>
<data name="Options_About_Translators">
<value>Översättare</value>
+1 -1
View File
@@ -402,7 +402,7 @@
繁體中文是為了不開補全功能也能與其他玩家正常顯示溝通所翻譯及修改。</value>
</data>
<data name="Options_About_CrowdIn">
<value>點選左邊的按鈕來幇助翻譯 {0}。</value>
<value>Help to translate: </value>
</data>
<data name="Options_About_Translators">
<value>翻譯人員</value>
+5 -1
View File
@@ -8,6 +8,8 @@ public static class Sheets
public static readonly ExcelSheet<Item> ItemSheet;
public static readonly ExcelSheet<World> WorldSheet;
public static readonly ExcelSheet<Status> StatusSheet;
public static readonly ExcelSheet<UIColor> UIColorSheet;
public static readonly ExcelSheet<LogKind> LogKindSheet;
public static readonly ExcelSheet<LogFilter> LogFilterSheet;
public static readonly ExcelSheet<EventItem> EventItemSheet;
public static readonly ExcelSheet<Completion> CompletionSheet;
@@ -20,8 +22,10 @@ public static class Sheets
ItemSheet = Plugin.DataManager.GetExcelSheet<Item>();
WorldSheet = Plugin.DataManager.GetExcelSheet<World>();
StatusSheet = Plugin.DataManager.GetExcelSheet<Status>();
EventItemSheet = Plugin.DataManager.GetExcelSheet<EventItem>();
UIColorSheet = Plugin.DataManager.GetExcelSheet<UIColor>();
LogKindSheet = Plugin.DataManager.GetExcelSheet<LogKind>();
LogFilterSheet = Plugin.DataManager.GetExcelSheet<LogFilter>();
EventItemSheet = Plugin.DataManager.GetExcelSheet<EventItem>();
CompletionSheet = Plugin.DataManager.GetExcelSheet<Completion>();
TerritorySheet = Plugin.DataManager.GetExcelSheet<TerritoryType>();
TextCommandSheet = Plugin.DataManager.GetExcelSheet<TextCommand>();
+45 -29
View File
@@ -20,6 +20,7 @@ using Dalamud.Memory;
using FFXIVClientStructs.FFXIV.Client.UI;
using Dalamud.Bindings.ImGui;
using Lumina.Excel.Sheets;
using Lumina.Extensions;
namespace ChatTwo.Ui;
@@ -68,7 +69,7 @@ public sealed class ChatLogWindow : Window
public PayloadHandler PayloadHandler { get; }
internal Lender<PayloadHandler> HandlerLender { get; }
private Dictionary<string, ChatType> TextCommandChannels { get; } = new();
private HashSet<string> AllCommands { get; } = [];
private Dictionary<string, TextCommand> AllCommands { get; } = [];
private const uint ChatOpenSfx = 35u;
private const uint ChatCloseSfx = 3u;
@@ -238,7 +239,7 @@ public sealed class ChatLogWindow : Window
private bool IsValidCommand(string command)
{
return Plugin.CommandManager.Commands.ContainsKey(command) || AllCommands.Contains(command);
return Plugin.CommandManager.Commands.ContainsKey(command) || AllCommands.ContainsKey(command);
}
private void ClearLog(string command, string arguments)
@@ -288,7 +289,7 @@ public sealed class ChatLogWindow : Window
foreach (var input in Enum.GetValues<InputChannel>())
{
var commands = input.TextCommands(Plugin.DataManager);
var commands = input.TextCommands();
if (commands == null)
continue;
@@ -297,11 +298,8 @@ public sealed class ChatLogWindow : Window
AddTextCommandChannel(command, type);
}
if (Sheets.TextCommandSheet.HasRow(116))
{
var echo = Sheets.TextCommandSheet.GetRow(116);
AddTextCommandChannel(echo, ChatType.Echo);
}
if (Sheets.TextCommandSheet.TryGetRow(116, out var row))
AddTextCommandChannel(row, ChatType.Echo);
}
private void AddTextCommandChannel(TextCommand command, ChatType type)
@@ -314,19 +312,20 @@ public sealed class ChatLogWindow : Window
private void SetUpAllCommands()
{
if (Plugin.DataManager.GetExcelSheet<TextCommand>() is not { } commands)
return;
var commandNames = commands.SelectMany(cmd => new[]
foreach (var command in Sheets.TextCommandSheet)
{
cmd.Command.ExtractText(),
cmd.ShortCommand.ExtractText(),
cmd.Alias.ExtractText(),
cmd.ShortAlias.ExtractText(),
});
if (!command.Command.IsEmpty)
AllCommands.TryAdd(command.Command.ToString(), command);
foreach (var command in commandNames)
AllCommands.Add(command);
if (!command.ShortCommand.IsEmpty)
AllCommands.TryAdd(command.ShortCommand.ToString(), command);
if (!command.Alias.IsEmpty)
AllCommands.TryAdd(command.Alias.ToString(), command);
if (!command.ShortAlias.IsEmpty)
AllCommands.TryAdd(command.ShortAlias.ToString(), command);
}
}
private void AddBacklog(string message)
@@ -736,7 +735,7 @@ public sealed class ChatLogWindow : Window
if (!channel.IsValid())
continue;
var name = Sheets.LogFilterSheet.FirstOrNull(row => row.LogKind == (byte) channel.ToChatType())?.Name.ExtractText() ?? channel.ToChatType().Name();
var name = Sheets.LogFilterSheet.FirstOrNull(row => row.LogKind == (byte) channel.ToChatType())?.Name.ToString() ?? channel.ToChatType().Name();
if (channel.IsLinkshell())
{
var lsName = Plugin.Functions.Chat.GetLinkshellName(channel.LinkshellIndex());
@@ -933,13 +932,19 @@ public sealed class ChatLogWindow : Window
AddBacklog(trimmed);
InputBacklogIdx = -1;
if (HasTranslationCommand(trimmed))
{
activeTab.CurrentChannel.ResetTempChannel();
Chat = string.Empty;
return;
}
if (TellSpecial)
{
var tellBytes = Encoding.UTF8.GetBytes(trimmed);
AutoTranslate.ReplaceWithPayload(ref tellBytes);
Plugin.Functions.Chat.SendTellUsingCommandInner(tellBytes);
TellSpecial = false;
activeTab.CurrentChannel.ResetTempChannel();
@@ -1000,6 +1005,18 @@ public sealed class ChatLogWindow : Window
Chat = string.Empty;
}
private bool HasTranslationCommand(string trimmed)
{
var messageBytes = Encoding.UTF8.GetBytes(trimmed);
if (AutoTranslate.StartsWithCommand(ref messageBytes))
{
ChatBox.SendMessageUnsafe(messageBytes);
return true;
}
return false;
}
internal void UserHide()
{
CurrentHideState = HideState.User;
@@ -1492,10 +1509,11 @@ public sealed class ChatLogWindow : Window
var button = (i + 1) % 10;
var text = string.Format(Language.AutoTranslate_Completion_Key, button);
var size = ImGui.CalcTextSize(text);
ImGui.SameLine(ImGui.GetContentRegionAvail().X - size.X);
ImGui.PushStyleColor(ImGuiCol.Text, *ImGui.GetStyleColorVec4(ImGuiCol.TextDisabled));
using (ImRaii.PushColor(ImGuiCol.Text, ImGui.GetStyle().Colors[(int)ImGuiCol.TextDisabled]))
ImGui.TextUnformatted(text);
ImGui.PopStyleColor();
}
if (!clicked)
@@ -1632,12 +1650,10 @@ public sealed class ChatLogWindow : Window
if (text.StartsWith('/'))
{
var command = text.Split(' ')[0];
var cmd = Sheets.TextCommandSheet.FirstOrNull(cmd =>
cmd.Command.ExtractText() == command || cmd.Alias.ExtractText() == command ||
cmd.ShortCommand.ExtractText() == command || cmd.ShortAlias.ExtractText() == command);
if (cmd != null)
Plugin.CommandHelpWindow.UpdateContent(cmd.Value);
if (AllCommands.TryGetValue(command, out var textCommand))
Plugin.CommandHelpWindow.UpdateContent(textCommand.Description);
else if (Plugin.CommandManager.Commands.TryGetValue(command, out var info) && info.ShowInHelp)
Plugin.CommandHelpWindow.UpdateContent(info.HelpMessage);
}
if (data.EventFlag != ImGuiInputTextFlags.CallbackHistory)
+7 -7
View File
@@ -4,15 +4,15 @@ using Dalamud.Interface.Utility;
using Dalamud.Interface.Windowing;
using Dalamud.Utility;
using Dalamud.Bindings.ImGui;
using Lumina.Excel.Sheets;
using Lumina.Text.ReadOnly;
namespace ChatTwo.Ui;
public class CommandHelpWindow : Window {
private ChatLogWindow LogWindow { get; }
private TextCommand? Command { get; set; }
private ReadOnlySeString? CommandDescription { get; set; }
internal CommandHelpWindow(ChatLogWindow logWindow) : base($"command help##chat2-commandhelp")
internal CommandHelpWindow(ChatLogWindow logWindow) : base("command help##chat2-commandhelp")
{
LogWindow = logWindow;
@@ -24,9 +24,9 @@ public class CommandHelpWindow : Window {
}
// Sets IsOpen to true if it should be drawn
public void UpdateContent(TextCommand command)
public void UpdateContent(ReadOnlySeString commandDesc)
{
Command = command;
CommandDescription = commandDesc;
var width = 350;
var scaledWidth = width * ImGuiHelpers.GlobalScale;
@@ -56,9 +56,9 @@ public class CommandHelpWindow : Window {
public override void Draw()
{
if (Command == null)
if (CommandDescription == null)
return;
LogWindow.DrawChunks(ChunkUtil.ToChunks(Command.Value.Description.ToDalamudString(), ChunkSource.None, null).ToList());
LogWindow.DrawChunks(ChunkUtil.ToChunks(CommandDescription.Value.ToDalamudString(), ChunkSource.None, null).ToList());
}
}
+1 -1
View File
@@ -33,7 +33,7 @@ internal sealed class About : ISettingsTab
public void Draw(bool changed)
{
using var wrap = ImGuiUtil.TextWrapPos();
using var wrap = ImRaii.TextWrapPos(0.0f);
ImGui.TextUnformatted(string.Format(Language.Options_About_Opening, Plugin.PluginName));
+1 -1
View File
@@ -18,7 +18,7 @@ internal sealed class Changelog : ISettingsTab
public void Draw(bool changed)
{
using var wrap = ImGuiUtil.TextWrapPos();
using var wrap = ImRaii.TextWrapPos(0.0f);
ImGui.TextUnformatted(Language.Options_Warning_NotImplemented);
ImGuiUtil.OptionCheckbox(ref Mutable.PrintChangelog, Language.Options_PrintChangelog_Name, Language.Options_PrintChangelog_Description);
+1 -1
View File
@@ -21,7 +21,7 @@ internal sealed class ChatLog : ISettingsTab
public void Draw(bool changed)
{
using (ImGuiUtil.TextWrapPos())
using (ImRaii.TextWrapPos(0.0f))
{
ImGuiUtil.OptionCheckbox(ref Mutable.KeepInputFocus, Language.Options_KeepInputFocus_Name, Language.Options_KeepInputFocus_Description);
ImGui.Spacing();
+1 -1
View File
@@ -138,7 +138,7 @@ internal sealed class Database : ISettingsTab
return;
using var treeNode = ImRaii.TreeNode(Language.Options_Database_Advanced);
using var wrap = ImGuiUtil.TextWrapPos();
using var wrap = ImRaii.TextWrapPos(0.0f);
ImGuiUtil.WarningText(Language.Options_Database_Advanced_Warning);
if (ImGuiUtil.CtrlShiftButton("Perform maintenance", "Ctrl+Shift: MessageManager.Store.PerformMaintenance()"))
+1 -1
View File
@@ -19,7 +19,7 @@ internal sealed class Display : ISettingsTab
public void Draw(bool changed)
{
using var wrap = ImGuiUtil.TextWrapPos();
using var wrap = ImRaii.TextWrapPos(0.0f);
ImGuiUtil.OptionCheckbox(ref Mutable.HideChat, Language.Options_HideChat_Name, Language.Options_HideChat_Description);
ImGui.Spacing();
+1 -1
View File
@@ -38,7 +38,7 @@ internal sealed class Emote : ISettingsTab
public void Draw(bool changed)
{
using var wrap = ImGuiUtil.TextWrapPos();
using var wrap = ImRaii.TextWrapPos(0.0f);
ImGuiUtil.OptionCheckbox(ref Mutable.ShowEmotes, Language.Options_ShowEmotes_Name, Language.Options_ShowEmotes_Desc);
ImGui.Spacing();
+2 -1
View File
@@ -3,6 +3,7 @@ using ChatTwo.Util;
using Dalamud;
using Dalamud.Interface.FontIdentifier;
using Dalamud.Bindings.ImGui;
using Dalamud.Interface.Utility.Raii;
namespace ChatTwo.Ui.SettingsTabs;
@@ -19,7 +20,7 @@ public class Fonts : ISettingsTab
public void Draw(bool _)
{
using var wrap = ImGuiUtil.TextWrapPos();
using var wrap = ImRaii.TextWrapPos(0.0f);
ImGui.Checkbox(Language.Options_FontsEnabled, ref Mutable.FontsEnabled);
ImGui.Spacing();
+2 -1
View File
@@ -1,6 +1,7 @@
using ChatTwo.Resources;
using ChatTwo.Util;
using Dalamud.Bindings.ImGui;
using Dalamud.Interface.Utility.Raii;
namespace ChatTwo.Ui.SettingsTabs;
@@ -17,7 +18,7 @@ internal sealed class Preview : ISettingsTab
public void Draw(bool changed)
{
using var wrap = ImGuiUtil.TextWrapPos();
using var wrap = ImRaii.TextWrapPos(0.0f);
using (var combo = ImGuiUtil.BeginComboVertical(Language.Options_Preview_Name, Mutable.PreviewPosition.Name()))
{
+1 -1
View File
@@ -138,7 +138,7 @@ internal sealed class Webinterface(Plugin plugin, Configuration mutable) : ISett
clicked |= ImGuiUtil.IconButton(FontAwesomeIcon.ExternalLinkAlt, "urlOpen");
if (clicked)
WrapperUtil.TryOpenURI(uri);
WrapperUtil.TryOpenUri(uri);
}
else
{
+68 -41
View File
@@ -3,13 +3,12 @@ using System.Globalization;
using System.Runtime.InteropServices;
using System.Text;
using Dalamud.Game;
using Dalamud.Game.Text.SeStringHandling;
using Dalamud.Game.Text.SeStringHandling.Payloads;
using Dalamud.Utility;
using Lumina.Excel;
using Lumina.Text.Payloads;
using Lumina.Text.ReadOnly;
using Pidgin;
using static Pidgin.Parser;
using static Pidgin.Parser<char>;
@@ -157,6 +156,9 @@ internal static class AutoTranslate
}
else if (lookup is not "@")
{
if (row.Text.IsEmpty)
continue;
list.Add(new AutoTranslateEntry(row.Group, row.RowId, row.Text.ToString(), row.GroupTitle.ToString()));
if (shouldAdd)
@@ -242,9 +244,7 @@ internal static class AutoTranslate
var parts = tag[4..^1].Split(',', 2);
if (parts.Length == 2 && uint.TryParse(parts[0], out var group) && uint.TryParse(parts[1], out var key))
{
var payload = ValidEntries.Contains((group, key))
? new AutoTranslatePayload(group, key).Encode()
: [];
var payload = ValidEntries.Contains((group, key)) ? CreateFixedTranslation(group, key) : [];
var oldBytes = bytes.ToArray();
var lengthDiff = payload.Length - (i - start);
@@ -263,56 +263,83 @@ internal static class AutoTranslate
start = i;
}
}
public static bool StartsWithCommand(ref byte[] bytes)
{
var search = "<at:"u8;
if (bytes.Length <= search.Length)
return false;
// populate the list of valid entries
if (ValidEntries.Count == 0)
AllEntries();
for (var i = 0; i < search.Length; i++)
{
if (bytes[i] != search[i])
return false;
}
for (var i = 0; i < bytes.Length; i++)
{
if (bytes[i] != '>')
continue;
var tag = Encoding.UTF8.GetString(bytes[..(i + 1)]);
var parts = tag[4..^1].Split(',', 2);
if (parts.Length == 2 && uint.TryParse(parts[0], out var group) && uint.TryParse(parts[1], out var key))
{
if (!ValidEntries.Contains((group, key)))
return false;
var evaluated = Plugin.Evaluator.Evaluate(new ReadOnlySeString(CreateFixedTranslation(group, key))).ToString();
if (!evaluated.StartsWith('/'))
return false;
bytes = Encoding.UTF8.GetBytes(evaluated);
return true;
}
}
return false;
}
private static byte[] CreateFixedTranslation(uint group, uint key)
{
using var rssb = new RentedSeStringBuilder();
return rssb.Builder
.BeginMacro(MacroCode.Fixed)
.AppendUIntExpression(group - 1)
.AppendUIntExpression(key)
.EndMacro()
.ToArray();
}
}
internal interface ISelectorPart { }
internal class SingleRow : ISelectorPart
internal class SingleRow(uint row) : ISelectorPart
{
public uint Row { get; }
public SingleRow(uint row)
{
Row = row;
}
public uint Row { get; } = row;
}
internal class IndexRange : ISelectorPart
internal class IndexRange(uint start, uint end) : ISelectorPart
{
public uint Start { get; }
public uint End { get; }
public IndexRange(uint start, uint end)
{
Start = start;
End = end;
}
public uint Start { get; } = start;
public uint End { get; } = end;
}
internal class NounMarker : ISelectorPart { }
internal class ColumnSpecifier : ISelectorPart
internal class ColumnSpecifier(uint column) : ISelectorPart
{
public uint Column { get; }
public ColumnSpecifier(uint column)
{
Column = column;
}
public uint Column { get; } = column;
}
internal class AutoTranslateEntry
internal class AutoTranslateEntry(uint group, uint row, string str, string title)
{
internal uint Group { get; }
internal uint Row { get; }
internal string Text { get; }
internal string Title { get; }
public AutoTranslateEntry(uint group, uint row, string str, string title)
{
Group = group;
Row = row;
Text = str;
Title = title;
}
internal uint Group { get; } = group;
internal uint Row { get; } = row;
internal string Text { get; } = str;
internal string Title { get; } = title;
}
+277 -12
View File
@@ -1,8 +1,11 @@
using System.Runtime.CompilerServices;
using ChatTwo.Code;
using Dalamud.Game.Text.SeStringHandling;
using Dalamud.Game.Text.SeStringHandling.Payloads;
using System.Text;
using Lumina.Text.Expressions;
using Lumina.Text.Payloads;
using Lumina.Text.ReadOnly;
using PayloadType = Dalamud.Game.Text.SeStringHandling.PayloadType;
@@ -10,6 +13,244 @@ namespace ChatTwo.Util;
internal static class ChunkUtil
{
// internal static IEnumerable<Chunk> ToChunks(ReadOnlySeString msg, ChunkSource source, ChatType? defaultColour)
// {
// var chunks = new List<Chunk>();
//
// var italic = false;
// var foreground = new Stack<uint>();
// var glow = new Stack<uint>();
// Payload? link = null;
//
// void Append(string text)
// {
// chunks.Add(new TextChunk(source, link, text)
// {
// FallbackColour = defaultColour,
// Foreground = foreground.Count > 0 ? foreground.Peek() : null,
// Glow = glow.Count > 0 ? glow.Peek() : null,
// Italic = italic,
// });
// }
//
// foreach (var payload in msg)
// {
// if (payload.Type == ReadOnlySePayloadType.Text)
// {
// // We don't want to parse any null string
// var str = payload.ToString();
// var nulIndex = str.IndexOf('\0');
// if (nulIndex > 0)
// str = str[..nulIndex];
// if (string.IsNullOrEmpty(str))
// continue;
//
// Append(str);
// continue;
// }
//
// switch (payload.MacroCode)
// {
// case MacroCode.Italic:
// var newStatus = payload.TryGetExpression(out var expression) && expression.TryGetUInt(out var value) && value == 1;
// italic = newStatus;
// break;
// case MacroCode.Color:
// if (payload.TryGetExpression(out var eColor))
// {
// if (eColor.TryGetPlaceholderExpression(out var ph) && ph == (int)ExpressionType.StackColor)
// {
// if (foreground.Count > 0)
// foreground.Pop();
// }
// else if (TryResolveUInt(eColor, out var eColorVal))
// {
// var color = ColourUtil.ArgbToRgba(eColorVal);
//
// if (color > 0)
// foreground.Push(color);
// else if (foreground.Count > 0) // Push the previous color as we don't want invisible text
// foreground.Push(foreground.Peek());
// }
// }
// break;
// case MacroCode.EdgeColor:
// if (payload.TryGetExpression(out eColor))
// {
// if (eColor.TryGetPlaceholderExpression(out var ph) && ph == (int)ExpressionType.StackColor)
// {
// if (glow.Count > 0)
// glow.Pop();
// }
// else if (TryResolveUInt(eColor, out var eColorVal))
// {
// glow.Push(ColourUtil.ArgbToRgba(eColorVal));
// }
// }
// break;
// case MacroCode.ColorType:
// if (!payload.TryGetExpression(out var eColorType) || !eColorType.TryGetUInt(out var eColorTypeVal))
// {
// if (foreground.Count > 0)
// foreground.Pop();
// break;
// }
//
// if (eColorTypeVal == 0)
// {
// if (foreground.Count > 0)
// foreground.Pop();
// }
// else if (Sheets.UIColorSheet.TryGetRow(eColorTypeVal, out var row))
// {
// foreground.Push(row.Dark);
// }
// break;
// case MacroCode.EdgeColorType:
// if (!payload.TryGetExpression(out var eEdgeColor) || !eEdgeColor.TryGetUInt(out var eEdgeColorVal))
// {
// if (glow.Count > 0)
// glow.Pop();
// break;
// }
//
// if (eEdgeColorVal == 0)
// {
// if (glow.Count > 0)
// glow.Pop();
// }
// else if (Sheets.UIColorSheet.TryGetRow(eEdgeColorVal, out var row))
// {
// glow.Push(row.Dark);
// }
// break;
// case MacroCode.Fixed:
// if (!payload.TryGetExpression(out var expr1, out var expr2))
// break;
//
// if (expr1.TryGetUInt(out var group) && expr2.TryGetUInt(out var key))
// {
// chunks.Add(new IconChunk(source, null, BitmapFontIcon.AutoTranslateBegin));
// using var rssb = new RentedSeStringBuilder();
// var translatePayload = rssb.Builder
// .BeginMacro(MacroCode.Fixed)
// .AppendUIntExpression(group - 1)
// .AppendUIntExpression(key)
// .EndMacro()
// .ToReadOnlySeString();
//
// Append(Plugin.Evaluator.Evaluate(translatePayload).ToString());
// chunks.Add(new IconChunk(source, null, BitmapFontIcon.AutoTranslateEnd));
// }
// break;
// case MacroCode.Icon:
// if (payload.TryGetExpression(out var eIcon) && TryResolveInt(eIcon, out var iconVal))
// chunks.Add(new IconChunk(source, link, (BitmapFontIcon)iconVal));
// break;
// case MacroCode.Link:
// if (!payload.TryGetExpression(
// out var linkTypeExpr1,
// out var uintExpr2,
// out var intExpr3,
// out var intExpr4,
// out var strExpr5))
// break;
//
// if (!linkTypeExpr1.TryGetUInt(out var linkType))
// break;
//
// switch ((LinkMacroPayloadType)linkType)
// {
// case LinkMacroPayloadType.Terminator:
// link = null;
// break;
// case LinkMacroPayloadType.MapPosition:
// if (!uintExpr2.TryGetUInt(out var ids))
// break;
//
// if (!intExpr3.TryGetInt(out var rawX))
// break;
//
// if (!intExpr4.TryGetInt(out var rawY))
// break;
//
// var mapId = ids & 0xFF;
// var territoryId = (ids >> 16) & 0xFF;
// break;
// case (LinkMacroPayloadType)Payload.EmbeddedInfoType.DalamudLink - 1:
// if (!uintExpr2.TryGetUInt(out var commandId))
// break;
//
// if (!intExpr3.TryGetInt(out var extra1))
// break;
//
// if (!intExpr4.TryGetInt(out var extra2))
// break;
//
// if (!strExpr5.TryGetString(out var extraStr))
// break;
// break;
// case LinkMacroPayloadType.Quest:
// if (!uintExpr2.TryGetUInt(out var questId))
// break;
// break;
// case LinkMacroPayloadType.Status:
// if (!uintExpr2.TryGetUInt(out var statusId))
// break;
// break;
// case LinkMacroPayloadType.Item:
// if (!uintExpr2.TryGetUInt(out var itemId))
// break;
// break;
// case LinkMacroPayloadType.Character:
// if (!uintExpr2.TryGetUInt(out var flags))
// break;
//
// if (!intExpr3.TryGetUInt(out var worldId))
// break;
// break;
// case LinkMacroPayloadType.PartyFinder:
// if (!uintExpr2.TryGetUInt(out var listingId))
// break;
//
// // intExpr3 is unused
//
// if (!intExpr4.TryGetUInt(out worldId))
// break;
// break;
// case LinkMacroPayloadType.PartyFinderNotification:
// // no expr used
// break;
// case LinkMacroPayloadType.Achievement:
// if (!uintExpr2.TryGetUInt(out var achievementId))
// break;
// break;
// }
// break;
// case MacroCode.NonBreakingSpace:
// Append(" ");
// break;
// case PayloadType.Unknown:
// var rawPayload = (RawPayload)payload;
// else if (rawPayload.Data.Length > 1 && rawPayload.Data[1] == 0x14)
// {
// if (glow.Count > 0)
// {
// glow.Pop();
// }
// else if (rawPayload.Data.Length > 6 && rawPayload.Data[2] == 0x05 && rawPayload.Data[3] == 0xF6)
// {
// var (r, g, b) = (rawPayload.Data[4], rawPayload.Data[5], rawPayload.Data[6]);
// glow.Push(ColourUtil.ComponentsToRgba(r, g, b));
// }
// }
// break;
// }
// }
//
// return chunks;
// }
internal static IEnumerable<Chunk> ToChunks(SeString msg, ChunkSource source, ChatType? defaultColour)
{
var chunks = new List<Chunk>();
@@ -176,22 +417,46 @@ internal static class ChunkUtil
return BitConverter.ToUInt32(numArray, 0);
}
public static unsafe void PrintMemoryArea(nint address, int length)
private static bool TryResolveUInt(in ReadOnlySeExpressionSpan expression, out uint value)
{
var ptr = (byte*)address;
var str = new StringBuilder("\n");
for(var i = 0; i < length; i++)
if (expression.TryGetUInt(out value))
return true;
if (expression.TryGetParameterExpression(out var exprType, out var operand1))
{
str.Append($"{ptr![i]:X02}");
if (!TryResolveUInt(operand1, out var paramIndex))
return false;
if (i == 0)
continue;
if (paramIndex == 0)
return false;
if ((i+1) % 16 == 0)
str.Append('\n');
else if ((i+1) % 4 == 0)
str.Append(' ');
paramIndex--;
if ((ExpressionType)exprType == ExpressionType.GlobalNumber)
{
value = (uint) GlobalParametersCache.GetValue((int)paramIndex);
return true;
}
Plugin.Log.Information(str.ToString());
// return (ExpressionType)exprType switch
// {
// // ExpressionType.LocalNumber => context.TryGetLNum((int)paramIndex, out value), // lnum
// ExpressionType.GlobalNumber => (uint) GlobalParametersCache.GetValue((int)paramIndex), // gnum
// _ => false, // gstr, lstr
// };
}
return false;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static bool TryResolveInt(in ReadOnlySeExpressionSpan expression, out int value)
{
if (TryResolveUInt(expression, out var u32))
{
value = (int)u32;
return true;
}
value = 0;
return false;
}
}
+1 -1
View File
@@ -4,7 +4,7 @@ using System.Numerics;
namespace ChatTwo.Util;
internal static class ColourUtil {
internal static (byte r, byte g, byte b, byte a) RgbaToComponents(uint rgba)
private static (byte r, byte g, byte b, byte a) RgbaToComponents(uint rgba)
{
var r = (byte) ((rgba & 0xFF000000) >> 24);
var g = (byte) ((rgba & 0xFF0000) >> 16);
+3 -3
View File
@@ -25,7 +25,7 @@ public static class DateWidget
private static float LongestMonthWidth;
private static readonly float[] MonthWidths = new float[12];
private static uint LastOpenComboID;
private static uint LastOpenComboId;
public static bool Validate(DateTime minimal, ref DateTime currentMin, ref DateTime currentMax)
{
@@ -109,9 +109,9 @@ public static class DateWidget
// reset date when user right-clicks the date chooser header when the dialog is open
dateOut = DateTime.Now;
}
else if (LastOpenComboID != id)
else if (LastOpenComboId != id)
{
LastOpenComboID = id;
LastOpenComboId = id;
if (dateOut.Year == 1)
dateOut = DateTime.Now;
}
+2 -2
View File
@@ -2,7 +2,7 @@
public class ColorPayload
{
private const byte START_BYTE = 2;
private const byte StartByte = 2;
public bool Enabled;
public uint Color;
@@ -11,7 +11,7 @@ public class ColorPayload
public static ColorPayload? From(byte[] data)
{
using var stream = new MemoryStream(data);
if (stream.ReadByte() != START_BYTE || stream.ReadByte() != 0x13)
if (stream.ReadByte() != StartByte || stream.ReadByte() != 0x13)
return null;
stream.ReadByte(); // skip the length byte;
+23 -115
View File
@@ -183,13 +183,15 @@ internal static class ImGuiUtil
if (id != null)
label += $"##{id}";
Plugin.FontManager.FontAwesome.Push();
var size = new Vector2(0, 0);
bool ret;
using (Plugin.FontManager.FontAwesome.Push())
{
var size = Vector2.Zero;
if (width > 0)
size.X = width - 2 * ImGui.GetStyle().CellPadding.X;
var ret = ImGui.Button(label, size);
Plugin.FontManager.FontAwesome.Pop();
ret = ImGui.Button(label, size);
}
if (!string.IsNullOrEmpty(tooltip) && ImGui.IsItemHovered(ImGuiHoveredFlags.AllowWhenDisabled))
Tooltip(tooltip);
@@ -200,7 +202,7 @@ internal static class ImGuiUtil
internal static bool OptionCheckbox(ref bool value, string label, string? description = null)
{
var ret = ImGui.Checkbox(label, ref value);
if (description != null)
if (!string.IsNullOrEmpty(description))
HelpText(description);
return ret;
@@ -208,26 +210,20 @@ internal static class ImGuiUtil
internal static void HelpText(string text)
{
var colour = ImGui.GetStyle().Colors[(int) ImGuiCol.TextDisabled];
using (TextWrapPos())
using (ImRaii.PushColor(ImGuiCol.Text, colour))
{
using (ImRaii.TextWrapPos(0.0f))
using (ImRaii.PushColor(ImGuiCol.Text, ImGui.GetStyle().Colors[(int) ImGuiCol.TextDisabled]))
ImGui.TextUnformatted(text);
}
}
internal static void WarningText(string text, bool wrap = true)
{
var style = StyleModel.GetConfiguredStyle() ?? StyleModel.GetFromCurrent();
var dalamudOrange = style.BuiltInColors?.DalamudOrange;
using (TextWrapPos(wrap))
using (ImRaii.TextWrapPos(wrap ? 0.0f : ImGui.GetFontSize() * 35.0f))
using (ImRaii.PushColor(ImGuiCol.Text, dalamudOrange ?? Vector4.Zero, dalamudOrange != null))
{
ImGui.TextUnformatted(text);
}
}
internal static ImRaii.IEndObject BeginComboVertical(string label, string previewValue, ImGuiComboFlags flags = ImGuiComboFlags.None)
{
@@ -267,10 +263,8 @@ internal static class ImGuiUtil
{
using (ImRaii.Tooltip())
using (ImRaii.TextWrapPos(ImGui.GetFontSize() * 35.0f))
{
ImGui.TextUnformatted(tooltip);
}
}
public static SingleFontChooserDialog? FontChooser(string label, SingleFontSpec font, bool checkbox, ref bool checkboxValue, Predicate<IFontFamilyId>? exclusion = null, string? preview = null)
{
@@ -306,21 +300,19 @@ internal static class ImGuiUtil
ImGui.TextUnformatted(label);
ImGui.SetNextItemWidth(-1);
using var combo = ImRaii.Combo($"##{label}", $"{currentSize:###.##}pt");
if (combo)
{
if (!combo.Success)
return;
foreach (var size in FontManager.AxisFontSizeList)
if (ImGui.Selectable($"{size:###.##}pt", currentSize.Equals(size)))
currentSize = size;
}
}
public static bool Button(string id, FontAwesomeIcon icon, bool disabled)
{
using (ImRaii.Disabled(disabled))
{
return ImGuiComponents.IconButton(id, icon);
}
}
internal static bool CtrlShiftButton(string label, string tooltip = "")
{
@@ -330,30 +322,12 @@ internal static class ImGuiUtil
using (ImRaii.Disabled(!ctrlShiftHeld))
ret = ImGui.Button(label) && ctrlShiftHeld;
if (!string.IsNullOrEmpty(tooltip) && ImGui.IsItemHovered(ImGuiHoveredFlags.AllowWhenDisabled))
if (tooltip.Length != 0 && ImGui.IsItemHovered(ImGuiHoveredFlags.AllowWhenDisabled))
Tooltip(tooltip);
return ret;
}
internal static bool CtrlShiftButtonColored(string label, string tooltip = "")
{
var ctrlShiftHeld = ImGui.GetIO() is { KeyCtrl: true, KeyShift: true };
var colorNormal = new Vector4(0.780f, 0.245f, 0.245f, 1.0f);
var colorHovered = new Vector4(0.7f, 0.0f, 0.0f, 1.0f);
using (ImRaii.PushColor(ImGuiCol.Button, colorNormal))
using (ImRaii.PushColor(ImGuiCol.ButtonHovered, colorHovered))
{
var ret = ImGui.Button(label) && ctrlShiftHeld;
if (!string.IsNullOrEmpty(tooltip) && ImGui.IsItemHovered(ImGuiHoveredFlags.AllowWhenDisabled))
Tooltip(tooltip);
return ret;
}
}
internal static void KeybindInput(string id, ref ConfigKeyBind? keybind)
{
var idUint = ImGui.GetID(id);
@@ -394,19 +368,15 @@ internal static class ImGuiUtil
return;
}
foreach (var vk in Enum.GetValues(typeof(VirtualKey)).Cast<VirtualKey>())
foreach (var vk in Enum.GetValues<VirtualKey>())
{
if (vk is VirtualKey.NO_KEY or VirtualKey.CONTROL or VirtualKey.LCONTROL or VirtualKey.RCONTROL or VirtualKey.SHIFT or VirtualKey.LSHIFT or VirtualKey.RSHIFT or VirtualKey.MENU or VirtualKey.LMENU or VirtualKey.RMENU)
continue;
if (!TryToImGui(vk, out var imKey) || !ImGui.IsKeyPressed(imKey))
if (!vk.TryToImGui(out var imKey) || !ImGui.IsKeyPressed(imKey))
continue;
keybind = new ConfigKeyBind
{
Modifier = currentMods,
Key = vk
};
keybind = new ConfigKeyBind { Modifier = currentMods, Key = vk };
ImGui.GetStateStorage().SetBool(idUint, false);
return;
}
@@ -442,6 +412,12 @@ internal static class ImGuiUtil
}
}
public static void WrappedTextWithColor(Vector4 color, string text)
{
using (ImRaii.PushColor(ImGuiCol.Text, color))
ImGui.TextWrapped(text);
}
public static void CenterText(string text, float indent = 0.0f)
{
indent *= ImGuiHelpers.GlobalScale;
@@ -566,63 +542,6 @@ internal static class ImGuiUtil
return result != 0 || key == VirtualKey.NO_KEY;
}
public struct EndUnconditionally(Action endAction, bool success) : ImRaii.IEndObject
{
public bool Success { get; } = success;
private bool Disposed { get; set; } = false;
private Action EndAction { get; } = endAction;
public void Dispose()
{
if (!Disposed)
{
EndAction();
Disposed = true;
}
}
}
// Use end-function only on success.
private struct EndConditionally(Action endAction, bool success) : ImRaii.IEndObject
{
public bool Success { get; } = success;
private bool Disposed { get; set; } = false;
private Action EndAction { get; } = endAction;
public void Dispose()
{
if (Disposed)
return;
if (Success)
EndAction();
Disposed = true;
}
}
public static ImRaii.IEndObject TextWrapPos()
{
ImGui.PushTextWrapPos();
return new EndUnconditionally(ImGui.PopTextWrapPos, true);
}
public static ImRaii.IEndObject TextWrapPos(bool condition)
{
if (!condition)
return new EndUnconditionally(Nop, false);
ImGui.PushTextWrapPos();
return new EndUnconditionally(ImGui.PopTextWrapPos, true);
}
public static ImRaii.IEndObject Menu(string label)
{
return new EndConditionally(ImGui.EndMenu, ImGui.BeginMenu(label));
}
public static void ChannelSelector(string headerText, Dictionary<ChatType, ChatSource> chatCodes)
{
using var channelNode = ImRaii.TreeNode(headerText);
@@ -696,15 +615,4 @@ internal static class ImGuiUtil
extraChatChannels.Remove(id);
}
}
public static void WrappedTextWithColor(Vector4 color, string text)
{
using (ImRaii.PushColor(ImGuiCol.Text, color))
{
ImGui.TextWrapped(text);
}
}
// Used to avoid pops if condition is false for Push.
private static void Nop() { }
}
+2 -5
View File
@@ -25,13 +25,10 @@ public static class MathUtil
SizeY = Y + Height;
}
public Rectangle(Vector2 pos, Vector2 size)
: this((int) pos.X, (int) pos.Y, (int) size.X, (int) size.Y) { }
public Rectangle(Vector2 pos, Vector2 size) : this((int) pos.X, (int) pos.Y, (int) size.X, (int) size.Y) { }
public override string ToString()
{
return $"X: {X} Y: {Y} Width: {Width} Height: {Height}";
}
=> $"X: {X} Y: {Y} Width: {Width} Height: {Height}";
}
// From: https://stackoverflow.com/a/306379
+26
View File
@@ -0,0 +1,26 @@
using System.Text;
namespace ChatTwo.Util;
public static class MemoryUtil
{
public static unsafe void PrintMemoryArea(nint address, int length)
{
var ptr = (byte*)address;
var str = new StringBuilder("\n");
for(var i = 0; i < length; i++)
{
str.Append($"{ptr![i]:X02}");
if (i == 0)
continue;
if ((i+1) % 16 == 0)
str.Append('\n');
else if ((i+1) % 4 == 0)
str.Append(' ');
}
Plugin.Log.Information(str.ToString());
}
}
+7 -7
View File
@@ -63,18 +63,18 @@ internal class UriPayload(Uri uri) : Payload
/// <exception cref="UriFormatException">
/// If the URI is invalid, or if the scheme is not supported.
/// </exception>
public static UriPayload ResolveURI(string rawURI)
public static UriPayload ResolveUri(string rawUri)
{
ArgumentNullException.ThrowIfNull(rawURI);
ArgumentNullException.ThrowIfNull(rawUri);
// Check for an expected scheme '://', if not add 'https://'
if (ExpectedSchemes.Any(scheme => rawURI.StartsWith($"{scheme}://")))
return new UriPayload(new Uri(rawURI));
if (ExpectedSchemes.Any(scheme => rawUri.StartsWith($"{scheme}://")))
return new UriPayload(new Uri(rawUri));
if (rawURI.Contains("://"))
throw new UriFormatException($"Unsupported scheme in URL: {rawURI}");
if (rawUri.Contains("://"))
throw new UriFormatException($"Unsupported scheme in URL: {rawUri}");
return new UriPayload(new Uri($"{DefaultScheme}://{rawURI}"));
return new UriPayload(new Uri($"{DefaultScheme}://{rawUri}"));
}
protected override void DecodeImpl(BinaryReader reader, long endOfStream)
+2 -1
View File
@@ -10,6 +10,7 @@ internal static class TabsUtil
var channels = new Dictionary<ChatType, ChatSource>();
foreach (var chatType in Enum.GetValues<ChatType>())
channels[chatType] = ChatSourceExt.All;
return channels;
}
@@ -76,7 +77,7 @@ internal static class TabsUtil
[ChatType.MessageBook] = ChatSourceExt.All,
[ChatType.Alarm] = ChatSourceExt.All,
[ChatType.GlamourNotifications] = ChatSourceExt.All,
},
}
};
internal static Tab VanillaEvent => new()
+3 -3
View File
@@ -47,7 +47,7 @@ public static class Tokenizer
new TokenDefinition(TokenType.Whitespace, "\\s", 1),
new TokenDefinition(TokenType.Equals, "=", 1),
new TokenDefinition(TokenType.OpenParenthesis, "\\(", 1),
new TokenDefinition(TokenType.UrlString, URLRegex, 1),
new TokenDefinition(TokenType.UrlString, UrlRegex, 1),
new TokenDefinition(TokenType.StringValue, "\\p{IsBasicLatin}", 2),
new TokenDefinition(TokenType.Leftover, ".", 3)
];
@@ -127,7 +127,7 @@ public static class Tokenizer
private class TokenMatch
{
public TokenType TokenType { get; set; }
public string Value { get; set; }
public required string Value { get; set; }
public int StartIndex { get; set; }
public int EndIndex { get; set; }
public int Precedence { get; set; }
@@ -145,7 +145,7 @@ public static class Tokenizer
/// It matches URLs with www. or https:// prefix, and also matches URLs
/// without a prefix on specific TLDs.
/// </summary>
private static Regex URLRegex = new(
private static readonly Regex UrlRegex = new(
@"(?<URL>((https?:\/\/|www\.)[a-z0-9-]+(\.[a-z0-9-]+)*|([a-z0-9-]+(\.[a-z0-9-]+)*\.(com|net|org|co|io|app)))(:[\d]{1,5})?(\/[^\s]*)?)",
RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture
);
+2 -20
View File
@@ -1,5 +1,4 @@
using System.Runtime.CompilerServices;
using ChatTwo.Resources;
using ChatTwo.Resources;
using Dalamud.Interface.ImGuiNotification;
namespace ChatTwo.Util;
@@ -11,7 +10,7 @@ public static class WrapperUtil
Plugin.Notification.AddNotification(new Notification { Content = content, Type = type, Minimized = minimized });
}
public static void TryOpenURI(Uri uri)
public static void TryOpenUri(Uri uri)
{
try
{
@@ -24,21 +23,4 @@ public static class WrapperUtil
AddNotification(Language.Context_OpenInBrowserError, NotificationType.Error);
}
}
public static IEnumerable<(T Value, int Index)> WithIndex<T>(this IEnumerable<T> list)
=> list.Select((x, i) => (x, i));
/// <summary> Return the first object fulfilling the predicate or null for structs.</summary>
/// <param name="values"> The enumerable. </param>
/// <param name="predicate"> The predicate. </param>
/// <returns> The first object fulfilling the predicate, or a null-optional. </returns>
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static T? FirstOrNull<T>(this IEnumerable<T> values, Func<T, bool> predicate) where T : struct
{
foreach(var val in values)
if (predicate(val))
return val;
return null;
}
}
+3 -3
View File
@@ -4,9 +4,9 @@
"net10.0-windows7.0": {
"DalamudPackager": {
"type": "Direct",
"requested": "[14.0.1, )",
"resolved": "14.0.1",
"contentHash": "y0WWyUE6dhpGdolK3iKgwys05/nZaVf4ZPtIjpLhJBZvHxkkiE23zYRo7K7uqAgoK/QvK5cqF6l3VG5AbgC6KA=="
"requested": "[14.0.2, )",
"resolved": "14.0.2",
"contentHash": "dQJeq+8eyHzra4Cg5eZ/3LAeS3/UpvvLriYJGSncMK9LqJ7Q4B6jwcOsxo3PfxVd15xj+IzVFxkPqIBmPQu8/w=="
},
"DotNet.ReproducibleBuilds": {
"type": "Direct",