Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e594258cf3 | |||
| bb863c5b32 | |||
| 0797d1a517 | |||
| 8dc8b87580 | |||
| baeec369e6 | |||
| a1f2b22b19 | |||
| 5931f2f301 | |||
| 0b25df0ea7 | |||
| b75c7b177a | |||
| ccc5a4e17a | |||
| daa800c8b1 | |||
| a531973c0d |
@@ -8,7 +8,7 @@ body:
|
|||||||
value: |
|
value: |
|
||||||
Thanks for reporting. Please fill in the fields below so I can
|
Thanks for reporting. Please fill in the fields below so I can
|
||||||
reproduce the issue. If this is a security issue, stop here and
|
reproduce the issue. If this is a security issue, stop here and
|
||||||
use the [private vulnerability advisory](https://github.com/JonKazama-Hellion/HellionChat/security/advisories/new)
|
report it privately to [kontakt@hellion-media.de](mailto:kontakt@hellion-media.de?subject=%5BHellionChat%20Security%5D)
|
||||||
instead.
|
instead.
|
||||||
|
|
||||||
- type: input
|
- type: input
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ blank_issues_enabled: false
|
|||||||
|
|
||||||
contact_links:
|
contact_links:
|
||||||
- name: Security vulnerability
|
- name: Security vulnerability
|
||||||
url: https://github.com/JonKazama-Hellion/HellionChat/security/advisories/new
|
url: mailto:kontakt@hellion-media.de?subject=%5BHellionChat%20Security%5D
|
||||||
about: Do not open a public issue for security problems. Use the private advisory instead.
|
about: Do not open a public issue for security problems. Report by e-mail instead.
|
||||||
|
|
||||||
- name: Upstream Chat 2 issue
|
- name: Upstream Chat 2 issue
|
||||||
url: https://github.com/Infiziert90/ChatTwo/issues
|
url: https://github.com/Infiziert90/ChatTwo/issues
|
||||||
|
|||||||
@@ -3,9 +3,9 @@ Thanks for contributing to HellionChat. Please fill in the sections
|
|||||||
below so the review goes quickly. Delete sections that genuinely do
|
below so the review goes quickly. Delete sections that genuinely do
|
||||||
not apply, but do not delete the whole template.
|
not apply, but do not delete the whole template.
|
||||||
|
|
||||||
If this is a security fix, stop here and use a private security
|
If this is a security fix, stop here and report it privately by
|
||||||
advisory instead:
|
e-mail instead of opening a public PR:
|
||||||
https://github.com/JonKazama-Hellion/HellionChat/security/advisories/new
|
mailto:kontakt@hellion-media.de?subject=%5BHellionChat%20Security%5D
|
||||||
-->
|
-->
|
||||||
|
|
||||||
## Summary
|
## Summary
|
||||||
|
|||||||
@@ -0,0 +1,45 @@
|
|||||||
|
---
|
||||||
|
subtitle: Async-Lifecycle + Gitea-Cutover
|
||||||
|
versionsnatur: Architecture-Refactor
|
||||||
|
---
|
||||||
|
|
||||||
|
**Hellion Chat 1.4.3 — Plugin-Load Async-Init + Repo-Cutover**
|
||||||
|
|
||||||
|
Vierter Sub-Patch der v1.4.x Polish-Sweep-Serie. Plugin-
|
||||||
|
Lifecycle auf Dalamud's `IAsyncDalamudPlugin`-API migriert
|
||||||
|
und das Custom-Repo zieht von GitHub auf Gitea um.
|
||||||
|
|
||||||
|
- **Async-Plugin-Architektur.** Konstruktor übernimmt nur
|
||||||
|
noch die Bootstrap-Essentials (Config-Load, Language-Init,
|
||||||
|
Conflict-Detection). Migrationen, Service-Allokationen,
|
||||||
|
Window-Konstruktion und Hook-Subscription wandern in
|
||||||
|
LoadAsync, sodass Dalamud die UI während der schweren
|
||||||
|
Arbeit responsive halten kann. Per-Line-CaptureFailure in
|
||||||
|
DisposeAsync mirrort LightlessSync's Pattern, plus
|
||||||
|
Idempotency-Guard gegen Reload-Races
|
||||||
|
- **Custom-Repo-URL umgezogen auf Gitea.** Bestehende Tester
|
||||||
|
müssen einmalig in XIVLauncher die Custom-Repo-URL auf
|
||||||
|
`https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/raw/branch/main/repo.json`
|
||||||
|
umstellen, dann XIVLauncher neu starten. Das alte
|
||||||
|
GitHub-Repo bleibt als eingefrorener v1.4.2-Snapshot
|
||||||
|
stehen und wird nicht mehr aktualisiert
|
||||||
|
- **Schema-Gate statt Migrations-Kette.** Die v9 → v16
|
||||||
|
Migrationen sind raus, ersetzt durch einen harten
|
||||||
|
Schema-Check in Phase 1. Configs auf Schema v16+ laden
|
||||||
|
direkt; ältere Configs (vor v1.2.1) bekommen jetzt eine
|
||||||
|
klare „install v1.4.2 first"-Fehlermeldung statt eines
|
||||||
|
impliziten Migrations-Pfads
|
||||||
|
- **AutoTranslate-Cache läuft im Hintergrund.** Der Cache
|
||||||
|
füllt sich jetzt fire-and-forget statt blockierend im
|
||||||
|
Plugin-Load. Trade-off: die erste Auto-Translate-Nutzung
|
||||||
|
einer Session kann einen kurzen Hitch haben, dafür kein
|
||||||
|
300-ms-Block beim Plugin-Start
|
||||||
|
- **Plugin-Load-Zeit ehrlich.** Median 3,7 s über fünf
|
||||||
|
Reloads, vergleichbar mit v1.4.2. Der Async-Refactor ist
|
||||||
|
Foundation für künftige Lazy-Init-Optimierungen (v1.4.4)
|
||||||
|
und Code-Architektur-Hygiene, kein direkter
|
||||||
|
User-spürbarer Speed-Win in dieser Release
|
||||||
|
|
||||||
|
Keine User-sichtbaren Funktions-Änderungen außer dem
|
||||||
|
Repo-URL-Update. Settings, Themes und Tabs bleiben
|
||||||
|
unangetastet.
|
||||||
@@ -8,19 +8,19 @@ Dalamud main plugin repo. To install:
|
|||||||
|
|
||||||
1. In XIVLauncher: **Settings → Experimental → Custom Plugin Repositories**
|
1. In XIVLauncher: **Settings → Experimental → Custom Plugin Repositories**
|
||||||
2. Add the URL:
|
2. Add the URL:
|
||||||
`https://gitea.com/JonKazama-Hellion/HellionChat/raw/branch/main/repo.json`
|
`https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/raw/branch/main/repo.json`
|
||||||
3. Enable, save, then `/xlplugins` → search **Hellion Chat** → install
|
3. Enable, save, then `/xlplugins` → search **Hellion Chat** → install
|
||||||
|
|
||||||
## Project documents
|
## Project documents
|
||||||
|
|
||||||
- [README](https://gitea.com/JonKazama-Hellion/HellionChat/src/branch/main/README.md) — features, architecture, build
|
- [README](https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/src/branch/main/README.md) — features, architecture, build
|
||||||
- [Privacy notice](https://gitea.com/JonKazama-Hellion/HellionChat/src/branch/main/PRIVACY.md) — what the plugin stores and sends
|
- [Privacy notice](https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/src/branch/main/PRIVACY.md) — what the plugin stores and sends
|
||||||
- [Third-party notices](https://gitea.com/JonKazama-Hellion/HellionChat/src/branch/main/docs/THIRD_PARTY_NOTICES.md) — dependencies and licences
|
- [Third-party notices](https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/src/branch/main/docs/THIRD_PARTY_NOTICES.md) — dependencies and licences
|
||||||
- [Security policy](https://gitea.com/JonKazama-Hellion/HellionChat/src/branch/main/SECURITY.md) — vulnerability reporting
|
- [Security policy](https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/src/branch/main/SECURITY.md) — vulnerability reporting
|
||||||
- [Support](https://gitea.com/JonKazama-Hellion/HellionChat/src/branch/main/SUPPORT.md) — bug reports, questions, contact paths
|
- [Support](https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/src/branch/main/SUPPORT.md) — bug reports, questions, contact paths
|
||||||
|
|
||||||
## Licence
|
## Licence
|
||||||
|
|
||||||
[EUPL-1.2](https://gitea.com/JonKazama-Hellion/HellionChat/src/branch/main/LICENSE).
|
[EUPL-1.2](https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/src/branch/main/LICENSE).
|
||||||
Based on [Chat 2](https://github.com/Infiziert90/ChatTwo) by Infi and Anna,
|
Based on [Chat 2](https://github.com/Infiziert90/ChatTwo) by Infi and Anna,
|
||||||
also EUPL-1.2.
|
also EUPL-1.2.
|
||||||
|
|||||||
@@ -133,7 +133,7 @@ jobs:
|
|||||||
# ---------- Embed-Payload bauen ----------
|
# ---------- Embed-Payload bauen ----------
|
||||||
$payload = [ordered]@{
|
$payload = [ordered]@{
|
||||||
username = "Forge Herald"
|
username = "Forge Herald"
|
||||||
avatar_url = "https://gitea.com/JonKazama-Hellion/HellionChat/raw/branch/main/HellionChat/images/icon.png"
|
avatar_url = "https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/raw/branch/main/HellionChat/images/icon.png"
|
||||||
content = "<@&1500489631555260446>"
|
content = "<@&1500489631555260446>"
|
||||||
allowed_mentions = [ordered]@{
|
allowed_mentions = [ordered]@{
|
||||||
parse = @()
|
parse = @()
|
||||||
@@ -142,7 +142,7 @@ jobs:
|
|||||||
embeds = @(
|
embeds = @(
|
||||||
[ordered]@{
|
[ordered]@{
|
||||||
title = $title
|
title = $title
|
||||||
url = "https://gitea.com/JonKazama-Hellion/HellionChat/releases/tag/$tag"
|
url = "https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/releases/tag/$tag"
|
||||||
color = 12730636
|
color = 12730636
|
||||||
description = $description
|
description = $description
|
||||||
footer = [ordered]@{ text = $footerText }
|
footer = [ordered]@{ text = $footerText }
|
||||||
|
|||||||
@@ -100,6 +100,19 @@ public class FontManager
|
|||||||
JpRange = BuildRange(GlyphRangesJapanese.GlyphRanges);
|
JpRange = BuildRange(GlyphRangesJapanese.GlyphRanges);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Async wrapper around <see cref="BuildFonts"/> for the Phase-1 LoadAsync
|
||||||
|
/// path. The font-atlas build is CPU-bound, so we offload via Task.Run and
|
||||||
|
/// honour the cancellation token at the scheduling boundary; this lets the
|
||||||
|
/// font build run in parallel with the theme init without blocking the
|
||||||
|
/// loader. Settings-driven manual rebuilds keep using the sync entry point.
|
||||||
|
/// </summary>
|
||||||
|
public async Task BuildFontsAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
await Task.Run(BuildFonts, cancellationToken).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
public void BuildFonts()
|
public void BuildFonts()
|
||||||
{
|
{
|
||||||
SetUpRanges();
|
SetUpRanges();
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
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>1.4.2</Version>
|
<Version>1.4.3</Version>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<!-- Honor packages.lock.json on restore so floating version ranges
|
<!-- Honor packages.lock.json on restore so floating version ranges
|
||||||
|
|||||||
@@ -58,18 +58,27 @@ description: |-
|
|||||||
its tab aggregation behind the same one-second cache it uses
|
its tab aggregation behind the same one-second cache it uses
|
||||||
for the format strings.
|
for the format strings.
|
||||||
|
|
||||||
|
v1.4.3 — Plugin-Load Async-Init plus Repo-Cutover. Plugin
|
||||||
|
migrated to Dalamud's IAsyncDalamudPlugin so the heavy work
|
||||||
|
(migrations, service allocations, window construction, hook
|
||||||
|
subscription) runs in LoadAsync without blocking Dalamud's
|
||||||
|
UI. Schema-gate replaces the v9 → v16 migration chain;
|
||||||
|
configs on schema v16+ load directly. Custom-repo URL moves
|
||||||
|
to gitea.hellion-forge.cloud, the GitHub repo stays as a
|
||||||
|
frozen v1.4.2 snapshot.
|
||||||
|
|
||||||
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.
|
||||||
|
|
||||||
Modding & support: join the Hellion Forge Discord at
|
Modding & support: join the Hellion Forge Discord at
|
||||||
https://discord.gg/X9V7Kcv5gR — community for Hellion Chat and
|
https://discord.gg/X9V7Kcv5gR — community for Hellion Chat and
|
||||||
other Hellion Online Media plugins/tools.
|
other Hellion Online Media plugins/tools.
|
||||||
repo_url: https://github.com/JonKazama-Hellion/HellionChat
|
repo_url: https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat
|
||||||
accepts_feedback: true
|
accepts_feedback: true
|
||||||
icon_url: https://raw.githubusercontent.com/JonKazama-Hellion/HellionChat/main/HellionChat/images/icon.png
|
icon_url: https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/raw/branch/main/HellionChat/images/icon.png
|
||||||
image_urls:
|
image_urls:
|
||||||
- https://raw.githubusercontent.com/JonKazama-Hellion/HellionChat/main/HellionChat/images/chatWindow.png
|
- https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/raw/branch/main/HellionChat/images/chatWindow.png
|
||||||
- https://raw.githubusercontent.com/JonKazama-Hellion/HellionChat/main/HellionChat/images/settingsOverview.png
|
- https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/raw/branch/main/HellionChat/images/settingsOverview.png
|
||||||
- https://raw.githubusercontent.com/JonKazama-Hellion/HellionChat/main/HellionChat/images/themesPicker.png
|
- https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/raw/branch/main/HellionChat/images/themesPicker.png
|
||||||
tags:
|
tags:
|
||||||
- Social
|
- Social
|
||||||
- UI
|
- UI
|
||||||
@@ -77,6 +86,42 @@ tags:
|
|||||||
- Replacement
|
- Replacement
|
||||||
- Privacy
|
- Privacy
|
||||||
changelog: |-
|
changelog: |-
|
||||||
|
**Hellion Chat 1.4.3 — Plugin-Load Async-Init + Repo-Cutover (2026-05-08)**
|
||||||
|
|
||||||
|
Plugin lifecycle migrated to Dalamud's `IAsyncDalamudPlugin`
|
||||||
|
API. The constructor now does only the bootstrap-essentials
|
||||||
|
(config load, language init, conflict detection); migrations,
|
||||||
|
service allocations, window construction and hook subscription
|
||||||
|
move to LoadAsync. Dalamud can keep its UI responsive while the
|
||||||
|
heavy work runs.
|
||||||
|
|
||||||
|
- IAsyncDalamudPlugin two-phase load with per-line CaptureFailure
|
||||||
|
in DisposeAsync (mirrors LightlessSync's pattern); idempotency
|
||||||
|
guard protects against reload races
|
||||||
|
- Schema-gate replaces the v9 → v16 migration chain. Configs
|
||||||
|
on schema v16+ load directly; older configs trigger an
|
||||||
|
"install v1.4.2 first" error so the historic migration
|
||||||
|
path stays intact
|
||||||
|
- AutoTranslate.PreloadCache moved off the load path. First
|
||||||
|
use may have a sub-second hitch instead of every-load; the
|
||||||
|
upstream chose differently, we accept first-use latency
|
||||||
|
- FontManager.BuildFonts is called sync at the start of
|
||||||
|
LoadAsync; Dalamud rebuilds the font atlas on its own
|
||||||
|
pipeline so the custom Hellion-Exo2 font appears with a
|
||||||
|
brief font-pop after load (matches ChatTwo's behaviour)
|
||||||
|
- Custom-repo URL moved to gitea.hellion-forge.cloud/
|
||||||
|
JonKazama-Hellion/HellionChat. GitHub repo stays as a
|
||||||
|
frozen v1.4.2 snapshot; new releases ship from Gitea.
|
||||||
|
Existing testers need to update the custom-repo URL once
|
||||||
|
- Plugin-load time in this release sits at ~3.7 s median
|
||||||
|
(5 reloads), comparable to v1.4.2. Async migration is
|
||||||
|
foundational for v1.4.4 Lazy-Init optimisations rather
|
||||||
|
than an immediate user-perceived win
|
||||||
|
|
||||||
|
Modding & support: join Hellion Forge — https://discord.gg/X9V7Kcv5gR
|
||||||
|
|
||||||
|
Based on Chat 2 1.35.3 (upstream Infiziert90/ChatTwo, EUPL-1.2).
|
||||||
|
|
||||||
**Hellion Chat 1.4.2 — ChatLog Frame-Hot-Path**
|
**Hellion Chat 1.4.2 — ChatLog Frame-Hot-Path**
|
||||||
|
|
||||||
Third sub-patch of the v1.4.x Polish Sweep series. Per-frame
|
Third sub-patch of the v1.4.x Polish Sweep series. Per-frame
|
||||||
@@ -171,26 +216,6 @@ changelog: |-
|
|||||||
|
|
||||||
Based on Chat 2 1.35.3 (upstream Infiziert90/ChatTwo, EUPL-1.2).
|
Based on Chat 2 1.35.3 (upstream Infiziert90/ChatTwo, EUPL-1.2).
|
||||||
|
|
||||||
**Hellion Chat 1.3.0 - Plugin Integrations: Honorific**
|
|
||||||
|
|
||||||
First step on the plugin-integration roadmap. HellionChat now
|
|
||||||
listens to Honorific and shows your custom title in the chat
|
|
||||||
header. The slot auto-hides when Honorific is not installed,
|
|
||||||
when no custom title is active, or when you are using the
|
|
||||||
original FFXIV title.
|
|
||||||
|
|
||||||
- New "Integrations" settings tab
|
|
||||||
- Honorific integration with auto-detection and live updates
|
|
||||||
- "Coming soon" preview of the next five planned integrations:
|
|
||||||
context menu actions, smart notifications, RP status block,
|
|
||||||
ExtraChat channels, and quick DM compose
|
|
||||||
- Maintainer attribution buttons for Honorific repo and Caraxi
|
|
||||||
- New service-class pattern under HellionChat/Integrations/
|
|
||||||
|
|
||||||
Modding and support: join Hellion Forge - https://discord.gg/X9V7Kcv5gR
|
|
||||||
|
|
||||||
Based on Chat 2 1.35.3 (upstream Infiziert90/ChatTwo, EUPL-1.2).
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
Earlier history: https://github.com/JonKazama-Hellion/HellionChat/releases
|
Earlier history: https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/releases
|
||||||
|
|||||||
+231
-442
@@ -2,6 +2,7 @@ using System.Diagnostics;
|
|||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Runtime.ExceptionServices;
|
||||||
using HellionChat.Ipc;
|
using HellionChat.Ipc;
|
||||||
using HellionChat.Resources;
|
using HellionChat.Resources;
|
||||||
using HellionChat.Ui;
|
using HellionChat.Ui;
|
||||||
@@ -17,7 +18,7 @@ using Dalamud.Interface.ImGuiFileDialog;
|
|||||||
namespace HellionChat;
|
namespace HellionChat;
|
||||||
|
|
||||||
// ReSharper disable once ClassNeverInstantiated.Global
|
// ReSharper disable once ClassNeverInstantiated.Global
|
||||||
public sealed class Plugin : IDalamudPlugin
|
public sealed class Plugin : IAsyncDalamudPlugin
|
||||||
{
|
{
|
||||||
public const string PluginName = "Hellion Chat";
|
public const string PluginName = "Hellion Chat";
|
||||||
|
|
||||||
@@ -47,27 +48,36 @@ public sealed class Plugin : IDalamudPlugin
|
|||||||
public static FileDialogManager FileDialogManager { get; private set; } = null!;
|
public static FileDialogManager FileDialogManager { get; private set; } = null!;
|
||||||
|
|
||||||
public readonly WindowSystem WindowSystem = new(PluginName);
|
public readonly WindowSystem WindowSystem = new(PluginName);
|
||||||
public SettingsWindow SettingsWindow { get; }
|
|
||||||
public ChatLogWindow ChatLogWindow { get; }
|
|
||||||
public DbViewer DbViewer { get; }
|
|
||||||
public InputPreview InputPreview { get; }
|
|
||||||
public CommandHelpWindow CommandHelpWindow { get; }
|
|
||||||
public SeStringDebugger SeStringDebugger { get; }
|
|
||||||
public FirstRunWizard FirstRunWizard { get; }
|
|
||||||
public DebuggerWindow DebuggerWindow { get; }
|
|
||||||
|
|
||||||
internal Commands Commands { get; }
|
// v1.4.3: properties moved from { get; } to { get; private set; } = null!;
|
||||||
internal GameFunctions.GameFunctions Functions { get; }
|
// because LoadAsync now owns construction of the Phase-2 services.
|
||||||
internal MessageManager MessageManager { get; }
|
// Phase-1 services use the same shape for consistency, even though
|
||||||
internal AutoTellTabsService AutoTellTabsService { get; }
|
// they're still allocated in the ctor.
|
||||||
internal IpcManager Ipc { get; }
|
public SettingsWindow SettingsWindow { get; private set; } = null!;
|
||||||
internal ExtraChat ExtraChat { get; }
|
public ChatLogWindow ChatLogWindow { get; private set; } = null!;
|
||||||
internal TypingIpc TypingIpc { get; }
|
public DbViewer DbViewer { get; private set; } = null!;
|
||||||
internal FontManager FontManager { get; }
|
public InputPreview InputPreview { get; private set; } = null!;
|
||||||
|
public CommandHelpWindow CommandHelpWindow { get; private set; } = null!;
|
||||||
|
public SeStringDebugger SeStringDebugger { get; private set; } = null!;
|
||||||
|
public FirstRunWizard FirstRunWizard { get; private set; } = null!;
|
||||||
|
public DebuggerWindow DebuggerWindow { get; private set; } = null!;
|
||||||
|
|
||||||
|
internal Commands Commands { get; private set; } = null!;
|
||||||
|
internal GameFunctions.GameFunctions Functions { get; private set; } = null!;
|
||||||
|
internal MessageManager MessageManager { get; private set; } = null!;
|
||||||
|
internal AutoTellTabsService AutoTellTabsService { get; private set; } = null!;
|
||||||
|
internal IpcManager Ipc { get; private set; } = null!;
|
||||||
|
internal ExtraChat ExtraChat { get; private set; } = null!;
|
||||||
|
internal TypingIpc TypingIpc { get; private set; } = null!;
|
||||||
|
internal FontManager FontManager { get; private set; } = null!;
|
||||||
internal Themes.ThemeRegistry ThemeRegistry { get; private set; } = null!;
|
internal Themes.ThemeRegistry ThemeRegistry { get; private set; } = null!;
|
||||||
internal Ui.StatusBar StatusBar { get; private set; } = null!;
|
internal Ui.StatusBar StatusBar { get; private set; } = null!;
|
||||||
internal Integrations.HonorificService HonorificService { get; private set; } = null!;
|
internal Integrations.HonorificService HonorificService { get; private set; } = null!;
|
||||||
|
|
||||||
|
// (B3) Lightless idempotency guard — Dalamud may fire DisposeAsync twice
|
||||||
|
// in a reload race; second call short-circuits.
|
||||||
|
private int _disposeStarted;
|
||||||
|
|
||||||
internal int DeferredSaveFrames = -1;
|
internal int DeferredSaveFrames = -1;
|
||||||
|
|
||||||
// Serialises retention sweeps. The 24h auto-sweep on plugin load and
|
// Serialises retention sweeps. The 24h auto-sweep on plugin load and
|
||||||
@@ -98,326 +108,59 @@ public sealed class Plugin : IDalamudPlugin
|
|||||||
|
|
||||||
public Plugin()
|
public Plugin()
|
||||||
{
|
{
|
||||||
|
// Phase-1 ctor stays minimal: bootstrap-essentials only (conflict
|
||||||
|
// gate, config load, language + ImGui init, WindowSystem skeleton).
|
||||||
|
// Schema migrations and every service / window allocation moved to
|
||||||
|
// LoadAsync so the sync ctor returns fast. On failure here nothing
|
||||||
|
// is initialized yet, so just throw — there is nothing to clean up.
|
||||||
|
|
||||||
// Refuse to start if upstream Chat 2 is loaded — prevents IPC
|
// Refuse to start if upstream Chat 2 is loaded — prevents IPC
|
||||||
// channel collisions and double-replacement of the in-game chat
|
// channel collisions and double-replacement of the in-game chat
|
||||||
// window. Throwing here makes Dalamud abort the load cleanly with
|
// window. Throwing here makes Dalamud abort the load cleanly with
|
||||||
// our localized message instead of crashing FFXIV mid-frame.
|
// our localized message instead of crashing FFXIV mid-frame.
|
||||||
ChatTwoConflictDetector.ThrowIfChatTwoIsLoaded(Interface);
|
ChatTwoConflictDetector.ThrowIfChatTwoIsLoaded(Interface);
|
||||||
|
|
||||||
|
GameStarted = Process.GetCurrentProcess().StartTime.ToUniversalTime();
|
||||||
|
|
||||||
|
// Hellion Chat: take over config + database from upstream ChatTwo
|
||||||
|
// before Dalamud loads our plugin config. Idempotent: only acts on
|
||||||
|
// the first start where the legacy paths exist and ours don't.
|
||||||
|
MigrateFromChatTwoLayout();
|
||||||
|
|
||||||
|
Config = Interface.GetPluginConfig() as Configuration ?? new Configuration();
|
||||||
|
|
||||||
|
// Schema-gate: v1.4.3 only supports config schema v16. Older configs
|
||||||
|
// went through their migrations in v1.2.1 (v15→v16) and earlier; users
|
||||||
|
// who skipped past those releases need to install v1.4.2 first to run
|
||||||
|
// the migration chain, then upgrade to v1.4.3.
|
||||||
|
if (Config.Version < 16)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException(
|
||||||
|
$"HellionChat v1.4.3 requires config schema v16, got v{Config.Version}. " +
|
||||||
|
"Please install v1.4.2 first to migrate the configuration, then upgrade to v1.4.3.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hellion Chat — Auto-Tell-Tabs Defense-in-Depth. SaveConfig
|
||||||
|
// already strips temp tabs before persistence, but a previous
|
||||||
|
// crash or external write could have left them in the JSON.
|
||||||
|
// Drop them on load to guarantee the session-only invariant.
|
||||||
|
Config.Tabs.RemoveAll(t => t.IsTempTab);
|
||||||
|
|
||||||
|
LanguageChanged(Interface.UiLanguage);
|
||||||
|
ImGuiUtil.Initialize(this);
|
||||||
|
|
||||||
|
DeferredSaveFrames = -1;
|
||||||
|
|
||||||
|
// WindowSystem skeleton is initialised by the readonly field above —
|
||||||
|
// no AddWindow yet; window construction lives in LoadAsync.
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task LoadAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
GameStarted = Process.GetCurrentProcess().StartTime.ToUniversalTime();
|
|
||||||
|
|
||||||
// Hellion Chat: take over config + database from upstream ChatTwo
|
|
||||||
// before Dalamud loads our plugin config. Idempotent: only acts on
|
|
||||||
// the first start where the legacy paths exist and ours don't.
|
|
||||||
MigrateFromChatTwoLayout();
|
|
||||||
|
|
||||||
Config = Interface.GetPluginConfig() as Configuration ?? new Configuration();
|
|
||||||
|
|
||||||
// Hellion Chat — Auto-Tell-Tabs Defense-in-Depth. SaveConfig
|
|
||||||
// already strips temp tabs before persistence, but a previous
|
|
||||||
// crash or external write could have left them in the JSON.
|
|
||||||
// Drop them on load to guarantee the session-only invariant.
|
|
||||||
Config.Tabs.RemoveAll(t => t.IsTempTab);
|
|
||||||
|
|
||||||
// Hellion Chat v9 → v10 — wipes the configuration so the new 8-tab
|
|
||||||
// layout starts from defaults instead of mapping every previous setting
|
|
||||||
// to its new position. Backup-Failure ist non-fatal, der Wipe läuft
|
|
||||||
// trotzdem; dem User fehlt dann nur das manuelle Restore-Sicherheitsnetz.
|
|
||||||
if (Config.Version < 10)
|
|
||||||
{
|
|
||||||
var pluginConfigsDir = Interface.ConfigDirectory.Parent?.FullName;
|
|
||||||
if (pluginConfigsDir is not null)
|
|
||||||
{
|
|
||||||
var liveConfigPath = Path.Combine(pluginConfigsDir, $"{Interface.InternalName}.json");
|
|
||||||
var backupPath = Path.Combine(pluginConfigsDir, $"{Interface.InternalName}.json.pre-v10-backup");
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (File.Exists(liveConfigPath))
|
|
||||||
{
|
|
||||||
File.Copy(liveConfigPath, backupPath, overwrite: true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Log.Warning(ex, "HellionChat: pre-v10 config backup failed");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Config = new Configuration
|
|
||||||
{
|
|
||||||
Version = 10,
|
|
||||||
FirstRunCompleted = true,
|
|
||||||
};
|
|
||||||
SaveConfig();
|
|
||||||
|
|
||||||
Notification.AddNotification(new Dalamud.Interface.ImGuiNotification.Notification
|
|
||||||
{
|
|
||||||
Title = HellionStrings.SettingsRefactor_Migration_Title,
|
|
||||||
Content = HellionStrings.SettingsRefactor_Migration_Content,
|
|
||||||
Type = Dalamud.Interface.ImGuiNotification.NotificationType.Info,
|
|
||||||
InitialDuration = TimeSpan.FromSeconds(25),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hellion Chat v10 → v11 — adds the global Configuration.PopOutInputEnabled
|
|
||||||
// master switch and SeenPopOutInputHint flag for the v0.6.0 pop-out
|
|
||||||
// input feature. Lightweight migration: defaults both fields,
|
|
||||||
// no user-facing notification because the change is opt-in only.
|
|
||||||
if (Config.Version < 11)
|
|
||||||
{
|
|
||||||
Config.PopOutInputEnabled = false;
|
|
||||||
Config.SeenPopOutInputHint = false;
|
|
||||||
Config.Version = 11;
|
|
||||||
SaveConfig();
|
|
||||||
Log.Information(
|
|
||||||
"Migrated config v10 → v11: PopOutInputEnabled added (global, default off), " +
|
|
||||||
"SeenPopOutInputHint added (default false)");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hellion Chat v11 → v12 — flips Configuration.PopOutInputEnabled from
|
|
||||||
// the v0.6.0 opt-in default (false) to opt-out (true) per v0.6.1 UX
|
|
||||||
// polish. Hard-flip is a deliberate design call (see Spec section 5.7);
|
|
||||||
// users are notified via the v0.6.1 hint banner (SeenPopOutHeaderHint
|
|
||||||
// reset). Re-toggle after migration is preserved because this block
|
|
||||||
// only fires for Version < 12.
|
|
||||||
if (Config.Version < 12)
|
|
||||||
{
|
|
||||||
Config.PopOutInputEnabled = true;
|
|
||||||
Config.SeenPopOutHeaderHint = false;
|
|
||||||
Config.Version = 12;
|
|
||||||
SaveConfig();
|
|
||||||
Log.Information(
|
|
||||||
"Migrated config v11 → v12: PopOutInputEnabled hard-flipped to true (v0.6.1 default), " +
|
|
||||||
"SeenPopOutHeaderHint reset to false (v0.6.1 banner re-armed)");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hellion Chat v12 → v13 — hard-resets the tab layout to the
|
|
||||||
// sharpened v1.0.0 defaults (5 thematic tabs, see TabsUtil and
|
|
||||||
// the default-fill block below). Existing tab state is wiped
|
|
||||||
// because per-channel mapping from the old General preset to
|
|
||||||
// the new General/System split would be ambiguous and would
|
|
||||||
// produce subtly wrong results for users who tweaked the old
|
|
||||||
// layout. A timestamped backup of the live config is written
|
|
||||||
// alongside it as a manual restore safety net. The wipe scope
|
|
||||||
// is intentionally narrow: only Config.Tabs is reset; Privacy,
|
|
||||||
// Retention, Theme and every other knob keeps its current value.
|
|
||||||
if (Config.Version < 13)
|
|
||||||
{
|
|
||||||
var pluginConfigsDir = Interface.ConfigDirectory.Parent?.FullName;
|
|
||||||
if (pluginConfigsDir is not null)
|
|
||||||
{
|
|
||||||
var liveConfigPath = Path.Combine(pluginConfigsDir, $"{Interface.InternalName}.json");
|
|
||||||
var backupPath = Path.Combine(pluginConfigsDir, $"{Interface.InternalName}.json.pre-v13-backup");
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (File.Exists(liveConfigPath))
|
|
||||||
File.Copy(liveConfigPath, backupPath, overwrite: true);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Log.Warning(ex, "HellionChat: pre-v13 config backup failed");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Config.Tabs.Clear();
|
|
||||||
Config.Version = 13;
|
|
||||||
SaveConfig();
|
|
||||||
|
|
||||||
Log.Information(
|
|
||||||
"Migrated config v12 → v13: tab layout hard-reset to v1.0.0 defaults; " +
|
|
||||||
"pre-v13 config backup written next to the live file. " +
|
|
||||||
"Default tabs will be populated by the Tabs.Count == 0 block.");
|
|
||||||
|
|
||||||
Notification.AddNotification(new Dalamud.Interface.ImGuiNotification.Notification
|
|
||||||
{
|
|
||||||
Title = HellionStrings.SettingsRefactor_Migration_Title,
|
|
||||||
Content = HellionStrings.SettingsRefactor_Migration_Content,
|
|
||||||
Type = Dalamud.Interface.ImGuiNotification.NotificationType.Info,
|
|
||||||
InitialDuration = TimeSpan.FromSeconds(25),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hellion Chat v13 → v14 — theme-engine migration. Alle User landen
|
|
||||||
// auf "hellion-arctic" als neues Default-Theme; die alte
|
|
||||||
// HellionThemeEnabled-Flag wird deprecated und nur noch ein Release
|
|
||||||
// als Safety-Net im JSON behalten. Window-Opacity wandert von
|
|
||||||
// HellionThemeWindowOpacity in das neue WindowOpacity-Feld.
|
|
||||||
//
|
|
||||||
// v1.4.0 (F5.4): Pre-v13-Backup wird gelesen, HellionThemeWindowOpacity
|
|
||||||
// ins neue Feld gezogen. Override nur wenn WindowOpacity noch beim
|
|
||||||
// Default sitzt — sonst hat der User in der Zwischenzeit (z.B. via
|
|
||||||
// WindowAlpha → WindowOpacity in v15→v16) explizit etwas gesetzt.
|
|
||||||
if (Config.Version < 14)
|
|
||||||
{
|
|
||||||
Config.Theme = "hellion-arctic";
|
|
||||||
|
|
||||||
var oldThemeOpacity = TryReadPreV13ThemeOpacity();
|
|
||||||
if (oldThemeOpacity is { } legacy
|
|
||||||
&& Math.Abs(Config.WindowOpacity - 0.85f) < 0.001f)
|
|
||||||
{
|
|
||||||
Config.WindowOpacity = Math.Clamp(legacy, 0.5f, 1.0f);
|
|
||||||
Log.Information(
|
|
||||||
$"Migrated pre-v13 HellionThemeWindowOpacity {legacy} to WindowOpacity {Config.WindowOpacity}");
|
|
||||||
}
|
|
||||||
|
|
||||||
Config.ReduceMotion = false;
|
|
||||||
Config.UseCompactDensity = false;
|
|
||||||
Config.Version = 14;
|
|
||||||
SaveConfig();
|
|
||||||
Log.Information(
|
|
||||||
"Migrated config v13 → v14: theme engine introduced, all users land on hellion-arctic; " +
|
|
||||||
"pick chat2-classic in Settings → Themes for the upstream look");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Config.Version < 15)
|
|
||||||
{
|
|
||||||
// v1.2.0 — keine Datenmigration nötig. Removal der deprecated
|
|
||||||
// Theme-Felder ist reine Schema-Bereinigung (System.Text.Json
|
|
||||||
// ignoriert unbekannte Felder im JSON, daher kein Crash bei
|
|
||||||
// Configs die noch HellionThemeEnabled/HellionThemeWindowOpacity
|
|
||||||
// serialisiert haben — die Werte verfallen einfach).
|
|
||||||
Config.Version = 15;
|
|
||||||
SaveConfig();
|
|
||||||
Log.Information(
|
|
||||||
"Migrated config v14 → v15: legacy theme fields removed " +
|
|
||||||
"(HellionThemeEnabled, HellionThemeWindowOpacity)");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hellion Chat v15 → v16 — Settings Cleanup. Re-Sortierung der
|
|
||||||
// Tabs auf der UI-Seite (datenneutral). 4 tote Felder verfallen
|
|
||||||
// beim System.Text.Json-Deserialize (OverrideStyle, ChosenStyle,
|
|
||||||
// WindowAlpha, ShowThemeQuickPicker — sind alle nicht mehr im
|
|
||||||
// Configuration-Schema definiert). WindowAlpha wird zuvor auf
|
|
||||||
// WindowOpacity gemappt damit User die ihn gesetzt hatten ihre
|
|
||||||
// Transparenz-Einstellung behalten.
|
|
||||||
if (Config.Version < 16)
|
|
||||||
{
|
|
||||||
var pluginConfigsDir = Interface.ConfigDirectory.Parent?.FullName;
|
|
||||||
var liveConfigPath = pluginConfigsDir is not null
|
|
||||||
? Path.Combine(pluginConfigsDir, $"{Interface.InternalName}.json")
|
|
||||||
: null;
|
|
||||||
|
|
||||||
// Backup-Datei neben der live Config — Pattern aus v13 Branch.
|
|
||||||
if (pluginConfigsDir is not null && liveConfigPath is not null)
|
|
||||||
{
|
|
||||||
var backupPath = Path.Combine(pluginConfigsDir, $"{Interface.InternalName}.json.pre-v16-backup");
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (File.Exists(liveConfigPath))
|
|
||||||
File.Copy(liveConfigPath, backupPath, overwrite: true);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Log.Warning(ex, "HellionChat: pre-v16 config backup failed");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pre-v16 Felder einmalig roh aus dem JSON lesen, da sie nicht
|
|
||||||
// mehr im Configuration-Schema sind (und damit aus Config nicht
|
|
||||||
// mehr abrufbar). WindowAlpha → WindowOpacity Mapping nur wenn
|
|
||||||
// User WindowOpacity noch nicht selbst angefasst hat (Default
|
|
||||||
// 0.85), sonst gewinnt der User-Wert.
|
|
||||||
float oldWindowAlpha = 100f;
|
|
||||||
bool oldOverrideStyle = false;
|
|
||||||
if (liveConfigPath is not null)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (File.Exists(liveConfigPath))
|
|
||||||
{
|
|
||||||
var rawJson = File.ReadAllText(liveConfigPath);
|
|
||||||
using var doc = System.Text.Json.JsonDocument.Parse(rawJson);
|
|
||||||
if (doc.RootElement.TryGetProperty("WindowAlpha", out var alphaProp)
|
|
||||||
&& alphaProp.ValueKind == System.Text.Json.JsonValueKind.Number)
|
|
||||||
{
|
|
||||||
oldWindowAlpha = alphaProp.GetSingle();
|
|
||||||
}
|
|
||||||
if (doc.RootElement.TryGetProperty("OverrideStyle", out var ovProp)
|
|
||||||
&& ovProp.ValueKind is System.Text.Json.JsonValueKind.True)
|
|
||||||
{
|
|
||||||
oldOverrideStyle = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Log.Warning(ex, "HellionChat: pre-v16 legacy-field lookup failed, defaults assumed");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TEST-MIRROR: Hellion Build test/_Helpers/MigrationLogic.cs (local-only repo)
|
|
||||||
// If you change the formula here, change the mirror — tests assert the contract.
|
|
||||||
if (oldWindowAlpha != 100f
|
|
||||||
&& Math.Abs(Config.WindowOpacity - 0.85f) < 0.001f)
|
|
||||||
{
|
|
||||||
Config.WindowOpacity = Math.Clamp(oldWindowAlpha / 100f, 0.5f, 1.0f);
|
|
||||||
Log.Information(
|
|
||||||
$"Migrated WindowAlpha {oldWindowAlpha} to WindowOpacity {Config.WindowOpacity}");
|
|
||||||
}
|
|
||||||
else if (oldWindowAlpha != 100f)
|
|
||||||
{
|
|
||||||
Log.Information(
|
|
||||||
$"Skipped WindowAlpha→WindowOpacity migration: WindowOpacity already user-set " +
|
|
||||||
$"({Config.WindowOpacity}), legacy WindowAlpha value {oldWindowAlpha} dropped.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (oldOverrideStyle)
|
|
||||||
{
|
|
||||||
Notification.AddNotification(new Dalamud.Interface.ImGuiNotification.Notification
|
|
||||||
{
|
|
||||||
Title = "Hellion Chat 1.2.1",
|
|
||||||
Content = HellionStrings.Migration_v16_OverrideStyle_Toast,
|
|
||||||
Type = Dalamud.Interface.ImGuiNotification.NotificationType.Info,
|
|
||||||
InitialDuration = TimeSpan.FromSeconds(25),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// v1.2.1 Default-Bumps für UX-Verbesserungen. Pattern: nur
|
|
||||||
// migrieren wenn der User noch auf dem alten Default ist.
|
|
||||||
// Bei bool-Werten ist die Erkennung pragmatisch — wer den
|
|
||||||
// alten Default aktiv ausgeschaltet hatte, erlebt das als
|
|
||||||
// Regression und stellt es einmal in den Settings zurück.
|
|
||||||
// Der Trade-Off ist akzeptabel weil die alten Defaults in
|
|
||||||
// v1.2.0 erst neu eingeführt wurden und kaum jemand aktiv
|
|
||||||
// umgeschaltet hat.
|
|
||||||
if (!Config.UseCompactDensity)
|
|
||||||
{
|
|
||||||
Config.UseCompactDensity = true;
|
|
||||||
Log.Information("v16 default-bump: UseCompactDensity false → true");
|
|
||||||
}
|
|
||||||
if (!Config.HideInNewGamePlusMenu)
|
|
||||||
{
|
|
||||||
Config.HideInNewGamePlusMenu = true;
|
|
||||||
Log.Information("v16 default-bump: HideInNewGamePlusMenu false → true");
|
|
||||||
}
|
|
||||||
if (!Config.HideSameTimestamps)
|
|
||||||
{
|
|
||||||
Config.HideSameTimestamps = true;
|
|
||||||
Log.Information("v16 default-bump: HideSameTimestamps false → true");
|
|
||||||
}
|
|
||||||
if (Config.MaxLinesToRender == 5000)
|
|
||||||
{
|
|
||||||
Config.MaxLinesToRender = 2500;
|
|
||||||
Log.Information("v16 default-bump: MaxLinesToRender 5000 → 2500");
|
|
||||||
}
|
|
||||||
if (Config.ChatColours.Count == 0)
|
|
||||||
{
|
|
||||||
foreach (var (channel, colour) in Resources.ChatColourPresets.All["Hellion"].Colours)
|
|
||||||
Config.ChatColours[channel] = colour;
|
|
||||||
Log.Information("v16 default-bump: ChatColours empty → Hellion brand preset");
|
|
||||||
}
|
|
||||||
|
|
||||||
Config.Version = 16;
|
|
||||||
SaveConfig();
|
|
||||||
Log.Information(
|
|
||||||
"Migrated config v15 → v16: settings cleanup, " +
|
|
||||||
"OverrideStyle/ChosenStyle/WindowAlpha/ShowThemeQuickPicker dropped from schema");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hellion v1.0.0 default tab layout. Five thematically separated
|
// Hellion v1.0.0 default tab layout. Five thematically separated
|
||||||
// tabs: General catches the immediate-surroundings public chat
|
// tabs: General catches the immediate-surroundings public chat
|
||||||
// (Say/Yell/Shout) only; System absorbs the rest of the technical
|
// (Say/Yell/Shout) only; System absorbs the rest of the technical
|
||||||
@@ -435,54 +178,54 @@ public sealed class Plugin : IDalamudPlugin
|
|||||||
Config.Tabs.Add(TabsUtil.HellionLinkshell);
|
Config.Tabs.Add(TabsUtil.HellionLinkshell);
|
||||||
}
|
}
|
||||||
|
|
||||||
LanguageChanged(Interface.UiLanguage);
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
ImGuiUtil.Initialize(this);
|
|
||||||
|
|
||||||
FileDialogManager = new FileDialogManager();
|
// Sync allocation + handle registration. BuildFonts() registers
|
||||||
|
// IFontHandles with Dalamud's UiBuilder.FontAtlas — registration
|
||||||
Commands = new Commands();
|
// itself is non-blocking (handles stored, lambdas queued). Dalamud
|
||||||
Functions = new GameFunctions.GameFunctions(this);
|
// rebuilds the atlas on its own pipeline a few frames later; first
|
||||||
Ipc = new IpcManager();
|
// frames render with the default font until the rebuild lands and
|
||||||
TypingIpc = new TypingIpc(this);
|
// ImGui switches to Hellion-Exo2 / NotoSans (visible "font-pop").
|
||||||
ExtraChat = new ExtraChat();
|
// Mirrors ChatTwo Plugin.cs:152.
|
||||||
FontManager = new FontManager();
|
FontManager = new FontManager();
|
||||||
|
FontManager.BuildFonts();
|
||||||
|
|
||||||
// v1.1.0 — Theme-Engine init. Custom-Themes liegen in
|
// Theme init stays sync on the LoadAsync continuation — cheap,
|
||||||
// pluginConfigs/HellionChat/themes/, lazy geladen beim ersten Get.
|
// and Active is read every Draw frame, so the registry must be
|
||||||
|
// wired before the first hook fires.
|
||||||
var customThemesDir = Path.Combine(Interface.ConfigDirectory.FullName, "themes");
|
var customThemesDir = Path.Combine(Interface.ConfigDirectory.FullName, "themes");
|
||||||
Directory.CreateDirectory(customThemesDir);
|
Directory.CreateDirectory(customThemesDir);
|
||||||
SeedExampleThemeIfEmpty(customThemesDir);
|
SeedExampleThemeIfEmpty(customThemesDir);
|
||||||
ThemeRegistry = new Themes.ThemeRegistry(customThemesDir);
|
ThemeRegistry = new Themes.ThemeRegistry(customThemesDir);
|
||||||
ThemeRegistry.Switch(Config.Theme);
|
ThemeRegistry.Switch(Config.Theme);
|
||||||
|
|
||||||
// SelfTest hooks live alongside the live registry — the steps
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
// poll Active per frame and need the registry already wired.
|
|
||||||
SelfTestRegistry.RegisterTestSteps([
|
|
||||||
new SelfTests.ThemeSwitchSelfTestStep(this),
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Plugin integrations register their IPC subscribers up-front so
|
// Service allocations: order encodes dependencies. Commands is
|
||||||
// Ready/Disposing events from the target plugins are caught from
|
// alloc-only here; Initialise() runs after windows exist so the
|
||||||
// the very first frame, even if the user's Honorific reloads
|
// slash-commands can toggle their visibility. HonorificService
|
||||||
// mid-session. See HellionChat/Integrations/HonorificService.cs.
|
// registers IPC subscribers up-front so Ready/Disposing events
|
||||||
|
// are caught from the very first frame.
|
||||||
|
FileDialogManager = new FileDialogManager();
|
||||||
|
Commands = new Commands();
|
||||||
|
Functions = new GameFunctions.GameFunctions(this);
|
||||||
|
Ipc = new IpcManager();
|
||||||
|
TypingIpc = new TypingIpc(this);
|
||||||
|
ExtraChat = new ExtraChat();
|
||||||
HonorificService = new Integrations.HonorificService(Interface, Log, Framework);
|
HonorificService = new Integrations.HonorificService(Interface, Log, Framework);
|
||||||
|
|
||||||
StatusBar = new Ui.StatusBar();
|
StatusBar = new Ui.StatusBar();
|
||||||
|
MessageManager = new MessageManager(this);
|
||||||
|
|
||||||
MessageManager = new MessageManager(this); // Does it require UI?
|
// Auto-Tell-Tabs subscribes to MessageManager.MessageProcessed for
|
||||||
|
// live tells and to ClientState.Logout for cleanup; needs the live
|
||||||
// Hellion Chat — Auto-Tell-Tabs service. Subscribes to the
|
// store handed in at construction.
|
||||||
// MessageManager's MessageProcessed event for live tells and
|
|
||||||
// to ClientState.Logout for the cleanup pass. Created after
|
|
||||||
// MessageManager so the constructor can hand off the live
|
|
||||||
// store and event source.
|
|
||||||
AutoTellTabsService = new AutoTellTabsService(this, MessageManager, MessageManager.Store);
|
AutoTellTabsService = new AutoTellTabsService(this, MessageManager, MessageManager.Store);
|
||||||
AutoTellTabsService.Initialize();
|
AutoTellTabsService.Initialize();
|
||||||
|
|
||||||
// Hellion Chat — daily retention sweep, off-thread so it never
|
// SelfTest steps poll Active per frame and need the registry wired.
|
||||||
// blocks plugin load. Skips itself when disabled or already ran
|
SelfTestRegistry.RegisterTestSteps([
|
||||||
// within the past 24 hours.
|
new SelfTests.ThemeSwitchSelfTestStep(this),
|
||||||
RunRetentionSweepIfDue();
|
]);
|
||||||
|
|
||||||
ChatLogWindow = new ChatLogWindow(this);
|
ChatLogWindow = new ChatLogWindow(this);
|
||||||
SettingsWindow = new SettingsWindow(this);
|
SettingsWindow = new SettingsWindow(this);
|
||||||
@@ -507,17 +250,38 @@ public sealed class Plugin : IDalamudPlugin
|
|||||||
if (!Config.FirstRunCompleted)
|
if (!Config.FirstRunCompleted)
|
||||||
FirstRunWizard.IsOpen = true;
|
FirstRunWizard.IsOpen = true;
|
||||||
|
|
||||||
FontManager.BuildFonts();
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
Interface.UiBuilder.DisableCutsceneUiHide = true;
|
|
||||||
Interface.UiBuilder.DisableGposeUiHide = true;
|
|
||||||
|
|
||||||
// let all the other components register, then initialize commands
|
// let all the other components register, then initialize commands
|
||||||
Commands.Initialise();
|
Commands.Initialise();
|
||||||
|
|
||||||
|
// Daily retention sweep, fire-and-forget. Skips itself when
|
||||||
|
// disabled or when it already ran within the past 24 hours.
|
||||||
|
RunRetentionSweepIfDue();
|
||||||
|
|
||||||
|
if (Config.ShowEmotes)
|
||||||
|
_ = EmoteCache.LoadData(); // Fire-and-forget, exceptions caught inside
|
||||||
|
|
||||||
if (Interface.Reason is not PluginLoadReason.Boot)
|
if (Interface.Reason is not PluginLoadReason.Boot)
|
||||||
MessageManager.FilterAllTabsAsync();
|
MessageManager.FilterAllTabsAsync();
|
||||||
|
|
||||||
|
Interface.UiBuilder.DisableCutsceneUiHide = true;
|
||||||
|
Interface.UiBuilder.DisableGposeUiHide = true;
|
||||||
|
|
||||||
|
#if !DEBUG
|
||||||
|
// Fire-and-forget on a worker thread. The first auto-translate use of
|
||||||
|
// a session may have a sub-second hitch if the cache hasn't filled yet,
|
||||||
|
// but that's preferable to making every user wait ~300 ms during
|
||||||
|
// plugin load for a cache they may never touch. ChatTwo (upstream)
|
||||||
|
// does this sync; we trade load-time for first-use latency.
|
||||||
|
_ = Task.Run(AutoTranslate.PreloadCache, cancellationToken);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
|
// (B1) Hooks last: every service and window must be live before
|
||||||
|
// Dalamud fires our first Draw / FrameworkUpdate tick. Anything
|
||||||
|
// earlier risks rendering against null FontManager / ThemeRegistry.
|
||||||
Framework.Update += FrameworkUpdate;
|
Framework.Update += FrameworkUpdate;
|
||||||
Interface.UiBuilder.Draw += Draw;
|
Interface.UiBuilder.Draw += Draw;
|
||||||
Interface.LanguageChanged += LanguageChanged;
|
Interface.LanguageChanged += LanguageChanged;
|
||||||
@@ -526,102 +290,127 @@ public sealed class Plugin : IDalamudPlugin
|
|||||||
// most useful landing place; OpenConfigUi is already wired to
|
// most useful landing place; OpenConfigUi is already wired to
|
||||||
// the same toggle inside SettingsWindow.
|
// the same toggle inside SettingsWindow.
|
||||||
Interface.UiBuilder.OpenMainUi += OpenMainUi;
|
Interface.UiBuilder.OpenMainUi += OpenMainUi;
|
||||||
|
|
||||||
if (Config.ShowEmotes)
|
|
||||||
_ = EmoteCache.LoadData(); // Fire-and-forget intentional, exceptions are caught inside
|
|
||||||
|
|
||||||
#if !DEBUG
|
|
||||||
// Avoid 300ms hitch when sending first message by preloading the
|
|
||||||
// auto-translate cache. Don't do this in debug because it makes
|
|
||||||
// profiling difficult.
|
|
||||||
AutoTranslate.PreloadCache();
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch
|
||||||
{
|
{
|
||||||
Log.Error(ex, "Plugin load threw an error, turning off plugin");
|
// Mirror the v1.4.0 load-failure recovery: hand off to DisposeAsync
|
||||||
Dispose();
|
// so partially-built services are torn down. Swallow the cleanup
|
||||||
|
// exception so the original load failure stays the visible cause.
|
||||||
// Re-throw the exception to fail the plugin load.
|
try { await DisposeAsync().ConfigureAwait(false); }
|
||||||
|
catch { /* keep original failure */ }
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Suppressing this warning because Dispose() is called in Plugin() if the
|
// Suppressing this warning because DisposeAsync may run after a partial
|
||||||
// load fails, so some values may not be initialized.
|
// LoadAsync, so some properties may not be initialized.
|
||||||
[SuppressMessage("ReSharper", "ConditionalAccessQualifierIsNonNullableAccordingToAPIContract")]
|
[SuppressMessage("ReSharper", "ConditionalAccessQualifierIsNonNullableAccordingToAPIContract")]
|
||||||
public void Dispose()
|
public async ValueTask DisposeAsync()
|
||||||
{
|
{
|
||||||
Interface.UiBuilder.OpenMainUi -= OpenMainUi;
|
// (B3) Idempotency guard — Dalamud may reload-race us; second
|
||||||
Interface.LanguageChanged -= LanguageChanged;
|
// call short-circuits so we don't double-dispose services.
|
||||||
Interface.UiBuilder.Draw -= Draw;
|
if (Interlocked.Exchange(ref _disposeStarted, 1) != 0)
|
||||||
Framework.Update -= FrameworkUpdate;
|
return;
|
||||||
GameFunctions.GameFunctions.SetChatInteractable(true);
|
|
||||||
|
|
||||||
// FrameworkUpdate would have fired the pending save in N frames,
|
Exception? failure = null;
|
||||||
// but we just unsubscribed it. -1 is the idle sentinel.
|
|
||||||
if (DeferredSaveFrames >= 0)
|
// Hooks unsubscribe FIRST so no Draw / FrameworkUpdate / LanguageChanged
|
||||||
|
// tick can fire while we're tearing services down. Mirrors the
|
||||||
|
// hooks-last subscribe order in LoadAsync.
|
||||||
|
failure = CaptureFailure(failure, () => Interface.UiBuilder.OpenMainUi -= OpenMainUi);
|
||||||
|
failure = CaptureFailure(failure, () => Interface.LanguageChanged -= LanguageChanged);
|
||||||
|
failure = CaptureFailure(failure, () => Interface.UiBuilder.Draw -= Draw);
|
||||||
|
failure = CaptureFailure(failure, () => Framework.Update -= FrameworkUpdate);
|
||||||
|
|
||||||
|
// v1.4.0 F5.3 — flush a pending DeferredSave before service teardown,
|
||||||
|
// since FrameworkUpdate just got unsubscribed and won't fire it.
|
||||||
|
failure = CaptureFailure(failure, () =>
|
||||||
{
|
{
|
||||||
SaveConfig();
|
if (DeferredSaveFrames >= 0)
|
||||||
DeferredSaveFrames = -1;
|
{
|
||||||
|
SaveConfig();
|
||||||
|
DeferredSaveFrames = -1;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Auto-Tell-Tabs unsubscribes from MessageProcessed before MessageManager
|
||||||
|
// goes away. Pure-memory cleanup, no framework-thread requirement.
|
||||||
|
failure = CaptureFailure(failure, () => AutoTellTabsService?.Dispose());
|
||||||
|
|
||||||
|
// v1.4.0 F6.2 — MessageManager has its own async dispose path
|
||||||
|
// (DB flush, pending-message thread shutdown). Run it before the
|
||||||
|
// framework-block so the worker threads are quiesced first.
|
||||||
|
if (MessageManager is not null)
|
||||||
|
{
|
||||||
|
failure = await CaptureFailureAsync(failure, () => MessageManager.DisposeAsync().AsTask())
|
||||||
|
.ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
HonorificService?.Dispose();
|
// (B4) Game-Function / IPC / UI-Window cleanup MUST run on the
|
||||||
|
// framework thread. WindowSystem mutations and IPC subscriber
|
||||||
WindowSystem?.RemoveAllWindows();
|
// disposes touch Dalamud state that's only safe from the framework.
|
||||||
ChatLogWindow?.Dispose();
|
// Worker-thread DisposeAsync would race the next Draw tick.
|
||||||
DbViewer?.Dispose();
|
// Per-line CaptureFailure so a single throw can't strand the lines
|
||||||
InputPreview?.Dispose();
|
// behind it; mirrors Lightless DisposeFrameworkBoundServicesAsync.
|
||||||
SettingsWindow?.Dispose();
|
|
||||||
DebuggerWindow?.Dispose();
|
|
||||||
SeStringDebugger?.Dispose();
|
|
||||||
|
|
||||||
TypingIpc?.Dispose();
|
|
||||||
ExtraChat?.Dispose();
|
|
||||||
Ipc?.Dispose();
|
|
||||||
// Dispose the Auto-Tell-Tabs service before MessageManager so it
|
|
||||||
// can cleanly unsubscribe from the MessageProcessed event before
|
|
||||||
// its source goes away.
|
|
||||||
AutoTellTabsService?.Dispose();
|
|
||||||
MessageManager?.DisposeAsync().AsTask().Wait();
|
|
||||||
Functions?.Dispose();
|
|
||||||
Commands?.Dispose();
|
|
||||||
|
|
||||||
EmoteCache.Dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reads HellionThemeWindowOpacity from the pre-v13 backup the v12→v13
|
|
||||||
// block writes alongside the live config. Null when absent, unreadable,
|
|
||||||
// or schema-incompatible — all valid steady states (fresh install,
|
|
||||||
// backup pruned, pre-v12 config). Errors log at Warning so a corrupted
|
|
||||||
// backup stays visible in /xllog without breaking the migration.
|
|
||||||
private static float? TryReadPreV13ThemeOpacity()
|
|
||||||
{
|
|
||||||
var pluginConfigsDir = Interface.ConfigDirectory.Parent?.FullName;
|
|
||||||
if (pluginConfigsDir is null)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
var backupPath = Path.Combine(pluginConfigsDir, $"{Interface.InternalName}.json.pre-v13-backup");
|
|
||||||
if (!File.Exists(backupPath))
|
|
||||||
return null;
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
using var stream = File.OpenRead(backupPath);
|
await Framework.RunOnFrameworkThread(() =>
|
||||||
using var doc = System.Text.Json.JsonDocument.Parse(stream);
|
|
||||||
if (doc.RootElement.TryGetProperty("HellionThemeWindowOpacity", out var prop)
|
|
||||||
&& prop.ValueKind == System.Text.Json.JsonValueKind.Number
|
|
||||||
&& prop.TryGetSingle(out var value))
|
|
||||||
{
|
{
|
||||||
return value;
|
// Game-Functions first — other services may still query
|
||||||
}
|
// chat-interactable state during their Dispose.
|
||||||
return null;
|
failure = CaptureFailure(failure, () => GameFunctions.GameFunctions.SetChatInteractable(true));
|
||||||
|
|
||||||
|
// IPC subscribers — dispose before windows so any final
|
||||||
|
// event firing from the IPC source can't reach a half-torn
|
||||||
|
// ChatLogWindow.
|
||||||
|
failure = CaptureFailure(failure, () => HonorificService?.Dispose());
|
||||||
|
failure = CaptureFailure(failure, () => TypingIpc?.Dispose());
|
||||||
|
failure = CaptureFailure(failure, () => ExtraChat?.Dispose());
|
||||||
|
failure = CaptureFailure(failure, () => Ipc?.Dispose());
|
||||||
|
|
||||||
|
// Windows — RemoveAllWindows first, then per-window Dispose.
|
||||||
|
// Order matches the pre-v1.4.3 Dispose body byte-for-byte.
|
||||||
|
// CommandHelpWindow and FirstRunWizard don't implement
|
||||||
|
// IDisposable; their resources are reclaimed via WindowSystem.
|
||||||
|
failure = CaptureFailure(failure, () => WindowSystem?.RemoveAllWindows());
|
||||||
|
failure = CaptureFailure(failure, () => ChatLogWindow?.Dispose());
|
||||||
|
failure = CaptureFailure(failure, () => DbViewer?.Dispose());
|
||||||
|
failure = CaptureFailure(failure, () => InputPreview?.Dispose());
|
||||||
|
failure = CaptureFailure(failure, () => SettingsWindow?.Dispose());
|
||||||
|
failure = CaptureFailure(failure, () => DebuggerWindow?.Dispose());
|
||||||
|
failure = CaptureFailure(failure, () => SeStringDebugger?.Dispose());
|
||||||
|
}).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Log.Warning(ex, "HellionChat: pre-v13 backup lookup failed, defaulting WindowOpacity");
|
failure ??= ex;
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Pure-memory cleanups — no Framework / UI / IPC touch, so they
|
||||||
|
// run on whatever thread DisposeAsync resumes on.
|
||||||
|
failure = CaptureFailure(failure, () => Functions?.Dispose());
|
||||||
|
failure = CaptureFailure(failure, () => Commands?.Dispose());
|
||||||
|
failure = CaptureFailure(failure, () => EmoteCache.Dispose());
|
||||||
|
|
||||||
|
if (failure is not null)
|
||||||
|
ExceptionDispatchInfo.Capture(failure).Throw();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lightless-pattern capture helpers: run cleanup, remember the FIRST
|
||||||
|
// exception, keep going. Without these one mid-teardown failure would
|
||||||
|
// skip every cleanup behind it and leave services half-torn.
|
||||||
|
private static Exception? CaptureFailure(Exception? failure, Action action)
|
||||||
|
{
|
||||||
|
try { action(); }
|
||||||
|
catch (Exception ex) { failure ??= ex; }
|
||||||
|
return failure;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async ValueTask<Exception?> CaptureFailureAsync(Exception? failure, Func<Task> action)
|
||||||
|
{
|
||||||
|
try { await action().ConfigureAwait(false); }
|
||||||
|
catch (Exception ex) { failure ??= ex; }
|
||||||
|
return failure;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void MigrateFromChatTwoLayout()
|
private static void MigrateFromChatTwoLayout()
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ internal sealed class Information : ISettingsTab
|
|||||||
ImGui.TextUnformatted(Language.Options_About_Github_Issues);
|
ImGui.TextUnformatted(Language.Options_About_Github_Issues);
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
if (ImGuiUtil.IconButton(FontAwesomeIcon.ExternalLinkAlt, "githubIssues"))
|
if (ImGuiUtil.IconButton(FontAwesomeIcon.ExternalLinkAlt, "githubIssues"))
|
||||||
Dalamud.Utility.Util.OpenLink("https://github.com/JonKazama-Hellion/HellionChat/issues");
|
Dalamud.Utility.Util.OpenLink("https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/issues");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ is below.
|
|||||||
If something in HellionChat causes problems, especially if it relates back
|
If something in HellionChat causes problems, especially if it relates back
|
||||||
to Chat 2 or to anything Infi or Anna would want flagged:
|
to Chat 2 or to anything Infi or Anna would want flagged:
|
||||||
|
|
||||||
- **GitHub Issues:** [JonKazama-Hellion/HellionChat/issues](https://github.com/JonKazama-Hellion/HellionChat/issues)
|
- **Gitea Issues:** [JonKazama-Hellion/HellionChat/issues](https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/issues)
|
||||||
- **Discord:** `@j.j_kazama`
|
- **Discord:** `@j.j_kazama`
|
||||||
- **Email (business):** kontakt@hellion-media.de
|
- **Email (business):** kontakt@hellion-media.de
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -121,10 +121,10 @@ Adjust the channel whitelist or set retention to a low value. Both take effect i
|
|||||||
| Party | Why they appear | What reaches them | Their privacy policy |
|
| Party | Why they appear | What reaches them | Their privacy policy |
|
||||||
| --- | --- | --- | --- |
|
| --- | --- | --- | --- |
|
||||||
| BetterTTV (NightDev LLC) | Optional emote rendering | HTTPS request for an emote ID; your IP | <https://betterttv.com/privacy> |
|
| BetterTTV (NightDev LLC) | Optional emote rendering | HTTPS request for an emote ID; your IP | <https://betterttv.com/privacy> |
|
||||||
| GitHub (Microsoft) | Plugin distribution via custom repo, issue tracker | Whatever GitHub sees from any HTTPS request to a public repo | <https://docs.github.com/site-policy/privacy-policies/github-general-privacy-statement> |
|
| Hellion Forge (Gitea, self-hosted by Hellion Online Media) | Plugin distribution via custom repo, issue tracker | Whatever the Gitea instance sees from any HTTPS request to a public repo | <https://hellion-media.de/datenschutz> |
|
||||||
| Dalamud / XIVLauncher (goatcorp) | Plugin loader, font subsystem, repo polling | Whatever Dalamud reports for itself; out of HellionChat's scope | <https://github.com/goatcorp/Dalamud> |
|
| Dalamud / XIVLauncher (goatcorp) | Plugin loader, font subsystem, repo polling | Whatever Dalamud reports for itself; out of HellionChat's scope | <https://github.com/goatcorp/Dalamud> |
|
||||||
|
|
||||||
GitHub and the Dalamud/XIVLauncher loader are unavoidable for anyone playing FFXIV through Dalamud at all. BetterTTV is the only third party HellionChat introduces on top of that baseline, and it is opt-out via settings.
|
The Hellion Forge Gitea instance and the Dalamud/XIVLauncher loader are unavoidable for anyone using HellionChat through Dalamud at all. BetterTTV is the only third party HellionChat introduces on top of that baseline, and it is opt-out via settings.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
# Hellion Chat
|
# Hellion Chat
|
||||||
|
|
||||||
[](https://github.com/JonKazama-Hellion/HellionChat/actions/workflows/build.yml)
|
[](https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/actions/workflows/build.yml)
|
||||||
[](https://github.com/JonKazama-Hellion/HellionChat/security/code-scanning)
|
|
||||||
[](LICENSE)
|
[](LICENSE)
|
||||||
[](https://github.com/JonKazama-Hellion/HellionChat/releases/latest)
|
[](https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/releases/latest)
|
||||||
[](https://github.com/goatcorp/Dalamud)
|
[](https://github.com/goatcorp/Dalamud)
|
||||||
[](https://dotnet.microsoft.com/)
|
[](https://dotnet.microsoft.com/)
|
||||||
[](https://www.finalfantasyxiv.com/)
|
[](https://www.finalfantasyxiv.com/)
|
||||||
@@ -12,7 +11,7 @@
|
|||||||
<img src="docs/images/hellion-forge.png" alt="Hellion Forge" width="180" />
|
<img src="docs/images/hellion-forge.png" alt="Hellion Forge" width="180" />
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
**Version 1.4.2** — Privacy-First-Chat-Plugin für FINAL FANTASY XIV / Dalamud, basierend auf [Chat 2](https://github.com/Infiziert90/ChatTwo) (EUPL-1.2).
|
**Version 1.4.3** — Privacy-First-Chat-Plugin für FINAL FANTASY XIV / Dalamud, basierend auf [Chat 2](https://github.com/Infiziert90/ChatTwo) (EUPL-1.2).
|
||||||
|
|
||||||
Hellion Chat ist ein Privacy-First-Plugin auf dem Chat-2-Fundament. Der größte Teil der Engine kommt aus Chat 2 (Message-Store, Channel-Logik, Hook-System), die meisten Tastenkürzel funktionieren weiterhin wie gewohnt. Was sich ändert: schärfere Privacy-Defaults von Haus aus, eigene Slash-Commands unter `/hellionchat`, kein Webinterface mehr, und mit v1.1.0 eine Theme-Engine als Schritt in Richtung eigenes UI-Look-and-Feel.
|
Hellion Chat ist ein Privacy-First-Plugin auf dem Chat-2-Fundament. Der größte Teil der Engine kommt aus Chat 2 (Message-Store, Channel-Logik, Hook-System), die meisten Tastenkürzel funktionieren weiterhin wie gewohnt. Was sich ändert: schärfere Privacy-Defaults von Haus aus, eigene Slash-Commands unter `/hellionchat`, kein Webinterface mehr, und mit v1.1.0 eine Theme-Engine als Schritt in Richtung eigenes UI-Look-and-Feel.
|
||||||
|
|
||||||
@@ -170,7 +169,7 @@ Hellion Chat wird über ein Dalamud-**Custom-Repository** verteilt.
|
|||||||
1. Dalamud-Settings (`/xlsettings`) → **Experimental** öffnen.
|
1. Dalamud-Settings (`/xlsettings`) → **Experimental** öffnen.
|
||||||
2. Neuen Eintrag unter **Custom Plugin Repositories** anlegen:
|
2. Neuen Eintrag unter **Custom Plugin Repositories** anlegen:
|
||||||
```
|
```
|
||||||
https://raw.githubusercontent.com/JonKazama-Hellion/HellionChat/main/repo.json
|
https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/raw/branch/main/repo.json
|
||||||
```
|
```
|
||||||
3. **Save**, dann in `/xlplugins` → **All Plugins** → Refresh.
|
3. **Save**, dann in `/xlplugins` → **All Plugins** → Refresh.
|
||||||
4. Hellion Chat taucht in der Liste auf, dann installieren wie jedes andere Plugin.
|
4. Hellion Chat taucht in der Liste auf, dann installieren wie jedes andere Plugin.
|
||||||
@@ -225,7 +224,7 @@ Eine optionale Submission ans Dalamud-Main-Plugin-Repo (zusätzlich zum eigenen
|
|||||||
|
|
||||||
## Projektstatus
|
## Projektstatus
|
||||||
|
|
||||||
**Version 1.4.2** — ChatLog Frame-Hot-Path: Per-Frame-Allokationen aus dem ChatLogWindow-Render-Pfad und der Settings-StatusBar eliminiert. Card-Mode-Border-Loop in DrawMessages hebt fünf Invarianten in einen Pre-Loop-Hoist innerhalb des BeginChild-Scopes, AutoTellTabTint bekommt einen Per-Tab-Cache via TabTintCache (separate Validation-Keys pro Cache, kein Cross-Invalidation), StatusBar zieht den Cache-Gate-Check vor die LINQ-Pfade und ersetzt Sum+Count durch eine Single-Pass-Foreach. Realistische Frame-Time-Recovery: 2-5 % in typischen Szenen, mehr bei Pop-Out-Heavy-Setups. Dritter Sub-Patch der v1.4.x Polish-Sweep-Serie (Stand: 2026-05-07).
|
**Version 1.4.3** — Plugin-Load Async-Init plus Repo-Cutover: Plugin auf Dalamud's IAsyncDalamudPlugin-API migriert. Der Konstruktor übernimmt nur noch Bootstrap-Essentials (Config-Load, Language-Init, Conflict-Detection); Migrationen, Service-Allokationen, Window-Konstruktion und Hook-Subscription wandern in LoadAsync, sodass Dalamud die UI während der schweren Arbeit responsive halten kann. Schema-Gate ersetzt die v9 → v16 Migrations-Kette; Configs auf Schema v16+ laden direkt, ältere Configs triggern eine "install v1.4.2 first"-Fehlermeldung. Custom-Repo-URL auf `gitea.hellion-forge.cloud` migriert; das GitHub-Repo bleibt als eingefrorener v1.4.2-Snapshot stehen. Plugin-Load-Zeit liegt bei ~3.7 s Median (5 Reloads), vergleichbar mit v1.4.2: Async-Migration ist Foundation für v1.4.4 Lazy-Init-Optimierungen, kein direkter User-spürbarer Win. Vierter Sub-Patch der v1.4.x Polish-Sweep-Serie (Stand: 2026-05-08).
|
||||||
|
|
||||||
Hellion Chat ist ein eigenständiges Plugin, kein Fork mehr im Repository-Sinne. Vollständig abgeschlossen:
|
Hellion Chat ist ein eigenständiges Plugin, kein Fork mehr im Repository-Sinne. Vollständig abgeschlossen:
|
||||||
|
|
||||||
@@ -244,7 +243,7 @@ Hellion Chat ist ein eigenständiges Plugin, kein Fork mehr im Repository-Sinne.
|
|||||||
- Theme-Engine mit zehn eingebauten Themes plus JSON-Authoring-Format (Engine v1.1.0, Katalog erweitert in v1.2.3, inkl. CVD-safe Hellion Spectrum; Synthwave Sunset in v1.4.1)
|
- Theme-Engine mit zehn eingebauten Themes plus JSON-Authoring-Format (Engine v1.1.0, Katalog erweitert in v1.2.3, inkl. CVD-safe Hellion Spectrum; Synthwave Sunset in v1.4.1)
|
||||||
- ABGR-Cache auf den Theme-Records: HellionStyle.PushGlobal liest pre-computed ABGR statt RGBA→ABGR pro Slot pro Frame (v1.4.1, ~13 % Render-Time-Recovery)
|
- ABGR-Cache auf den Theme-Records: HellionStyle.PushGlobal liest pre-computed ABGR statt RGBA→ABGR pro Slot pro Frame (v1.4.1, ~13 % Render-Time-Recovery)
|
||||||
|
|
||||||
In Arbeit: schrittweise Modernisierung des UI-Look-and-Feel über die Theme-Engine hinaus. Was als Nächstes geplant ist und welche Themen langfristig auf der Liste stehen, steht in [`docs/ROADMAP.md`](docs/ROADMAP.md). Konkrete eingeplante Items werden zusätzlich im [GitHub-Issue-Tracker](https://github.com/JonKazama-Hellion/HellionChat/issues) mit dem `roadmap`-Label geführt.
|
In Arbeit: schrittweise Modernisierung des UI-Look-and-Feel über die Theme-Engine hinaus. Was als Nächstes geplant ist und welche Themen langfristig auf der Liste stehen, steht in [`docs/ROADMAP.md`](docs/ROADMAP.md). Konkrete eingeplante Items werden zusätzlich im [Gitea-Issue-Tracker](https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/issues) mit dem `roadmap`-Label geführt.
|
||||||
|
|
||||||
### Zur Release-Kadenz
|
### Zur Release-Kadenz
|
||||||
|
|
||||||
@@ -255,7 +254,7 @@ Wer den Repo zum ersten Mal sieht, bemerkt schnell viele Releases und sehr viele
|
|||||||
## Community und Support
|
## Community und Support
|
||||||
|
|
||||||
- **Hellion Forge Discord** (Modding- und Plugin-Community von Hellion Online Media): https://discord.gg/X9V7Kcv5gR
|
- **Hellion Forge Discord** (Modding- und Plugin-Community von Hellion Online Media): https://discord.gg/X9V7Kcv5gR
|
||||||
- Bug-Reports und Feature-Requests: [GitHub Issues](https://github.com/JonKazama-Hellion/HellionChat/issues)
|
- Bug-Reports und Feature-Requests: [Gitea Issues](https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/issues)
|
||||||
- Discord DM: `@j.j_kazama`
|
- Discord DM: `@j.j_kazama`
|
||||||
- Weitere Kontaktwege (Security, Privacy, Quick-Questions): siehe [SUPPORT.md](SUPPORT.md)
|
- Weitere Kontaktwege (Security, Privacy, Quick-Questions): siehe [SUPPORT.md](SUPPORT.md)
|
||||||
|
|
||||||
|
|||||||
+5
-9
@@ -3,24 +3,20 @@
|
|||||||
## Reporting a Vulnerability
|
## Reporting a Vulnerability
|
||||||
|
|
||||||
If you find a security issue in HellionChat, please do not open a
|
If you find a security issue in HellionChat, please do not open a
|
||||||
public GitHub issue. Use one of the private channels below so I can
|
public Gitea issue. Use one of the private channels below so I can
|
||||||
investigate and ship a fix before the details go public.
|
investigate and ship a fix before the details go public.
|
||||||
|
|
||||||
**Preferred:**
|
**Preferred:**
|
||||||
[Privately report a vulnerability](https://github.com/JonKazama-Hellion/HellionChat/security/advisories/new)
|
|
||||||
via GitHub Security Advisories. This routes the report directly to me
|
|
||||||
and keeps the conversation off the public timeline.
|
|
||||||
|
|
||||||
**Alternative:**
|
|
||||||
|
|
||||||
| Channel | Address |
|
| Channel | Address |
|
||||||
| ---------- | -------------------------- |
|
| ---------- | -------------------------- |
|
||||||
| Email | `kontakt@hellion-media.de` |
|
| Email | `kontakt@hellion-media.de` |
|
||||||
| Discord DM | `@j.j_kazama` |
|
| Discord DM | `@j.j_kazama` |
|
||||||
|
|
||||||
I respond on weekdays during European business hours. For urgent
|
For urgent disclosures (active exploitation, user-data exposure) email
|
||||||
disclosures (active exploitation, user-data exposure) email is the
|
is the fastest path.
|
||||||
fastest path.
|
|
||||||
|
I respond on weekdays during European business hours.
|
||||||
|
|
||||||
## Scope
|
## Scope
|
||||||
|
|
||||||
|
|||||||
+6
-6
@@ -4,19 +4,19 @@ HellionChat is a small hobby project maintained by one person. There are a few d
|
|||||||
|
|
||||||
## Bugs and feature requests
|
## Bugs and feature requests
|
||||||
|
|
||||||
GitHub issues, using the templates:
|
Gitea issues, using the templates:
|
||||||
|
|
||||||
- [Bug report](https://github.com/JonKazama-Hellion/HellionChat/issues/new?template=bug_report.yml)
|
- [Bug report](https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/issues/new?template=bug_report.yml)
|
||||||
- [Feature request](https://github.com/JonKazama-Hellion/HellionChat/issues/new?template=feature_request.yml)
|
- [Feature request](https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/issues/new?template=feature_request.yml)
|
||||||
|
|
||||||
Please search [existing issues](https://github.com/JonKazama-Hellion/HellionChat/issues?q=is%3Aissue) first. Duplicates get closed and pointed at the original.
|
Please search [existing issues](https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/issues?type=issue) first. Duplicates get closed and pointed at the original.
|
||||||
|
|
||||||
## Security
|
## Security
|
||||||
|
|
||||||
Do **not** open a public issue for security-relevant findings. Use the private advisory route described in [SECURITY.md](SECURITY.md):
|
Do **not** open a public issue for security-relevant findings. Use the private advisory route described in [SECURITY.md](SECURITY.md):
|
||||||
|
|
||||||
- [Private vulnerability advisory](https://github.com/JonKazama-Hellion/HellionChat/security/advisories/new)
|
- Email `kontakt@hellion-media.de` (preferred for security reports)
|
||||||
- Email `kontakt@hellion-media.de`
|
- Discord DM `@j.j_kazama` for time-sensitive findings
|
||||||
|
|
||||||
## Privacy questions
|
## Privacy questions
|
||||||
|
|
||||||
|
|||||||
@@ -82,4 +82,4 @@ Both are good projects. Use what fits you best.
|
|||||||
## Contact
|
## Contact
|
||||||
|
|
||||||
Questions about this disclosure:
|
Questions about this disclosure:
|
||||||
<https://github.com/JonKazama-Hellion/HellionChat/issues>
|
<https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/issues>
|
||||||
|
|||||||
+48
-9
@@ -5,13 +5,52 @@ sich an [Keep a Changelog](https://keepachangelog.com/de/1.0.0/), die
|
|||||||
Version-Nummern folgen [Semantischer Versionierung](https://semver.org/lang/de/).
|
Version-Nummern folgen [Semantischer Versionierung](https://semver.org/lang/de/).
|
||||||
|
|
||||||
Detaillierte Release-Notes pro Version stehen direkt am
|
Detaillierte Release-Notes pro Version stehen direkt am
|
||||||
[GitHub-Release](https://github.com/JonKazama-Hellion/HellionChat/releases)
|
[Gitea-Release](https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/releases)
|
||||||
und im Plugin-Changelog-Block (`HellionChat/HellionChat.yaml` →
|
und im Plugin-Changelog-Block (`HellionChat/HellionChat.yaml` →
|
||||||
`changelog:`). Diese Datei fasst die Releases als Überblick zusammen
|
`changelog:`). Diese Datei fasst die Releases als Überblick zusammen
|
||||||
und verlinkt für Details auf die Release-Pages.
|
und verlinkt für Details auf die Release-Pages.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## Hellion Chat 1.4.3 — Plugin-Load Async-Init + Repo-Cutover (2026-05-08)
|
||||||
|
|
||||||
|
Plugin lifecycle migrated to Dalamud's `IAsyncDalamudPlugin`
|
||||||
|
API. The constructor now does only the bootstrap-essentials
|
||||||
|
(config load, language init, conflict detection); migrations,
|
||||||
|
service allocations, window construction and hook subscription
|
||||||
|
move to `LoadAsync`. Dalamud can keep its UI responsive while
|
||||||
|
the heavy work runs.
|
||||||
|
|
||||||
|
- `IAsyncDalamudPlugin` two-phase load with per-line
|
||||||
|
`CaptureFailure` in `DisposeAsync` (mirrors LightlessSync's
|
||||||
|
pattern); idempotency guard protects against reload races
|
||||||
|
- Schema-gate replaces the v9 → v16 migration chain. Configs
|
||||||
|
on schema v16+ load directly; older configs trigger an
|
||||||
|
"install v1.4.2 first" error so the historic migration
|
||||||
|
path stays intact
|
||||||
|
- `AutoTranslate.PreloadCache` moved off the load path. First
|
||||||
|
use may have a sub-second hitch instead of every-load; the
|
||||||
|
upstream chose differently, we accept first-use latency
|
||||||
|
- `FontManager.BuildFonts` is called sync at the start of
|
||||||
|
`LoadAsync`; Dalamud rebuilds the font atlas on its own
|
||||||
|
pipeline so the custom Hellion-Exo2 font appears with a
|
||||||
|
brief font-pop after load (matches ChatTwo's behaviour)
|
||||||
|
- Custom-repo URL moved to
|
||||||
|
`gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat`.
|
||||||
|
GitHub repo stays as a frozen v1.4.2 snapshot; new
|
||||||
|
releases ship from Gitea. Existing testers need to
|
||||||
|
update the custom-repo URL once
|
||||||
|
- Plugin-load time in this release sits at ~3.7 s median
|
||||||
|
(5 reloads), comparable to v1.4.2. Async migration is
|
||||||
|
foundational for v1.4.4 Lazy-Init optimisations rather
|
||||||
|
than an immediate user-perceived win
|
||||||
|
|
||||||
|
Modding & support: join Hellion Forge — https://discord.gg/X9V7Kcv5gR
|
||||||
|
|
||||||
|
Based on Chat 2 1.35.3 (upstream Infiziert90/ChatTwo, EUPL-1.2).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Hellion Chat 1.4.2 — ChatLog Frame-Hot-Path
|
## Hellion Chat 1.4.2 — ChatLog Frame-Hot-Path
|
||||||
|
|
||||||
Third sub-patch of the v1.4.x Polish Sweep series. Per-frame
|
Third sub-patch of the v1.4.x Polish Sweep series. Per-frame
|
||||||
@@ -279,7 +318,7 @@ Vier kleine Polish-Items aus dem Backlog gebündelt:
|
|||||||
auf Verbose-Level. Aus by default, Aktivierung via
|
auf Verbose-Level. Aus by default, Aktivierung via
|
||||||
`/xllog set HellionChat verbose` für Bug-Report-Diagnose.
|
`/xllog set HellionChat verbose` für Bug-Report-Diagnose.
|
||||||
|
|
||||||
[Release-Notes 1.0.3](https://github.com/JonKazama-Hellion/HellionChat/releases/tag/v1.0.3)
|
[Release-Notes 1.0.3](https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/releases/tag/v1.0.3)
|
||||||
|
|
||||||
## [1.0.1] — 2026-05-04 — Window Position Recovery
|
## [1.0.1] — 2026-05-04 — Window Position Recovery
|
||||||
|
|
||||||
@@ -295,7 +334,7 @@ Bundled housekeeping since v1.0.0: documentation restructured into
|
|||||||
parser library bumped from 3.3.0 to 3.5.1, GitHub Actions bumps for
|
parser library bumped from 3.3.0 to 3.5.1, GitHub Actions bumps for
|
||||||
`actions/setup-dotnet` (4 → 5) and `github/codeql-action` (3 → 4).
|
`actions/setup-dotnet` (4 → 5) and `github/codeql-action` (3 → 4).
|
||||||
|
|
||||||
[Release-Notes 1.0.1](https://github.com/JonKazama-Hellion/HellionChat/releases/tag/v1.0.1)
|
[Release-Notes 1.0.1](https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/releases/tag/v1.0.1)
|
||||||
|
|
||||||
## [1.0.0] — 2026-05-03 — Standalone Major Release
|
## [1.0.0] — 2026-05-03 — Standalone Major Release
|
||||||
|
|
||||||
@@ -308,7 +347,7 @@ User auf Config-Version 12 oder älter neu strukturiert (5 thematische
|
|||||||
Tabs statt 6+ kitchen-sink). Sweep aus Critical- und Major-Findings
|
Tabs statt 6+ kitchen-sink). Sweep aus Critical- und Major-Findings
|
||||||
aus dem Codebase-Audit eingearbeitet.
|
aus dem Codebase-Audit eingearbeitet.
|
||||||
|
|
||||||
[Release-Notes 1.0.0](https://github.com/JonKazama-Hellion/HellionChat/releases/tag/v1.0.0)
|
[Release-Notes 1.0.0](https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/releases/tag/v1.0.0)
|
||||||
|
|
||||||
## [0.6.1] — 2026-05-03 — Pop-Out Discoverability & /tell Auto-Pop-Out
|
## [0.6.1] — 2026-05-03 — Pop-Out Discoverability & /tell Auto-Pop-Out
|
||||||
|
|
||||||
@@ -318,7 +357,7 @@ Pop-Out öffnen". Pop-Out-Input ist jetzt standardmäßig aktiv.
|
|||||||
Bugfixes: Ghost-Windows bei LRU-Drop / Logout, Dead-Zone unter dem
|
Bugfixes: Ghost-Windows bei LRU-Drop / Logout, Dead-Zone unter dem
|
||||||
Input-Bar bei aktivem Hint-Banner.
|
Input-Bar bei aktivem Hint-Banner.
|
||||||
|
|
||||||
[Release-Notes 0.6.1](https://github.com/JonKazama-Hellion/HellionChat/releases/tag/v0.6.1)
|
[Release-Notes 0.6.1](https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/releases/tag/v0.6.1)
|
||||||
|
|
||||||
## [0.6.0] — 2026-05-03 — UX Polish: Pop-Out Input + Colour Presets
|
## [0.6.0] — 2026-05-03 — UX Polish: Pop-Out Input + Colour Presets
|
||||||
|
|
||||||
@@ -328,7 +367,7 @@ Text-Buffer pro Pop-Out. Sieben Built-in-Color-Presets (Klassik,
|
|||||||
High-Contrast, Pastell, Dark-Mode-Tuned, Hellion, Night Blue, Indigo
|
High-Contrast, Pastell, Dark-Mode-Tuned, Hellion, Night Blue, Indigo
|
||||||
Violet) zum One-Click-Apply. Konfigurations-Migration v10 → v11.
|
Violet) zum One-Click-Apply. Konfigurations-Migration v10 → v11.
|
||||||
|
|
||||||
[Release-Notes 0.6.0](https://github.com/JonKazama-Hellion/HellionChat/releases/tag/v0.6.0)
|
[Release-Notes 0.6.0](https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/releases/tag/v0.6.0)
|
||||||
|
|
||||||
## [0.5.4] — 2026-05-02 — WrapText Hardening
|
## [0.5.4] — 2026-05-02 — WrapText Hardening
|
||||||
|
|
||||||
@@ -338,7 +377,7 @@ CodeQL-Critical-Alert "unvalidated local pointer arithmetic"
|
|||||||
dauerhaft. Keine nutzersichtbare Verhaltensänderung — Word-Wrap-Output
|
dauerhaft. Keine nutzersichtbare Verhaltensänderung — Word-Wrap-Output
|
||||||
ist byte-identisch zu 0.5.3.
|
ist byte-identisch zu 0.5.3.
|
||||||
|
|
||||||
[Release-Notes 0.5.4](https://github.com/JonKazama-Hellion/HellionChat/releases/tag/v0.5.4)
|
[Release-Notes 0.5.4](https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/releases/tag/v0.5.4)
|
||||||
|
|
||||||
## [0.5.3] — 2026-05-02 — Pointer Arithmetic Hardening
|
## [0.5.3] — 2026-05-02 — Pointer Arithmetic Hardening
|
||||||
|
|
||||||
@@ -346,7 +385,7 @@ Erster Anlauf zur Schließung des CodeQL-Critical-Alerts in
|
|||||||
`ImGuiUtil.WrapText`. Encoded-Byte-Buffer-Length wird vor der
|
`ImGuiUtil.WrapText`. Encoded-Byte-Buffer-Length wird vor der
|
||||||
Pointer-Arithmetik via `GetByteCount` validiert.
|
Pointer-Arithmetik via `GetByteCount` validiert.
|
||||||
|
|
||||||
[Release-Notes 0.5.3](https://github.com/JonKazama-Hellion/HellionChat/releases/tag/v0.5.3)
|
[Release-Notes 0.5.3](https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/releases/tag/v0.5.3)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -355,7 +394,7 @@ Pointer-Arithmetik via `GetByteCount` validiert.
|
|||||||
Releases vor 0.5.3 (Bootstrap-Phase 0.1.0 bis 0.5.2) sind direkt am
|
Releases vor 0.5.3 (Bootstrap-Phase 0.1.0 bis 0.5.2) sind direkt am
|
||||||
GitHub-Release-Stream einsehbar:
|
GitHub-Release-Stream einsehbar:
|
||||||
|
|
||||||
[Alle Releases](https://github.com/JonKazama-Hellion/HellionChat/releases)
|
[Alle Releases](https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/releases)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ Die Upstream-Sprach-Dateien (`Language.<lang>.resx`) sind nicht Teil dieser Date
|
|||||||
|
|
||||||
## Wie du beitragen kannst
|
## Wie du beitragen kannst
|
||||||
|
|
||||||
Bug-Reports, Feature-Wünsche und Pull-Requests laufen über [GitHub Issues](https://github.com/JonKazama-Hellion/HellionChat/issues). Workflow und Erwartungen stehen in [`../CONTRIBUTING.md`](../CONTRIBUTING.md), Code of Conduct in [`../CODE_OF_CONDUCT.md`](../CODE_OF_CONDUCT.md).
|
Bug-Reports, Feature-Wünsche und Pull-Requests laufen über [Gitea Issues](https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/issues). Workflow und Erwartungen stehen in [`../CONTRIBUTING.md`](../CONTRIBUTING.md), Code of Conduct in [`../CODE_OF_CONDUCT.md`](../CODE_OF_CONDUCT.md).
|
||||||
|
|
||||||
Tester-Pool für neue Versionen läuft über den Hellion-Forge-Discord: [discord.gg/X9V7Kcv5gR](https://discord.gg/X9V7Kcv5gR). Wer in den Tester-Channel rein will, einfach im Forge melden.
|
Tester-Pool für neue Versionen läuft über den Hellion-Forge-Discord: [discord.gg/X9V7Kcv5gR](https://discord.gg/X9V7Kcv5gR). Wer in den Tester-Channel rein will, einfach im Forge melden.
|
||||||
|
|
||||||
|
|||||||
+26
-6
@@ -3,7 +3,7 @@
|
|||||||
Geplante Arbeit nach dem v1.0.0 Standalone-Cut. Diese Liste ist absichtlich
|
Geplante Arbeit nach dem v1.0.0 Standalone-Cut. Diese Liste ist absichtlich
|
||||||
grob: konkrete Specs, Größenschätzungen und Repro-Steps liegen im
|
grob: konkrete Specs, Größenschätzungen und Repro-Steps liegen im
|
||||||
internen Backlog. Tracking nach außen läuft über
|
internen Backlog. Tracking nach außen läuft über
|
||||||
[GitHub Issues](https://github.com/JonKazama-Hellion/HellionChat/issues)
|
[Gitea Issues](https://gitea.hellion-forge.cloud/JonKazama-Hellion/HellionChat/issues)
|
||||||
mit dem `roadmap`-Label, sobald ein Item für einen Cycle eingeplant ist.
|
mit dem `roadmap`-Label, sobald ein Item für einen Cycle eingeplant ist.
|
||||||
|
|
||||||
Reihenfolge ist Priorität, nicht Garantie. Items können sich verschieben
|
Reihenfolge ist Priorität, nicht Garantie. Items können sich verschieben
|
||||||
@@ -12,12 +12,32 @@ Privacy-First-Schnittmenge des Plugins erweisen.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Nächster Cycle (v1.4.3)
|
## Nächster Cycle (v1.4.4)
|
||||||
|
|
||||||
**Plugin-Load Async-Init** — IAsyncDalamudPlugin-Migration
|
**Window-Lazy-Open + Render-Init-Cost-Optimisation** — die in v1.4.3
|
||||||
und FontManager-async, Plugin-Konstruktor von 3.16 Sek auf
|
gelegte IAsyncDalamudPlugin-Foundation jetzt für die echten User-
|
||||||
unter 500 ms perceived load time. Größter und riskantester
|
spürbaren Wins nutzen. Window-Konstruktion erst beim ersten Open,
|
||||||
Patch der Serie, kommt nach drei stabilen Vorläufer-Patches.
|
Render-Path-Init-Kosten in den ersten Frames runter. Konkrete
|
||||||
|
Kandidaten und Größenschätzungen werden im v1.4.4-Brainstorm
|
||||||
|
konsolidiert.
|
||||||
|
|
||||||
|
## v1.4.3 — Plugin-Load Async-Init + Repo-Cutover (released 2026-05-08)
|
||||||
|
|
||||||
|
Vierter und größter Sub-Patch der v1.4.x Polish-Sweep-Serie. Plugin
|
||||||
|
auf Dalamud's IAsyncDalamudPlugin-API migriert: der Konstruktor
|
||||||
|
übernimmt nur noch Bootstrap-Essentials (Config-Load, Language-Init,
|
||||||
|
Conflict-Detection), Migrationen, Service-Allokationen, Window-
|
||||||
|
Konstruktion und Hook-Subscription wandern in LoadAsync. Schema-
|
||||||
|
Gate ersetzt die v9 → v16 Migrations-Kette; Configs auf Schema
|
||||||
|
v16+ laden direkt, ältere Configs triggern eine "install v1.4.2
|
||||||
|
first"-Fehlermeldung. AutoTranslate.PreloadCache vom Load-Pfad
|
||||||
|
runter. FontManager.BuildFonts läuft sync am Start von LoadAsync,
|
||||||
|
Dalamud baut den Font-Atlas auf seiner eigenen Pipeline.
|
||||||
|
Custom-Repo-URL auf `gitea.hellion-forge.cloud` cut-over, das
|
||||||
|
GitHub-Repo bleibt als eingefrorener v1.4.2-Snapshot stehen.
|
||||||
|
Plugin-Load-Zeit liegt bei ~3.7 s Median (5 Reloads), vergleichbar
|
||||||
|
mit v1.4.2: Async-Migration ist Foundation für v1.4.4 Lazy-Init-
|
||||||
|
Optimierungen, kein direkter User-spürbarer Win.
|
||||||
|
|
||||||
## v1.4.2 — ChatLog Frame-Hot-Path (released <Datum>)
|
## v1.4.2 — ChatLog Frame-Hot-Path (released <Datum>)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user