395a0d7c98
Drop the redundant inner Advanced TreeNode in the Maintenance section, flatten the duplicated indent in the Overview section, and rename the section headings so they reflect their content (Overview shows metadata, Maintenance hosts the shift-gated tooling).
260 lines
10 KiB
C#
Executable File
260 lines
10 KiB
C#
Executable File
using System.Diagnostics;
|
|
using ChatTwo.Code;
|
|
using ChatTwo.Resources;
|
|
using ChatTwo.Util;
|
|
using Dalamud.Game.Text.SeStringHandling;
|
|
using Dalamud.Game.Text.SeStringHandling.Payloads;
|
|
using Dalamud.Interface.ImGuiNotification;
|
|
using Dalamud.Interface.Utility.Raii;
|
|
using Dalamud.Bindings.ImGui;
|
|
using Dalamud.Game.Text;
|
|
|
|
namespace ChatTwo.Ui.SettingsTabs;
|
|
|
|
internal sealed class Database : ISettingsTab
|
|
{
|
|
private Plugin Plugin { get; }
|
|
private Configuration Mutable { get; }
|
|
|
|
public string Name => Language.Options_Database_Tab + "###tabs-database";
|
|
|
|
internal Database(Plugin plugin, Configuration mutable)
|
|
{
|
|
Plugin = plugin;
|
|
Mutable = mutable;
|
|
}
|
|
|
|
private bool ShowAdvanced;
|
|
|
|
private long DatabaseLastRefreshTicks;
|
|
private long DatabaseSize;
|
|
private long DatabaseLogSize;
|
|
private int DatabaseMessageCount;
|
|
|
|
public void Draw(bool changed)
|
|
{
|
|
// Shift-on-open keeps the Advanced tools available without a permanent
|
|
// toggle in the UI, mirroring upstream Chat 2 behaviour.
|
|
if (changed)
|
|
ShowAdvanced = ImGui.GetIO().KeyShift;
|
|
|
|
DrawStorageSection();
|
|
ImGui.Spacing();
|
|
DrawViewerSection();
|
|
ImGui.Spacing();
|
|
DrawStatsSection();
|
|
}
|
|
|
|
private void DrawStorageSection()
|
|
{
|
|
using var tree = ImRaii.TreeNode(HellionStrings.Settings_Database_Storage_Heading);
|
|
if (!tree.Success)
|
|
return;
|
|
|
|
using (ImRaii.PushIndent(ImGui.GetStyle().IndentSpacing, false))
|
|
{
|
|
ImGui.Checkbox(Language.Options_DatabaseBattleMessages_Name, ref Mutable.DatabaseBattleMessages);
|
|
ImGuiUtil.HelpMarker(Language.Options_DatabaseBattleMessages_Description);
|
|
|
|
if (ImGui.Checkbox(Language.Options_LoadPreviousSession_Name, ref Mutable.LoadPreviousSession))
|
|
if (Mutable.LoadPreviousSession)
|
|
Mutable.FilterIncludePreviousSessions = true;
|
|
ImGuiUtil.HelpMarker(Language.Options_LoadPreviousSession_Description);
|
|
|
|
if (ImGui.Checkbox(Language.Options_FilterIncludePreviousSessions_Name, ref Mutable.FilterIncludePreviousSessions))
|
|
if (!Mutable.FilterIncludePreviousSessions)
|
|
Mutable.LoadPreviousSession = false;
|
|
ImGuiUtil.HelpMarker(Language.Options_FilterIncludePreviousSessions_Description);
|
|
|
|
var old = new FileInfo(Path.Join(Plugin.Interface.ConfigDirectory.FullName, "chat.db"));
|
|
var migratedOld = new FileInfo(Path.Join(Plugin.Interface.ConfigDirectory.FullName, "chat-litedb.db"));
|
|
if (old.Exists || migratedOld.Exists)
|
|
{
|
|
ImGui.Spacing();
|
|
ImGui.Separator();
|
|
ImGui.Spacing();
|
|
|
|
ImGui.TextUnformatted(Language.Options_Database_Old_Heading);
|
|
ImGui.Spacing();
|
|
|
|
if (ImGuiUtil.CtrlShiftButton(Language.Options_Database_Old_Delete, Language.Options_Database_Old_Delete_Tooltip))
|
|
{
|
|
try
|
|
{
|
|
if (old.Exists)
|
|
old.Delete();
|
|
else
|
|
migratedOld.Delete();
|
|
WrapperUtil.AddNotification(Language.Options_Database_Old_Delete_Success, NotificationType.Success);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Plugin.Log.Error(e, "Unable to delete old database");
|
|
WrapperUtil.AddNotification(Language.Options_Database_Old_Delete_Error, NotificationType.Error);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void DrawViewerSection()
|
|
{
|
|
using var tree = ImRaii.TreeNode(HellionStrings.Settings_Database_Viewer_Heading);
|
|
if (!tree.Success)
|
|
return;
|
|
|
|
using (ImRaii.PushIndent(ImGui.GetStyle().IndentSpacing, false))
|
|
{
|
|
// Refresh the database size and message count every 5 seconds to avoid
|
|
// constant stat calls and spamming the database.
|
|
if (DatabaseLastRefreshTicks + 5 * 1000 < Environment.TickCount64)
|
|
{
|
|
DatabaseSize = Plugin.MessageManager.Store.DatabaseSize();
|
|
DatabaseLogSize = Plugin.MessageManager.Store.DatabaseLogSize();
|
|
DatabaseMessageCount = Plugin.MessageManager.Store.MessageCount();
|
|
DatabaseLastRefreshTicks = Environment.TickCount64;
|
|
}
|
|
|
|
// Copy the directory path instead of the file path so people can
|
|
// paste it into their file explorer.
|
|
ImGuiUtil.HelpText(string.Format(Language.Options_Database_Metadata_Path, MessageManager.DatabasePath()));
|
|
if (ImGui.IsItemClicked(ImGuiMouseButton.Left))
|
|
{
|
|
var path = Path.GetDirectoryName(MessageManager.DatabasePath());
|
|
ImGui.SetClipboardText(path);
|
|
WrapperUtil.AddNotification(Language.Options_Database_Metadata_CopyConfigPathNotification, NotificationType.Info);
|
|
}
|
|
|
|
if (ImGui.IsItemHovered())
|
|
{
|
|
ImGui.SetMouseCursor(ImGuiMouseCursor.Hand);
|
|
ImGuiUtil.Tooltip(Language.Options_Database_Metadata_CopyConfigPath);
|
|
}
|
|
|
|
ImGuiUtil.HelpText(string.Format(Language.Options_Database_Metadata_Size, StringUtil.BytesToString(DatabaseSize)));
|
|
if (ImGui.IsItemHovered())
|
|
ImGuiUtil.Tooltip(StringUtil.BytesToString(DatabaseSize));
|
|
|
|
ImGuiUtil.HelpText(string.Format(Language.Options_Database_Metadata_LogSize, StringUtil.BytesToString(DatabaseLogSize)));
|
|
if (ImGui.IsItemHovered())
|
|
ImGuiUtil.Tooltip(StringUtil.BytesToString(DatabaseLogSize));
|
|
|
|
ImGuiUtil.HelpText(string.Format(Language.Options_Database_Metadata_MessageCount, DatabaseMessageCount));
|
|
|
|
if (ImGuiUtil.CtrlShiftButton(Language.Options_ClearDatabase_Button, Language.Options_ClearDatabase_Tooltip))
|
|
{
|
|
Plugin.Log.Warning("Clearing messages from database");
|
|
Plugin.MessageManager.Store.ClearMessages();
|
|
Plugin.MessageManager.ClearAllTabs();
|
|
|
|
// Refresh on next draw
|
|
DatabaseLastRefreshTicks = 0;
|
|
WrapperUtil.AddNotification(Language.Options_ClearDatabase_Success, NotificationType.Info);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void DrawStatsSection()
|
|
{
|
|
if (!ShowAdvanced)
|
|
return;
|
|
|
|
using var tree = ImRaii.TreeNode(HellionStrings.Settings_Database_Stats_Heading);
|
|
if (!tree.Success)
|
|
return;
|
|
|
|
using (ImRaii.PushIndent(ImGui.GetStyle().IndentSpacing, false))
|
|
{
|
|
using var wrap = ImRaii.TextWrapPos(0.0f);
|
|
|
|
ImGuiUtil.WarningText(Language.Options_Database_Advanced_Warning);
|
|
if (ImGuiUtil.CtrlShiftButton("Perform maintenance", "Ctrl+Shift: MessageManager.Store.PerformMaintenance()"))
|
|
Plugin.MessageManager.Store.PerformMaintenance();
|
|
|
|
if (ImGuiUtil.CtrlShiftButton("Reload messages from database", "Ctrl+Shift: MessageManager.FilterAllTabs()"))
|
|
{
|
|
Plugin.MessageManager.ClearAllTabs();
|
|
Plugin.MessageManager.FilterAllTabsAsync();
|
|
}
|
|
|
|
if (ImGuiUtil.CtrlShiftButton("Inject 10,000 messages", "Ctrl+Shift: creates 10,000 unique messages (async)"))
|
|
new Thread(() => InsertMessages(10_000)).Start();
|
|
}
|
|
}
|
|
|
|
private void InsertMessages(int count)
|
|
{
|
|
Plugin.Log.Info($"Inserting {count} messages due to user request");
|
|
|
|
// Generate
|
|
var stopwatch = Stopwatch.StartNew();
|
|
var playerName = Plugin.PlayerState.CharacterName;
|
|
var worldId = Plugin.PlayerState.HomeWorld.ValueNullable?.RowId ?? 0;
|
|
var senderSource = new SeStringBuilder()
|
|
.AddText("<")
|
|
.Add(new PlayerPayload(playerName, worldId))
|
|
.AddText("Random Message")
|
|
.Add(RawPayload.LinkTerminator)
|
|
.AddText(">: ")
|
|
.Build();
|
|
var senderChunks = ChunkUtil.ToChunks(senderSource, ChunkSource.Sender, ChatType.Debug).ToList();
|
|
var messages = new List<Message>(count);
|
|
for (var i = 0; i < count; i++)
|
|
{
|
|
var contentSource = new SeStringBuilder()
|
|
.AddText("Random message payload - ")
|
|
.AddItalics(Guid.NewGuid().ToString())
|
|
.Build();
|
|
var contentChunks = ChunkUtil.ToChunks(contentSource, ChunkSource.Content, ChatType.Debug).ToList();
|
|
|
|
var chatCode = new ChatCode(XivChatType.Say, 0, 0);
|
|
messages.Add(new Message(
|
|
Guid.NewGuid(),
|
|
Plugin.MessageManager.CurrentContentId,
|
|
Plugin.MessageManager.CurrentContentId,
|
|
DateTimeOffset.UtcNow,
|
|
chatCode,
|
|
senderChunks,
|
|
contentChunks,
|
|
senderSource,
|
|
contentSource,
|
|
Guid.Empty
|
|
));
|
|
}
|
|
|
|
var elapsedTicks = stopwatch.ElapsedTicks;
|
|
stopwatch.Stop();
|
|
Plugin.Log.Info($"Crafted {count} messages in {elapsedTicks} ticks ({elapsedTicks / TimeSpan.TicksPerMillisecond}ms)");
|
|
|
|
// Insert
|
|
stopwatch = Stopwatch.StartNew();
|
|
foreach (var message in messages)
|
|
Plugin.MessageManager.Store.UpsertMessage(message);
|
|
|
|
elapsedTicks = stopwatch.ElapsedTicks;
|
|
stopwatch.Stop();
|
|
Plugin.Log.Info($"Upserted {count} messages in {elapsedTicks} ticks ({elapsedTicks / TimeSpan.TicksPerMillisecond}ms)");
|
|
|
|
// Clear tabs during framework frame
|
|
Plugin.Framework.Run(() =>
|
|
{
|
|
stopwatch = Stopwatch.StartNew();
|
|
Plugin.MessageManager.ClearAllTabs();
|
|
elapsedTicks = stopwatch.ElapsedTicks;
|
|
stopwatch.Stop();
|
|
Plugin.Log.Info($"Cleared {Plugin.Config.Tabs.Count} tabs in {elapsedTicks} ticks ({elapsedTicks / TimeSpan.TicksPerMillisecond}ms)");
|
|
}).Wait();
|
|
|
|
// Fetch and filter during framework frame
|
|
Plugin.Framework.Run(() =>
|
|
{
|
|
stopwatch = Stopwatch.StartNew();
|
|
// Intentionally synchronous
|
|
Plugin.MessageManager.FilterAllTabs();
|
|
elapsedTicks = stopwatch.ElapsedTicks;
|
|
stopwatch.Stop();
|
|
Plugin.Log.Info($"Fetched and filtered all tabs in {elapsedTicks} ticks ({elapsedTicks / TimeSpan.TicksPerMillisecond}ms)");
|
|
}).Wait();
|
|
}
|
|
}
|