merge: v1.0.0 Standalone Rebrand & DAU Crash-Schutz
Brings the feature/hellionchat-rebrand-v1.0.0 branch into main as the first fully standalone Hellion Chat release. Includes: - Rebrand from ChatTwo.* fork identity to standalone HellionChat identity (namespace, repo folder, IPC channels, ImGui IDs, build pipeline) - Runtime detector that refuses to load the plugin when upstream Chat 2 is also active, preventing the parallel-load FFXIV crash - Three critical and twenty-one major pre-existing defects fixed in the same release (CodeRabbit-flagged): AABB overlap correctness, Equals/GetHashCode anti-pattern, IPC dispose mismatch, IDisposable contract on DebuggerWindow, ExtraChat / GameFunctions null-deref guards, AutoTranslate concurrent-access lock, Privacy retention bounded waits, EmoteCache and FontManager HttpClient leaks, SearchSelector ImGui ID collision, DbViewer count caching, plus Sheets/Tabs/Popout bounds checks and the IconUtil binary-search off-by-one - SQLite native binary pinned to 3.50.3 (CVE-2025-6965, CVE-2025-7709) - packages.lock.json now enforced via RestorePackagesWithLockFile - Public-facing branding text aligned to standalone framing while EUPL-1.2 license attribution is preserved unchanged Verified locally on KAZAMA via dotnet build and manual FFXIV smoketests A/B/C plus the post-fix sweep test.
This commit is contained in:
@@ -5,7 +5,7 @@ updates:
|
|||||||
# noise down while still catching transitive security advisories within
|
# noise down while still catching transitive security advisories within
|
||||||
# a few days of disclosure.
|
# a few days of disclosure.
|
||||||
- package-ecosystem: nuget
|
- package-ecosystem: nuget
|
||||||
directory: /ChatTwo
|
directory: /HellionChat
|
||||||
schedule:
|
schedule:
|
||||||
interval: weekly
|
interval: weekly
|
||||||
day: monday
|
day: monday
|
||||||
|
|||||||
@@ -42,15 +42,15 @@ jobs:
|
|||||||
Expand-Archive -Force -Path dalamud.zip -DestinationPath $hooks
|
Expand-Archive -Force -Path dalamud.zip -DestinationPath $hooks
|
||||||
|
|
||||||
- name: Restore
|
- name: Restore
|
||||||
run: dotnet restore ChatTwo/ChatTwo.csproj
|
run: dotnet restore HellionChat/HellionChat.csproj
|
||||||
|
|
||||||
- name: Build (Release)
|
- name: Build (Release)
|
||||||
run: dotnet build ChatTwo/ChatTwo.csproj --configuration Release --no-restore
|
run: dotnet build HellionChat/HellionChat.csproj --configuration Release --no-restore
|
||||||
|
|
||||||
- name: Upload build output
|
- name: Upload build output
|
||||||
uses: actions/upload-artifact@v7
|
uses: actions/upload-artifact@v7
|
||||||
with:
|
with:
|
||||||
name: HellionChat-build-${{ github.run_number }}
|
name: HellionChat-build-${{ github.run_number }}
|
||||||
path: ChatTwo/bin/Release/**/HellionChat/**
|
path: HellionChat/bin/Release/**/HellionChat/**
|
||||||
if-no-files-found: warn
|
if-no-files-found: warn
|
||||||
retention-days: 14
|
retention-days: 14
|
||||||
|
|||||||
@@ -62,10 +62,10 @@ jobs:
|
|||||||
queries: security-extended
|
queries: security-extended
|
||||||
|
|
||||||
- name: Restore
|
- name: Restore
|
||||||
run: dotnet restore ChatTwo/ChatTwo.csproj
|
run: dotnet restore HellionChat/HellionChat.csproj
|
||||||
|
|
||||||
- name: Build (Release)
|
- name: Build (Release)
|
||||||
run: dotnet build ChatTwo/ChatTwo.csproj --configuration Release --no-restore
|
run: dotnet build HellionChat/HellionChat.csproj --configuration Release --no-restore
|
||||||
|
|
||||||
- name: Perform CodeQL analysis
|
- name: Perform CodeQL analysis
|
||||||
uses: github/codeql-action/analyze@v3
|
uses: github/codeql-action/analyze@v3
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ name: Release
|
|||||||
# - the tag name (filtered by on.tags = v*, validated again at runtime
|
# - the tag name (filtered by on.tags = v*, validated again at runtime
|
||||||
# against ^v\d+\.\d+\.\d+$ before being used in any string)
|
# against ^v\d+\.\d+\.\d+$ before being used in any string)
|
||||||
# All other values are either repo-controlled (paths under
|
# All other values are either repo-controlled (paths under
|
||||||
# ChatTwo/bin/Release derived from Get-ChildItem) or pinned URLs to
|
# HellionChat/bin/Release derived from Get-ChildItem) or pinned URLs to
|
||||||
# goatcorp / GitHub. Nothing from a webhook event payload (issue/PR
|
# goatcorp / GitHub. Nothing from a webhook event payload (issue/PR
|
||||||
# titles, commit messages, etc.) flows into a run-step.
|
# titles, commit messages, etc.) flows into a run-step.
|
||||||
|
|
||||||
@@ -60,16 +60,16 @@ jobs:
|
|||||||
Expand-Archive -Force -Path dalamud.zip -DestinationPath $hooks
|
Expand-Archive -Force -Path dalamud.zip -DestinationPath $hooks
|
||||||
|
|
||||||
- name: Build (Release)
|
- name: Build (Release)
|
||||||
run: dotnet build ChatTwo/ChatTwo.csproj --configuration Release
|
run: dotnet build HellionChat/HellionChat.csproj --configuration Release
|
||||||
|
|
||||||
- name: Locate latest.zip
|
- name: Locate latest.zip
|
||||||
id: locate
|
id: locate
|
||||||
shell: pwsh
|
shell: pwsh
|
||||||
run: |
|
run: |
|
||||||
$zip = Get-ChildItem -Path ChatTwo\bin\Release -Recurse -Filter latest.zip | Select-Object -First 1
|
$zip = Get-ChildItem -Path HellionChat\bin\Release -Recurse -Filter latest.zip | Select-Object -First 1
|
||||||
if (-not $zip)
|
if (-not $zip)
|
||||||
{
|
{
|
||||||
throw "latest.zip not found under ChatTwo\bin\Release"
|
throw "latest.zip not found under HellionChat\bin\Release"
|
||||||
}
|
}
|
||||||
Write-Host "Found: $($zip.FullName)"
|
Write-Host "Found: $($zip.FullName)"
|
||||||
"path=$($zip.FullName)" | Out-File -FilePath $env:GITHUB_OUTPUT -Append
|
"path=$($zip.FullName)" | Out-File -FilePath $env:GITHUB_OUTPUT -Append
|
||||||
@@ -100,7 +100,7 @@ jobs:
|
|||||||
}
|
}
|
||||||
$version = $tag.Substring(1)
|
$version = $tag.Substring(1)
|
||||||
|
|
||||||
$yamlPath = "ChatTwo/HellionChat.yaml"
|
$yamlPath = "HellionChat/HellionChat.yaml"
|
||||||
$raw = Get-Content -Path $yamlPath -Raw
|
$raw = Get-Content -Path $yamlPath -Raw
|
||||||
|
|
||||||
$marker = "changelog: |-"
|
$marker = "changelog: |-"
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("ChatTwo.Tests")]
|
|
||||||
@@ -1,8 +1,6 @@
|
|||||||
|
|
||||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ChatTwo", "ChatTwo\ChatTwo.csproj", "{739F75E6-B65F-41EF-9D90-F7BC519E4875}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HellionChat", "HellionChat\HellionChat.csproj", "{739F75E6-B65F-41EF-9D90-F7BC519E4875}"
|
||||||
EndProject
|
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ChatTwo.Tests", "ChatTwo.Tests\ChatTwo.Tests.csproj", "{A9FE423A-240C-4EDA-ACC6-21474B562128}"
|
|
||||||
EndProject
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
@@ -14,9 +12,5 @@ Global
|
|||||||
{739F75E6-B65F-41EF-9D90-F7BC519E4875}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{739F75E6-B65F-41EF-9D90-F7BC519E4875}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{739F75E6-B65F-41EF-9D90-F7BC519E4875}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{739F75E6-B65F-41EF-9D90-F7BC519E4875}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{739F75E6-B65F-41EF-9D90-F7BC519E4875}.Release|Any CPU.Build.0 = Release|Any CPU
|
{739F75E6-B65F-41EF-9D90-F7BC519E4875}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
{A9FE423A-240C-4EDA-ACC6-21474B562128}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{A9FE423A-240C-4EDA-ACC6-21474B562128}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{A9FE423A-240C-4EDA-ACC6-21474B562128}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{A9FE423A-240C-4EDA-ACC6-21474B562128}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
EndGlobal
|
EndGlobal
|
||||||
@@ -1,14 +1,14 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using ChatTwo.Code;
|
using HellionChat.Code;
|
||||||
using ChatTwo.GameFunctions.Types;
|
using HellionChat.GameFunctions.Types;
|
||||||
using ChatTwo.Resources;
|
using HellionChat.Resources;
|
||||||
using ChatTwo.Util;
|
using HellionChat.Util;
|
||||||
using Dalamud.Game.Text;
|
using Dalamud.Game.Text;
|
||||||
using Dalamud.Game.Text.SeStringHandling;
|
using Dalamud.Game.Text.SeStringHandling;
|
||||||
|
|
||||||
namespace ChatTwo;
|
namespace HellionChat;
|
||||||
|
|
||||||
// Hellion Chat — Auto-Tell-Tabs.
|
// Hellion Chat — Auto-Tell-Tabs.
|
||||||
//
|
//
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
using System.Linq;
|
||||||
|
using HellionChat.Resources;
|
||||||
|
using Dalamud.Plugin;
|
||||||
|
|
||||||
|
namespace HellionChat;
|
||||||
|
|
||||||
|
internal static class ChatTwoConflictDetector
|
||||||
|
{
|
||||||
|
private const string UpstreamInternalName = "ChatTwo";
|
||||||
|
|
||||||
|
public static void ThrowIfChatTwoIsLoaded(IDalamudPluginInterface pluginInterface)
|
||||||
|
{
|
||||||
|
var conflict = pluginInterface.InstalledPlugins
|
||||||
|
.FirstOrDefault(p =>
|
||||||
|
p.InternalName == UpstreamInternalName &&
|
||||||
|
p.IsLoaded);
|
||||||
|
|
||||||
|
if (conflict is null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var message = HellionStrings.ChatTwoConflictTitle + "\n\n" +
|
||||||
|
HellionStrings.ChatTwoConflictBody + "\n\n" +
|
||||||
|
HellionStrings.ChatTwoConflictAction;
|
||||||
|
|
||||||
|
throw new System.InvalidOperationException(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
using ChatTwo.Code;
|
using HellionChat.Code;
|
||||||
using Dalamud.Game.Text.SeStringHandling;
|
using Dalamud.Game.Text.SeStringHandling;
|
||||||
using MessagePack;
|
using MessagePack;
|
||||||
|
|
||||||
namespace ChatTwo;
|
namespace HellionChat;
|
||||||
|
|
||||||
[Union(0, typeof(TextChunk))]
|
[Union(0, typeof(TextChunk))]
|
||||||
[Union(1, typeof(IconChunk))]
|
[Union(1, typeof(IconChunk))]
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
using Dalamud.Game.Text;
|
using Dalamud.Game.Text;
|
||||||
|
|
||||||
namespace ChatTwo.Code;
|
namespace HellionChat.Code;
|
||||||
|
|
||||||
public class ChatCode
|
public class ChatCode
|
||||||
{
|
{
|
||||||
@@ -91,13 +91,10 @@ public class ChatCode
|
|||||||
|
|
||||||
public override bool Equals(object? obj)
|
public override bool Equals(object? obj)
|
||||||
{
|
{
|
||||||
if (obj == null)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (obj is not ChatCode code)
|
if (obj is not ChatCode code)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return GetHashCode() == code.GetHashCode();
|
return Type == code.Type && Source == code.Source && Target == code.Target;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override int GetHashCode()
|
public override int GetHashCode()
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
using Dalamud.Game.Text;
|
using Dalamud.Game.Text;
|
||||||
|
|
||||||
namespace ChatTwo.Code;
|
namespace HellionChat.Code;
|
||||||
|
|
||||||
[Flags]
|
[Flags]
|
||||||
public enum ChatSource : ushort
|
public enum ChatSource : ushort
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
using ChatTwo.Resources;
|
using HellionChat.Resources;
|
||||||
|
|
||||||
namespace ChatTwo.Code;
|
namespace HellionChat.Code;
|
||||||
|
|
||||||
internal static class ChatSourceExt
|
internal static class ChatSourceExt
|
||||||
{
|
{
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace ChatTwo.Code;
|
namespace HellionChat.Code;
|
||||||
|
|
||||||
public enum ChatType : ushort
|
public enum ChatType : ushort
|
||||||
{
|
{
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
using ChatTwo.Resources;
|
using HellionChat.Resources;
|
||||||
using ChatTwo.Util;
|
using HellionChat.Util;
|
||||||
using Dalamud.Game.Config;
|
using Dalamud.Game.Config;
|
||||||
|
|
||||||
namespace ChatTwo.Code;
|
namespace HellionChat.Code;
|
||||||
|
|
||||||
internal static class ChatTypeExt
|
internal static class ChatTypeExt
|
||||||
{
|
{
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace ChatTwo.Code;
|
namespace HellionChat.Code;
|
||||||
|
|
||||||
public enum InputChannel : uint
|
public enum InputChannel : uint
|
||||||
{
|
{
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
using Lumina.Excel.Sheets;
|
using Lumina.Excel.Sheets;
|
||||||
|
|
||||||
namespace ChatTwo.Code;
|
namespace HellionChat.Code;
|
||||||
|
|
||||||
internal static class InputChannelExt
|
internal static class InputChannelExt
|
||||||
{
|
{
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
using Dalamud.Game.Command;
|
using Dalamud.Game.Command;
|
||||||
|
|
||||||
namespace ChatTwo;
|
namespace HellionChat;
|
||||||
|
|
||||||
internal sealed class Commands : IDisposable
|
internal sealed class Commands : IDisposable
|
||||||
{
|
{
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
using System.Collections;
|
using System.Collections;
|
||||||
using ChatTwo.Code;
|
using HellionChat.Code;
|
||||||
using ChatTwo.GameFunctions.Types;
|
using HellionChat.GameFunctions.Types;
|
||||||
using ChatTwo.Resources;
|
using HellionChat.Resources;
|
||||||
using ChatTwo.Util;
|
using HellionChat.Util;
|
||||||
using Dalamud;
|
using Dalamud;
|
||||||
using Dalamud.Configuration;
|
using Dalamud.Configuration;
|
||||||
using Dalamud.Game.ClientState.Keys;
|
using Dalamud.Game.ClientState.Keys;
|
||||||
@@ -10,7 +10,7 @@ using Dalamud.Game.Text.SeStringHandling.Payloads;
|
|||||||
using Dalamud.Interface.FontIdentifier;
|
using Dalamud.Interface.FontIdentifier;
|
||||||
using Dalamud.Bindings.ImGui;
|
using Dalamud.Bindings.ImGui;
|
||||||
|
|
||||||
namespace ChatTwo;
|
namespace HellionChat;
|
||||||
|
|
||||||
[Serializable]
|
[Serializable]
|
||||||
public class ConfigKeyBind
|
public class ConfigKeyBind
|
||||||
@@ -8,7 +8,7 @@ using Dalamud.Bindings.ImGui;
|
|||||||
using SixLabors.ImageSharp;
|
using SixLabors.ImageSharp;
|
||||||
using SixLabors.ImageSharp.PixelFormats;
|
using SixLabors.ImageSharp.PixelFormats;
|
||||||
|
|
||||||
namespace ChatTwo;
|
namespace HellionChat;
|
||||||
|
|
||||||
public static class EmoteCache
|
public static class EmoteCache
|
||||||
{
|
{
|
||||||
@@ -192,7 +192,7 @@ public static class EmoteCache
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var content = await new HttpClient().GetAsync(EmotePath.Format(emote.Id));
|
var content = await Client.GetAsync(EmotePath.Format(emote.Id));
|
||||||
RawData = await content.Content.ReadAsByteArrayAsync();
|
RawData = await content.Content.ReadAsByteArrayAsync();
|
||||||
|
|
||||||
await using var stream = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.Read);
|
await using var stream = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.Read);
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using ChatTwo.Code;
|
using HellionChat.Code;
|
||||||
|
|
||||||
namespace ChatTwo.Export;
|
namespace HellionChat.Export;
|
||||||
|
|
||||||
internal enum ExportFormat
|
internal enum ExportFormat
|
||||||
{
|
{
|
||||||
@@ -6,7 +6,7 @@ using Dalamud.Interface.ManagedFontAtlas;
|
|||||||
using Dalamud.Interface.Utility;
|
using Dalamud.Interface.Utility;
|
||||||
using Dalamud.Bindings.ImGui;
|
using Dalamud.Bindings.ImGui;
|
||||||
|
|
||||||
namespace ChatTwo;
|
namespace HellionChat;
|
||||||
|
|
||||||
public class FontManager
|
public class FontManager
|
||||||
{
|
{
|
||||||
@@ -39,11 +39,18 @@ public class FontManager
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
GameSymFont = new HttpClient().GetAsync("https://img.finalfantasyxiv.com/lds/pc/global/fonts/FFXIV_Lodestone_SSF.ttf")
|
// Dispose HttpClient and HttpResponseMessage to avoid socket
|
||||||
.Result
|
// exhaustion on repeated cold-start downloads. GetAwaiter().GetResult()
|
||||||
.Content
|
// unwraps AggregateException so failures surface cleanly. A full
|
||||||
.ReadAsByteArrayAsync()
|
// async refactor of the constructor would be cleaner but is out of
|
||||||
.Result;
|
// scope for v1.0.0 — tracked in the backlog.
|
||||||
|
using var client = new HttpClient();
|
||||||
|
using var response = client
|
||||||
|
.GetAsync("https://img.finalfantasyxiv.com/lds/pc/global/fonts/FFXIV_Lodestone_SSF.ttf")
|
||||||
|
.GetAwaiter()
|
||||||
|
.GetResult();
|
||||||
|
response.EnsureSuccessStatusCode();
|
||||||
|
GameSymFont = response.Content.ReadAsByteArrayAsync().GetAwaiter().GetResult();
|
||||||
|
|
||||||
Dalamud.Utility.FilesystemUtil.WriteAllBytesSafe(filePath, GameSymFont);
|
Dalamud.Utility.FilesystemUtil.WriteAllBytesSafe(filePath, GameSymFont);
|
||||||
}
|
}
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using ChatTwo.Code;
|
using HellionChat.Code;
|
||||||
using ChatTwo.GameFunctions.Types;
|
using HellionChat.GameFunctions.Types;
|
||||||
using ChatTwo.Resources;
|
using HellionChat.Resources;
|
||||||
using ChatTwo.Util;
|
using HellionChat.Util;
|
||||||
using Dalamud.Game.Config;
|
using Dalamud.Game.Config;
|
||||||
using Dalamud.Game.Text.SeStringHandling;
|
using Dalamud.Game.Text.SeStringHandling;
|
||||||
using Dalamud.Hooking;
|
using Dalamud.Hooking;
|
||||||
@@ -22,7 +22,7 @@ using Lumina.Text.ReadOnly;
|
|||||||
|
|
||||||
using ValueType = FFXIVClientStructs.FFXIV.Component.GUI.AtkValueType;
|
using ValueType = FFXIVClientStructs.FFXIV.Component.GUI.AtkValueType;
|
||||||
|
|
||||||
namespace ChatTwo.GameFunctions;
|
namespace HellionChat.GameFunctions;
|
||||||
|
|
||||||
internal sealed unsafe class Chat : IDisposable
|
internal sealed unsafe class Chat : IDisposable
|
||||||
{
|
{
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using ChatTwo.Resources;
|
using HellionChat.Resources;
|
||||||
using Dalamud.Memory;
|
using Dalamud.Memory;
|
||||||
using FFXIVClientStructs.FFXIV.Client.System.String;
|
using FFXIVClientStructs.FFXIV.Client.System.String;
|
||||||
using FFXIVClientStructs.FFXIV.Client.UI;
|
using FFXIVClientStructs.FFXIV.Client.UI;
|
||||||
|
|
||||||
namespace ChatTwo.GameFunctions;
|
namespace HellionChat.GameFunctions;
|
||||||
|
|
||||||
public unsafe class ChatBox
|
public unsafe class ChatBox
|
||||||
{
|
{
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
using ChatTwo.Util;
|
using HellionChat.Util;
|
||||||
using FFXIVClientStructs.FFXIV.Client.UI.Agent;
|
using FFXIVClientStructs.FFXIV.Client.UI.Agent;
|
||||||
using FFXIVClientStructs.FFXIV.Client.UI.Info;
|
using FFXIVClientStructs.FFXIV.Client.UI.Info;
|
||||||
using FFXIVClientStructs.FFXIV.Client.UI.Misc;
|
using FFXIVClientStructs.FFXIV.Client.UI.Misc;
|
||||||
|
|
||||||
namespace ChatTwo.GameFunctions;
|
namespace HellionChat.GameFunctions;
|
||||||
|
|
||||||
internal sealed unsafe class Context
|
internal sealed unsafe class Context
|
||||||
{
|
{
|
||||||
@@ -16,7 +16,7 @@ using Lumina.Excel;
|
|||||||
using Lumina.Excel.Sheets;
|
using Lumina.Excel.Sheets;
|
||||||
using ValueType = FFXIVClientStructs.FFXIV.Component.GUI.AtkValueType;
|
using ValueType = FFXIVClientStructs.FFXIV.Component.GUI.AtkValueType;
|
||||||
|
|
||||||
namespace ChatTwo.GameFunctions;
|
namespace HellionChat.GameFunctions;
|
||||||
|
|
||||||
internal unsafe class GameFunctions : IDisposable
|
internal unsafe class GameFunctions : IDisposable
|
||||||
{
|
{
|
||||||
@@ -249,9 +249,15 @@ internal unsafe class GameFunctions : IDisposable
|
|||||||
|
|
||||||
private nint ResolveTextCommandPlaceholderDetour(nint a1, byte* placeholderText, byte a3, byte a4)
|
private nint ResolveTextCommandPlaceholderDetour(nint a1, byte* placeholderText, byte a3, byte a4)
|
||||||
{
|
{
|
||||||
|
// The detour is only invoked through the hook, so the hook should
|
||||||
|
// never be null here, but the nullable field declaration forces us
|
||||||
|
// to handle the theoretical race during teardown.
|
||||||
|
if (ResolveTextCommandPlaceholderHook is null)
|
||||||
|
return nint.Zero;
|
||||||
|
|
||||||
var placeholder = MemoryHelper.ReadStringNullTerminated((nint) placeholderText);
|
var placeholder = MemoryHelper.ReadStringNullTerminated((nint) placeholderText);
|
||||||
if (ReplacementName == null || placeholder != Placeholder)
|
if (ReplacementName == null || placeholder != Placeholder)
|
||||||
return ResolveTextCommandPlaceholderHook!.Original(a1, placeholderText, a3, a4);
|
return ResolveTextCommandPlaceholderHook.Original(a1, placeholderText, a3, a4);
|
||||||
|
|
||||||
MemoryHelper.WriteString(PlaceholderNamePtr, ReplacementName);
|
MemoryHelper.WriteString(PlaceholderNamePtr, ReplacementName);
|
||||||
ReplacementName = null;
|
ReplacementName = null;
|
||||||
+5
-5
@@ -1,16 +1,16 @@
|
|||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using ChatTwo.Code;
|
using HellionChat.Code;
|
||||||
using ChatTwo.GameFunctions.Types;
|
using HellionChat.GameFunctions.Types;
|
||||||
using ChatTwo.Util;
|
using HellionChat.Util;
|
||||||
using Dalamud.Game.ClientState.Keys;
|
using Dalamud.Game.ClientState.Keys;
|
||||||
using Dalamud.Game.Config;
|
using Dalamud.Game.Config;
|
||||||
using Dalamud.Plugin.Services;
|
using Dalamud.Plugin.Services;
|
||||||
using FFXIVClientStructs.FFXIV.Client.System.String;
|
using FFXIVClientStructs.FFXIV.Client.System.String;
|
||||||
using FFXIVClientStructs.FFXIV.Client.UI;
|
using FFXIVClientStructs.FFXIV.Client.UI;
|
||||||
using Dalamud.Bindings.ImGui;
|
using Dalamud.Bindings.ImGui;
|
||||||
using ModifierFlag = ChatTwo.GameFunctions.Types.ModifierFlag;
|
using ModifierFlag = HellionChat.GameFunctions.Types.ModifierFlag;
|
||||||
|
|
||||||
namespace ChatTwo.GameFunctions;
|
namespace HellionChat.GameFunctions;
|
||||||
|
|
||||||
internal enum KeyboardSource {
|
internal enum KeyboardSource {
|
||||||
Game,
|
Game,
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
using ChatTwo.Resources;
|
using HellionChat.Resources;
|
||||||
using ChatTwo.Util;
|
using HellionChat.Util;
|
||||||
using Dalamud.Interface.ImGuiNotification;
|
using Dalamud.Interface.ImGuiNotification;
|
||||||
using FFXIVClientStructs.FFXIV.Client.UI.Agent;
|
using FFXIVClientStructs.FFXIV.Client.UI.Agent;
|
||||||
using FFXIVClientStructs.FFXIV.Client.UI.Info;
|
using FFXIVClientStructs.FFXIV.Client.UI.Info;
|
||||||
|
|
||||||
namespace ChatTwo.GameFunctions;
|
namespace HellionChat.GameFunctions;
|
||||||
|
|
||||||
internal static unsafe class Party
|
internal static unsafe class Party
|
||||||
{
|
{
|
||||||
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
using ChatTwo.Code;
|
using HellionChat.Code;
|
||||||
|
|
||||||
namespace ChatTwo.GameFunctions.Types;
|
namespace HellionChat.GameFunctions.Types;
|
||||||
|
|
||||||
internal class ChannelSwitchInfo {
|
internal class ChannelSwitchInfo {
|
||||||
internal InputChannel? Channel { get; }
|
internal InputChannel? Channel { get; }
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
namespace ChatTwo.GameFunctions.Types;
|
namespace HellionChat.GameFunctions.Types;
|
||||||
|
|
||||||
internal sealed class ChatActivatedArgs
|
internal sealed class ChatActivatedArgs
|
||||||
{
|
{
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
using Dalamud.Game.ClientState.Keys;
|
using Dalamud.Game.ClientState.Keys;
|
||||||
|
|
||||||
namespace ChatTwo.GameFunctions.Types;
|
namespace HellionChat.GameFunctions.Types;
|
||||||
|
|
||||||
internal class Keybind
|
internal class Keybind
|
||||||
{
|
{
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
namespace ChatTwo.GameFunctions.Types;
|
namespace HellionChat.GameFunctions.Types;
|
||||||
|
|
||||||
[Flags]
|
[Flags]
|
||||||
public enum ModifierFlag
|
public enum ModifierFlag
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
namespace ChatTwo.GameFunctions.Types;
|
namespace HellionChat.GameFunctions.Types;
|
||||||
|
|
||||||
internal enum RotateMode
|
internal enum RotateMode
|
||||||
{
|
{
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
namespace ChatTwo.GameFunctions.Types;
|
namespace HellionChat.GameFunctions.Types;
|
||||||
|
|
||||||
internal sealed class TellHistoryInfo
|
internal sealed class TellHistoryInfo
|
||||||
{
|
{
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
namespace ChatTwo.GameFunctions.Types;
|
namespace HellionChat.GameFunctions.Types;
|
||||||
|
|
||||||
public enum TellReason
|
public enum TellReason
|
||||||
{
|
{
|
||||||
+4
-1
@@ -1,7 +1,7 @@
|
|||||||
using Dalamud.Game.ClientState.Objects.SubKinds;
|
using Dalamud.Game.ClientState.Objects.SubKinds;
|
||||||
using FFXIVClientStructs.FFXIV.Client.Game.Character;
|
using FFXIVClientStructs.FFXIV.Client.Game.Character;
|
||||||
|
|
||||||
namespace ChatTwo.GameFunctions.Types;
|
namespace HellionChat.GameFunctions.Types;
|
||||||
|
|
||||||
[Serializable]
|
[Serializable]
|
||||||
public class TellTarget
|
public class TellTarget
|
||||||
@@ -30,6 +30,9 @@ public class TellTarget
|
|||||||
|
|
||||||
public unsafe void FromTarget(IPlayerCharacter target)
|
public unsafe void FromTarget(IPlayerCharacter target)
|
||||||
{
|
{
|
||||||
|
if (target.Address == nint.Zero)
|
||||||
|
return;
|
||||||
|
|
||||||
Name = target.Name.TextValue;
|
Name = target.Name.TextValue;
|
||||||
World = target.HomeWorld.RowId;
|
World = target.HomeWorld.RowId;
|
||||||
ContentId = ((Character*)target.Address)->ContentId;
|
ContentId = ((Character*)target.Address)->ContentId;
|
||||||
@@ -4,20 +4,29 @@
|
|||||||
0.1.0 is our bootstrap release; the underlying Chat 2 base is
|
0.1.0 is our bootstrap release; the underlying Chat 2 base is
|
||||||
called out in the yaml changelog so users can see what it
|
called out in the yaml changelog so users can see what it
|
||||||
derives from. -->
|
derives from. -->
|
||||||
<Version>0.6.1</Version>
|
<Version>1.0.0</Version>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<!-- HellionChat fork: assembly is renamed so Dalamud uses
|
<!-- Honor packages.lock.json on restore so floating version ranges
|
||||||
pluginConfigs/HellionChat instead of pluginConfigs/ChatTwo,
|
don't silently drift between machines or CI runs. -->
|
||||||
keeping our state independent from the upstream plugin.
|
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
|
||||||
Code namespace stays ChatTwo.* so upstream cherry-picks
|
<!-- v1.0.0 standalone cut — both AssemblyName and RootNamespace
|
||||||
apply cleanly. -->
|
are HellionChat. The plugin no longer maintains source-level
|
||||||
|
cherry-pick compatibility with upstream Infiziert90/ChatTwo;
|
||||||
|
upstream changes are integrated manually if at all. -->
|
||||||
<AssemblyName>HellionChat</AssemblyName>
|
<AssemblyName>HellionChat</AssemblyName>
|
||||||
<RootNamespace>ChatTwo</RootNamespace>
|
<RootNamespace>HellionChat</RootNamespace>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="MessagePack" Version="3.1.4" />
|
<PackageReference Include="MessagePack" Version="3.1.4" />
|
||||||
<PackageReference Include="Microsoft.Data.Sqlite" Version="10.0.7" />
|
<PackageReference Include="Microsoft.Data.Sqlite" Version="10.0.7" />
|
||||||
|
<!-- Override the transitively-referenced native SQLite build to one
|
||||||
|
that ships SQLite >= 3.50.3 (CVE-2025-6965 memory corruption,
|
||||||
|
CVE-2025-7709 fixed in 3.50.x). Microsoft.Data.Sqlite 10.0.7
|
||||||
|
pulls SQLitePCLRaw 2.1.11 which carries the older lib; pinning
|
||||||
|
the lib package directly forces the newer native binary
|
||||||
|
without a major bump on the managed wrapper. -->
|
||||||
|
<PackageReference Include="SQLitePCLRaw.lib.e_sqlite3" Version="3.50.3" />
|
||||||
<PackageReference Include="morelinq" Version="4.4.0" />
|
<PackageReference Include="morelinq" Version="4.4.0" />
|
||||||
<PackageReference Include="Pidgin" Version="3.3.0" />
|
<PackageReference Include="Pidgin" Version="3.3.0" />
|
||||||
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.12" />
|
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.12" />
|
||||||
@@ -1,14 +1,15 @@
|
|||||||
name: Hellion Chat
|
name: Hellion Chat
|
||||||
author: JonKazama-Hellion
|
author: JonKazama-Hellion
|
||||||
punchline: Chat 2 with privacy controls aligned to EU, US and JP rules
|
punchline: Chat replacement with privacy controls aligned to EU, US and JP rules — based on Chat 2 (EUPL-1.2)
|
||||||
description: |-
|
description: |-
|
||||||
Hellion Chat is built on top of Chat 2 with one removal and a stack
|
Hellion Chat is a privacy-focused chat replacement for FINAL FANTASY XIV
|
||||||
of privacy controls on top. Tabs, channel filters, RGB colours,
|
based on the Chat 2 codebase (EUPL-1.2). One feature is intentionally
|
||||||
emotes, screenshot mode, IPC integration and the chat replacement
|
removed (the optional webinterface) and a stack of privacy controls is
|
||||||
window itself work the same. The optional webinterface that Chat 2
|
added on top. Tabs, channel filters, RGB colours, emotes, screenshot
|
||||||
ships is intentionally not part of this fork because it serves a
|
mode, IPC integration and the chat replacement window itself work the
|
||||||
different use case from the smaller default footprint Hellion Chat
|
same. The webinterface is intentionally not part of Hellion Chat because
|
||||||
is built around.
|
it serves a different use case from the smaller default footprint this
|
||||||
|
plugin is built around.
|
||||||
|
|
||||||
On top of that, Hellion Chat adds privacy and data-handling controls
|
On top of that, Hellion Chat adds privacy and data-handling controls
|
||||||
designed to align with the modern data protection rules that apply
|
designed to align with the modern data protection rules that apply
|
||||||
@@ -18,7 +19,7 @@ description: |-
|
|||||||
channel, history can be wiped retroactively, and stored data can be
|
channel, history can be wiped retroactively, and stored data can be
|
||||||
exported on demand.
|
exported on demand.
|
||||||
|
|
||||||
Key additions on top of Chat 2:
|
Key privacy and data-handling features:
|
||||||
|
|
||||||
- Channel whitelist with a Privacy-First default
|
- Channel whitelist with a Privacy-First default
|
||||||
- Per-channel retention with a daily background sweep
|
- Per-channel retention with a daily background sweep
|
||||||
@@ -28,7 +29,7 @@ description: |-
|
|||||||
Full History)
|
Full History)
|
||||||
- Bilingual UI (English and German) with live language switching
|
- Bilingual UI (English and German) with live language switching
|
||||||
- Independent plugin state — own config file and database directory,
|
- Independent plugin state — own config file and database directory,
|
||||||
so Hellion Chat does not share state with the upstream plugin
|
so Hellion Chat does not share state with upstream Chat 2
|
||||||
|
|
||||||
Based on Chat 2 by Infi and Anna, licensed under EUPL-1.2.
|
Based on Chat 2 by Infi and Anna, licensed under EUPL-1.2.
|
||||||
|
|
||||||
@@ -37,10 +38,10 @@ description: |-
|
|||||||
other Hellion Online Media plugins/tools.
|
other Hellion Online Media plugins/tools.
|
||||||
repo_url: https://github.com/JonKazama-Hellion/HellionChat
|
repo_url: https://github.com/JonKazama-Hellion/HellionChat
|
||||||
accepts_feedback: true
|
accepts_feedback: true
|
||||||
icon_url: https://raw.githubusercontent.com/JonKazama-Hellion/HellionChat/main/ChatTwo/images/icon.png
|
icon_url: https://raw.githubusercontent.com/JonKazama-Hellion/HellionChat/main/HellionChat/images/icon.png
|
||||||
image_urls:
|
image_urls:
|
||||||
- https://raw.githubusercontent.com/JonKazama-Hellion/HellionChat/main/ChatTwo/images/chatWindow.png
|
- https://raw.githubusercontent.com/JonKazama-Hellion/HellionChat/main/HellionChat/images/chatWindow.png
|
||||||
- https://raw.githubusercontent.com/JonKazama-Hellion/HellionChat/main/ChatTwo/images/withSimpleTweaks.png
|
- https://raw.githubusercontent.com/JonKazama-Hellion/HellionChat/main/HellionChat/images/withSimpleTweaks.png
|
||||||
tags:
|
tags:
|
||||||
- Social
|
- Social
|
||||||
- UI
|
- UI
|
||||||
@@ -48,6 +49,105 @@ tags:
|
|||||||
- Replacement
|
- Replacement
|
||||||
- Privacy
|
- Privacy
|
||||||
changelog: |-
|
changelog: |-
|
||||||
|
**Hellion Chat 1.0.0 — Standalone Major Release**
|
||||||
|
|
||||||
|
First fully standalone release. Internal cleanup plus a sweep of
|
||||||
|
pre-existing correctness, security, threading and resource-leak
|
||||||
|
fixes carried over from the upstream codebase. No user action
|
||||||
|
required — auto-update applies cleanly, configuration and database
|
||||||
|
paths unchanged.
|
||||||
|
|
||||||
|
Standalone identity:
|
||||||
|
|
||||||
|
- Code namespace consolidated from ChatTwo.* to HellionChat.* across
|
||||||
|
all source files
|
||||||
|
- IPC channels migrated from ChatTwo.* to HellionChat.* (6 channels:
|
||||||
|
Register, Available, Unregister, Invoke, GetChatInputState,
|
||||||
|
ChatInputStateChanged) — third-party plugins that bound to the old
|
||||||
|
channels need to be updated; none known at release time
|
||||||
|
- ImGui popup ID renamed to hellionchat-context-popup
|
||||||
|
- Repository folder restructured (ChatTwo/ → HellionChat/), all CI
|
||||||
|
and build paths updated accordingly
|
||||||
|
- Public-facing descriptions reworded from upstream-fork framing to
|
||||||
|
standalone framing (Chat 2 attribution preserved per EUPL-1.2)
|
||||||
|
- Colour preset 'ChatTwo Default' is now 'Klassik (Chat 2 Default)'
|
||||||
|
|
||||||
|
Safety:
|
||||||
|
|
||||||
|
- Plugin now refuses to load when upstream Chat 2 is also active —
|
||||||
|
bilingual conflict message in EN/DE, throw before any subsystem
|
||||||
|
initialization, prevents the runtime crash that previously occurred
|
||||||
|
when both plugins replaced the same chat window in parallel
|
||||||
|
- SQLite native binary bumped to 3.50.3 (CVE-2025-6965 memory
|
||||||
|
corruption from aggregate-term overflow, CVE-2025-7709)
|
||||||
|
- NuGet restore now honors packages.lock.json so transitive
|
||||||
|
dependencies don't drift between machines or CI runs
|
||||||
|
|
||||||
|
Crash-class fixes (formerly latent in upstream):
|
||||||
|
|
||||||
|
- MathUtil.HasOverlap now uses a correct AABB test; identical or
|
||||||
|
edge-touching rectangles are no longer reported as non-overlapping
|
||||||
|
- ChatCode.Equals compares fields directly instead of GetHashCode;
|
||||||
|
removes the hash-collision anti-pattern
|
||||||
|
- IpcManager.Dispose uses UnregisterAction to match the matching
|
||||||
|
RegisterAction call; previous mismatch leaked the action
|
||||||
|
subscription on every plugin reload
|
||||||
|
- ExtraChat.Dispose now unsubscribes all three IPC subscriptions
|
||||||
|
(was only the first); leaks closed
|
||||||
|
- TellTarget.FromTarget guards against a zero IPlayerCharacter.Address
|
||||||
|
before dereferencing the unsafe Character* cast
|
||||||
|
- GameFunctions ResolveTextCommandPlaceholderDetour null-checks the
|
||||||
|
Hook reference instead of using the null-forgiving operator
|
||||||
|
- Popout.cs and SettingsTabs/Tabs.cs bounds-check list indexing so
|
||||||
|
a tab drop or empty-worlds list no longer crashes the UI
|
||||||
|
- Debugger.cs now declares IDisposable so the existing Dispose runs
|
||||||
|
|
||||||
|
Correctness fixes:
|
||||||
|
|
||||||
|
- GlobalParametersCache.GetValue captures Cache into a local before
|
||||||
|
the bounds check, so a concurrent Refresh can't slip a different
|
||||||
|
array between check and read
|
||||||
|
- IconUtil binary search bounds initialized to entries.Length-1 and
|
||||||
|
reset on redirect-restart; entries.Length==0 short-circuits
|
||||||
|
- Sheets.WorldsOnDatacenter now compares DataCenter.RowId (was
|
||||||
|
Region.RowId) so it actually returns same-DC worlds
|
||||||
|
- Message.cs back-reference loop iterates the processed Sender/Content
|
||||||
|
properties so chunks added by CheckMessageContent get Message set
|
||||||
|
- Language.zh-Hans Webinterface_Start_Success corrected to
|
||||||
|
"网页界面已启动" (was "网页界面已停止")
|
||||||
|
|
||||||
|
Threading and async:
|
||||||
|
|
||||||
|
- AutoTranslate Entries/ValidEntries are now serialized behind a
|
||||||
|
single lock; the preload worker thread and main thread no longer
|
||||||
|
race on the underlying dictionary/hash set
|
||||||
|
- Privacy retention and cleanup workers bound their framework-refresh
|
||||||
|
waits to 5 seconds with a logged timeout; a hung framework tick can
|
||||||
|
no longer deadlock the background worker
|
||||||
|
|
||||||
|
Resource handling:
|
||||||
|
|
||||||
|
- EmoteCache reuses the static HttpClient instead of allocating a new
|
||||||
|
one per call (closed socket leak)
|
||||||
|
- FontManager wraps HttpClient/HttpResponseMessage in using-blocks
|
||||||
|
and adds EnsureSuccessStatusCode; failed downloads no longer
|
||||||
|
silently produce a zero-byte font file
|
||||||
|
- SearchSelector mixes the row index into the ImGui ID stack so
|
||||||
|
selectables don't collapse to a single ambiguous ID
|
||||||
|
- SettingsTabs/Chat blocked-emote add-button now opens its selector
|
||||||
|
popup on left-click
|
||||||
|
|
||||||
|
Performance:
|
||||||
|
|
||||||
|
- DbViewer text export caches filteredHistory.Count once instead of
|
||||||
|
re-enumerating the IEnumerable on every batch (O(N) instead of
|
||||||
|
O(N²) on large histories)
|
||||||
|
|
||||||
|
License attribution (NOTICE.md, COPYRIGHT, THIRD_PARTY_NOTICES.md
|
||||||
|
and the Credits section in README) is unchanged.
|
||||||
|
|
||||||
|
Based on Chat 2 1.35.3 (upstream Infiziert90/ChatTwo, EUPL-1.2).
|
||||||
|
|
||||||
**Hellion Chat 0.6.1 — Pop-Out Discoverability & /tell Auto-Pop-Out**
|
**Hellion Chat 0.6.1 — Pop-Out Discoverability & /tell Auto-Pop-Out**
|
||||||
|
|
||||||
- Pop-out button now visible in the chat header (no more hunting
|
- Pop-out button now visible in the chat header (no more hunting
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace ChatTwo;
|
namespace HellionChat;
|
||||||
|
|
||||||
// Hellion Chat — v0.6.0 shared input history. Replaces the embedded
|
// Hellion Chat — v0.6.0 shared input history. Replaces the embedded
|
||||||
// ChatLogWindow.InputBacklog so that pop-out windows with their own
|
// ChatLogWindow.InputBacklog so that pop-out windows with their own
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
using Dalamud.Plugin.Ipc;
|
using Dalamud.Plugin.Ipc;
|
||||||
|
|
||||||
namespace ChatTwo.Ipc;
|
namespace HellionChat.Ipc;
|
||||||
|
|
||||||
public sealed class ExtraChat : IDisposable
|
public sealed class ExtraChat : IDisposable
|
||||||
{
|
{
|
||||||
@@ -49,6 +49,8 @@ public sealed class ExtraChat : IDisposable
|
|||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
OverrideChannelGate.Unsubscribe(OnOverrideChannel);
|
OverrideChannelGate.Unsubscribe(OnOverrideChannel);
|
||||||
|
ChannelCommandColoursGate.Unsubscribe(OnChannelCommandColours);
|
||||||
|
ChannelNamesGate.Unsubscribe(OnChannelNames);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnOverrideChannel(OverrideInfo info)
|
private void OnOverrideChannel(OverrideInfo info)
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
using ChatTwo.Code;
|
using HellionChat.Code;
|
||||||
using Dalamud.Plugin.Ipc;
|
using Dalamud.Plugin.Ipc;
|
||||||
|
|
||||||
namespace ChatTwo.Ipc;
|
namespace HellionChat.Ipc;
|
||||||
|
|
||||||
using ChatInputState = (bool InputVisible, bool InputFocused, bool HasText, bool IsTyping, int TextLength, ChatType ChannelType);
|
using ChatInputState = (bool InputVisible, bool InputFocused, bool HasText, bool IsTyping, int TextLength, ChatType ChannelType);
|
||||||
|
|
||||||
@@ -19,8 +19,8 @@ internal sealed class TypingIpc : IDisposable
|
|||||||
{
|
{
|
||||||
Plugin = plugin;
|
Plugin = plugin;
|
||||||
|
|
||||||
StateQueryGate = Plugin.Interface.GetIpcProvider<ChatInputState>("ChatTwo.GetChatInputState");
|
StateQueryGate = Plugin.Interface.GetIpcProvider<ChatInputState>("HellionChat.GetChatInputState");
|
||||||
StateChangedGate = Plugin.Interface.GetIpcProvider<ChatInputState, object?>("ChatTwo.ChatInputStateChanged");
|
StateChangedGate = Plugin.Interface.GetIpcProvider<ChatInputState, object?>("HellionChat.ChatInputStateChanged");
|
||||||
|
|
||||||
StateQueryGate.RegisterFunc(GetState);
|
StateQueryGate.RegisterFunc(GetState);
|
||||||
}
|
}
|
||||||
@@ -2,7 +2,7 @@ using Dalamud.Game.Text.SeStringHandling;
|
|||||||
using Dalamud.Game.Text.SeStringHandling.Payloads;
|
using Dalamud.Game.Text.SeStringHandling.Payloads;
|
||||||
using Dalamud.Plugin.Ipc;
|
using Dalamud.Plugin.Ipc;
|
||||||
|
|
||||||
namespace ChatTwo;
|
namespace HellionChat;
|
||||||
|
|
||||||
internal sealed class IpcManager : IDisposable
|
internal sealed class IpcManager : IDisposable
|
||||||
{
|
{
|
||||||
@@ -15,15 +15,15 @@ internal sealed class IpcManager : IDisposable
|
|||||||
|
|
||||||
public IpcManager()
|
public IpcManager()
|
||||||
{
|
{
|
||||||
RegisterGate = Plugin.Interface.GetIpcProvider<string>("ChatTwo.Register");
|
RegisterGate = Plugin.Interface.GetIpcProvider<string>("HellionChat.Register");
|
||||||
RegisterGate.RegisterFunc(Register);
|
RegisterGate.RegisterFunc(Register);
|
||||||
|
|
||||||
AvailableGate = Plugin.Interface.GetIpcProvider<object?>("ChatTwo.Available");
|
AvailableGate = Plugin.Interface.GetIpcProvider<object?>("HellionChat.Available");
|
||||||
|
|
||||||
UnregisterGate = Plugin.Interface.GetIpcProvider<string, object?>("ChatTwo.Unregister");
|
UnregisterGate = Plugin.Interface.GetIpcProvider<string, object?>("HellionChat.Unregister");
|
||||||
UnregisterGate.RegisterAction(Unregister);
|
UnregisterGate.RegisterAction(Unregister);
|
||||||
|
|
||||||
InvokeGate = Plugin.Interface.GetIpcProvider<string, PlayerPayload?, ulong, Payload?, SeString?, SeString?, object?>("ChatTwo.Invoke");
|
InvokeGate = Plugin.Interface.GetIpcProvider<string, PlayerPayload?, ulong, Payload?, SeString?, SeString?, object?>("HellionChat.Invoke");
|
||||||
|
|
||||||
AvailableGate.SendMessage();
|
AvailableGate.SendMessage();
|
||||||
}
|
}
|
||||||
@@ -47,7 +47,7 @@ internal sealed class IpcManager : IDisposable
|
|||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
UnregisterGate.UnregisterFunc();
|
UnregisterGate.UnregisterAction();
|
||||||
RegisterGate.UnregisterFunc();
|
RegisterGate.UnregisterFunc();
|
||||||
Registered.Clear();
|
Registered.Clear();
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using ChatTwo.Code;
|
using HellionChat.Code;
|
||||||
using ChatTwo.Util;
|
using HellionChat.Util;
|
||||||
using Dalamud.Game.Text.SeStringHandling;
|
using Dalamud.Game.Text.SeStringHandling;
|
||||||
using Dalamud.Game.Text.SeStringHandling.Payloads;
|
using Dalamud.Game.Text.SeStringHandling.Payloads;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
@@ -8,7 +8,7 @@ using Dalamud.Game.Text;
|
|||||||
using Dalamud.Utility;
|
using Dalamud.Utility;
|
||||||
using FFXIVClientStructs.FFXIV.Client.UI.Agent;
|
using FFXIVClientStructs.FFXIV.Client.UI.Agent;
|
||||||
|
|
||||||
namespace ChatTwo;
|
namespace HellionChat;
|
||||||
|
|
||||||
public partial class Message
|
public partial class Message
|
||||||
{
|
{
|
||||||
@@ -49,7 +49,10 @@ public partial class Message
|
|||||||
ExtraChatChannel = extraChatChannel;
|
ExtraChatChannel = extraChatChannel;
|
||||||
Hash = GenerateHash();
|
Hash = GenerateHash();
|
||||||
|
|
||||||
foreach (var chunk in sender.Concat(content))
|
// Iterate the processed Content list (returned by CheckMessageContent)
|
||||||
|
// rather than the raw constructor parameter — chunks added or replaced
|
||||||
|
// by CheckMessageContent must also have their back-reference set.
|
||||||
|
foreach (var chunk in Sender.Concat(Content))
|
||||||
chunk.Message = this;
|
chunk.Message = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using ChatTwo.Code;
|
using HellionChat.Code;
|
||||||
using ChatTwo.Resources;
|
using HellionChat.Resources;
|
||||||
using ChatTwo.Util;
|
using HellionChat.Util;
|
||||||
using Dalamud.Game.Chat;
|
using Dalamud.Game.Chat;
|
||||||
using Dalamud.Game.Text;
|
using Dalamud.Game.Text;
|
||||||
using Dalamud.Game.Text.SeStringHandling;
|
using Dalamud.Game.Text.SeStringHandling;
|
||||||
@@ -15,7 +15,7 @@ using Lumina.Text.Expressions;
|
|||||||
using Lumina.Text.Payloads;
|
using Lumina.Text.Payloads;
|
||||||
using Lumina.Text.ReadOnly;
|
using Lumina.Text.ReadOnly;
|
||||||
|
|
||||||
namespace ChatTwo;
|
namespace HellionChat;
|
||||||
|
|
||||||
internal class MessageManager : IAsyncDisposable
|
internal class MessageManager : IAsyncDisposable
|
||||||
{
|
{
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
using System.Buffers;
|
using System.Buffers;
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Data.Common;
|
using System.Data.Common;
|
||||||
using ChatTwo.Code;
|
using HellionChat.Code;
|
||||||
using ChatTwo.Ui;
|
using HellionChat.Ui;
|
||||||
using ChatTwo.Util;
|
using HellionChat.Util;
|
||||||
using Dalamud.Game.Text.SeStringHandling;
|
using Dalamud.Game.Text.SeStringHandling;
|
||||||
using MessagePack;
|
using MessagePack;
|
||||||
using MessagePack.Formatters;
|
using MessagePack.Formatters;
|
||||||
@@ -13,7 +13,7 @@ using Microsoft.Data.Sqlite;
|
|||||||
using DalamudUtil = Dalamud.Utility.Util;
|
using DalamudUtil = Dalamud.Utility.Util;
|
||||||
using Encoding = System.Text.Encoding;
|
using Encoding = System.Text.Encoding;
|
||||||
|
|
||||||
namespace ChatTwo;
|
namespace HellionChat;
|
||||||
|
|
||||||
internal static class DbExtensions
|
internal static class DbExtensions
|
||||||
{
|
{
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using ChatTwo.Code;
|
using HellionChat.Code;
|
||||||
using ChatTwo.Resources;
|
using HellionChat.Resources;
|
||||||
using ChatTwo.Ui;
|
using HellionChat.Ui;
|
||||||
using ChatTwo.Util;
|
using HellionChat.Util;
|
||||||
using Dalamud.Game.Addon.Lifecycle;
|
using Dalamud.Game.Addon.Lifecycle;
|
||||||
using Dalamud.Game.Addon.Lifecycle.AddonArgTypes;
|
using Dalamud.Game.Addon.Lifecycle.AddonArgTypes;
|
||||||
using Dalamud.Game.ClientState.Objects.SubKinds;
|
using Dalamud.Game.ClientState.Objects.SubKinds;
|
||||||
@@ -22,13 +22,13 @@ using Dalamud.Bindings.ImGui;
|
|||||||
using Lumina.Excel.Sheets;
|
using Lumina.Excel.Sheets;
|
||||||
using Action = System.Action;
|
using Action = System.Action;
|
||||||
using DalamudPartyFinderPayload = Dalamud.Game.Text.SeStringHandling.Payloads.PartyFinderPayload;
|
using DalamudPartyFinderPayload = Dalamud.Game.Text.SeStringHandling.Payloads.PartyFinderPayload;
|
||||||
using ChatTwoPartyFinderPayload = ChatTwo.Util.PartyFinderPayload;
|
using ChatTwoPartyFinderPayload = HellionChat.Util.PartyFinderPayload;
|
||||||
|
|
||||||
namespace ChatTwo;
|
namespace HellionChat;
|
||||||
|
|
||||||
public sealed class PayloadHandler
|
public sealed class PayloadHandler
|
||||||
{
|
{
|
||||||
private const string PopupId = "chat2-context-popup";
|
private const string PopupId = "hellionchat-context-popup";
|
||||||
|
|
||||||
private ChatLogWindow LogWindow { get; }
|
private ChatLogWindow LogWindow { get; }
|
||||||
private (Chunk, Payload?)? Popup { get; set; }
|
private (Chunk, Payload?)? Popup { get; set; }
|
||||||
@@ -2,10 +2,10 @@ using System.Diagnostics;
|
|||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using ChatTwo.Ipc;
|
using HellionChat.Ipc;
|
||||||
using ChatTwo.Resources;
|
using HellionChat.Resources;
|
||||||
using ChatTwo.Ui;
|
using HellionChat.Ui;
|
||||||
using ChatTwo.Util;
|
using HellionChat.Util;
|
||||||
using Dalamud.Game.ClientState.Conditions;
|
using Dalamud.Game.ClientState.Conditions;
|
||||||
using Dalamud.Interface.Windowing;
|
using Dalamud.Interface.Windowing;
|
||||||
using Dalamud.IoC;
|
using Dalamud.IoC;
|
||||||
@@ -14,7 +14,7 @@ using Dalamud.Plugin.Services;
|
|||||||
using Dalamud.Bindings.ImGui;
|
using Dalamud.Bindings.ImGui;
|
||||||
using Dalamud.Interface.ImGuiFileDialog;
|
using Dalamud.Interface.ImGuiFileDialog;
|
||||||
|
|
||||||
namespace ChatTwo;
|
namespace HellionChat;
|
||||||
|
|
||||||
// ReSharper disable once ClassNeverInstantiated.Global
|
// ReSharper disable once ClassNeverInstantiated.Global
|
||||||
public sealed class Plugin : IDalamudPlugin
|
public sealed class Plugin : IDalamudPlugin
|
||||||
@@ -94,6 +94,12 @@ public sealed class Plugin : IDalamudPlugin
|
|||||||
|
|
||||||
public Plugin()
|
public Plugin()
|
||||||
{
|
{
|
||||||
|
// Refuse to start if upstream Chat 2 is loaded — prevents IPC
|
||||||
|
// channel collisions and double-replacement of the in-game chat
|
||||||
|
// window. Throwing here makes Dalamud abort the load cleanly with
|
||||||
|
// our localized message instead of crashing FFXIV mid-frame.
|
||||||
|
ChatTwoConflictDetector.ThrowIfChatTwoIsLoaded(Interface);
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
GameStarted = Process.GetCurrentProcess().StartTime.ToUniversalTime();
|
GameStarted = Process.GetCurrentProcess().StartTime.ToUniversalTime();
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
using ChatTwo.Code;
|
using HellionChat.Code;
|
||||||
|
|
||||||
namespace ChatTwo.Privacy;
|
namespace HellionChat.Privacy;
|
||||||
|
|
||||||
internal static class PrivacyDefaults
|
internal static class PrivacyDefaults
|
||||||
{
|
{
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using ChatTwo.Code;
|
using HellionChat.Code;
|
||||||
using ChatTwo.Util;
|
using HellionChat.Util;
|
||||||
|
|
||||||
namespace ChatTwo.Resources;
|
namespace HellionChat.Resources;
|
||||||
|
|
||||||
// Hellion Chat — v0.6.0 built-in colour presets for the ChatColours
|
// Hellion Chat — v0.6.0 built-in colour presets for the ChatColours
|
||||||
// settings section. Read-only static data; users apply a preset via the
|
// settings section. Read-only static data; users apply a preset via the
|
||||||
Generated
+7
-2
@@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
#nullable enable
|
#nullable enable
|
||||||
|
|
||||||
namespace ChatTwo.Resources;
|
namespace HellionChat.Resources;
|
||||||
|
|
||||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
|
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
|
||||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute]
|
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute]
|
||||||
@@ -26,7 +26,7 @@ internal class HellionStrings
|
|||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (resourceMan is null)
|
if (resourceMan is null)
|
||||||
resourceMan = new global::System.Resources.ResourceManager("ChatTwo.Resources.HellionStrings", typeof(HellionStrings).Assembly);
|
resourceMan = new global::System.Resources.ResourceManager("HellionChat.Resources.HellionStrings", typeof(HellionStrings).Assembly);
|
||||||
return resourceMan;
|
return resourceMan;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -270,4 +270,9 @@ internal class HellionStrings
|
|||||||
internal static string Hint_v061_PopOutHeader_Body => Get(nameof(Hint_v061_PopOutHeader_Body));
|
internal static string Hint_v061_PopOutHeader_Body => Get(nameof(Hint_v061_PopOutHeader_Body));
|
||||||
internal static string Hint_v061_PopOutHeader_Ack => Get(nameof(Hint_v061_PopOutHeader_Ack));
|
internal static string Hint_v061_PopOutHeader_Ack => Get(nameof(Hint_v061_PopOutHeader_Ack));
|
||||||
internal static string Hint_v061_PopOutHeader_OpenSettings => Get(nameof(Hint_v061_PopOutHeader_OpenSettings));
|
internal static string Hint_v061_PopOutHeader_OpenSettings => Get(nameof(Hint_v061_PopOutHeader_OpenSettings));
|
||||||
|
|
||||||
|
// Hellion Chat — v1.0.0 Chat 2 parallel-load conflict detection
|
||||||
|
internal static string ChatTwoConflictTitle => Get(nameof(ChatTwoConflictTitle));
|
||||||
|
internal static string ChatTwoConflictBody => Get(nameof(ChatTwoConflictBody));
|
||||||
|
internal static string ChatTwoConflictAction => Get(nameof(ChatTwoConflictAction));
|
||||||
}
|
}
|
||||||
+12
-3
@@ -19,7 +19,7 @@
|
|||||||
<value>Datenschutz-Filter aktivieren</value>
|
<value>Datenschutz-Filter aktivieren</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Privacy_FilterEnabled_Description" xml:space="preserve">
|
<data name="Privacy_FilterEnabled_Description" xml:space="preserve">
|
||||||
<value>Wenn aktiviert, werden nur Nachrichten aus den erlaubten Kanälen in die Datenbank gespeichert. Beim Deaktivieren gilt wieder das Standard-Verhalten von ChatTwo, also alles außer Battle-Logs wird gespeichert.</value>
|
<value>Wenn aktiviert, werden nur Nachrichten aus den erlaubten Kanälen in die Datenbank gespeichert. Beim Deaktivieren gilt wieder das Standardverhalten, also alles außer Battle-Logs wird gespeichert.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Privacy_FilterEnabled_StorageOnly_Help" xml:space="preserve">
|
<data name="Privacy_FilterEnabled_StorageOnly_Help" xml:space="preserve">
|
||||||
<value>Der Filter steuert nur, was in die lokale Datenbank geschrieben wird. Im Chat-Log siehst du weiterhin jede Nachricht live, ausgeschlossene Kanäle werden nur nicht mehr gespeichert. Wenn du Kanäle auch aus der sichtbaren Anzeige entfernen willst, nutze die normalen Chat-Tab-Filter im Spiel.</value>
|
<value>Der Filter steuert nur, was in die lokale Datenbank geschrieben wird. Im Chat-Log siehst du weiterhin jede Nachricht live, ausgeschlossene Kanäle werden nur nicht mehr gespeichert. Wenn du Kanäle auch aus der sichtbaren Anzeige entfernen willst, nutze die normalen Chat-Tab-Filter im Spiel.</value>
|
||||||
@@ -211,7 +211,7 @@
|
|||||||
<value>Volle Historie</value>
|
<value>Volle Historie</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Wizard_Profile_FullHistory_Description" xml:space="preserve">
|
<data name="Wizard_Profile_FullHistory_Description" xml:space="preserve">
|
||||||
<value>Deaktiviert den Datenschutz-Filter komplett. Speichert alles außer Battle-Logs, wie das Original-Chat 2. Aufbewahrung ist AUS, die Historie wächst dauerhaft.</value>
|
<value>Deaktiviert den Datenschutz-Filter komplett. Speichert alles außer Battle-Logs (das ursprüngliche Voll-Historie-Verhalten). Aufbewahrung ist AUS, die Historie wächst dauerhaft.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Wizard_Profile_FullHistory_GdprWarning" xml:space="preserve">
|
<data name="Wizard_Profile_FullHistory_GdprWarning" xml:space="preserve">
|
||||||
<value>DSGVO-Hinweis: Wenn du Nachrichten Dritter (Sagen/Schreien/Rufen fremder Spieler, NPC-Dialoge mit Spielernamen usw.) zeitlich unbegrenzt speicherst, kann das die Ausnahme für rein persönliche oder familiäre Tätigkeiten (Art. 2 Abs. 2 Buchst. c) sprengen. Nutze dieses Profil nur, wenn du einen klaren Grund hast, das volle Archiv zu behalten.</value>
|
<value>DSGVO-Hinweis: Wenn du Nachrichten Dritter (Sagen/Schreien/Rufen fremder Spieler, NPC-Dialoge mit Spielernamen usw.) zeitlich unbegrenzt speicherst, kann das die Ausnahme für rein persönliche oder familiäre Tätigkeiten (Art. 2 Abs. 2 Buchst. c) sprengen. Nutze dieses Profil nur, wenn du einen klaren Grund hast, das volle Archiv zu behalten.</value>
|
||||||
@@ -562,7 +562,7 @@
|
|||||||
<value>Wenn du mehrere Linkshells benutzt, empfiehlt der Maintainer einen Tab pro Shell für eine sauberere Übersicht. Tab duplizieren und je Kopie die Kanalauswahl einschränken.</value>
|
<value>Wenn du mehrere Linkshells benutzt, empfiehlt der Maintainer einen Tab pro Shell für eine sauberere Übersicht. Tab duplizieren und je Kopie die Kanalauswahl einschränken.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ChatColourPresets_Default" xml:space="preserve">
|
<data name="ChatColourPresets_Default" xml:space="preserve">
|
||||||
<value>ChatTwo Standard</value>
|
<value>Klassik (Chat 2 Default)</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ChatColourPresets_HighContrast" xml:space="preserve">
|
<data name="ChatColourPresets_HighContrast" xml:space="preserve">
|
||||||
<value>Hoher Kontrast</value>
|
<value>Hoher Kontrast</value>
|
||||||
@@ -609,4 +609,13 @@
|
|||||||
<data name="Hint_v061_PopOutHeader_OpenSettings" xml:space="preserve">
|
<data name="Hint_v061_PopOutHeader_OpenSettings" xml:space="preserve">
|
||||||
<value>Einstellungen öffnen</value>
|
<value>Einstellungen öffnen</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="ChatTwoConflictTitle" xml:space="preserve">
|
||||||
|
<value>Hellion Chat kann nicht starten, solange Chat 2 geladen ist.</value>
|
||||||
|
</data>
|
||||||
|
<data name="ChatTwoConflictBody" xml:space="preserve">
|
||||||
|
<value>Hellion Chat ist ein eigenständiger Fork von Chat 2. Beide Plugins ersetzen dasselbe Chat-Fenster im Spiel und würden zur Laufzeit kollidieren.</value>
|
||||||
|
</data>
|
||||||
|
<data name="ChatTwoConflictAction" xml:space="preserve">
|
||||||
|
<value>Chat 2 in /xlplugins deaktivieren, danach Hellion Chat erneut aktivieren.</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
@@ -19,7 +19,7 @@
|
|||||||
<value>Enable privacy filter</value>
|
<value>Enable privacy filter</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Privacy_FilterEnabled_Description" xml:space="preserve">
|
<data name="Privacy_FilterEnabled_Description" xml:space="preserve">
|
||||||
<value>When enabled, only messages from whitelisted channels are persisted to the database. Disabling restores upstream ChatTwo behavior (everything except battle messages is stored).</value>
|
<value>When enabled, only messages from whitelisted channels are persisted to the database. Disabling restores the original behavior (everything except battle messages is stored).</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Privacy_FilterEnabled_StorageOnly_Help" xml:space="preserve">
|
<data name="Privacy_FilterEnabled_StorageOnly_Help" xml:space="preserve">
|
||||||
<value>The filter only controls what is written to the local database. The chat log itself keeps showing every message live, disallowed channels just stop being saved. Use the channel hide options in your in-game chat tabs if you want to remove channels from the visible chat.</value>
|
<value>The filter only controls what is written to the local database. The chat log itself keeps showing every message live, disallowed channels just stop being saved. Use the channel hide options in your in-game chat tabs if you want to remove channels from the visible chat.</value>
|
||||||
@@ -211,7 +211,7 @@
|
|||||||
<value>Full History</value>
|
<value>Full History</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Wizard_Profile_FullHistory_Description" xml:space="preserve">
|
<data name="Wizard_Profile_FullHistory_Description" xml:space="preserve">
|
||||||
<value>Disables the privacy filter entirely. Stores everything except battle logs, just like upstream Chat 2. Retention is OFF, history grows forever.</value>
|
<value>Disables the privacy filter entirely. Stores everything except battle logs (the original full-history behavior). Retention is OFF, history grows forever.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Wizard_Profile_FullHistory_GdprWarning" xml:space="preserve">
|
<data name="Wizard_Profile_FullHistory_GdprWarning" xml:space="preserve">
|
||||||
<value>GDPR notice: storing third-party messages (Say/Shout/Yell of strangers, NPC dialogue with player names, etc.) for an unlimited time may exceed the personal/household exemption (Art. 2(2)(c)). Use this profile only if you have a clear reason to keep the full archive.</value>
|
<value>GDPR notice: storing third-party messages (Say/Shout/Yell of strangers, NPC dialogue with player names, etc.) for an unlimited time may exceed the personal/household exemption (Art. 2(2)(c)). Use this profile only if you have a clear reason to keep the full archive.</value>
|
||||||
@@ -562,7 +562,7 @@
|
|||||||
<value>If you use multiple linkshells, the maintainer recommends one tab per shell for cleaner readability. Duplicate this tab and narrow the channel selection per copy.</value>
|
<value>If you use multiple linkshells, the maintainer recommends one tab per shell for cleaner readability. Duplicate this tab and narrow the channel selection per copy.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ChatColourPresets_Default" xml:space="preserve">
|
<data name="ChatColourPresets_Default" xml:space="preserve">
|
||||||
<value>ChatTwo Default</value>
|
<value>Klassik (Chat 2 Default)</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ChatColourPresets_HighContrast" xml:space="preserve">
|
<data name="ChatColourPresets_HighContrast" xml:space="preserve">
|
||||||
<value>High-Contrast</value>
|
<value>High-Contrast</value>
|
||||||
@@ -609,4 +609,13 @@
|
|||||||
<data name="Hint_v061_PopOutHeader_OpenSettings" xml:space="preserve">
|
<data name="Hint_v061_PopOutHeader_OpenSettings" xml:space="preserve">
|
||||||
<value>Open Settings</value>
|
<value>Open Settings</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="ChatTwoConflictTitle" xml:space="preserve">
|
||||||
|
<value>Hellion Chat cannot start while Chat 2 is loaded.</value>
|
||||||
|
</data>
|
||||||
|
<data name="ChatTwoConflictBody" xml:space="preserve">
|
||||||
|
<value>Hellion Chat is a standalone fork of Chat 2. Both plugins replace the same in-game chat window and would conflict at runtime if loaded together.</value>
|
||||||
|
</data>
|
||||||
|
<data name="ChatTwoConflictAction" xml:space="preserve">
|
||||||
|
<value>Disable Chat 2 in /xlplugins, then re-enable Hellion Chat.</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
+2
-2
@@ -7,7 +7,7 @@
|
|||||||
// </auto-generated>
|
// </auto-generated>
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
namespace ChatTwo.Resources {
|
namespace HellionChat.Resources {
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
|
|
||||||
@@ -38,7 +38,7 @@ namespace ChatTwo.Resources {
|
|||||||
internal static global::System.Resources.ResourceManager ResourceManager {
|
internal static global::System.Resources.ResourceManager ResourceManager {
|
||||||
get {
|
get {
|
||||||
if (object.ReferenceEquals(resourceMan, null)) {
|
if (object.ReferenceEquals(resourceMan, null)) {
|
||||||
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("ChatTwo.Resources.Language", typeof(Language).Assembly);
|
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("HellionChat.Resources.Language", typeof(Language).Assembly);
|
||||||
resourceMan = temp;
|
resourceMan = temp;
|
||||||
}
|
}
|
||||||
return resourceMan;
|
return resourceMan;
|
||||||
+1
-1
@@ -1364,7 +1364,7 @@
|
|||||||
<value>启动</value>
|
<value>启动</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Webinterface_Start_Success" xml:space="preserve">
|
<data name="Webinterface_Start_Success" xml:space="preserve">
|
||||||
<value>网页界面已停止。</value>
|
<value>网页界面已启动。</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="Webinterface_Start_Failed" xml:space="preserve">
|
<data name="Webinterface_Start_Failed" xml:space="preserve">
|
||||||
<value>网页界面启动失败。检查 /xllog 获取更多信息。</value>
|
<value>网页界面启动失败。检查 /xllog 获取更多信息。</value>
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
using Lumina.Excel;
|
using Lumina.Excel;
|
||||||
using Lumina.Excel.Sheets;
|
using Lumina.Excel.Sheets;
|
||||||
|
|
||||||
namespace ChatTwo;
|
namespace HellionChat;
|
||||||
|
|
||||||
public static class Sheets
|
public static class Sheets
|
||||||
{
|
{
|
||||||
@@ -37,7 +37,7 @@ public static class Sheets
|
|||||||
|
|
||||||
public static IEnumerable<World> WorldsOnDatacenter(IPlayerCharacter character)
|
public static IEnumerable<World> WorldsOnDatacenter(IPlayerCharacter character)
|
||||||
{
|
{
|
||||||
var dcRow = character.HomeWorld.Value.DataCenter.Value.Region.RowId;
|
var dcRow = character.HomeWorld.Value.DataCenter.RowId;
|
||||||
return WorldSheet.Where(world => world.IsPublic && world.DataCenter.Value.Region.RowId == dcRow);
|
return WorldSheet.Where(world => world.IsPublic && world.DataCenter.RowId == dcRow);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace ChatTwo.Ui;
|
namespace HellionChat.Ui;
|
||||||
|
|
||||||
internal class AutoCompleteInfo
|
internal class AutoCompleteInfo
|
||||||
{
|
{
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using ChatTwo.Code;
|
using HellionChat.Code;
|
||||||
using ChatTwo.Util;
|
using HellionChat.Util;
|
||||||
using Dalamud.Bindings.ImGui;
|
using Dalamud.Bindings.ImGui;
|
||||||
using Dalamud.Interface.Utility.Raii;
|
using Dalamud.Interface.Utility.Raii;
|
||||||
|
|
||||||
namespace ChatTwo.Ui;
|
namespace HellionChat.Ui;
|
||||||
|
|
||||||
// Hellion Chat — v0.6.0 input bar component for pop-out windows.
|
// Hellion Chat — v0.6.0 input bar component for pop-out windows.
|
||||||
//
|
//
|
||||||
@@ -3,11 +3,11 @@ using System.Globalization;
|
|||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using ChatTwo.Code;
|
using HellionChat.Code;
|
||||||
using ChatTwo.GameFunctions;
|
using HellionChat.GameFunctions;
|
||||||
using ChatTwo.GameFunctions.Types;
|
using HellionChat.GameFunctions.Types;
|
||||||
using ChatTwo.Resources;
|
using HellionChat.Resources;
|
||||||
using ChatTwo.Util;
|
using HellionChat.Util;
|
||||||
using Dalamud.Game.Addon.Lifecycle;
|
using Dalamud.Game.Addon.Lifecycle;
|
||||||
using Dalamud.Game.Text.SeStringHandling;
|
using Dalamud.Game.Text.SeStringHandling;
|
||||||
using Dalamud.Game.Text.SeStringHandling.Payloads;
|
using Dalamud.Game.Text.SeStringHandling.Payloads;
|
||||||
@@ -22,7 +22,7 @@ using Dalamud.Bindings.ImGui;
|
|||||||
using Lumina.Excel.Sheets;
|
using Lumina.Excel.Sheets;
|
||||||
using Lumina.Extensions;
|
using Lumina.Extensions;
|
||||||
|
|
||||||
namespace ChatTwo.Ui;
|
namespace HellionChat.Ui;
|
||||||
|
|
||||||
public sealed class ChatLogWindow : Window
|
public sealed class ChatLogWindow : Window
|
||||||
{
|
{
|
||||||
@@ -1,12 +1,12 @@
|
|||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using ChatTwo.Util;
|
using HellionChat.Util;
|
||||||
using Dalamud.Interface.Utility;
|
using Dalamud.Interface.Utility;
|
||||||
using Dalamud.Interface.Windowing;
|
using Dalamud.Interface.Windowing;
|
||||||
using Dalamud.Utility;
|
using Dalamud.Utility;
|
||||||
using Dalamud.Bindings.ImGui;
|
using Dalamud.Bindings.ImGui;
|
||||||
using Lumina.Text.ReadOnly;
|
using Lumina.Text.ReadOnly;
|
||||||
|
|
||||||
namespace ChatTwo.Ui;
|
namespace HellionChat.Ui;
|
||||||
|
|
||||||
public class CommandHelpWindow : Window {
|
public class CommandHelpWindow : Window {
|
||||||
private ChatLogWindow LogWindow { get; }
|
private ChatLogWindow LogWindow { get; }
|
||||||
@@ -2,9 +2,9 @@
|
|||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using ChatTwo.Code;
|
using HellionChat.Code;
|
||||||
using ChatTwo.Resources;
|
using HellionChat.Resources;
|
||||||
using ChatTwo.Util;
|
using HellionChat.Util;
|
||||||
using Dalamud.Interface;
|
using Dalamud.Interface;
|
||||||
using Dalamud.Interface.Colors;
|
using Dalamud.Interface.Colors;
|
||||||
using Dalamud.Interface.Utility;
|
using Dalamud.Interface.Utility;
|
||||||
@@ -18,7 +18,7 @@ using Lumina.Data.Files;
|
|||||||
using Lumina.Text.ReadOnly;
|
using Lumina.Text.ReadOnly;
|
||||||
using MoreLinq;
|
using MoreLinq;
|
||||||
|
|
||||||
namespace ChatTwo.Ui;
|
namespace HellionChat.Ui;
|
||||||
|
|
||||||
public class DbViewer : Window
|
public class DbViewer : Window
|
||||||
{
|
{
|
||||||
@@ -391,6 +391,10 @@ public class DbViewer : Window
|
|||||||
await rangeMessageEnumerator.DisposeAsync();
|
await rangeMessageEnumerator.DisposeAsync();
|
||||||
|
|
||||||
var filteredHistory = Filter(messageHistory);
|
var filteredHistory = Filter(messageHistory);
|
||||||
|
// Materialize Count once — re-enumerating the IEnumerable on
|
||||||
|
// every batch (twice per batch in the Notification update)
|
||||||
|
// turned the export into an O(N²) hot loop on large histories.
|
||||||
|
var totalCount = filteredHistory.Count;
|
||||||
|
|
||||||
var sb = new StringBuilder();
|
var sb = new StringBuilder();
|
||||||
await using var stream = new StreamWriter(Path.Join(InputPath, $"Chat2_{DateTime.Now:yyyy_dd_M__HH_mm_ss}.txt"));
|
await using var stream = new StreamWriter(Path.Join(InputPath, $"Chat2_{DateTime.Now:yyyy_dd_M__HH_mm_ss}.txt"));
|
||||||
@@ -416,8 +420,8 @@ public class DbViewer : Window
|
|||||||
}
|
}
|
||||||
}, delayTicks: 5);
|
}, delayTicks: 5);
|
||||||
|
|
||||||
Notification.Progress = (float)batch / filteredHistory.Count;
|
Notification.Progress = (float)batch / totalCount;
|
||||||
Notification.Content = $"Exported {batch} of {filteredHistory.Count} messages";
|
Notification.Content = $"Exported {batch} of {totalCount} messages";
|
||||||
await stream.WriteAsync(sb.ToString());
|
await stream.WriteAsync(sb.ToString());
|
||||||
sb.Clear();
|
sb.Clear();
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using ChatTwo.Code;
|
using HellionChat.Code;
|
||||||
using Dalamud.Interface.Colors;
|
using Dalamud.Interface.Colors;
|
||||||
using Dalamud.Interface.Utility;
|
using Dalamud.Interface.Utility;
|
||||||
using Dalamud.Interface.Windowing;
|
using Dalamud.Interface.Windowing;
|
||||||
@@ -7,9 +7,9 @@ using FFXIVClientStructs.FFXIV.Client.UI.Agent;
|
|||||||
using Dalamud.Bindings.ImGui;
|
using Dalamud.Bindings.ImGui;
|
||||||
using Lumina.Text.ReadOnly;
|
using Lumina.Text.ReadOnly;
|
||||||
|
|
||||||
namespace ChatTwo.Ui;
|
namespace HellionChat.Ui;
|
||||||
|
|
||||||
public class DebuggerWindow : Window
|
public class DebuggerWindow : Window, IDisposable
|
||||||
{
|
{
|
||||||
private readonly Plugin Plugin;
|
private readonly Plugin Plugin;
|
||||||
private readonly ChatLogWindow ChatLogWindow;
|
private readonly ChatLogWindow ChatLogWindow;
|
||||||
@@ -1,13 +1,13 @@
|
|||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using ChatTwo.Code;
|
using HellionChat.Code;
|
||||||
using ChatTwo.Privacy;
|
using HellionChat.Privacy;
|
||||||
using ChatTwo.Resources;
|
using HellionChat.Resources;
|
||||||
using ChatTwo.Util;
|
using HellionChat.Util;
|
||||||
using Dalamud.Interface.Utility.Raii;
|
using Dalamud.Interface.Utility.Raii;
|
||||||
using Dalamud.Interface.Windowing;
|
using Dalamud.Interface.Windowing;
|
||||||
using Dalamud.Bindings.ImGui;
|
using Dalamud.Bindings.ImGui;
|
||||||
|
|
||||||
namespace ChatTwo.Ui;
|
namespace HellionChat.Ui;
|
||||||
|
|
||||||
public sealed class FirstRunWizard : Window
|
public sealed class FirstRunWizard : Window
|
||||||
{
|
{
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
using ChatTwo.Util;
|
using HellionChat.Util;
|
||||||
using Dalamud.Bindings.ImGui;
|
using Dalamud.Bindings.ImGui;
|
||||||
using Dalamud.Interface.Utility.Raii;
|
using Dalamud.Interface.Utility.Raii;
|
||||||
|
|
||||||
namespace ChatTwo.Ui;
|
namespace HellionChat.Ui;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// ImGui style override for Hellion Chat. Industrial HUD palette with three
|
/// ImGui style override for Hellion Chat. Industrial HUD palette with three
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using ChatTwo.Code;
|
using HellionChat.Code;
|
||||||
using ChatTwo.Resources;
|
using HellionChat.Resources;
|
||||||
using ChatTwo.Util;
|
using HellionChat.Util;
|
||||||
using Dalamud.Game.Text;
|
using Dalamud.Game.Text;
|
||||||
using Dalamud.Game.Text.SeStringHandling;
|
using Dalamud.Game.Text.SeStringHandling;
|
||||||
using Dalamud.Game.Text.SeStringHandling.Payloads;
|
using Dalamud.Game.Text.SeStringHandling.Payloads;
|
||||||
@@ -12,7 +12,7 @@ using Dalamud.Interface.Windowing;
|
|||||||
using Dalamud.Plugin.Services;
|
using Dalamud.Plugin.Services;
|
||||||
using Dalamud.Bindings.ImGui;
|
using Dalamud.Bindings.ImGui;
|
||||||
|
|
||||||
namespace ChatTwo.Ui;
|
namespace HellionChat.Ui;
|
||||||
|
|
||||||
public partial class InputPreview : Window
|
public partial class InputPreview : Window
|
||||||
{
|
{
|
||||||
@@ -4,7 +4,7 @@ using Dalamud.Interface.Utility.Raii;
|
|||||||
using Dalamud.Interface.Windowing;
|
using Dalamud.Interface.Windowing;
|
||||||
using Dalamud.Bindings.ImGui;
|
using Dalamud.Bindings.ImGui;
|
||||||
|
|
||||||
namespace ChatTwo.Ui;
|
namespace HellionChat.Ui;
|
||||||
|
|
||||||
internal class Popout : Window
|
internal class Popout : Window
|
||||||
{
|
{
|
||||||
@@ -80,7 +80,10 @@ internal class Popout : Window
|
|||||||
if (!Tab.CanResize)
|
if (!Tab.CanResize)
|
||||||
Flags |= ImGuiWindowFlags.NoResize;
|
Flags |= ImGuiWindowFlags.NoResize;
|
||||||
|
|
||||||
if (!ChatLogWindow.PopOutDocked[Idx])
|
// Idx may point past the end if PopOutDocked was resized (e.g., a tab
|
||||||
|
// dropped) between the AddPopOutsToDraw() snapshot and this frame.
|
||||||
|
// Guard the read so we don't index into stale state.
|
||||||
|
if (Idx >= 0 && Idx < ChatLogWindow.PopOutDocked.Count && !ChatLogWindow.PopOutDocked[Idx])
|
||||||
{
|
{
|
||||||
if (Tab.IndependentOpacity)
|
if (Tab.IndependentOpacity)
|
||||||
{
|
{
|
||||||
@@ -195,6 +198,7 @@ internal class Popout : Window
|
|||||||
|
|
||||||
public override void PostDraw()
|
public override void PostDraw()
|
||||||
{
|
{
|
||||||
|
if (Idx >= 0 && Idx < ChatLogWindow.PopOutDocked.Count)
|
||||||
ChatLogWindow.PopOutDocked[Idx] = ImGui.IsWindowDocked();
|
ChatLogWindow.PopOutDocked[Idx] = ImGui.IsWindowDocked();
|
||||||
|
|
||||||
if (Plugin.Config is { OverrideStyle: true, ChosenStyle: not null })
|
if (Plugin.Config is { OverrideStyle: true, ChosenStyle: not null })
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using ChatTwo.Util;
|
using HellionChat.Util;
|
||||||
using Dalamud.Game.Text.SeStringHandling;
|
using Dalamud.Game.Text.SeStringHandling;
|
||||||
using Dalamud.Game.Text.SeStringHandling.Payloads;
|
using Dalamud.Game.Text.SeStringHandling.Payloads;
|
||||||
using Dalamud.Interface.Utility.Raii;
|
using Dalamud.Interface.Utility.Raii;
|
||||||
@@ -10,7 +10,7 @@ using Dalamud.Bindings.ImGui;
|
|||||||
using Dalamud.Utility;
|
using Dalamud.Utility;
|
||||||
using DalamudPartyFinderPayload = Dalamud.Game.Text.SeStringHandling.Payloads.PartyFinderPayload;
|
using DalamudPartyFinderPayload = Dalamud.Game.Text.SeStringHandling.Payloads.PartyFinderPayload;
|
||||||
|
|
||||||
namespace ChatTwo.Ui;
|
namespace HellionChat.Ui;
|
||||||
|
|
||||||
public class SeStringDebugger : Window
|
public class SeStringDebugger : Window
|
||||||
{
|
{
|
||||||
@@ -1,13 +1,13 @@
|
|||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using ChatTwo.Resources;
|
using HellionChat.Resources;
|
||||||
using ChatTwo.Ui.SettingsTabs;
|
using HellionChat.Ui.SettingsTabs;
|
||||||
using ChatTwo.Util;
|
using HellionChat.Util;
|
||||||
using Dalamud.Interface.Utility.Raii;
|
using Dalamud.Interface.Utility.Raii;
|
||||||
using Dalamud.Interface.Windowing;
|
using Dalamud.Interface.Windowing;
|
||||||
using Dalamud.Utility;
|
using Dalamud.Utility;
|
||||||
using Dalamud.Bindings.ImGui;
|
using Dalamud.Bindings.ImGui;
|
||||||
|
|
||||||
namespace ChatTwo.Ui;
|
namespace HellionChat.Ui;
|
||||||
|
|
||||||
public sealed class SettingsWindow : Dalamud.Interface.Windowing.Window
|
public sealed class SettingsWindow : Dalamud.Interface.Windowing.Window
|
||||||
{
|
{
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
using ChatTwo.Code;
|
using HellionChat.Code;
|
||||||
using ChatTwo.Resources;
|
using HellionChat.Resources;
|
||||||
using ChatTwo.Util;
|
using HellionChat.Util;
|
||||||
using Dalamud;
|
using Dalamud;
|
||||||
using Dalamud.Interface;
|
using Dalamud.Interface;
|
||||||
using Dalamud.Interface.FontIdentifier;
|
using Dalamud.Interface.FontIdentifier;
|
||||||
@@ -9,7 +9,7 @@ using Dalamud.Interface.Utility;
|
|||||||
using Dalamud.Interface.Utility.Raii;
|
using Dalamud.Interface.Utility.Raii;
|
||||||
using Dalamud.Bindings.ImGui;
|
using Dalamud.Bindings.ImGui;
|
||||||
|
|
||||||
namespace ChatTwo.Ui.SettingsTabs;
|
namespace HellionChat.Ui.SettingsTabs;
|
||||||
|
|
||||||
internal sealed class Appearance : ISettingsTab
|
internal sealed class Appearance : ISettingsTab
|
||||||
{
|
{
|
||||||
@@ -1,13 +1,13 @@
|
|||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using ChatTwo.Resources;
|
using HellionChat.Resources;
|
||||||
using ChatTwo.Util;
|
using HellionChat.Util;
|
||||||
using Dalamud.Interface;
|
using Dalamud.Interface;
|
||||||
using Dalamud.Interface.Colors;
|
using Dalamud.Interface.Colors;
|
||||||
using Dalamud.Interface.Utility;
|
using Dalamud.Interface.Utility;
|
||||||
using Dalamud.Interface.Utility.Raii;
|
using Dalamud.Interface.Utility.Raii;
|
||||||
using Dalamud.Bindings.ImGui;
|
using Dalamud.Bindings.ImGui;
|
||||||
|
|
||||||
namespace ChatTwo.Ui.SettingsTabs;
|
namespace HellionChat.Ui.SettingsTabs;
|
||||||
|
|
||||||
// Chat-Tab — vier eigenständige Sektionen: Auto-Tell-Tabs, Behaviour,
|
// Chat-Tab — vier eigenständige Sektionen: Auto-Tell-Tabs, Behaviour,
|
||||||
// Preview, Emotes. Der Emotes-Block ist 1:1 aus der Bestand-Datei
|
// Preview, Emotes. Der Emotes-Block ist 1:1 aus der Bestand-Datei
|
||||||
@@ -169,6 +169,13 @@ internal sealed class Chat : ISettingsTab
|
|||||||
using (Plugin.FontManager.FontAwesome.Push())
|
using (Plugin.FontManager.FontAwesome.Push())
|
||||||
ImGui.Button(FontAwesomeIcon.Plus.ToIconString(), new Vector2(buttonWidth, 0));
|
ImGui.Button(FontAwesomeIcon.Plus.ToIconString(), new Vector2(buttonWidth, 0));
|
||||||
|
|
||||||
|
// Open the selector popup on left-click; SelectorPopup uses
|
||||||
|
// ImRaii.ContextPopupItem internally which only opens on right-
|
||||||
|
// click otherwise — without this OpenPopup the button looked
|
||||||
|
// active but the popup never appeared on a normal click.
|
||||||
|
if (ImGui.IsItemClicked())
|
||||||
|
ImGui.OpenPopup("WordAddPopup");
|
||||||
|
|
||||||
if (SearchSelector.SelectorPopup("WordAddPopup", out var newWord, WordPopupOptions))
|
if (SearchSelector.SelectorPopup("WordAddPopup", out var newWord, WordPopupOptions))
|
||||||
{
|
{
|
||||||
Mutable.BlockedEmotes.Add(newWord);
|
Mutable.BlockedEmotes.Add(newWord);
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using ChatTwo.Code;
|
using HellionChat.Code;
|
||||||
using ChatTwo.Resources;
|
using HellionChat.Resources;
|
||||||
using ChatTwo.Util;
|
using HellionChat.Util;
|
||||||
using Dalamud.Game.Text.SeStringHandling;
|
using Dalamud.Game.Text.SeStringHandling;
|
||||||
using Dalamud.Game.Text.SeStringHandling.Payloads;
|
using Dalamud.Game.Text.SeStringHandling.Payloads;
|
||||||
using Dalamud.Interface.ImGuiNotification;
|
using Dalamud.Interface.ImGuiNotification;
|
||||||
@@ -9,7 +9,7 @@ using Dalamud.Interface.Utility.Raii;
|
|||||||
using Dalamud.Bindings.ImGui;
|
using Dalamud.Bindings.ImGui;
|
||||||
using Dalamud.Game.Text;
|
using Dalamud.Game.Text;
|
||||||
|
|
||||||
namespace ChatTwo.Ui.SettingsTabs;
|
namespace HellionChat.Ui.SettingsTabs;
|
||||||
|
|
||||||
internal sealed class Database : ISettingsTab
|
internal sealed class Database : ISettingsTab
|
||||||
{
|
{
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
using ChatTwo.Resources;
|
using HellionChat.Resources;
|
||||||
using ChatTwo.Util;
|
using HellionChat.Util;
|
||||||
using Dalamud.Interface.Utility;
|
using Dalamud.Interface.Utility;
|
||||||
using Dalamud.Interface.Utility.Raii;
|
using Dalamud.Interface.Utility.Raii;
|
||||||
using Dalamud.Bindings.ImGui;
|
using Dalamud.Bindings.ImGui;
|
||||||
|
|
||||||
namespace ChatTwo.Ui.SettingsTabs;
|
namespace HellionChat.Ui.SettingsTabs;
|
||||||
|
|
||||||
internal sealed class General : ISettingsTab
|
internal sealed class General : ISettingsTab
|
||||||
{
|
{
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
namespace ChatTwo.Ui.SettingsTabs;
|
namespace HellionChat.Ui.SettingsTabs;
|
||||||
|
|
||||||
internal interface ISettingsTab
|
internal interface ISettingsTab
|
||||||
{
|
{
|
||||||
@@ -1,12 +1,12 @@
|
|||||||
using ChatTwo.Resources;
|
using HellionChat.Resources;
|
||||||
using ChatTwo.Util;
|
using HellionChat.Util;
|
||||||
using Dalamud.Interface;
|
using Dalamud.Interface;
|
||||||
using Dalamud.Interface.Colors;
|
using Dalamud.Interface.Colors;
|
||||||
using Dalamud.Interface.Utility;
|
using Dalamud.Interface.Utility;
|
||||||
using Dalamud.Interface.Utility.Raii;
|
using Dalamud.Interface.Utility.Raii;
|
||||||
using Dalamud.Bindings.ImGui;
|
using Dalamud.Bindings.ImGui;
|
||||||
|
|
||||||
namespace ChatTwo.Ui.SettingsTabs;
|
namespace HellionChat.Ui.SettingsTabs;
|
||||||
|
|
||||||
// Information-Tab vereint die früheren About- und Changelog-Tabs in
|
// Information-Tab vereint die früheren About- und Changelog-Tabs in
|
||||||
// drei kollabierbaren Sektionen. Der About-Inhalt ist 1:1 aus About.cs
|
// drei kollabierbaren Sektionen. Der About-Inhalt ist 1:1 aus About.cs
|
||||||
@@ -1,15 +1,15 @@
|
|||||||
using ChatTwo.Code;
|
using HellionChat.Code;
|
||||||
using ChatTwo.Export;
|
using HellionChat.Export;
|
||||||
using ChatTwo.Privacy;
|
using HellionChat.Privacy;
|
||||||
using ChatTwo.Resources;
|
using HellionChat.Resources;
|
||||||
using ChatTwo.Util;
|
using HellionChat.Util;
|
||||||
using Dalamud.Interface.Colors;
|
using Dalamud.Interface.Colors;
|
||||||
using Dalamud.Interface.ImGuiNotification;
|
using Dalamud.Interface.ImGuiNotification;
|
||||||
using Dalamud.Interface.Utility;
|
using Dalamud.Interface.Utility;
|
||||||
using Dalamud.Interface.Utility.Raii;
|
using Dalamud.Interface.Utility.Raii;
|
||||||
using Dalamud.Bindings.ImGui;
|
using Dalamud.Bindings.ImGui;
|
||||||
|
|
||||||
namespace ChatTwo.Ui.SettingsTabs;
|
namespace HellionChat.Ui.SettingsTabs;
|
||||||
|
|
||||||
internal sealed class Privacy : ISettingsTab
|
internal sealed class Privacy : ISettingsTab
|
||||||
{
|
{
|
||||||
@@ -455,11 +455,18 @@ internal sealed class Privacy : ISettingsTab
|
|||||||
|
|
||||||
if (deleted > 0)
|
if (deleted > 0)
|
||||||
{
|
{
|
||||||
Plugin.Framework.Run(() =>
|
// Bound the wait so a hung framework tick can't deadlock
|
||||||
|
// the background retention worker. Five seconds is well
|
||||||
|
// beyond a normal frame; if we time out we log and let
|
||||||
|
// the next FilterAllTabsAsync call recover the state.
|
||||||
|
if (!Plugin.Framework.Run(() =>
|
||||||
{
|
{
|
||||||
Plugin.MessageManager.ClearAllTabs();
|
Plugin.MessageManager.ClearAllTabs();
|
||||||
Plugin.MessageManager.FilterAllTabsAsync();
|
Plugin.MessageManager.FilterAllTabsAsync();
|
||||||
}).Wait();
|
}).Wait(TimeSpan.FromSeconds(5)))
|
||||||
|
{
|
||||||
|
Plugin.Log.Warning("Retention sweep: framework refresh timed out after 5s.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
WrapperUtil.AddNotification(string.Format(HellionStrings.Retention_Success, deleted), NotificationType.Success);
|
WrapperUtil.AddNotification(string.Format(HellionStrings.Retention_Success, deleted), NotificationType.Success);
|
||||||
@@ -615,11 +622,17 @@ internal sealed class Privacy : ISettingsTab
|
|||||||
var deleted = Plugin.MessageManager.Store.CleanupRetainOnly(allowed);
|
var deleted = Plugin.MessageManager.Store.CleanupRetainOnly(allowed);
|
||||||
Plugin.Log.Information($"Privacy cleanup: deleted {deleted} messages");
|
Plugin.Log.Information($"Privacy cleanup: deleted {deleted} messages");
|
||||||
|
|
||||||
Plugin.Framework.Run(() =>
|
// Bound the wait so a hung framework tick can't deadlock
|
||||||
|
// the background cleanup worker. See the matching comment in
|
||||||
|
// the retention path above for rationale.
|
||||||
|
if (!Plugin.Framework.Run(() =>
|
||||||
{
|
{
|
||||||
Plugin.MessageManager.ClearAllTabs();
|
Plugin.MessageManager.ClearAllTabs();
|
||||||
Plugin.MessageManager.FilterAllTabsAsync();
|
Plugin.MessageManager.FilterAllTabsAsync();
|
||||||
}).Wait();
|
}).Wait(TimeSpan.FromSeconds(5)))
|
||||||
|
{
|
||||||
|
Plugin.Log.Warning("Privacy cleanup: framework refresh timed out after 5s.");
|
||||||
|
}
|
||||||
|
|
||||||
WrapperUtil.AddNotification(string.Format(HellionStrings.Cleanup_Success, deleted), NotificationType.Success);
|
WrapperUtil.AddNotification(string.Format(HellionStrings.Cleanup_Success, deleted), NotificationType.Success);
|
||||||
}
|
}
|
||||||
@@ -1,12 +1,12 @@
|
|||||||
using ChatTwo.Code;
|
using HellionChat.Code;
|
||||||
using ChatTwo.Resources;
|
using HellionChat.Resources;
|
||||||
using ChatTwo.Util;
|
using HellionChat.Util;
|
||||||
using Dalamud.Interface;
|
using Dalamud.Interface;
|
||||||
using Dalamud.Interface.Utility.Raii;
|
using Dalamud.Interface.Utility.Raii;
|
||||||
using Dalamud.Bindings.ImGui;
|
using Dalamud.Bindings.ImGui;
|
||||||
using Dalamud.Game.ClientState.Objects.SubKinds;
|
using Dalamud.Game.ClientState.Objects.SubKinds;
|
||||||
|
|
||||||
namespace ChatTwo.Ui.SettingsTabs;
|
namespace HellionChat.Ui.SettingsTabs;
|
||||||
|
|
||||||
internal sealed class Tabs : ISettingsTab
|
internal sealed class Tabs : ISettingsTab
|
||||||
{
|
{
|
||||||
@@ -181,6 +181,16 @@ internal sealed class Tabs : ISettingsTab
|
|||||||
|
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
|
|
||||||
|
// Guard against an empty worlds list — can happen briefly
|
||||||
|
// when switching characters or if the datacenter sheet
|
||||||
|
// has not yet populated. Without the guard the indexed
|
||||||
|
// access into worlds[selectedWorld] would crash.
|
||||||
|
if (worlds.Count == 0)
|
||||||
|
{
|
||||||
|
ImGui.TextDisabled("(no worlds available)");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
var selectedWorld = worlds.FindIndex(world => world.RowId == tab.TellTarget.World);
|
var selectedWorld = worlds.FindIndex(world => world.RowId == tab.TellTarget.World);
|
||||||
if (selectedWorld == -1)
|
if (selectedWorld == -1)
|
||||||
selectedWorld = 0;
|
selectedWorld = 0;
|
||||||
@@ -207,6 +217,7 @@ internal sealed class Tabs : ISettingsTab
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var target = (Plugin.TargetManager.SoftTarget ?? Plugin.TargetManager.Target) as IPlayerCharacter;
|
var target = (Plugin.TargetManager.SoftTarget ?? Plugin.TargetManager.Target) as IPlayerCharacter;
|
||||||
using (ImRaii.Disabled(target == null))
|
using (ImRaii.Disabled(target == null))
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
using ChatTwo.Resources;
|
using HellionChat.Resources;
|
||||||
using ChatTwo.Util;
|
using HellionChat.Util;
|
||||||
using Dalamud.Interface.Utility.Raii;
|
using Dalamud.Interface.Utility.Raii;
|
||||||
using Dalamud.Bindings.ImGui;
|
using Dalamud.Bindings.ImGui;
|
||||||
|
|
||||||
namespace ChatTwo.Ui.SettingsTabs;
|
namespace HellionChat.Ui.SettingsTabs;
|
||||||
|
|
||||||
internal sealed class Window : ISettingsTab
|
internal sealed class Window : ISettingsTab
|
||||||
{
|
{
|
||||||
@@ -12,13 +12,19 @@ using Pidgin;
|
|||||||
using static Pidgin.Parser;
|
using static Pidgin.Parser;
|
||||||
using static Pidgin.Parser<char>;
|
using static Pidgin.Parser<char>;
|
||||||
|
|
||||||
namespace ChatTwo.Util;
|
namespace HellionChat.Util;
|
||||||
|
|
||||||
internal static class AutoTranslate
|
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.
|
||||||
|
// PreloadCache spawns a worker thread that fills both, while the main
|
||||||
|
// thread reads them via Matching / ReplaceWithPayload / StartsWithCommand
|
||||||
|
// — without this lock the HashSet/Dictionary access is undefined.
|
||||||
|
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()
|
||||||
{
|
{
|
||||||
var sheetName = Any
|
var sheetName = Any
|
||||||
@@ -67,10 +73,18 @@ internal static class AutoTranslate
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static List<AutoTranslateEntry> AllEntries()
|
private static List<AutoTranslateEntry> AllEntries()
|
||||||
|
{
|
||||||
|
lock (EntriesLock)
|
||||||
{
|
{
|
||||||
if (Entries.TryGetValue(Plugin.DataManager.Language, out var entries))
|
if (Entries.TryGetValue(Plugin.DataManager.Language, out var entries))
|
||||||
return entries;
|
return entries;
|
||||||
|
|
||||||
|
return BuildEntriesLocked();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<AutoTranslateEntry> BuildEntriesLocked()
|
||||||
|
{
|
||||||
var shouldAdd = ValidEntries.Count == 0;
|
var shouldAdd = ValidEntries.Count == 0;
|
||||||
|
|
||||||
var parser = Parser();
|
var parser = Parser();
|
||||||
@@ -229,7 +243,10 @@ internal static class AutoTranslate
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
// populate the list of valid entries
|
// populate the list of valid entries
|
||||||
if (ValidEntries.Count == 0)
|
bool needBuild;
|
||||||
|
lock (EntriesLock)
|
||||||
|
needBuild = ValidEntries.Count == 0;
|
||||||
|
if (needBuild)
|
||||||
AllEntries();
|
AllEntries();
|
||||||
|
|
||||||
var start = -1;
|
var start = -1;
|
||||||
@@ -244,7 +261,10 @@ internal static class AutoTranslate
|
|||||||
var parts = tag[4..^1].Split(',', 2);
|
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 (parts.Length == 2 && uint.TryParse(parts[0], out var group) && uint.TryParse(parts[1], out var key))
|
||||||
{
|
{
|
||||||
var payload = ValidEntries.Contains((group, key)) ? CreateFixedTranslation(group, key) : [];
|
bool isValid;
|
||||||
|
lock (EntriesLock)
|
||||||
|
isValid = ValidEntries.Contains((group, key));
|
||||||
|
var payload = isValid ? CreateFixedTranslation(group, key) : [];
|
||||||
|
|
||||||
var oldBytes = bytes.ToArray();
|
var oldBytes = bytes.ToArray();
|
||||||
var lengthDiff = payload.Length - (i - start);
|
var lengthDiff = payload.Length - (i - start);
|
||||||
@@ -271,7 +291,10 @@ internal static class AutoTranslate
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
// populate the list of valid entries
|
// populate the list of valid entries
|
||||||
if (ValidEntries.Count == 0)
|
bool needBuild;
|
||||||
|
lock (EntriesLock)
|
||||||
|
needBuild = ValidEntries.Count == 0;
|
||||||
|
if (needBuild)
|
||||||
AllEntries();
|
AllEntries();
|
||||||
|
|
||||||
for (var i = 0; i < search.Length; i++)
|
for (var i = 0; i < search.Length; i++)
|
||||||
@@ -289,7 +312,10 @@ internal static class AutoTranslate
|
|||||||
var parts = tag[4..^1].Split(',', 2);
|
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 (parts.Length == 2 && uint.TryParse(parts[0], out var group) && uint.TryParse(parts[1], out var key))
|
||||||
{
|
{
|
||||||
if (!ValidEntries.Contains((group, key)))
|
bool isValid;
|
||||||
|
lock (EntriesLock)
|
||||||
|
isValid = ValidEntries.Contains((group, key));
|
||||||
|
if (!isValid)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
var evaluated = Plugin.Evaluator.Evaluate(new ReadOnlySeString(CreateFixedTranslation(group, key))).ToString();
|
var evaluated = Plugin.Evaluator.Evaluate(new ReadOnlySeString(CreateFixedTranslation(group, key))).ToString();
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
using ChatTwo.Code;
|
using HellionChat.Code;
|
||||||
using Dalamud.Game.Text.SeStringHandling;
|
using Dalamud.Game.Text.SeStringHandling;
|
||||||
using Dalamud.Game.Text.SeStringHandling.Payloads;
|
using Dalamud.Game.Text.SeStringHandling.Payloads;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
@@ -6,7 +6,7 @@ using Lumina.Text.Payloads;
|
|||||||
|
|
||||||
using PayloadType = Dalamud.Game.Text.SeStringHandling.PayloadType;
|
using PayloadType = Dalamud.Game.Text.SeStringHandling.PayloadType;
|
||||||
|
|
||||||
namespace ChatTwo.Util;
|
namespace HellionChat.Util;
|
||||||
|
|
||||||
internal static class ChunkUtil
|
internal static class ChunkUtil
|
||||||
{
|
{
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
using System.Buffers.Binary;
|
using System.Buffers.Binary;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
|
|
||||||
namespace ChatTwo.Util;
|
namespace HellionChat.Util;
|
||||||
|
|
||||||
internal static class ColourUtil {
|
internal static class ColourUtil {
|
||||||
private static (byte r, byte g, byte b) RgbaToRgbComponents(uint rgba)
|
private static (byte r, byte g, byte b) RgbaToRgbComponents(uint rgba)
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using ChatTwo.Resources;
|
using HellionChat.Resources;
|
||||||
using Dalamud.Interface;
|
using Dalamud.Interface;
|
||||||
using Dalamud.Interface.Colors;
|
using Dalamud.Interface.Colors;
|
||||||
using Dalamud.Interface.ImGuiNotification;
|
using Dalamud.Interface.ImGuiNotification;
|
||||||
@@ -9,7 +9,7 @@ using Dalamud.Interface.Utility.Raii;
|
|||||||
using Dalamud.Utility;
|
using Dalamud.Utility;
|
||||||
using Dalamud.Bindings.ImGui;
|
using Dalamud.Bindings.ImGui;
|
||||||
|
|
||||||
namespace ChatTwo.Util;
|
namespace HellionChat.Util;
|
||||||
|
|
||||||
// From https://github.com/Flix01/imgui/blob/imgui_with_addons/addons/imguidatechooser/imguidatechooser.cpp
|
// From https://github.com/Flix01/imgui/blob/imgui_with_addons/addons/imguidatechooser/imguidatechooser.cpp
|
||||||
public static class DateWidget
|
public static class DateWidget
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace ChatTwo.Util;
|
namespace HellionChat.Util;
|
||||||
|
|
||||||
public class ColorPayload
|
public class ColorPayload
|
||||||
{
|
{
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
using FFXIVClientStructs.FFXIV.Client.UI.Misc;
|
using FFXIVClientStructs.FFXIV.Client.UI.Misc;
|
||||||
using FFXIVClientStructs.FFXIV.Component.Text;
|
using FFXIVClientStructs.FFXIV.Component.Text;
|
||||||
|
|
||||||
namespace ChatTwo.Util;
|
namespace HellionChat.Util;
|
||||||
|
|
||||||
public static class GlobalParametersCache
|
public static class GlobalParametersCache
|
||||||
{
|
{
|
||||||
@@ -10,10 +10,14 @@ public static class GlobalParametersCache
|
|||||||
|
|
||||||
public static int GetValue(int index)
|
public static int GetValue(int index)
|
||||||
{
|
{
|
||||||
if (index < 0 || index >= Cache.Length)
|
// Capture the array reference once so the bounds check and the
|
||||||
|
// indexed read operate on the same instance, even if Refresh
|
||||||
|
// reassigns Cache between the two operations.
|
||||||
|
var cache = Cache;
|
||||||
|
if (index < 0 || index >= cache.Length)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
return Cache[index];
|
return cache[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -3,7 +3,7 @@ using System.Runtime.InteropServices;
|
|||||||
using SixLabors.ImageSharp;
|
using SixLabors.ImageSharp;
|
||||||
using SixLabors.ImageSharp.PixelFormats;
|
using SixLabors.ImageSharp.PixelFormats;
|
||||||
|
|
||||||
namespace ChatTwo.Util;
|
namespace HellionChat.Util;
|
||||||
|
|
||||||
// From Kizer: https://github.com/Soreepeong/Dalamud/blob/feature/log-wordwrap/Dalamud/Interface/Spannables/Internal/GfdFileView.cs
|
// From Kizer: https://github.com/Soreepeong/Dalamud/blob/feature/log-wordwrap/Dalamud/Interface/Spannables/Internal/GfdFileView.cs
|
||||||
public readonly unsafe ref struct GfdFileView
|
public readonly unsafe ref struct GfdFileView
|
||||||
@@ -59,8 +59,14 @@ public readonly unsafe ref struct GfdFileView
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (entries.Length == 0)
|
||||||
|
{
|
||||||
|
entry = default;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
var lo = 0;
|
var lo = 0;
|
||||||
var hi = entries.Length;
|
var hi = entries.Length - 1;
|
||||||
while (lo <= hi)
|
while (lo <= hi)
|
||||||
{
|
{
|
||||||
var i = lo + ((hi - lo) >> 1);
|
var i = lo + ((hi - lo) >> 1);
|
||||||
@@ -70,7 +76,7 @@ public readonly unsafe ref struct GfdFileView
|
|||||||
{
|
{
|
||||||
iconId = entries[i].Redirect;
|
iconId = entries[i].Redirect;
|
||||||
lo = 0;
|
lo = 0;
|
||||||
hi = entries.Length;
|
hi = entries.Length - 1;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
using System.Buffers;
|
using System.Buffers;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using ChatTwo.Code;
|
using HellionChat.Code;
|
||||||
using ChatTwo.GameFunctions.Types;
|
using HellionChat.GameFunctions.Types;
|
||||||
using ChatTwo.Resources;
|
using HellionChat.Resources;
|
||||||
using Dalamud.Game.ClientState.Keys;
|
using Dalamud.Game.ClientState.Keys;
|
||||||
using Dalamud.Game.Text.SeStringHandling;
|
using Dalamud.Game.Text.SeStringHandling;
|
||||||
using Dalamud.Interface;
|
using Dalamud.Interface;
|
||||||
@@ -15,7 +15,7 @@ using Dalamud.Interface.Utility;
|
|||||||
using Dalamud.Interface.Utility.Raii;
|
using Dalamud.Interface.Utility.Raii;
|
||||||
using Dalamud.Bindings.ImGui;
|
using Dalamud.Bindings.ImGui;
|
||||||
|
|
||||||
namespace ChatTwo.Util;
|
namespace HellionChat.Util;
|
||||||
|
|
||||||
internal static class ImGuiUtil
|
internal static class ImGuiUtil
|
||||||
{
|
{
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user