docs: Fix the last comments i think now
Security / scan (push) Successful in 12s

This commit is contained in:
2026-05-11 08:11:30 +02:00
parent 7d73def53d
commit b1b6402827
10 changed files with 70 additions and 199 deletions
+11 -11
View File
@@ -7,36 +7,36 @@ public enum ChatSource : ushort
{ {
None = 0, None = 0,
/// <summary>The player currently controlled by the local client.</summary> // The player controlled by this client
LocalPlayer = 1 << XivChatRelationKind.LocalPlayer, LocalPlayer = 1 << XivChatRelationKind.LocalPlayer,
/// <summary>A player in the same 4-man or 8-man party as the local player.</summary> // Member of the local party
PartyMember = 1 << XivChatRelationKind.PartyMember, PartyMember = 1 << XivChatRelationKind.PartyMember,
/// <summary>A player in the same alliance raid.</summary> // Member of the alliance
AllianceMember = 1 << XivChatRelationKind.AllianceMember, AllianceMember = 1 << XivChatRelationKind.AllianceMember,
/// <summary>A player not in the local player's party or alliance.</summary> // Other player
OtherPlayer = 1 << XivChatRelationKind.OtherPlayer, OtherPlayer = 1 << XivChatRelationKind.OtherPlayer,
/// <summary>An enemy entity that is currently in combat with the player or party.</summary> // Enemy in combat
EngagedEnemy = 1 << XivChatRelationKind.EngagedEnemy, EngagedEnemy = 1 << XivChatRelationKind.EngagedEnemy,
/// <summary>An enemy entity that is not yet in combat or claimed.</summary> // Enemy out of combat
UnengagedEnemy = 1 << XivChatRelationKind.UnengagedEnemy, UnengagedEnemy = 1 << XivChatRelationKind.UnengagedEnemy,
/// <summary>An NPC that is friendly or neutral to the player (e.g., EventNPCs).</summary> // Friendly NPC
FriendlyNpc = 1 << XivChatRelationKind.FriendlyNpc, FriendlyNpc = 1 << XivChatRelationKind.FriendlyNpc,
/// <summary>A pet (Summoner/Scholar) or companion (Chocobo) belonging to the local player.</summary> // Own pet or companion
PetOrCompanion = 1 << XivChatRelationKind.PetOrCompanion, PetOrCompanion = 1 << XivChatRelationKind.PetOrCompanion,
/// <summary>A pet or companion belonging to a member of the local player's party.</summary> // Pet or companion of party members
PetOrCompanionParty = 1 << XivChatRelationKind.PetOrCompanionParty, PetOrCompanionParty = 1 << XivChatRelationKind.PetOrCompanionParty,
/// <summary>A pet or companion belonging to a member of the alliance.</summary> // Pet or companion of alliance members
PetOrCompanionAlliance = 1 << XivChatRelationKind.PetOrCompanionAlliance, PetOrCompanionAlliance = 1 << XivChatRelationKind.PetOrCompanionAlliance,
/// <summary>A pet or companion belonging to a player not in the party or alliance.</summary> // Pet or companion of other players
PetOrCompanionOther = 1 << XivChatRelationKind.PetOrCompanionOther, PetOrCompanionOther = 1 << XivChatRelationKind.PetOrCompanionOther,
} }
+9 -30
View File
@@ -174,8 +174,7 @@ internal sealed unsafe class Chat : IDisposable
internal static void RotateCrossLinkshellHistory(RotateMode mode) => internal static void RotateCrossLinkshellHistory(RotateMode mode) =>
UIModule.Instance()->RotateCrossLinkshellHistory(GetRotateIdx(mode)); UIModule.Instance()->RotateCrossLinkshellHistory(GetRotateIdx(mode));
// This function looks up a channel's user-defined color. // Look up a channel's user-defined color, returns null if 0
// If this function ever returns 0, it returns null instead.
internal uint? GetChannelColor(ChatType type) internal uint? GetChannelColor(ChatType type)
{ {
var parent = type.Parent(); var parent = type.Parent();
@@ -215,8 +214,7 @@ internal sealed unsafe class Chat : IDisposable
if (Plugin.Functions.KeybindManager.DirectChat && LastTypedCharacter != null) if (Plugin.Functions.KeybindManager.DirectChat && LastTypedCharacter != null)
{ {
// FIXME: this whole system sucks // Capture the just-typed character input
// FIXME v2: I hate everything about this, but it works
Plugin.Framework.RunOnTick(() => Plugin.Framework.RunOnTick(() =>
{ {
string? input = null; string? input = null;
@@ -255,13 +253,9 @@ internal sealed unsafe class Chat : IDisposable
try try
{ {
// We already called this function once, so we skip the duplicated call // Prevent duplicate calls
// Also return the original value here so that vanilla chat receives all information
if (Plugin.ChatLogWindow.TellSpecial) if (Plugin.ChatLogWindow.TellSpecial)
{
Plugin.Log.Information("Return early to prevent duplicated call...");
return ChatLogRefreshHook!.Original(log, eventId, value); return ChatLogRefreshHook!.Original(log, eventId, value);
}
Plugin.ChatLogWindow.Activated( Plugin.ChatLogWindow.Activated(
new ChatActivatedArgs(new ChannelSwitchInfo(null)) new ChatActivatedArgs(new ChannelSwitchInfo(null))
@@ -275,8 +269,7 @@ internal sealed unsafe class Chat : IDisposable
Plugin.Log.Error(ex, "Error in chat Activated event"); Plugin.Log.Error(ex, "Error in chat Activated event");
} }
// prevent the game from focusing the chat log return 1; // Prevent vanilla chat log from gaining focus
return 1;
} }
private CStringPointer ChangeChannelNameDetour(AgentChatLog* agent) private CStringPointer ChangeChannelNameDetour(AgentChatLog* agent)
@@ -430,10 +423,7 @@ internal sealed unsafe class Chat : IDisposable
); );
} }
/// <summary> // Check if channel is valid (non-linkshell or existing linkshell)
/// Returns true if the channel is any non-linkshell channel, or if the
/// linkshell actually exists.
/// </summary>
internal static bool ValidAnyLinkshell(InputChannel channel) internal static bool ValidAnyLinkshell(InputChannel channel)
{ {
var idx = channel.LinkshellIndex(); var idx = channel.LinkshellIndex();
@@ -477,8 +467,7 @@ internal sealed unsafe class Chat : IDisposable
_ => 1, _ => 1,
}; };
// Iterate up to 8 times to find a valid linkshell. for (var i = 0; i < 8; i++) // Find valid linkshell within 8 iterations
for (var i = 0; i < 8; i++)
{ {
currentIndex = (uint)((8 + currentIndex + delta) % 8); currentIndex = (uint)((8 + currentIndex + delta) % 8);
if (validFn(currentIndex)) if (validFn(currentIndex))
@@ -524,7 +513,7 @@ internal sealed unsafe class Chat : IDisposable
); );
// RotateLinkshell returns null when no valid linkshell is found within 8 iterations. // RotateLinkshell returns null when no valid linkshell is found within 8 iterations.
// Forward the null so the caller can keep the existing channel instead of crashing on nullable arithmetic. // Forward the null so the caller can keep the existing channel instead of crashing on nullable arithmetic.
return idx is null ? null : channel + idx.Value; return idx is null ? null : channel + idx.Value; // null if not found, otherwise new channel
} }
default: default:
return channel; return channel;
@@ -533,11 +522,7 @@ internal sealed unsafe class Chat : IDisposable
internal void SetChannel(InputChannel channel, TellTarget? tellTarget = null) internal void SetChannel(InputChannel channel, TellTarget? tellTarget = null)
{ {
// ExtraChat linkshells aren't supported in game so we never want to // Ignore ExtraChat linkshells (use ChatLogWindow.SetChannel() instead)
// call the ChangeChatChannel function with them.
//
// Callers should call ChatLogWindow.SetChannel() which handles
// ExtraChat channels
if (channel.IsExtraChatLinkshell()) if (channel.IsExtraChatLinkshell())
return; return;
@@ -565,9 +550,6 @@ internal sealed unsafe class Chat : IDisposable
bool setChatType bool setChatType
) )
{ {
// param6 is 0 for contentId and 1 for objectId
// param7 is always 0 ?
if (!Plugin.CurrentTab.CurrentChannel.UseTempChannel) if (!Plugin.CurrentTab.CurrentChannel.UseTempChannel)
Plugin.CurrentTab.CurrentChannel.UseTempChannel = true; Plugin.CurrentTab.CurrentChannel.UseTempChannel = true;
@@ -742,10 +724,7 @@ internal sealed unsafe class Chat : IDisposable
internal bool CheckHideFlags() internal bool CheckHideFlags()
{ {
// Only hide the chat in a cutscene when the vanilla chat would've // Only hide chat in cutscene when vanilla chat would also be hidden
// also been hidden. This prevents Chat 2 from hiding for a split
// second before the cutscene actually starts, because the game sets
// the cutscene conditions before processing the skip.
var raptureAtkUnitManager = RaptureAtkUnitManager.Instance(); var raptureAtkUnitManager = RaptureAtkUnitManager.Instance();
return raptureAtkUnitManager == null return raptureAtkUnitManager == null
|| raptureAtkUnitManager->UiFlags.HasFlag(UiFlags.Chat); || raptureAtkUnitManager->UiFlags.HasFlag(UiFlags.Chat);
+3 -6
View File
@@ -254,12 +254,9 @@ public sealed class SettingsWindow : Dalamud.Interface.Windowing.Window
Initialise(); Initialise();
} }
/// <summary> // Returns true if any filter-relevant setting changed between Plugin.Config
/// Returns true if any setting that influences message filtering changed // and the Mutable copy. Gates Clear+Refilter on Save so cosmetic changes
/// between Plugin.Config and the Mutable working copy. Gates the heavy // don't wipe in-session chat history.
/// ClearAllTabs+FilterAllTabsAsync cycle on Save so cosmetic changes
/// don't wipe in-session chat history.
/// </summary>
private bool HasFilterRelevantChanges() private bool HasFilterRelevantChanges()
{ {
if (Mutable.PrivacyFilterEnabled != Plugin.Config.PrivacyFilterEnabled) if (Mutable.PrivacyFilterEnabled != Plugin.Config.PrivacyFilterEnabled)
+11 -26
View File
@@ -17,10 +17,9 @@ internal static class AutoTranslate
private static readonly Dictionary<ClientLanguage, List<AutoTranslateEntry>> Entries = new(); private static readonly Dictionary<ClientLanguage, List<AutoTranslateEntry>> Entries = new();
private static readonly HashSet<(uint, uint)> ValidEntries = []; private static readonly HashSet<(uint, uint)> ValidEntries = [];
// Serializes all reads and writes against Entries / ValidEntries. // Serialises all reads/writes against Entries and ValidEntries.
// PreloadCache spawns a worker thread that fills both, while the main // PreloadCache fills both from a worker thread while the main thread
// thread reads them via Matching / ReplaceWithPayload / StartsWithCommand // reads via Matching/ReplaceWithPayload/StartsWithCommand.
// — without this lock the HashSet/Dictionary access is undefined.
private static readonly object EntriesLock = new(); private static readonly object EntriesLock = new();
private static Parser<char, (string name, Maybe<IEnumerable<ISelectorPart>> selector)> Parser() private static Parser<char, (string name, Maybe<IEnumerable<ISelectorPart>> selector)> Parser()
@@ -54,13 +53,8 @@ internal static class AutoTranslate
return Map((name, sel) => (name, sel), sheetName, selector.Optional()); return Map((name, sel) => (name, sel), sheetName, selector.Optional());
} }
/// <summary> // Warms the auto-translate cache on a background thread so the first
/// Preloads auto-translate entries into the cache for the current game // message send doesn't hitch the main thread.
/// language. Without this, the first message will take a long time to send
/// (which causes a hitch in the main thread).
///
/// This spawns a new thread.
/// </summary>
internal static void PreloadCache() internal static void PreloadCache()
{ {
new Thread(() => new Thread(() =>
@@ -104,7 +98,7 @@ internal static class AutoTranslate
{ {
if (lookup is not ("" or "@")) if (lookup is not ("" or "@"))
{ {
// SE added whitespace to the newest additions, but ParseOrThrow doesn't see them as valid // SE added whitespace to newer entries; strip it before parsing.
lookup = lookup.Replace(" ", ""); lookup = lookup.Replace(" ", "");
var (sheetName, selector) = parser.ParseOrThrow(lookup); var (sheetName, selector) = parser.ParseOrThrow(lookup);
@@ -144,19 +138,13 @@ internal static class AutoTranslate
columns.Add(0); columns.Add(0);
if (rows.Count == 0) if (rows.Count == 0)
// We can't use an "index from end" (like `^0`) here because // Can't use index-from-end here because we iterate over integers,
// we're iterating over integers, not an array directly. // not an array directly. `0..^0` would silently skip the sheet.
// Previously, we were setting `0..^0` which caused these
// sheets to be completely skipped due to this bug.
// See below.
rows.Add(..Index.FromStart((int)sheet.GetRowAt(sheet.Count - 1).RowId + 1)); rows.Add(..Index.FromStart((int)sheet.GetRowAt(sheet.Count - 1).RowId + 1));
foreach (var range in rows) foreach (var range in rows)
{ {
// We iterate over the range by numerical values here, so // Integer iteration -- can't use index-from-end (see above).
// we can't use an "index from end" otherwise nothing will
// happen.
// See above.
for (var i = range.Start.Value; i < range.End.Value; i++) for (var i = range.Start.Value; i < range.End.Value; i++)
{ {
if (!sheet.TryGetRow((uint)i, out var rowParser)) if (!sheet.TryGetRow((uint)i, out var rowParser))
@@ -261,7 +249,6 @@ internal static class AutoTranslate
if (bytes.Length <= search.Length) if (bytes.Length <= search.Length)
return; return;
// populate the list of valid entries
bool needBuild; bool needBuild;
lock (EntriesLock) lock (EntriesLock)
needBuild = ValidEntries.Count == 0; needBuild = ValidEntries.Count == 0;
@@ -308,9 +295,8 @@ internal static class AutoTranslate
start = -1; start = -1;
} }
// Pure managed comparison via Span avoids the msvcrt.dll P/Invoke, // Span comparison avoids the msvcrt.dll P/Invoke which is fragile
// which is fragile under Wine and triggered an extra managed-to- // under Wine and caused an extra managed-to-unmanaged copy per check.
// unmanaged copy per check.
if ( if (
i + search.Length < bytes.Length i + search.Length < bytes.Length
&& bytes.AsSpan(i, search.Length).SequenceEqual(search) && bytes.AsSpan(i, search.Length).SequenceEqual(search)
@@ -325,7 +311,6 @@ internal static class AutoTranslate
if (bytes.Length <= search.Length) if (bytes.Length <= search.Length)
return false; return false;
// populate the list of valid entries
bool needBuild; bool needBuild;
lock (EntriesLock) lock (EntriesLock)
needBuild = ValidEntries.Count == 0; needBuild = ValidEntries.Count == 0;
+3 -9
View File
@@ -10,9 +10,8 @@ public static class GlobalParametersCache
public static int GetValue(int index) public static int GetValue(int index)
{ {
// Capture the array reference once so the bounds check and the // Capture the array reference once so bounds check and read operate
// indexed read operate on the same instance, even if Refresh // on the same instance if Refresh reassigns Cache between the two.
// reassigns Cache between the two operations.
var cache = Cache; var cache = Cache;
if (index < 0 || index >= cache.Length) if (index < 0 || index >= cache.Length)
return 0; return 0;
@@ -20,12 +19,7 @@ public static class GlobalParametersCache
return cache[index]; return cache[index];
} }
/// <summary> // Refreshes the cache from RaptureTextModule. Must be called on the main thread.
/// Refresh the cache of global parameters from RaptureTextModule.
/// </summary>
/// <remarks>
/// This should be called in the main thread when updates are necessary.
/// </remarks>
public static unsafe void Refresh() public static unsafe void Refresh()
{ {
if (!ThreadSafety.IsMainThread) if (!ThreadSafety.IsMainThread)
+9 -37
View File
@@ -11,8 +11,7 @@ public readonly unsafe ref struct GfdFileView
private readonly ReadOnlySpan<byte> Span; private readonly ReadOnlySpan<byte> Span;
private readonly bool DirectLookup; private readonly bool DirectLookup;
/// <summary>Initializes a new instance of the <see cref="GfdFileView"/> struct.</summary> // span: raw .gfd file bytes
/// <param name="span">The data.</param>
public GfdFileView(ReadOnlySpan<byte> span) public GfdFileView(ReadOnlySpan<byte> span)
{ {
Span = span; Span = span;
@@ -27,18 +26,13 @@ public readonly unsafe ref struct GfdFileView
DirectLookup &= i + 1 == entries[i].Id; DirectLookup &= i + 1 == entries[i].Id;
} }
/// <summary>Gets the header.</summary>
private ref readonly GfdHeader Header => ref MemoryMarshal.AsRef<GfdHeader>(Span); private ref readonly GfdHeader Header => ref MemoryMarshal.AsRef<GfdHeader>(Span);
/// <summary>Gets the entries.</summary>
private ReadOnlySpan<GfdEntry> Entries => private ReadOnlySpan<GfdEntry> Entries =>
MemoryMarshal.Cast<byte, GfdEntry>(Span[sizeof(GfdHeader)..]); MemoryMarshal.Cast<byte, GfdEntry>(Span[sizeof(GfdHeader)..]);
/// <summary>Attempts to get an entry.</summary> // Returns true if the entry was found.
/// <param name="iconId">The icon ID.</param> // followRedirect: whether to chase redirect chains.
/// <param name="entry">The entry.</param>
/// <param name="followRedirect">Whether to follow redirects.</param>
/// <returns><c>true</c> if found.</returns>
public bool TryGetEntry(uint iconId, out GfdEntry entry, bool followRedirect = true) public bool TryGetEntry(uint iconId, out GfdEntry entry, bool followRedirect = true)
{ {
if (iconId == 0) if (iconId == 0)
@@ -50,9 +44,8 @@ public readonly unsafe ref struct GfdFileView
var entries = Entries; var entries = Entries;
if (DirectLookup) if (DirectLookup)
{ {
// Resolve redirects on the direct-lookup path too — the binary-search // Follow redirects on the direct-lookup path for consistency with
// path follows them, and skipping them here was inconsistent for // the binary-search path.
// contiguous ID sets.
var visited = 0; var visited = 0;
while (iconId <= entries.Length) while (iconId <= entries.Length)
{ {
@@ -107,49 +100,28 @@ public readonly unsafe ref struct GfdFileView
return false; return false;
} }
/// <summary>Header of a .gfd file.</summary> // .gfd file header
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]
public struct GfdHeader public struct GfdHeader
{ {
/// <summary>Signature: "gftd0100".</summary> public fixed byte Signature[8]; // "gftd0100"
public fixed byte Signature[8];
/// <summary>Number of entries.</summary>
public int Count; public int Count;
/// <summary>Unused/unknown.</summary>
public fixed byte Padding[4]; public fixed byte Padding[4];
} }
/// <summary>An entry of a .gfd file.</summary> // .gfd file entry -- one icon slot
[StructLayout(LayoutKind.Sequential, Size = 0x10)] [StructLayout(LayoutKind.Sequential, Size = 0x10)]
public struct GfdEntry public struct GfdEntry
{ {
/// <summary>ID of the entry.</summary>
public ushort Id; public ushort Id;
/// <summary>The left offset of the entry.</summary>
public ushort Left; public ushort Left;
/// <summary>The top offset of the entry.</summary>
public ushort Top; public ushort Top;
/// <summary>The width of the entry.</summary>
public ushort Width; public ushort Width;
/// <summary>The height of the entry.</summary>
public ushort Height; public ushort Height;
/// <summary>Unknown/unused.</summary>
public ushort Unk0A; public ushort Unk0A;
public ushort Redirect; // non-zero = redirects to another entry
/// <summary>The redirected entry, maybe.</summary>
public ushort Redirect;
/// <summary>Unknown/unused.</summary>
public ushort Unk0E; public ushort Unk0E;
/// <summary>Gets a value indicating whether this entry is effectively empty.</summary>
public bool IsEmpty => Width == 0 || Height == 0; public bool IsEmpty => Width == 0 || Height == 0;
} }
} }
+2 -10
View File
@@ -31,18 +31,10 @@ public static class MathUtil
public override string ToString() => $"X: {X} Y: {Y} Width: {Width} Height: {Height}"; public override string ToString() => $"X: {X} Y: {Y} Width: {Width} Height: {Height}";
} }
/// <summary> // Standard AABB overlap test. Inclusive on both axes to catch shared
/// Checks if two rectangles overlap at any point. // edges and identical rectangles (previous ValueInRange approach missed these).
/// </summary>
/// <param name="a"></param>
/// <param name="b"></param>
/// <returns>True if overlapping</returns>
public static bool HasOverlap(this Rectangle a, Rectangle b) public static bool HasOverlap(this Rectangle a, Rectangle b)
{ {
// Standard AABB overlap test: two rectangles overlap iff they
// overlap on both axes. The previous nested ValueInRange approach
// used strict inequalities at both ends, which dropped identical
// rectangles and shared-edge cases as false negatives.
return a.X < b.X + b.Width return a.X < b.X + b.Width
&& a.X + a.Width > b.X && a.X + a.Width > b.X
&& a.Y < b.Y + b.Height && a.Y < b.Y + b.Height
+11 -40
View File
@@ -13,15 +13,10 @@ internal class PartyFinderPayload : Payload
Id = id; Id = id;
} }
protected override byte[] EncodeImpl() protected override byte[] EncodeImpl() => throw new NotImplementedException();
{
throw new NotImplementedException();
}
protected override void DecodeImpl(BinaryReader reader, long endOfStream) protected override void DecodeImpl(BinaryReader reader, long endOfStream) =>
{
throw new NotImplementedException(); throw new NotImplementedException();
}
} }
internal class AchievementPayload : Payload internal class AchievementPayload : Payload
@@ -35,15 +30,10 @@ internal class AchievementPayload : Payload
Id = id; Id = id;
} }
protected override byte[] EncodeImpl() protected override byte[] EncodeImpl() => throw new NotImplementedException();
{
throw new NotImplementedException();
}
protected override void DecodeImpl(BinaryReader reader, long endOfStream) protected override void DecodeImpl(BinaryReader reader, long endOfStream) =>
{
throw new NotImplementedException(); throw new NotImplementedException();
}
} }
internal class UriPayload(Uri uri) : Payload internal class UriPayload(Uri uri) : Payload
@@ -55,20 +45,14 @@ internal class UriPayload(Uri uri) : Payload
private const string DefaultScheme = "https"; private const string DefaultScheme = "https";
private static readonly string[] ExpectedSchemes = ["http", "https"]; private static readonly string[] ExpectedSchemes = ["http", "https"];
/// <summary> // Parses a raw URI string. Defaults to https:// if no scheme is present.
/// Create a URIPayload from a raw URI string. If the URI does not have a // Throws UriFormatException for empty input or unsupported schemes.
/// scheme, it will default to https://.
/// </summary>
/// <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);
if (string.IsNullOrWhiteSpace(rawUri)) if (string.IsNullOrWhiteSpace(rawUri))
throw new UriFormatException("URI cannot be empty or whitespace."); throw new UriFormatException("URI cannot be empty or whitespace.");
// Check for an expected scheme '://', if not add 'https://'
if (ExpectedSchemes.Any(scheme => rawUri.StartsWith($"{scheme}://"))) if (ExpectedSchemes.Any(scheme => rawUri.StartsWith($"{scheme}://")))
return new UriPayload(new Uri(rawUri)); return new UriPayload(new Uri(rawUri));
@@ -78,15 +62,10 @@ internal class UriPayload(Uri uri) : Payload
return new UriPayload(new Uri($"{DefaultScheme}://{rawUri}")); return new UriPayload(new Uri($"{DefaultScheme}://{rawUri}"));
} }
protected override void DecodeImpl(BinaryReader reader, long endOfStream) protected override void DecodeImpl(BinaryReader reader, long endOfStream) =>
{
throw new NotImplementedException(); throw new NotImplementedException();
}
protected override byte[] EncodeImpl() protected override byte[] EncodeImpl() => throw new NotImplementedException();
{
throw new NotImplementedException();
}
} }
internal class EmotePayload : Payload internal class EmotePayload : Payload
@@ -95,18 +74,10 @@ internal class EmotePayload : Payload
public string Code = string.Empty; public string Code = string.Empty;
public static EmotePayload ResolveEmote(string code) public static EmotePayload ResolveEmote(string code) => new EmotePayload { Code = code };
{
return new EmotePayload { Code = code };
}
protected override void DecodeImpl(BinaryReader reader, long endOfStream) protected override void DecodeImpl(BinaryReader reader, long endOfStream) =>
{
throw new NotImplementedException(); throw new NotImplementedException();
}
protected override byte[] EncodeImpl() protected override byte[] EncodeImpl() => throw new NotImplementedException();
{
throw new NotImplementedException();
}
} }
+9 -18
View File
@@ -14,12 +14,8 @@ public static class TabsUtil
return channels; return channels;
} }
// Hellion-tuned General preset (v1.0.0 — sharpened defaults). // Public-chat-only: Say, Yell, Shout. Group/FC/Linkshell and gameplay
// Public-chat-only, the bare three channels you encounter in open // events live in their own tabs to keep General focused on open-world chat.
// world. Group/FC/Linkshell traffic moves to dedicated tabs, gameplay
// events (loot, crafting, gathering, NPC dialogue, PF pings) move to
// the System tab where they belong — keeps the General view focused
// on actual conversation in the immediate surroundings.
public static Tab VanillaGeneral => public static Tab VanillaGeneral =>
new() new()
{ {
@@ -55,11 +51,8 @@ public static class TabsUtil
AllSenderMessages = true, AllSenderMessages = true,
}; };
// Hellion default-tab presets used by the v10 wipe migration. Names are // Hellion tab presets. Names live in HellionStrings (EN+DE) so upstream
// kept in HellionStrings (EN+DE) instead of Language.* so the upstream // resource files stay untouched.
// resource files stay untouched. Channel selections cover the channels
// a typical Eorzea raider uses without forcing the user to hand-tick
// each box on first start.
public static Tab HellionFreeCompany => public static Tab HellionFreeCompany =>
new() new()
{ {
@@ -88,10 +81,8 @@ public static class TabsUtil
[ChatType.LootNotice] = (ChatSourceExt.All, ChatSourceExt.All), [ChatType.LootNotice] = (ChatSourceExt.All, ChatSourceExt.All),
[ChatType.LootRoll] = (ChatSourceExt.All, ChatSourceExt.All), [ChatType.LootRoll] = (ChatSourceExt.All, ChatSourceExt.All),
}, },
// No automatic input-channel switch; the Gruppe tab is a read // No input-channel switch: Party pulls in multiple channel types
// surface that pulls in Party, CrossParty, Alliance and PvpTeam // and auto-routing /party would surprise users wanting /alliance or /pvpteam.
// together. Auto-routing /party into this tab would surprise the
// user when they actually wanted /alliance or /pvpteam.
}; };
public static Tab HellionBeginner => public static Tab HellionBeginner =>
@@ -112,7 +103,7 @@ public static class TabsUtil
Name = HellionStrings.Tabs_Presets_System, Name = HellionStrings.Tabs_Presets_System,
SelectedChannels = new Dictionary<ChatType, (ChatSource, ChatSource)> SelectedChannels = new Dictionary<ChatType, (ChatSource, ChatSource)>
{ {
// Plain system noise // System noise
[ChatType.Debug] = (ChatSourceExt.All, ChatSourceExt.All), [ChatType.Debug] = (ChatSourceExt.All, ChatSourceExt.All),
[ChatType.Urgent] = (ChatSourceExt.All, ChatSourceExt.All), [ChatType.Urgent] = (ChatSourceExt.All, ChatSourceExt.All),
[ChatType.Notice] = (ChatSourceExt.All, ChatSourceExt.All), [ChatType.Notice] = (ChatSourceExt.All, ChatSourceExt.All),
@@ -122,7 +113,7 @@ public static class TabsUtil
[ChatType.GatheringSystem] = (ChatSourceExt.All, ChatSourceExt.All), [ChatType.GatheringSystem] = (ChatSourceExt.All, ChatSourceExt.All),
[ChatType.NoviceNetworkSystem] = (ChatSourceExt.All, ChatSourceExt.All), [ChatType.NoviceNetworkSystem] = (ChatSourceExt.All, ChatSourceExt.All),
[ChatType.BattleSystem] = (ChatSourceExt.All, ChatSourceExt.All), [ChatType.BattleSystem] = (ChatSourceExt.All, ChatSourceExt.All),
// Login / logout / announcement noise // Login/logout/announcement noise
[ChatType.NpcAnnouncement] = (ChatSourceExt.All, ChatSourceExt.All), [ChatType.NpcAnnouncement] = (ChatSourceExt.All, ChatSourceExt.All),
[ChatType.FreeCompanyAnnouncement] = (ChatSourceExt.All, ChatSourceExt.All), [ChatType.FreeCompanyAnnouncement] = (ChatSourceExt.All, ChatSourceExt.All),
[ChatType.FreeCompanyLoginLogout] = (ChatSourceExt.All, ChatSourceExt.All), [ChatType.FreeCompanyLoginLogout] = (ChatSourceExt.All, ChatSourceExt.All),
@@ -130,7 +121,7 @@ public static class TabsUtil
[ChatType.PvpTeamLoginLogout] = (ChatSourceExt.All, ChatSourceExt.All), [ChatType.PvpTeamLoginLogout] = (ChatSourceExt.All, ChatSourceExt.All),
[ChatType.RetainerSale] = (ChatSourceExt.All, ChatSourceExt.All), [ChatType.RetainerSale] = (ChatSourceExt.All, ChatSourceExt.All),
[ChatType.PeriodicRecruitmentNotification] = (ChatSourceExt.All, ChatSourceExt.All), [ChatType.PeriodicRecruitmentNotification] = (ChatSourceExt.All, ChatSourceExt.All),
// Gameplay-event streams (moved out of General in v1.0.0) // Gameplay event streams
[ChatType.NpcDialogue] = (ChatSourceExt.All, ChatSourceExt.All), [ChatType.NpcDialogue] = (ChatSourceExt.All, ChatSourceExt.All),
[ChatType.LootNotice] = (ChatSourceExt.All, ChatSourceExt.All), [ChatType.LootNotice] = (ChatSourceExt.All, ChatSourceExt.All),
[ChatType.LootRoll] = (ChatSourceExt.All, ChatSourceExt.All), [ChatType.LootRoll] = (ChatSourceExt.All, ChatSourceExt.All),
+2 -12
View File
@@ -135,18 +135,8 @@ public static class Tokenizer
public int Precedence { get; set; } public int Precedence { get; set; }
} }
/// <summary> // Matches URLs with http(s):// or www. prefix, and bare domains on known TLDs.
/// URLRegex returns a regex object that matches URLs like: // Examples: https://example.com, www.sub.example.com, example.com
/// - https://example.com
/// - http://example.com
/// - www.example.com
/// - https://sub.example.com
/// - example.com
/// - sub.example.com
///
/// It matches URLs with www. or https:// prefix, and also matches URLs
/// without a prefix on specific TLDs.
/// </summary>
private static readonly 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]*)?)", @"(?<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 RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture