feat: add chat database
This commit is contained in:
parent
4cf86079a1
commit
841688a7aa
|
@ -48,6 +48,7 @@
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="DalamudPackager" Version="2.1.5"/>
|
<PackageReference Include="DalamudPackager" Version="2.1.5"/>
|
||||||
|
<PackageReference Include="LiteDB" Version="5.0.11"/>
|
||||||
<PackageReference Include="SharpDX.Direct2D1" Version="4.2.0"/>
|
<PackageReference Include="SharpDX.Direct2D1" Version="4.2.0"/>
|
||||||
<PackageReference Include="XivCommon" Version="5.0.0"/>
|
<PackageReference Include="XivCommon" Version="5.0.0"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
|
@ -1,17 +1,33 @@
|
||||||
using ChatTwo.Code;
|
using ChatTwo.Code;
|
||||||
using Dalamud.Game.Text.SeStringHandling;
|
using Dalamud.Game.Text.SeStringHandling;
|
||||||
|
using LiteDB;
|
||||||
|
|
||||||
namespace ChatTwo;
|
namespace ChatTwo;
|
||||||
|
|
||||||
internal abstract class Chunk {
|
internal abstract class Chunk {
|
||||||
|
[BsonIgnore]
|
||||||
internal Message? Message { get; set; }
|
internal Message? Message { get; set; }
|
||||||
internal SeString? Source { get; set; }
|
|
||||||
|
internal ChunkSource Source { get; set; }
|
||||||
internal Payload? Link { get; set; }
|
internal Payload? Link { get; set; }
|
||||||
|
|
||||||
protected Chunk(SeString? source, Payload? link) {
|
protected Chunk(ChunkSource source, Payload? link) {
|
||||||
this.Source = source;
|
this.Source = source;
|
||||||
this.Link = link;
|
this.Link = link;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal SeString? GetSeString() => this.Source switch {
|
||||||
|
ChunkSource.None => null,
|
||||||
|
ChunkSource.Sender => this.Message?.SenderSource,
|
||||||
|
ChunkSource.Content => this.Message?.ContentSource,
|
||||||
|
_ => null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
internal enum ChunkSource {
|
||||||
|
None,
|
||||||
|
Sender,
|
||||||
|
Content,
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class TextChunk : Chunk {
|
internal class TextChunk : Chunk {
|
||||||
|
@ -21,23 +37,23 @@ internal class TextChunk : Chunk {
|
||||||
internal bool Italic { get; set; }
|
internal bool Italic { get; set; }
|
||||||
internal string Content { get; set; }
|
internal string Content { get; set; }
|
||||||
|
|
||||||
internal TextChunk(SeString? source, Payload? link, string content) : base(source, link) {
|
internal TextChunk(ChunkSource source, Payload? link, string content) : base(source, link) {
|
||||||
this.Content = content;
|
this.Content = content;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal TextChunk(SeString? source, Payload? link, ChatType? fallbackColour, uint? foreground, uint? glow, bool italic, string content) : base(source, link) {
|
#pragma warning disable CS8618
|
||||||
this.FallbackColour = fallbackColour;
|
public TextChunk() : base(ChunkSource.None, null) {
|
||||||
this.Foreground = foreground;
|
|
||||||
this.Glow = glow;
|
|
||||||
this.Italic = italic;
|
|
||||||
this.Content = content;
|
|
||||||
}
|
}
|
||||||
|
#pragma warning restore CS8618
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class IconChunk : Chunk {
|
internal class IconChunk : Chunk {
|
||||||
internal BitmapFontIcon Icon { get; set; }
|
internal BitmapFontIcon Icon { get; set; }
|
||||||
|
|
||||||
public IconChunk(SeString? source, Payload? link, BitmapFontIcon icon) : base(source, link) {
|
public IconChunk(ChunkSource source, Payload? link, BitmapFontIcon icon) : base(source, link) {
|
||||||
this.Icon = icon;
|
this.Icon = icon;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public IconChunk() : base(ChunkSource.None, null) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,17 +1,30 @@
|
||||||
namespace ChatTwo.Code;
|
using LiteDB;
|
||||||
|
|
||||||
|
namespace ChatTwo.Code;
|
||||||
|
|
||||||
internal class ChatCode {
|
internal class ChatCode {
|
||||||
private const ushort Clear7 = ~(~0 << 7);
|
private const ushort Clear7 = ~(~0 << 7);
|
||||||
|
|
||||||
internal ushort Raw { get; }
|
internal ushort Raw { get; }
|
||||||
|
|
||||||
internal ChatType Type => (ChatType) (this.Raw & Clear7);
|
internal ChatType Type { get; }
|
||||||
internal ChatSource Source => this.SourceFrom(11);
|
internal ChatSource Source { get; }
|
||||||
internal ChatSource Target => this.SourceFrom(7);
|
internal ChatSource Target { get; }
|
||||||
private ChatSource SourceFrom(ushort shift) => (ChatSource) (1 << ((this.Raw >> shift) & 0xF));
|
private ChatSource SourceFrom(ushort shift) => (ChatSource) (1 << ((this.Raw >> shift) & 0xF));
|
||||||
|
|
||||||
internal ChatCode(ushort raw) {
|
internal ChatCode(ushort raw) {
|
||||||
this.Raw = raw;
|
this.Raw = raw;
|
||||||
|
this.Type = (ChatType) (this.Raw & Clear7);
|
||||||
|
this.Source = this.SourceFrom(11);
|
||||||
|
this.Target = this.SourceFrom(7);
|
||||||
|
}
|
||||||
|
|
||||||
|
[BsonCtor]
|
||||||
|
public ChatCode(ushort raw, ChatType type, ChatSource source, ChatSource target) {
|
||||||
|
this.Raw = raw;
|
||||||
|
this.Type = type;
|
||||||
|
this.Source = source;
|
||||||
|
this.Target = target;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal ChatType Parent() => this.Type switch {
|
internal ChatType Parent() => this.Type switch {
|
||||||
|
|
|
@ -345,10 +345,16 @@ internal static class ChatTypeExt {
|
||||||
ChatType.LoseDebuff => true,
|
ChatType.LoseDebuff => true,
|
||||||
|
|
||||||
// Announcements
|
// Announcements
|
||||||
|
ChatType.System => true,
|
||||||
|
ChatType.BattleSystem => true,
|
||||||
|
ChatType.Error => true,
|
||||||
|
ChatType.LootNotice => true,
|
||||||
ChatType.Progress => true,
|
ChatType.Progress => true,
|
||||||
ChatType.LootRoll => true,
|
ChatType.LootRoll => true,
|
||||||
ChatType.Crafting => true,
|
ChatType.Crafting => true,
|
||||||
ChatType.Gathering => true,
|
ChatType.Gathering => true,
|
||||||
|
ChatType.FreeCompanyLoginLogout => true,
|
||||||
|
ChatType.PvpTeamLoginLogout => true,
|
||||||
_ => false,
|
_ => false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,9 @@ internal class Configuration : IPluginConfiguration {
|
||||||
public bool CanMove = true;
|
public bool CanMove = true;
|
||||||
public bool CanResize = true;
|
public bool CanResize = true;
|
||||||
public bool ShowTitleBar;
|
public bool ShowTitleBar;
|
||||||
|
public bool DatabaseBattleMessages;
|
||||||
|
public bool LoadPreviousSession;
|
||||||
|
public bool FilterIncludePreviousSessions;
|
||||||
|
|
||||||
public float FontSize = 17f;
|
public float FontSize = 17f;
|
||||||
public float JapaneseFontSize = 17f;
|
public float JapaneseFontSize = 17f;
|
||||||
|
@ -54,6 +57,9 @@ internal class Configuration : IPluginConfiguration {
|
||||||
this.CanMove = other.CanMove;
|
this.CanMove = other.CanMove;
|
||||||
this.CanResize = other.CanResize;
|
this.CanResize = other.CanResize;
|
||||||
this.ShowTitleBar = other.ShowTitleBar;
|
this.ShowTitleBar = other.ShowTitleBar;
|
||||||
|
this.DatabaseBattleMessages = other.DatabaseBattleMessages;
|
||||||
|
this.LoadPreviousSession = other.LoadPreviousSession;
|
||||||
|
this.FilterIncludePreviousSessions = other.FilterIncludePreviousSessions;
|
||||||
this.FontSize = other.FontSize;
|
this.FontSize = other.FontSize;
|
||||||
this.JapaneseFontSize = other.JapaneseFontSize;
|
this.JapaneseFontSize = other.JapaneseFontSize;
|
||||||
this.SymbolsFontSize = other.SymbolsFontSize;
|
this.SymbolsFontSize = other.SymbolsFontSize;
|
||||||
|
|
|
@ -503,7 +503,7 @@ internal sealed unsafe class Chat : IDisposable {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
var nameChunks = ChunkUtil.ToChunks(name, null).ToList();
|
var nameChunks = ChunkUtil.ToChunks(name, ChunkSource.None, null).ToList();
|
||||||
if (nameChunks.Count > 0 && nameChunks[0] is TextChunk text) {
|
if (nameChunks.Count > 0 && nameChunks[0] is TextChunk text) {
|
||||||
text.Content = text.Content.TrimStart('\uE01E').TrimStart();
|
text.Content = text.Content.TrimStart('\uE01E').TrimStart();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,32 @@
|
||||||
using ChatTwo.Code;
|
using ChatTwo.Code;
|
||||||
|
using Dalamud.Game.Text.SeStringHandling;
|
||||||
|
using LiteDB;
|
||||||
|
|
||||||
namespace ChatTwo;
|
namespace ChatTwo;
|
||||||
|
|
||||||
|
internal class SortCode {
|
||||||
|
internal ChatType Type { get; set; }
|
||||||
|
internal ChatSource Source { get; set; }
|
||||||
|
|
||||||
|
internal SortCode(ChatType type, ChatSource source) {
|
||||||
|
this.Type = type;
|
||||||
|
this.Source = source;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SortCode() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
internal class Message {
|
internal class Message {
|
||||||
internal ulong ContentId;
|
// ReSharper disable once UnusedMember.Global
|
||||||
|
internal ObjectId Id { get; } = ObjectId.NewObjectId();
|
||||||
|
internal ulong Receiver { get; }
|
||||||
|
internal ulong ContentId { get; set; }
|
||||||
|
|
||||||
|
[BsonIgnore]
|
||||||
internal float? Height;
|
internal float? Height;
|
||||||
|
|
||||||
|
[BsonIgnore]
|
||||||
internal bool IsVisible;
|
internal bool IsVisible;
|
||||||
|
|
||||||
internal DateTime Date { get; }
|
internal DateTime Date { get; }
|
||||||
|
@ -12,14 +34,40 @@ internal class Message {
|
||||||
internal List<Chunk> Sender { get; }
|
internal List<Chunk> Sender { get; }
|
||||||
internal List<Chunk> Content { get; }
|
internal List<Chunk> Content { get; }
|
||||||
|
|
||||||
internal Message(ChatCode code, List<Chunk> sender, List<Chunk> content) {
|
internal SeString SenderSource { get; }
|
||||||
|
internal SeString ContentSource { get; }
|
||||||
|
|
||||||
|
internal SortCode SortCode { get; }
|
||||||
|
|
||||||
|
internal Message(ulong receiver, ChatCode code, List<Chunk> sender, List<Chunk> content, SeString senderSource, SeString contentSource) {
|
||||||
|
this.Receiver = receiver;
|
||||||
this.Date = DateTime.UtcNow;
|
this.Date = DateTime.UtcNow;
|
||||||
this.Code = code;
|
this.Code = code;
|
||||||
this.Sender = sender;
|
this.Sender = sender;
|
||||||
this.Content = content;
|
this.Content = content;
|
||||||
|
this.SenderSource = senderSource;
|
||||||
|
this.ContentSource = contentSource;
|
||||||
|
this.SortCode = new SortCode(this.Code.Type, this.Code.Source);
|
||||||
|
|
||||||
foreach (var chunk in sender.Concat(content)) {
|
foreach (var chunk in sender.Concat(content)) {
|
||||||
chunk.Message = this;
|
chunk.Message = this;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal Message(ObjectId id, ulong receiver, ulong contentId, DateTime date, BsonDocument code, BsonArray sender, BsonArray content, BsonDocument senderSource, BsonDocument contentSource, BsonDocument sortCode) {
|
||||||
|
this.Id = id;
|
||||||
|
this.Receiver = receiver;
|
||||||
|
this.ContentId = contentId;
|
||||||
|
this.Date = date;
|
||||||
|
this.Code = BsonMapper.Global.ToObject<ChatCode>(code);
|
||||||
|
this.Sender = BsonMapper.Global.Deserialize<List<Chunk>>(sender);
|
||||||
|
this.Content = BsonMapper.Global.Deserialize<List<Chunk>>(content);
|
||||||
|
this.SenderSource = BsonMapper.Global.ToObject<SeString>(senderSource);
|
||||||
|
this.ContentSource = BsonMapper.Global.ToObject<SeString>(contentSource);
|
||||||
|
this.SortCode = BsonMapper.Global.ToObject<SortCode>(sortCode);
|
||||||
|
|
||||||
|
foreach (var chunk in this.Sender.Concat(this.Content)) {
|
||||||
|
chunk.Message = this;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -195,11 +195,11 @@ internal sealed class PayloadHandler {
|
||||||
InlineIcon(icon);
|
InlineIcon(icon);
|
||||||
}
|
}
|
||||||
|
|
||||||
var name = ChunkUtil.ToChunks(status.Status.Name.ToDalamudString(), null);
|
var name = ChunkUtil.ToChunks(status.Status.Name.ToDalamudString(), ChunkSource.None, null);
|
||||||
this.Log.DrawChunks(name.ToList());
|
this.Log.DrawChunks(name.ToList());
|
||||||
ImGui.Separator();
|
ImGui.Separator();
|
||||||
|
|
||||||
var desc = ChunkUtil.ToChunks(status.Status.Description.ToDalamudString(), null);
|
var desc = ChunkUtil.ToChunks(status.Status.Description.ToDalamudString(), ChunkSource.None, null);
|
||||||
this.Log.DrawChunks(desc.ToList());
|
this.Log.DrawChunks(desc.ToList());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -217,11 +217,11 @@ internal sealed class PayloadHandler {
|
||||||
InlineIcon(icon);
|
InlineIcon(icon);
|
||||||
}
|
}
|
||||||
|
|
||||||
var name = ChunkUtil.ToChunks(item.Item.Name.ToDalamudString(), null);
|
var name = ChunkUtil.ToChunks(item.Item.Name.ToDalamudString(), ChunkSource.None, null);
|
||||||
this.Log.DrawChunks(name.ToList());
|
this.Log.DrawChunks(name.ToList());
|
||||||
ImGui.Separator();
|
ImGui.Separator();
|
||||||
|
|
||||||
var desc = ChunkUtil.ToChunks(item.Item.Description.ToDalamudString(), null);
|
var desc = ChunkUtil.ToChunks(item.Item.Description.ToDalamudString(), ChunkSource.None, null);
|
||||||
this.Log.DrawChunks(desc.ToList());
|
this.Log.DrawChunks(desc.ToList());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -235,13 +235,13 @@ internal sealed class PayloadHandler {
|
||||||
InlineIcon(icon);
|
InlineIcon(icon);
|
||||||
}
|
}
|
||||||
|
|
||||||
var name = ChunkUtil.ToChunks(item.Name.ToDalamudString(), null);
|
var name = ChunkUtil.ToChunks(item.Name.ToDalamudString(), ChunkSource.None, null);
|
||||||
this.Log.DrawChunks(name.ToList());
|
this.Log.DrawChunks(name.ToList());
|
||||||
ImGui.Separator();
|
ImGui.Separator();
|
||||||
|
|
||||||
var help = this.Ui.Plugin.DataManager.GetExcelSheet<EventItemHelp>()?.GetRow(payload.RawItemId);
|
var help = this.Ui.Plugin.DataManager.GetExcelSheet<EventItemHelp>()?.GetRow(payload.RawItemId);
|
||||||
if (help != null) {
|
if (help != null) {
|
||||||
var desc = ChunkUtil.ToChunks(help.Description.ToDalamudString(), null);
|
var desc = ChunkUtil.ToChunks(help.Description.ToDalamudString(), ChunkSource.None, null);
|
||||||
this.Log.DrawChunks(desc.ToList());
|
this.Log.DrawChunks(desc.ToList());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -279,7 +279,7 @@ internal sealed class PayloadHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ClickLinkPayload(Chunk chunk, Payload payload, DalamudLinkPayload link) {
|
private void ClickLinkPayload(Chunk chunk, Payload payload, DalamudLinkPayload link) {
|
||||||
if (chunk.Source is not { } source) {
|
if (chunk.GetSeString() is not { } source) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -334,7 +334,7 @@ internal sealed class PayloadHandler {
|
||||||
name.Payloads.Add(new TextPayload(" "));
|
name.Payloads.Add(new TextPayload(" "));
|
||||||
}
|
}
|
||||||
|
|
||||||
this.Log.DrawChunks(ChunkUtil.ToChunks(name, null).ToList(), false);
|
this.Log.DrawChunks(ChunkUtil.ToChunks(name, ChunkSource.None, null).ToList(), false);
|
||||||
ImGui.Separator();
|
ImGui.Separator();
|
||||||
|
|
||||||
var realItemId = payload.RawItemId;
|
var realItemId = payload.RawItemId;
|
||||||
|
@ -383,7 +383,7 @@ internal sealed class PayloadHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
var name = item.Name.ToDalamudString();
|
var name = item.Name.ToDalamudString();
|
||||||
this.Log.DrawChunks(ChunkUtil.ToChunks(name, null).ToList(), false);
|
this.Log.DrawChunks(ChunkUtil.ToChunks(name, ChunkSource.None, null).ToList(), false);
|
||||||
ImGui.Separator();
|
ImGui.Separator();
|
||||||
|
|
||||||
var realItemId = payload.RawItemId;
|
var realItemId = payload.RawItemId;
|
||||||
|
@ -398,11 +398,11 @@ internal sealed class PayloadHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawPlayerPopup(Chunk chunk, PlayerPayload player) {
|
private void DrawPlayerPopup(Chunk chunk, PlayerPayload player) {
|
||||||
var name = new List<Chunk> { new TextChunk(null, null, player.PlayerName) };
|
var name = new List<Chunk> { new TextChunk(ChunkSource.None, null, player.PlayerName) };
|
||||||
if (player.World.IsPublic) {
|
if (player.World.IsPublic) {
|
||||||
name.AddRange(new Chunk[] {
|
name.AddRange(new Chunk[] {
|
||||||
new IconChunk(null, null, BitmapFontIcon.CrossWorld),
|
new IconChunk(ChunkSource.None, null, BitmapFontIcon.CrossWorld),
|
||||||
new TextChunk(null, null, player.World.Name),
|
new TextChunk(ChunkSource.None, null, player.World.Name),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using System.Globalization;
|
using System.Diagnostics;
|
||||||
|
using System.Globalization;
|
||||||
using ChatTwo.Resources;
|
using ChatTwo.Resources;
|
||||||
using Dalamud.Data;
|
using Dalamud.Data;
|
||||||
using Dalamud.Game;
|
using Dalamud.Game;
|
||||||
|
@ -66,8 +67,12 @@ public sealed class Plugin : IDalamudPlugin {
|
||||||
|
|
||||||
internal int DeferredSaveFrames = -1;
|
internal int DeferredSaveFrames = -1;
|
||||||
|
|
||||||
|
internal DateTime GameStarted { get; }
|
||||||
|
|
||||||
#pragma warning disable CS8618
|
#pragma warning disable CS8618
|
||||||
public Plugin() {
|
public Plugin() {
|
||||||
|
this.GameStarted = Process.GetCurrentProcess().StartTime.ToUniversalTime();
|
||||||
|
|
||||||
this.Config = this.Interface!.GetPluginConfig() as Configuration ?? new Configuration();
|
this.Config = this.Interface!.GetPluginConfig() as Configuration ?? new Configuration();
|
||||||
this.Config.Migrate();
|
this.Config.Migrate();
|
||||||
|
|
||||||
|
@ -79,6 +84,10 @@ public sealed class Plugin : IDalamudPlugin {
|
||||||
this.Store = new Store(this);
|
this.Store = new Store(this);
|
||||||
this.Ui = new PluginUi(this);
|
this.Ui = new PluginUi(this);
|
||||||
|
|
||||||
|
if (this.Interface.Reason is not PluginLoadReason.Boot) {
|
||||||
|
this.Store.FilterAllTabs(false);
|
||||||
|
}
|
||||||
|
|
||||||
this.Framework!.Update += this.FrameworkUpdate;
|
this.Framework!.Update += this.FrameworkUpdate;
|
||||||
this.Interface.LanguageChanged += this.LanguageChanged;
|
this.Interface.LanguageChanged += this.LanguageChanged;
|
||||||
}
|
}
|
||||||
|
|
|
@ -132,10 +132,26 @@ internal sealed class PluginUi : IDisposable {
|
||||||
component.Dispose();
|
component.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
this._regularFont.Item1.Free();
|
if (this._regularFont.Item1.IsAllocated) {
|
||||||
this._italicFont.Item1.Free();
|
this._regularFont.Item1.Free();
|
||||||
this._gameSymFont.Item1.Free();
|
}
|
||||||
this._symRange.Free();
|
|
||||||
|
if (this._italicFont.Item1.IsAllocated) {
|
||||||
|
this._italicFont.Item1.Free();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._jpFont.Item1.IsAllocated) {
|
||||||
|
this._jpFont.Item1.Free();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._gameSymFont.Item1.IsAllocated) {
|
||||||
|
this._gameSymFont.Item1.Free();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._symRange.IsAllocated) {
|
||||||
|
this._symRange.Free();
|
||||||
|
}
|
||||||
|
|
||||||
this._fontCfg.Destroy();
|
this._fontCfg.Destroy();
|
||||||
this._fontCfgMerge.Destroy();
|
this._fontCfgMerge.Destroy();
|
||||||
}
|
}
|
||||||
|
|
63
ChatTwo/Resources/Language.Designer.cs
generated
63
ChatTwo/Resources/Language.Designer.cs
generated
|
@ -402,6 +402,33 @@ namespace ChatTwo.Resources {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Database.
|
||||||
|
/// </summary>
|
||||||
|
internal static string Options_Database_Tab {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("Options_Database_Tab", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to If battle messages are saved to the database, the size of the database will grow much faster, and there will be a noticeable freeze when saving settings. It is recommended to leave this disabled..
|
||||||
|
/// </summary>
|
||||||
|
internal static string Options_DatabaseBattleMessages_Description {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("Options_DatabaseBattleMessages_Description", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Save battle messages in database.
|
||||||
|
/// </summary>
|
||||||
|
internal static string Options_DatabaseBattleMessages_Name {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("Options_DatabaseBattleMessages_Name", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to Display.
|
/// Looks up a localized string similar to Display.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -411,6 +438,24 @@ namespace ChatTwo.Resources {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Include messages from before the game was launched when populating tabs. Tabs are populated when the settings are saved..
|
||||||
|
/// </summary>
|
||||||
|
internal static string Options_FilterIncludePreviousSessions_Description {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("Options_FilterIncludePreviousSessions_Description", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Include previous sessions when populating tabs.
|
||||||
|
/// </summary>
|
||||||
|
internal static string Options_FilterIncludePreviousSessions_Name {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("Options_FilterIncludePreviousSessions_Name", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to The font {0} will use to display non-Japanese text..
|
/// Looks up a localized string similar to The font {0} will use to display non-Japanese text..
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -591,6 +636,24 @@ namespace ChatTwo.Resources {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to After logging in, populate the chat with the messages from when you last logged off..
|
||||||
|
/// </summary>
|
||||||
|
internal static string Options_LoadPreviousSession_Description {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("Options_LoadPreviousSession_Description", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Display previous chat session on login.
|
||||||
|
/// </summary>
|
||||||
|
internal static string Options_LoadPreviousSession_Name {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("Options_LoadPreviousSession_Name", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to Miscellaneous.
|
/// Looks up a localized string similar to Miscellaneous.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -437,4 +437,25 @@
|
||||||
<data name="Options_Language_Description" xml:space="preserve">
|
<data name="Options_Language_Description" xml:space="preserve">
|
||||||
<value>The language to display {0} in.</value>
|
<value>The language to display {0} in.</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Options_DatabaseBattleMessages_Name" xml:space="preserve">
|
||||||
|
<value>Save battle messages in database</value>
|
||||||
|
</data>
|
||||||
|
<data name="Options_DatabaseBattleMessages_Description" xml:space="preserve">
|
||||||
|
<value>If battle messages are saved to the database, the size of the database will grow much faster, and there will be a noticeable freeze when saving settings. It is recommended to leave this disabled.</value>
|
||||||
|
</data>
|
||||||
|
<data name="Options_LoadPreviousSession_Name" xml:space="preserve">
|
||||||
|
<value>Display previous chat session on login</value>
|
||||||
|
</data>
|
||||||
|
<data name="Options_LoadPreviousSession_Description" xml:space="preserve">
|
||||||
|
<value>After logging in, populate the chat with the messages from when you last logged off.</value>
|
||||||
|
</data>
|
||||||
|
<data name="Options_Database_Tab" xml:space="preserve">
|
||||||
|
<value>Database</value>
|
||||||
|
</data>
|
||||||
|
<data name="Options_FilterIncludePreviousSessions_Name" xml:space="preserve">
|
||||||
|
<value>Include previous sessions when populating tabs</value>
|
||||||
|
</data>
|
||||||
|
<data name="Options_FilterIncludePreviousSessions_Description" xml:space="preserve">
|
||||||
|
<value>Include messages from before the game was launched when populating tabs. Tabs are populated when the settings are saved.</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
|
|
183
ChatTwo/Store.cs
183
ChatTwo/Store.cs
|
@ -1,9 +1,11 @@
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
|
using System.Diagnostics;
|
||||||
using ChatTwo.Code;
|
using ChatTwo.Code;
|
||||||
using ChatTwo.Util;
|
using ChatTwo.Util;
|
||||||
using Dalamud.Game;
|
using Dalamud.Game;
|
||||||
using Dalamud.Game.Text;
|
using Dalamud.Game.Text;
|
||||||
using Dalamud.Game.Text.SeStringHandling;
|
using Dalamud.Game.Text.SeStringHandling;
|
||||||
|
using LiteDB;
|
||||||
using Lumina.Excel.GeneratedSheets;
|
using Lumina.Excel.GeneratedSheets;
|
||||||
|
|
||||||
namespace ChatTwo;
|
namespace ChatTwo;
|
||||||
|
@ -11,63 +13,146 @@ namespace ChatTwo;
|
||||||
internal class Store : IDisposable {
|
internal class Store : IDisposable {
|
||||||
internal const int MessagesLimit = 10_000;
|
internal const int MessagesLimit = 10_000;
|
||||||
|
|
||||||
internal sealed class MessagesLock : IDisposable {
|
|
||||||
private Mutex Mutex { get; }
|
|
||||||
internal List<Message> Messages { get; }
|
|
||||||
|
|
||||||
internal MessagesLock(List<Message> messages, Mutex mutex) {
|
|
||||||
this.Messages = messages;
|
|
||||||
this.Mutex = mutex;
|
|
||||||
|
|
||||||
this.Mutex.WaitOne();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose() {
|
|
||||||
this.Mutex.ReleaseMutex();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Plugin Plugin { get; }
|
private Plugin Plugin { get; }
|
||||||
|
|
||||||
private Mutex MessagesMutex { get; } = new();
|
|
||||||
private List<Message> Messages { get; } = new();
|
|
||||||
private ConcurrentQueue<(uint, Message)> Pending { get; } = new();
|
private ConcurrentQueue<(uint, Message)> Pending { get; } = new();
|
||||||
|
private Stopwatch CheckpointTimer { get; } = new();
|
||||||
|
internal ILiteDatabase Database { get; }
|
||||||
|
private ILiteCollection<Message> Messages => this.Database.GetCollection<Message>("messages");
|
||||||
|
|
||||||
private Dictionary<ChatType, NameFormatting> Formats { get; } = new();
|
private Dictionary<ChatType, NameFormatting> Formats { get; } = new();
|
||||||
|
private ulong LastContentId { get; set; }
|
||||||
|
|
||||||
|
private ulong CurrentContentId {
|
||||||
|
get {
|
||||||
|
var contentId = this.Plugin.ClientState.LocalContentId;
|
||||||
|
return contentId == 0 ? this.LastContentId : contentId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
internal Store(Plugin plugin) {
|
internal Store(Plugin plugin) {
|
||||||
this.Plugin = plugin;
|
this.Plugin = plugin;
|
||||||
|
this.CheckpointTimer.Start();
|
||||||
|
|
||||||
|
var dir = this.Plugin.Interface.ConfigDirectory;
|
||||||
|
dir.Create();
|
||||||
|
|
||||||
|
BsonMapper.Global = new BsonMapper {
|
||||||
|
IncludeNonPublic = true,
|
||||||
|
TrimWhitespace = false,
|
||||||
|
// EnumAsInteger = true,
|
||||||
|
};
|
||||||
|
BsonMapper.Global.Entity<Message>()
|
||||||
|
.Id(msg => msg.Id)
|
||||||
|
.Ctor(doc => new Message(
|
||||||
|
doc["_id"].AsObjectId,
|
||||||
|
(ulong) doc["Receiver"].AsInt64,
|
||||||
|
(ulong) doc["ContentId"].AsInt64,
|
||||||
|
DateTime.UnixEpoch.AddMilliseconds(doc["Date"].AsInt64),
|
||||||
|
doc["Code"].AsDocument,
|
||||||
|
doc["Sender"].AsArray,
|
||||||
|
doc["Content"].AsArray,
|
||||||
|
doc["SenderSource"].AsDocument,
|
||||||
|
doc["ContentSource"].AsDocument,
|
||||||
|
doc["SortCode"].AsDocument
|
||||||
|
));
|
||||||
|
BsonMapper.Global.RegisterType<Payload?>(
|
||||||
|
payload => {
|
||||||
|
switch (payload) {
|
||||||
|
case AchievementPayload achievement:
|
||||||
|
return new BsonDocument(new Dictionary<string, BsonValue> {
|
||||||
|
["Type"] = new("Achievement"),
|
||||||
|
["Id"] = new(achievement.Id),
|
||||||
|
});
|
||||||
|
case PartyFinderPayload partyFinder:
|
||||||
|
return new BsonDocument(new Dictionary<string, BsonValue> {
|
||||||
|
["Type"] = new("PartyFinder"),
|
||||||
|
["Id"] = new(partyFinder.Id),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return payload?.Encode();
|
||||||
|
},
|
||||||
|
bson => {
|
||||||
|
if (bson.IsNull) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bson.IsDocument) {
|
||||||
|
return bson["Type"].AsString switch {
|
||||||
|
"Achievement" => new AchievementPayload((uint) bson["Id"].AsInt64),
|
||||||
|
"PartyFinder" => new PartyFinderPayload((uint) bson["Id"].AsInt64),
|
||||||
|
_ => null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return Payload.Decode(new BinaryReader(new MemoryStream(bson.AsBinary)));
|
||||||
|
});
|
||||||
|
BsonMapper.Global.RegisterType(
|
||||||
|
type => (int) type,
|
||||||
|
bson => (ChatType) bson.AsInt32
|
||||||
|
);
|
||||||
|
BsonMapper.Global.RegisterType(
|
||||||
|
source => (int) source,
|
||||||
|
bson => (ChatSource) bson.AsInt32
|
||||||
|
);
|
||||||
|
BsonMapper.Global.RegisterType(
|
||||||
|
dateTime => dateTime.Subtract(DateTime.UnixEpoch).TotalMilliseconds,
|
||||||
|
bson => DateTime.UnixEpoch.AddMilliseconds(bson.AsInt64)
|
||||||
|
);
|
||||||
|
this.Database = new LiteDatabase(Path.Join(dir.FullName, "chat.db"), BsonMapper.Global) {
|
||||||
|
CheckpointSize = 1_000,
|
||||||
|
Timeout = TimeSpan.FromSeconds(1),
|
||||||
|
};
|
||||||
|
this.Messages.EnsureIndex(msg => msg.Date);
|
||||||
|
this.Messages.EnsureIndex(msg => msg.SortCode);
|
||||||
|
|
||||||
this.Plugin.ChatGui.ChatMessageUnhandled += this.ChatMessage;
|
this.Plugin.ChatGui.ChatMessageUnhandled += this.ChatMessage;
|
||||||
this.Plugin.Framework.Update += this.GetMessageInfo;
|
this.Plugin.Framework.Update += this.GetMessageInfo;
|
||||||
|
this.Plugin.Framework.Update += this.UpdateReceiver;
|
||||||
|
this.Plugin.ClientState.Logout += this.Logout;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose() {
|
public void Dispose() {
|
||||||
|
this.Plugin.ClientState.Logout -= this.Logout;
|
||||||
|
this.Plugin.Framework.Update -= this.UpdateReceiver;
|
||||||
this.Plugin.Framework.Update -= this.GetMessageInfo;
|
this.Plugin.Framework.Update -= this.GetMessageInfo;
|
||||||
this.Plugin.ChatGui.ChatMessageUnhandled -= this.ChatMessage;
|
this.Plugin.ChatGui.ChatMessageUnhandled -= this.ChatMessage;
|
||||||
|
|
||||||
this.MessagesMutex.Dispose();
|
this.Database.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Logout(object? sender, EventArgs eventArgs) {
|
||||||
|
this.LastContentId = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateReceiver(Framework framework) {
|
||||||
|
var contentId = this.Plugin.ClientState.LocalContentId;
|
||||||
|
if (contentId != 0) {
|
||||||
|
this.LastContentId = contentId;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void GetMessageInfo(Framework framework) {
|
private void GetMessageInfo(Framework framework) {
|
||||||
|
if (this.CheckpointTimer.Elapsed > TimeSpan.FromMinutes(5)) {
|
||||||
|
this.CheckpointTimer.Restart();
|
||||||
|
new Thread(() => this.Database.Checkpoint()).Start();
|
||||||
|
}
|
||||||
|
|
||||||
if (!this.Pending.TryDequeue(out var entry)) {
|
if (!this.Pending.TryDequeue(out var entry)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var contentId = this.Plugin.Functions.Chat.GetContentIdForEntry(entry.Item1);
|
var contentId = this.Plugin.Functions.Chat.GetContentIdForEntry(entry.Item1);
|
||||||
entry.Item2.ContentId = contentId ?? 0;
|
entry.Item2.ContentId = contentId ?? 0;
|
||||||
}
|
if (this.Plugin.Config.DatabaseBattleMessages || !entry.Item2.Code.IsBattle()) {
|
||||||
|
this.Messages.Update(entry.Item2);
|
||||||
internal MessagesLock GetMessages() {
|
}
|
||||||
return new MessagesLock(this.Messages, this.MessagesMutex);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void AddMessage(Message message, Tab? currentTab) {
|
internal void AddMessage(Message message, Tab? currentTab) {
|
||||||
using var messages = this.GetMessages();
|
if (this.Plugin.Config.DatabaseBattleMessages || !message.Code.IsBattle()) {
|
||||||
messages.Messages.Add(message);
|
this.Messages.Insert(message);
|
||||||
|
|
||||||
while (messages.Messages.Count > MessagesLimit) {
|
|
||||||
messages.Messages.RemoveAt(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var currentMatches = currentTab?.Matches(message) ?? false;
|
var currentMatches = currentTab?.Matches(message) ?? false;
|
||||||
|
@ -88,12 +173,36 @@ internal class Store : IDisposable {
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void FilterTab(Tab tab, bool unread) {
|
internal void FilterTab(Tab tab, bool unread) {
|
||||||
using var messages = this.GetMessages();
|
var sortCodes = new List<SortCode>();
|
||||||
foreach (var message in messages.Messages) {
|
foreach (var (type, sources) in tab.ChatCodes) {
|
||||||
if (tab.Matches(message)) {
|
sortCodes.Add(new SortCode(type, 0));
|
||||||
tab.AddMessage(message, unread);
|
sortCodes.Add(new SortCode(type, (ChatSource) 1));
|
||||||
|
|
||||||
|
if (type.HasSource()) {
|
||||||
|
foreach (var source in Enum.GetValues<ChatSource>()) {
|
||||||
|
if (sources.HasFlag(source)) {
|
||||||
|
sortCodes.Add(new SortCode(type, source));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var query = this.Messages
|
||||||
|
.Query()
|
||||||
|
.OrderByDescending(msg => msg.Date)
|
||||||
|
.Where(msg => sortCodes.Contains(msg.SortCode))
|
||||||
|
.Where(msg => msg.Receiver == this.CurrentContentId);
|
||||||
|
if (!this.Plugin.Config.FilterIncludePreviousSessions) {
|
||||||
|
query = query.Where(msg => msg.Date >= this.Plugin.GameStarted);
|
||||||
|
}
|
||||||
|
|
||||||
|
var messages = query
|
||||||
|
.Limit(MessagesLimit)
|
||||||
|
.ToEnumerable()
|
||||||
|
.Reverse();
|
||||||
|
foreach (var message in messages) {
|
||||||
|
tab.AddMessage(message, unread);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ChatMessage(XivChatType type, uint senderId, SeString sender, SeString message) {
|
private void ChatMessage(XivChatType type, uint senderId, SeString sender, SeString message) {
|
||||||
|
@ -106,18 +215,18 @@ internal class Store : IDisposable {
|
||||||
|
|
||||||
var senderChunks = new List<Chunk>();
|
var senderChunks = new List<Chunk>();
|
||||||
if (formatting is { IsPresent: true }) {
|
if (formatting is { IsPresent: true }) {
|
||||||
senderChunks.Add(new TextChunk(null, null, formatting.Before) {
|
senderChunks.Add(new TextChunk(ChunkSource.None, null, formatting.Before) {
|
||||||
FallbackColour = chatCode.Type,
|
FallbackColour = chatCode.Type,
|
||||||
});
|
});
|
||||||
senderChunks.AddRange(ChunkUtil.ToChunks(sender, chatCode.Type));
|
senderChunks.AddRange(ChunkUtil.ToChunks(sender, ChunkSource.Sender, chatCode.Type));
|
||||||
senderChunks.Add(new TextChunk(null, null, formatting.After) {
|
senderChunks.Add(new TextChunk(ChunkSource.None, null, formatting.After) {
|
||||||
FallbackColour = chatCode.Type,
|
FallbackColour = chatCode.Type,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
var messageChunks = ChunkUtil.ToChunks(message, chatCode.Type).ToList();
|
var messageChunks = ChunkUtil.ToChunks(message, ChunkSource.Content, chatCode.Type).ToList();
|
||||||
|
|
||||||
var msg = new Message(chatCode, senderChunks, messageChunks);
|
var msg = new Message(this.CurrentContentId, chatCode, senderChunks, messageChunks, sender, message);
|
||||||
this.AddMessage(msg, this.Plugin.Ui.CurrentTab);
|
this.AddMessage(msg, this.Plugin.Ui.CurrentTab);
|
||||||
|
|
||||||
var idx = this.Plugin.Functions.GetCurrentChatLogEntryIndex();
|
var idx = this.Plugin.Functions.GetCurrentChatLogEntryIndex();
|
||||||
|
|
|
@ -53,14 +53,28 @@ internal sealed class ChatLog : IUiComponent {
|
||||||
this._fontIcon = this.Ui.Plugin.DataManager.GetImGuiTexture("common/font/fonticon_ps5.tex");
|
this._fontIcon = this.Ui.Plugin.DataManager.GetImGuiTexture("common/font/fonticon_ps5.tex");
|
||||||
|
|
||||||
this.Ui.Plugin.Functions.Chat.Activated += this.Activated;
|
this.Ui.Plugin.Functions.Chat.Activated += this.Activated;
|
||||||
|
this.Ui.Plugin.ClientState.Login += this.Login;
|
||||||
|
this.Ui.Plugin.ClientState.Logout += this.Logout;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose() {
|
public void Dispose() {
|
||||||
|
this.Ui.Plugin.ClientState.Logout -= this.Logout;
|
||||||
|
this.Ui.Plugin.ClientState.Login -= this.Login;
|
||||||
this.Ui.Plugin.Functions.Chat.Activated -= this.Activated;
|
this.Ui.Plugin.Functions.Chat.Activated -= this.Activated;
|
||||||
this._fontIcon?.Dispose();
|
this._fontIcon?.Dispose();
|
||||||
this.Ui.Plugin.CommandManager.RemoveHandler("/clearlog2");
|
this.Ui.Plugin.CommandManager.RemoveHandler("/clearlog2");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void Logout(object? sender, EventArgs e) {
|
||||||
|
foreach (var tab in this.Ui.Plugin.Config.Tabs) {
|
||||||
|
tab.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Login(object? sender, EventArgs e) {
|
||||||
|
this.Ui.Plugin.Store.FilterAllTabs(false);
|
||||||
|
}
|
||||||
|
|
||||||
private void Activated(ChatActivatedArgs args) {
|
private void Activated(ChatActivatedArgs args) {
|
||||||
this.Activate = true;
|
this.Activate = true;
|
||||||
if (args.AddIfNotPresent != null && !this.Chat.Contains(args.AddIfNotPresent)) {
|
if (args.AddIfNotPresent != null && !this.Chat.Contains(args.AddIfNotPresent)) {
|
||||||
|
@ -126,10 +140,6 @@ internal sealed class ChatLog : IUiComponent {
|
||||||
private void ClearLog(string command, string arguments) {
|
private void ClearLog(string command, string arguments) {
|
||||||
switch (arguments) {
|
switch (arguments) {
|
||||||
case "all":
|
case "all":
|
||||||
using (var messages = this.Ui.Plugin.Store.GetMessages()) {
|
|
||||||
messages.Messages.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var tab in this.Ui.Plugin.Config.Tabs) {
|
foreach (var tab in this.Ui.Plugin.Config.Tabs) {
|
||||||
tab.Clear();
|
tab.Clear();
|
||||||
}
|
}
|
||||||
|
@ -374,10 +384,10 @@ internal sealed class ChatLog : IUiComponent {
|
||||||
?.RawString ?? "???";
|
?.RawString ?? "???";
|
||||||
|
|
||||||
this.DrawChunks(new Chunk[] {
|
this.DrawChunks(new Chunk[] {
|
||||||
new TextChunk(null, null, "Tell "),
|
new TextChunk(ChunkSource.None, null, "Tell "),
|
||||||
new TextChunk(null, null, this._tellTarget.Name),
|
new TextChunk(ChunkSource.None, null, this._tellTarget.Name),
|
||||||
new IconChunk(null, null, BitmapFontIcon.CrossWorld),
|
new IconChunk(ChunkSource.None, null, BitmapFontIcon.CrossWorld),
|
||||||
new TextChunk(null, null, world),
|
new TextChunk(ChunkSource.None, null, world),
|
||||||
});
|
});
|
||||||
} else if (this._tempChannel != null) {
|
} else if (this._tempChannel != null) {
|
||||||
if (this._tempChannel.Value.IsLinkshell()) {
|
if (this._tempChannel.Value.IsLinkshell()) {
|
||||||
|
@ -626,7 +636,7 @@ internal sealed class ChatLog : IUiComponent {
|
||||||
if (table) {
|
if (table) {
|
||||||
ImGui.TextUnformatted(timestamp);
|
ImGui.TextUnformatted(timestamp);
|
||||||
} else {
|
} else {
|
||||||
this.DrawChunk(new TextChunk(null, null, $"[{timestamp}]") {
|
this.DrawChunk(new TextChunk(ChunkSource.None, null, $"[{timestamp}]") {
|
||||||
Foreground = 0xFFFFFFFF,
|
Foreground = 0xFFFFFFFF,
|
||||||
});
|
});
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
|
|
|
@ -50,7 +50,7 @@ internal class CommandHelp {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.Log.DrawChunks(ChunkUtil.ToChunks(this.Command.Description.ToDalamudString(), null).ToList());
|
this.Log.DrawChunks(ChunkUtil.ToChunks(this.Command.Description.ToDalamudString(), ChunkSource.None, null).ToList());
|
||||||
|
|
||||||
ImGui.End();
|
ImGui.End();
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,7 @@ internal sealed class Settings : IUiComponent {
|
||||||
new Ui.SettingsTabs.Fonts(this.Mutable),
|
new Ui.SettingsTabs.Fonts(this.Mutable),
|
||||||
new ChatColours(this.Mutable, this.Ui.Plugin),
|
new ChatColours(this.Mutable, this.Ui.Plugin),
|
||||||
new Tabs(this.Mutable),
|
new Tabs(this.Mutable),
|
||||||
|
new Database(this.Mutable, this.Ui.Plugin.Store),
|
||||||
new Miscellaneous(this.Mutable),
|
new Miscellaneous(this.Mutable),
|
||||||
new About(),
|
new About(),
|
||||||
};
|
};
|
||||||
|
@ -70,9 +71,11 @@ internal sealed class Settings : IUiComponent {
|
||||||
|
|
||||||
ImGui.TableNextColumn();
|
ImGui.TableNextColumn();
|
||||||
|
|
||||||
|
var changed = false;
|
||||||
for (var i = 0; i < this.Tabs.Count; i++) {
|
for (var i = 0; i < this.Tabs.Count; i++) {
|
||||||
if (ImGui.Selectable($"{this.Tabs[i].Name}###tab-{i}", this._currentTab == i)) {
|
if (ImGui.Selectable($"{this.Tabs[i].Name}###tab-{i}", this._currentTab == i)) {
|
||||||
this._currentTab = i;
|
this._currentTab = i;
|
||||||
|
changed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -84,7 +87,7 @@ internal sealed class Settings : IUiComponent {
|
||||||
- ImGui.GetStyle().ItemInnerSpacing.Y * 2
|
- ImGui.GetStyle().ItemInnerSpacing.Y * 2
|
||||||
- ImGui.CalcTextSize("A").Y;
|
- ImGui.CalcTextSize("A").Y;
|
||||||
if (ImGui.BeginChild("##chat2-settings", new Vector2(-1, height))) {
|
if (ImGui.BeginChild("##chat2-settings", new Vector2(-1, height))) {
|
||||||
this.Tabs[this._currentTab].Draw();
|
this.Tabs[this._currentTab].Draw(changed);
|
||||||
ImGui.EndChild();
|
ImGui.EndChild();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,7 @@ internal sealed class About : ISettingsTab {
|
||||||
this._translators.Sort((a, b) => string.Compare(a.ToLowerInvariant(), b.ToLowerInvariant(), StringComparison.Ordinal));
|
this._translators.Sort((a, b) => string.Compare(a.ToLowerInvariant(), b.ToLowerInvariant(), StringComparison.Ordinal));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Draw() {
|
public void Draw(bool changed) {
|
||||||
ImGui.PushTextWrapPos();
|
ImGui.PushTextWrapPos();
|
||||||
|
|
||||||
ImGui.TextUnformatted(string.Format(Language.Options_About_Opening, Plugin.PluginName));
|
ImGui.TextUnformatted(string.Format(Language.Options_About_Opening, Plugin.PluginName));
|
||||||
|
|
|
@ -32,7 +32,7 @@ internal sealed class ChatColours : ISettingsTab {
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Draw() {
|
public void Draw(bool changed) {
|
||||||
foreach (var (_, types) in ChatTypeExt.SortOrder) {
|
foreach (var (_, types) in ChatTypeExt.SortOrder) {
|
||||||
foreach (var type in types) {
|
foreach (var type in types) {
|
||||||
if (ImGuiUtil.IconButton(FontAwesomeIcon.UndoAlt, $"{type}", Language.Options_ChatColours_Reset)) {
|
if (ImGuiUtil.IconButton(FontAwesomeIcon.UndoAlt, $"{type}", Language.Options_ChatColours_Reset)) {
|
||||||
|
|
60
ChatTwo/Ui/SettingsTabs/Database.cs
Executable file
60
ChatTwo/Ui/SettingsTabs/Database.cs
Executable file
|
@ -0,0 +1,60 @@
|
||||||
|
using ChatTwo.Resources;
|
||||||
|
using ChatTwo.Util;
|
||||||
|
using ImGuiNET;
|
||||||
|
|
||||||
|
namespace ChatTwo.Ui.SettingsTabs;
|
||||||
|
|
||||||
|
internal sealed class Database : ISettingsTab {
|
||||||
|
private Configuration Mutable { get; }
|
||||||
|
private Store Store { get; }
|
||||||
|
|
||||||
|
public string Name => Language.Options_Database_Tab + "###tabs-database";
|
||||||
|
|
||||||
|
internal Database(Configuration mutable, Store store) {
|
||||||
|
this.Store = store;
|
||||||
|
this.Mutable = mutable;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool _showAdvanced;
|
||||||
|
|
||||||
|
public void Draw(bool changed) {
|
||||||
|
if (changed) {
|
||||||
|
this._showAdvanced = ImGui.GetIO().KeyShift;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGuiUtil.OptionCheckbox(ref this.Mutable.DatabaseBattleMessages, Language.Options_DatabaseBattleMessages_Name, Language.Options_DatabaseBattleMessages_Description);
|
||||||
|
ImGui.Spacing();
|
||||||
|
|
||||||
|
if (ImGuiUtil.OptionCheckbox(ref this.Mutable.LoadPreviousSession, Language.Options_LoadPreviousSession_Name, Language.Options_LoadPreviousSession_Description)) {
|
||||||
|
if (this.Mutable.LoadPreviousSession) {
|
||||||
|
this.Mutable.FilterIncludePreviousSessions = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui.Spacing();
|
||||||
|
|
||||||
|
if (ImGuiUtil.OptionCheckbox(ref this.Mutable.FilterIncludePreviousSessions, Language.Options_FilterIncludePreviousSessions_Name, Language.Options_FilterIncludePreviousSessions_Description)) {
|
||||||
|
if (!this.Mutable.FilterIncludePreviousSessions) {
|
||||||
|
this.Mutable.LoadPreviousSession = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui.Spacing();
|
||||||
|
|
||||||
|
if (this._showAdvanced && ImGui.TreeNodeEx("Advanced")) {
|
||||||
|
ImGui.PushTextWrapPos();
|
||||||
|
ImGuiUtil.WarningText("Do not click these buttons unless you know what you're doing.");
|
||||||
|
|
||||||
|
if (ImGui.Button("Checkpoint")) {
|
||||||
|
this.Store.Database.Checkpoint();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui.Button("Rebuild")) {
|
||||||
|
this.Store.Database.Rebuild();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui.PopTextWrapPos();
|
||||||
|
ImGui.TreePop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -13,7 +13,7 @@ internal sealed class Display : ISettingsTab {
|
||||||
this.Mutable = mutable;
|
this.Mutable = mutable;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Draw() {
|
public void Draw(bool changed) {
|
||||||
ImGui.PushTextWrapPos();
|
ImGui.PushTextWrapPos();
|
||||||
|
|
||||||
ImGuiUtil.OptionCheckbox(ref this.Mutable.HideChat, Language.Options_HideChat_Name, Language.Options_HideChat_Description);
|
ImGuiUtil.OptionCheckbox(ref this.Mutable.HideChat, Language.Options_HideChat_Name, Language.Options_HideChat_Description);
|
||||||
|
|
|
@ -21,8 +21,8 @@ public class Fonts : ISettingsTab {
|
||||||
this.JpFonts = Ui.Fonts.GetJpFonts();
|
this.JpFonts = Ui.Fonts.GetJpFonts();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Draw() {
|
public void Draw(bool changed) {
|
||||||
if (ImGui.IsWindowAppearing()) {
|
if (changed) {
|
||||||
this.UpdateFonts();
|
this.UpdateFonts();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,5 +2,5 @@
|
||||||
|
|
||||||
internal interface ISettingsTab {
|
internal interface ISettingsTab {
|
||||||
string Name { get; }
|
string Name { get; }
|
||||||
void Draw();
|
void Draw(bool changed);
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@ internal sealed class Miscellaneous : ISettingsTab {
|
||||||
this.Mutable = mutable;
|
this.Mutable = mutable;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Draw() {
|
public void Draw(bool changed) {
|
||||||
if (ImGuiUtil.BeginComboVertical(Language.Options_Language_Name, this.Mutable.LanguageOverride.Name())) {
|
if (ImGuiUtil.BeginComboVertical(Language.Options_Language_Name, this.Mutable.LanguageOverride.Name())) {
|
||||||
foreach (var language in Enum.GetValues<LanguageOverride>()) {
|
foreach (var language in Enum.GetValues<LanguageOverride>()) {
|
||||||
if (ImGui.Selectable(language.Name())) {
|
if (ImGui.Selectable(language.Name())) {
|
||||||
|
|
|
@ -17,7 +17,7 @@ internal sealed class Tabs : ISettingsTab {
|
||||||
this.Mutable = mutable;
|
this.Mutable = mutable;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Draw() {
|
public void Draw(bool changed) {
|
||||||
const string addTabPopup = "add-tab-popup";
|
const string addTabPopup = "add-tab-popup";
|
||||||
|
|
||||||
if (ImGuiUtil.IconButton(FontAwesomeIcon.Plus, tooltip: Language.Options_Tabs_Add)) {
|
if (ImGuiUtil.IconButton(FontAwesomeIcon.Plus, tooltip: Language.Options_Tabs_Add)) {
|
||||||
|
|
|
@ -5,7 +5,7 @@ using Dalamud.Game.Text.SeStringHandling.Payloads;
|
||||||
namespace ChatTwo.Util;
|
namespace ChatTwo.Util;
|
||||||
|
|
||||||
internal static class ChunkUtil {
|
internal static class ChunkUtil {
|
||||||
internal static IEnumerable<Chunk> ToChunks(SeString msg, ChatType? defaultColour) {
|
internal static IEnumerable<Chunk> ToChunks(SeString msg, ChunkSource source, ChatType? defaultColour) {
|
||||||
var chunks = new List<Chunk>();
|
var chunks = new List<Chunk>();
|
||||||
|
|
||||||
var italic = false;
|
var italic = false;
|
||||||
|
@ -14,7 +14,7 @@ internal static class ChunkUtil {
|
||||||
Payload? link = null;
|
Payload? link = null;
|
||||||
|
|
||||||
void Append(string text) {
|
void Append(string text) {
|
||||||
chunks.Add(new TextChunk(msg, link, text) {
|
chunks.Add(new TextChunk(source, link, text) {
|
||||||
FallbackColour = defaultColour,
|
FallbackColour = defaultColour,
|
||||||
Foreground = foreground.Count > 0 ? foreground.Peek() : null,
|
Foreground = foreground.Count > 0 ? foreground.Peek() : null,
|
||||||
Glow = glow.Count > 0 ? glow.Peek() : null,
|
Glow = glow.Count > 0 ? glow.Peek() : null,
|
||||||
|
@ -47,13 +47,13 @@ internal static class ChunkUtil {
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case PayloadType.AutoTranslateText:
|
case PayloadType.AutoTranslateText:
|
||||||
chunks.Add(new IconChunk(msg, link, BitmapFontIcon.AutoTranslateBegin));
|
chunks.Add(new IconChunk(source, link, BitmapFontIcon.AutoTranslateBegin));
|
||||||
var autoText = ((AutoTranslatePayload) payload).Text;
|
var autoText = ((AutoTranslatePayload) payload).Text;
|
||||||
Append(autoText.Substring(2, autoText.Length - 4));
|
Append(autoText.Substring(2, autoText.Length - 4));
|
||||||
chunks.Add(new IconChunk(msg, link, BitmapFontIcon.AutoTranslateEnd));
|
chunks.Add(new IconChunk(source, link, BitmapFontIcon.AutoTranslateEnd));
|
||||||
break;
|
break;
|
||||||
case PayloadType.Icon:
|
case PayloadType.Icon:
|
||||||
chunks.Add(new IconChunk(msg, link, ((IconPayload) payload).Icon));
|
chunks.Add(new IconChunk(source, link, ((IconPayload) payload).Icon));
|
||||||
break;
|
break;
|
||||||
case PayloadType.MapLink:
|
case PayloadType.MapLink:
|
||||||
case PayloadType.Quest:
|
case PayloadType.Quest:
|
||||||
|
|
Loading…
Reference in New Issue
Block a user