- Migrate DB to v2 (Channel)

- Add channel selection to DBViewer
This commit is contained in:
Infi
2024-05-22 19:25:36 +02:00
parent 6e0dd15ac0
commit 88fbb24ff0
8 changed files with 176 additions and 32 deletions
+1 -1
View File
@@ -2,7 +2,7 @@ namespace ChatTwo.Code;
[System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1028:Enum Storage should be Int32")] [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1028:Enum Storage should be Int32")]
[Flags] [Flags]
internal enum ChatSource : ushort public enum ChatSource : ushort
{ {
Self = 2, Self = 2,
PartyMember = 4, PartyMember = 4,
+17 -14
View File
@@ -12,45 +12,48 @@ using Lumina.Excel.GeneratedSheets;
namespace ChatTwo; namespace ChatTwo;
internal class SortCode { internal class SortCode
{
internal ChatType Type { get; } internal ChatType Type { get; }
internal ChatSource Source { get; } internal ChatSource Source { get; }
[BsonCtor] // Used by LegacyMessageImporter [BsonCtor] // Used by LegacyMessageImporter
public SortCode(ChatType type, ChatSource source) { public SortCode(ChatType type, ChatSource source)
{
Type = type; Type = type;
Source = source; Source = source;
} }
internal SortCode(uint raw) { internal SortCode(uint raw)
{
Type = (ChatType)(raw >> 16); Type = (ChatType)(raw >> 16);
Source = (ChatSource)(raw & 0xFFFF); Source = (ChatSource)(raw & 0xFFFF);
} }
internal uint Encode() { internal uint Encode()
{
return ((uint) Type << 16) | (uint) Source; return ((uint) Type << 16) | (uint) Source;
} }
private bool Equals(SortCode other) { private bool Equals(SortCode other)
{
return Type == other.Type && Source == other.Source; return Type == other.Type && Source == other.Source;
} }
public override bool Equals(object? obj) { public override bool Equals(object? obj)
if (ReferenceEquals(null, obj)) { {
if (ReferenceEquals(null, obj))
return false; return false;
}
if (ReferenceEquals(this, obj)) { if (ReferenceEquals(this, obj))
return true; return true;
}
return obj.GetType() == GetType() && Equals((SortCode) obj); return obj.GetType() == GetType() && Equals((SortCode) obj);
} }
public override int GetHashCode() { public override int GetHashCode()
unchecked { {
return ((int) Type * 397) ^ (int) Source; unchecked { return ((int) Type * 397) ^ (int) Source; }
}
} }
} }
+21 -5
View File
@@ -167,12 +167,17 @@ internal class MessageStore : IDisposable
var userVersion = Convert.ToInt32(cmd.ExecuteScalar()); var userVersion = Convert.ToInt32(cmd.ExecuteScalar());
var migrationsToDo = new List<Action>(); var migrationsToDo = new List<Action>();
if (userVersion <= 0) switch (userVersion)
{ {
case <= 0:
migrationsToDo.Add(Migrate0); migrationsToDo.Add(Migrate0);
// Migration support was only added in version 1. Migrate0 is // Migration support was only added in version 1. Migrate0 is
// idempotent. // idempotent.
migrationsToDo.Add(Migrate1); migrationsToDo.Add(Migrate1);
break;
case 1:
migrationsToDo.Add(Migrate2);
break;
} }
foreach (var migration in migrationsToDo) foreach (var migration in migrationsToDo)
@@ -213,6 +218,17 @@ internal class MessageStore : IDisposable
SetMigrationVersion(1); SetMigrationVersion(1);
} }
private void Migrate2()
{
Connection.Execute(@"
-- Migration 2: Add Channel generated column
ALTER TABLE messages ADD COLUMN Channel INTEGER GENERATED ALWAYS AS (Code & 0x7f) VIRTUAL;
CREATE INDEX IF NOT EXISTS idx_messages_channel ON messages (Channel);
");
SetMigrationVersion(2);
}
private void SetMigrationVersion(int version) private void SetMigrationVersion(int version)
{ {
var cmd = Connection.CreateCommand(); var cmd = Connection.CreateCommand();
@@ -373,14 +389,14 @@ internal class MessageStore : IDisposable
cmd.ExecuteNonQuery(); cmd.ExecuteNonQuery();
} }
internal long CountDateRange(DateTime after, DateTime before, ulong? receiver = null) internal long CountDateRange(DateTime after, DateTime before, uint[] channels, ulong? receiver = null)
{ {
List<string> whereClauses = ["deleted = false"]; List<string> whereClauses = ["deleted = false"];
if (receiver != null) if (receiver != null)
whereClauses.Add("Receiver = $Receiver"); whereClauses.Add("Receiver = $Receiver");
whereClauses.Add("Date BETWEEN $After AND $Before"); whereClauses.Add("Date BETWEEN $After AND $Before");
whereClauses.Add("Code != 72"); whereClauses.Add($"Channel IN ({string.Join(", ", channels)})");
var whereClause = "WHERE " + string.Join(" AND ", whereClauses); var whereClause = "WHERE " + string.Join(" AND ", whereClauses);
@@ -402,14 +418,14 @@ internal class MessageStore : IDisposable
return (long) cmd.ExecuteScalar()!; return (long) cmd.ExecuteScalar()!;
} }
internal MessageEnumerator GetDateRange(DateTime after, DateTime before, ulong? receiver = null, int page = 0) internal MessageEnumerator GetDateRange(DateTime after, DateTime before, uint[] channels, ulong? receiver = null, int page = 0)
{ {
List<string> whereClauses = ["deleted = false"]; List<string> whereClauses = ["deleted = false"];
if (receiver != null) if (receiver != null)
whereClauses.Add("Receiver = $Receiver"); whereClauses.Add("Receiver = $Receiver");
whereClauses.Add("Date BETWEEN $After AND $Before"); whereClauses.Add("Date BETWEEN $After AND $Before");
whereClauses.Add("Code != 72"); whereClauses.Add($"Channel IN ({string.Join(", ", channels)})");
var whereClause = "WHERE " + string.Join(" AND ", whereClauses); var whereClause = "WHERE " + string.Join(" AND ", whereClauses);
+9
View File
@@ -1634,6 +1634,15 @@ namespace ChatTwo.Resources {
} }
} }
/// <summary>
/// Looks up a localized string similar to Chat Type.
/// </summary>
internal static string DbViewer_TableField_Type {
get {
return ResourceManager.GetString("DbViewer_TableField_Type", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Chinese (full). /// Looks up a localized string similar to Chinese (full).
/// </summary> /// </summary>
+3
View File
@@ -1168,6 +1168,9 @@
<data name="DbViewer_TableField_Date" xml:space="preserve"> <data name="DbViewer_TableField_Date" xml:space="preserve">
<value>Date</value> <value>Date</value>
</data> </data>
<data name="DbViewer_TableField_Type" xml:space="preserve">
<value>Chat Type</value>
</data>
<data name="DbViewer_TableField_Sender" xml:space="preserve"> <data name="DbViewer_TableField_Sender" xml:space="preserve">
<value>Sender</value> <value>Sender</value>
</data> </data>
+65 -8
View File
@@ -1,6 +1,7 @@
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Globalization; using System.Globalization;
using System.Numerics; using System.Numerics;
using ChatTwo.Code;
using ChatTwo.Resources; using ChatTwo.Resources;
using ChatTwo.Util; using ChatTwo.Util;
using Dalamud.Interface; using Dalamud.Interface;
@@ -24,10 +25,11 @@ public class DbViewer : Window
private int CurrentPage = 1; private int CurrentPage = 1;
private string SimpleSearchTerm = ""; private string SimpleSearchTerm = "";
private bool OnlyCurrentCharacter = true; private bool OnlyCurrentCharacter = true;
private readonly Dictionary<ChatType, ChatSource> ChatCodes;
private bool IsProcessing; private bool IsProcessing;
private long ProcessingStart = Environment.TickCount64; private long ProcessingStart = Environment.TickCount64;
private (DateTime Min, DateTime Max, int Page, bool Local) LastProcessed; private (DateTime Min, DateTime Max, int Page, bool Local, int ChannelCount) LastProcessed;
private string MinDateString = ""; private string MinDateString = "";
private string MaxDateString = ""; private string MaxDateString = "";
@@ -42,11 +44,12 @@ public class DbViewer : Window
public DbViewer(Plugin plugin) : base("DBViewer###chat2-dbviewer") public DbViewer(Plugin plugin) : base("DBViewer###chat2-dbviewer")
{ {
Plugin = plugin; Plugin = plugin;
ChatCodes = TabsUtil.MostlyPlayer;
DateFormat = CultureInfo.CurrentCulture.DateTimeFormat.ShortDatePattern; DateFormat = CultureInfo.CurrentCulture.DateTimeFormat.ShortDatePattern;
DateTimeFormat = CultureInfo.CurrentCulture.DateTimeFormat.FullDateTimePattern; DateTimeFormat = CultureInfo.CurrentCulture.DateTimeFormat.FullDateTimePattern;
LastProcessed = (AfterDate, BeforeDate, CurrentPage, OnlyCurrentCharacter); LastProcessed = (AfterDate, BeforeDate, CurrentPage, OnlyCurrentCharacter, ChatCodes.Count);
DateReset(); DateReset();
SizeConstraints = new WindowSizeConstraints SizeConstraints = new WindowSizeConstraints
@@ -88,6 +91,8 @@ public class DbViewer : Window
if (ImGuiUtil.IconButton(FontAwesomeIcon.Recycle)) if (ImGuiUtil.IconButton(FontAwesomeIcon.Recycle))
DateReset(); DateReset();
ImGuiUtil.DrawArrows(ref CurrentPage, 1, totalPages, spacing); ImGuiUtil.DrawArrows(ref CurrentPage, 1, totalPages, spacing);
ImGui.SameLine(0, spacing);
ChannelSelection();
var skipText = Language.DbViewer_CharacterOption; var skipText = Language.DbViewer_CharacterOption;
var textLength = ImGui.GetTextLineHeight() + ImGui.CalcTextSize(skipText).X + ImGui.GetStyle().ItemInnerSpacing.X + ImGui.GetStyle().FramePadding.X * 2; var textLength = ImGui.GetTextLineHeight() + ImGui.CalcTextSize(skipText).X + ImGui.GetStyle().ItemInnerSpacing.X + ImGui.GetStyle().FramePadding.X * 2;
@@ -107,7 +112,7 @@ public class DbViewer : Window
if (DateWidget.Validate(MinimalDate, ref AfterDate, ref BeforeDate)) if (DateWidget.Validate(MinimalDate, ref AfterDate, ref BeforeDate))
DateRefresh(); DateRefresh();
if (!IsProcessing && LastProcessed != (AfterDate, BeforeDate, CurrentPage, OnlyCurrentCharacter)) if (!IsProcessing && LastProcessed != (AfterDate, BeforeDate, CurrentPage, OnlyCurrentCharacter, ChatCodes.Count))
{ {
// Page hasn't changed, so we reset it back to 1 // Page hasn't changed, so we reset it back to 1
if (LastProcessed.Page == CurrentPage) if (LastProcessed.Page == CurrentPage)
@@ -116,17 +121,18 @@ public class DbViewer : Window
AdjustDates(); AdjustDates();
IsProcessing = true; IsProcessing = true;
ProcessingStart = Environment.TickCount64 + 1_000; // + 1 second ProcessingStart = Environment.TickCount64 + 1_000; // + 1 second
LastProcessed = (AfterDate, BeforeDate, CurrentPage, OnlyCurrentCharacter); LastProcessed = (AfterDate, BeforeDate, CurrentPage, OnlyCurrentCharacter, ChatCodes.Count);
Task.Run(() => Task.Run(() =>
{ {
try try
{ {
ulong? character = OnlyCurrentCharacter ? Plugin.ClientState.LocalContentId : null; ulong? character = OnlyCurrentCharacter ? Plugin.ClientState.LocalContentId : null;
var channels = ChatCodes.Select(c => (uint) c.Key).ToArray();
// We only want to fetch count if this is the first page // We only want to fetch count if this is the first page
if (CurrentPage == 1) if (CurrentPage == 1)
Count = Plugin.MessageManager.Store.CountDateRange(AfterDate, BeforeDate, character); Count = Plugin.MessageManager.Store.CountDateRange(AfterDate, BeforeDate, channels, character);
Messages = Plugin.MessageManager.Store.GetDateRange(AfterDate, BeforeDate, character, CurrentPage - 1).ToArray(); Messages = Plugin.MessageManager.Store.GetDateRange(AfterDate, BeforeDate, channels, character, CurrentPage - 1).ToArray();
Filter(); Filter();
} }
@@ -153,11 +159,13 @@ public class DbViewer : Window
if (!child.Success) if (!child.Success)
return; return;
using var table = ImRaii.Table("##messageHistory", 3, ImGuiTableFlags.Borders | ImGuiTableFlags.RowBg | ImGuiTableFlags.Resizable); using var table = ImRaii.Table("##messageHistory", 4, ImGuiTableFlags.Borders | ImGuiTableFlags.RowBg | ImGuiTableFlags.Resizable);
if (!table.Success) if (!table.Success)
return; return;
ImGui.TableSetupColumn(Language.DbViewer_TableField_Date, ImGuiTableColumnFlags.WidthFixed); var columnWidth = ImGui.CalcTextSize(Language.DbViewer_TableField_Type);
ImGui.TableSetupColumn(Language.DbViewer_TableField_Date, ImGuiTableColumnFlags.WidthFixed | ImGuiTableColumnFlags.NoResize);
ImGui.TableSetupColumn(Language.DbViewer_TableField_Type, ImGuiTableColumnFlags.WidthFixed | ImGuiTableColumnFlags.NoResize, columnWidth.X);
ImGui.TableSetupColumn(Language.DbViewer_TableField_Sender); ImGui.TableSetupColumn(Language.DbViewer_TableField_Sender);
ImGui.TableSetupColumn(Language.DbViewer_TableField_Content); ImGui.TableSetupColumn(Language.DbViewer_TableField_Content);
@@ -167,6 +175,14 @@ public class DbViewer : Window
ImGui.TableNextColumn(); ImGui.TableNextColumn();
ImGui.TextUnformatted(message.Date.ToLocalTime().ToString(DateTimeFormat)); ImGui.TextUnformatted(message.Date.ToLocalTime().ToString(DateTimeFormat));
ImGui.TableNextColumn();
var pos = ImGui.GetCursorPos();
ImGuiUtil.CenterText($"{message.Code.Raw}");
ImGui.SetCursorPos(pos);
ImGui.Dummy(columnWidth);
if (ImGui.IsItemHovered())
ImGui.SetTooltip(message.Code.Type.Name());
ImGui.TableNextColumn(); ImGui.TableNextColumn();
Plugin.ChatLogWindow.DrawChunks(message.Sender); Plugin.ChatLogWindow.DrawChunks(message.Sender);
@@ -175,6 +191,47 @@ public class DbViewer : Window
} }
} }
private void ChannelSelection()
{
const string addTabPopup = "add-channel-popup";
if (ImGui.Button("Channels"))
ImGui.OpenPopup(addTabPopup);
using var popup = ImRaii.Popup(addTabPopup);
if (!popup.Success)
return;
using var channelNode = ImRaii.TreeNode(Language.Options_Tabs_Channels);
if (!channelNode.Success)
return;
foreach (var (header, types) in ChatTypeExt.SortOrder)
{
using var headerNode = ImRaii.TreeNode(header);
if (!headerNode.Success)
continue;
foreach (var type in types)
{
if (type.IsGm())
continue;
var enabled = ChatCodes.ContainsKey(type);
if (ImGui.Checkbox($"##{type.Name()}", ref enabled))
{
if (enabled)
ChatCodes[type] = ChatSourceExt.All;
else
ChatCodes.Remove(type);
}
ImGui.SameLine();
ImGui.TextUnformatted(type.Name());
}
}
}
private void Filter() private void Filter()
{ {
if (SimpleSearchTerm == "") if (SimpleSearchTerm == "")
+7
View File
@@ -317,6 +317,13 @@ internal static class ImGuiUtil
} }
} }
public static void CenterText(string text, float indent = 0.0f)
{
indent *= ImGuiHelpers.GlobalScale;
ImGui.SameLine(((ImGui.GetContentRegionAvail().X - ImGui.CalcTextSize(text).X) * 0.5f) + indent);
ImGui.TextUnformatted(text);
}
internal static bool TryToImGui(this VirtualKey key, out ImGuiKey result) internal static bool TryToImGui(this VirtualKey key, out ImGuiKey result)
{ {
result = key switch { result = key switch {
+49
View File
@@ -73,4 +73,53 @@ internal static class TabsUtil {
[ChatType.NpcDialogue] = ChatSourceExt.All, [ChatType.NpcDialogue] = ChatSourceExt.All,
}, },
}; };
internal static Dictionary<ChatType, ChatSource> MostlyPlayer => new()
{
// Special
[ChatType.Debug] = ChatSourceExt.All,
[ChatType.Urgent] = ChatSourceExt.All,
[ChatType.Notice] = ChatSourceExt.All,
// Chat
[ChatType.Say] = ChatSourceExt.All,
[ChatType.Yell] = ChatSourceExt.All,
[ChatType.Shout] = ChatSourceExt.All,
[ChatType.TellIncoming] = ChatSourceExt.All,
[ChatType.TellOutgoing] = ChatSourceExt.All,
[ChatType.Party] = ChatSourceExt.All,
[ChatType.CrossParty] = ChatSourceExt.All,
[ChatType.Alliance] = ChatSourceExt.All,
[ChatType.FreeCompany] = ChatSourceExt.All,
[ChatType.PvpTeam] = ChatSourceExt.All,
[ChatType.CrossLinkshell1] = ChatSourceExt.All,
[ChatType.CrossLinkshell2] = ChatSourceExt.All,
[ChatType.CrossLinkshell3] = ChatSourceExt.All,
[ChatType.CrossLinkshell4] = ChatSourceExt.All,
[ChatType.CrossLinkshell5] = ChatSourceExt.All,
[ChatType.CrossLinkshell6] = ChatSourceExt.All,
[ChatType.CrossLinkshell7] = ChatSourceExt.All,
[ChatType.CrossLinkshell8] = ChatSourceExt.All,
[ChatType.Linkshell1] = ChatSourceExt.All,
[ChatType.Linkshell2] = ChatSourceExt.All,
[ChatType.Linkshell3] = ChatSourceExt.All,
[ChatType.Linkshell4] = ChatSourceExt.All,
[ChatType.Linkshell5] = ChatSourceExt.All,
[ChatType.Linkshell6] = ChatSourceExt.All,
[ChatType.Linkshell7] = ChatSourceExt.All,
[ChatType.Linkshell8] = ChatSourceExt.All,
[ChatType.NoviceNetwork] = ChatSourceExt.All,
[ChatType.StandardEmote] = ChatSourceExt.All,
[ChatType.CustomEmote] = ChatSourceExt.All,
// Announcements
[ChatType.System] = ChatSourceExt.All,
[ChatType.Error] = ChatSourceExt.All,
[ChatType.Echo] = ChatSourceExt.All,
[ChatType.NoviceNetworkSystem] = ChatSourceExt.All,
[ChatType.FreeCompanyAnnouncement] = ChatSourceExt.All,
[ChatType.PvpTeamAnnouncement] = ChatSourceExt.All,
[ChatType.FreeCompanyLoginLogout] = ChatSourceExt.All,
[ChatType.PvpTeamLoginLogout] = ChatSourceExt.All,
[ChatType.RandomNumber] = ChatSourceExt.All,
[ChatType.MessageBook] = ChatSourceExt.All,
};
} }