Fix ChatService dispose + Add chat configuration
This commit is contained in:
@@ -56,4 +56,8 @@ public class MareConfig : IMareConfiguration
|
||||
public NotificationLocation WarningNotification { get; set; } = NotificationLocation.Both;
|
||||
|
||||
public bool DisableSyncshellChat { get; set; } = false;
|
||||
public int ChatColor { get; set; } = 0; // 0 means "use plugin default"
|
||||
public int ChatLogKind { get; set; } = 1; // XivChatType.Debug
|
||||
public bool ExtraChatAPI { get; set; } = false;
|
||||
public bool ExtraChatTags { get; set; } = false;
|
||||
}
|
||||
@@ -3,5 +3,8 @@ namespace MareSynchronos.MareConfiguration.Models;
|
||||
[Serializable]
|
||||
public class ShellConfig
|
||||
{
|
||||
public bool Enabled { get; set; } = true;
|
||||
public int ShellNumber { get; set; }
|
||||
public int Color { get; set; } = 0; // 0 means "default to the global setting"
|
||||
public int LogKind { get; set; } = 0; // 0 means "default to the global setting"
|
||||
}
|
||||
@@ -5,9 +5,11 @@ using Dalamud.Plugin.Services;
|
||||
using MareSynchronos.API.Data;
|
||||
using MareSynchronos.Interop;
|
||||
using MareSynchronos.MareConfiguration;
|
||||
using MareSynchronos.MareConfiguration.Models;
|
||||
using MareSynchronos.PlayerData.Pairs;
|
||||
using MareSynchronos.Services.Mediator;
|
||||
using MareSynchronos.Services.ServerConfiguration;
|
||||
using MareSynchronos.Utils;
|
||||
using MareSynchronos.WebAPI;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
@@ -15,6 +17,9 @@ namespace MareSynchronos.Services;
|
||||
|
||||
public class ChatService : DisposableMediatorSubscriberBase
|
||||
{
|
||||
public const int DefaultColor = 710;
|
||||
public const int CommandMaxNumber = 50;
|
||||
|
||||
private readonly ILogger<ChatService> _logger;
|
||||
private readonly IChatGui _chatGui;
|
||||
private readonly DalamudUtilService _dalamudUtil;
|
||||
@@ -45,6 +50,7 @@ public class ChatService : DisposableMediatorSubscriberBase
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
if (_gameChatHooks.IsValueCreated)
|
||||
_gameChatHooks.Value!.Dispose();
|
||||
}
|
||||
@@ -61,28 +67,81 @@ public class ChatService : DisposableMediatorSubscriberBase
|
||||
});
|
||||
}
|
||||
|
||||
private ushort ResolveShellColor(int shellColor)
|
||||
{
|
||||
if (shellColor != 0)
|
||||
return (ushort)shellColor;
|
||||
var globalColor = _mareConfig.Current.ChatColor;
|
||||
if (globalColor != 0)
|
||||
return (ushort)globalColor;
|
||||
return (ushort)DefaultColor;
|
||||
}
|
||||
|
||||
private XivChatType ResolveShellLogKind(int shellLogKind)
|
||||
{
|
||||
if (shellLogKind != 0)
|
||||
return (XivChatType)shellLogKind;
|
||||
return (XivChatType)_mareConfig.Current.ChatLogKind;
|
||||
}
|
||||
|
||||
private void HandleGroupChat(GroupChatMsgMessage message)
|
||||
{
|
||||
if (_mareConfig.Current.DisableSyncshellChat)
|
||||
return;
|
||||
|
||||
var chatMsg = message.ChatMsg;
|
||||
var shellNumber = _serverConfigurationManager.GetShellNumberForGid(message.GroupInfo.GID);
|
||||
var shellConfig = _serverConfigurationManager.GetShellConfigForGid(message.GroupInfo.GID);
|
||||
var shellNumber = shellConfig.ShellNumber;
|
||||
|
||||
if (!shellConfig.Enabled)
|
||||
return;
|
||||
|
||||
ushort color = ResolveShellColor(shellConfig.Color);
|
||||
var extraChatTags = _mareConfig.Current.ExtraChatTags;
|
||||
var logKind = ResolveShellLogKind(shellConfig.LogKind);
|
||||
|
||||
var msg = new SeStringBuilder();
|
||||
// TODO: Configure colors and appearance
|
||||
msg.AddUiForeground(710);
|
||||
if (extraChatTags)
|
||||
{
|
||||
msg.Add(ChatUtils.CreateExtraChatTagPayload(message.GroupInfo.GID));
|
||||
msg.Add(RawPayload.LinkTerminator);
|
||||
}
|
||||
if (color != 0)
|
||||
msg.AddUiForeground((ushort)color);
|
||||
msg.AddText($"[SS{shellNumber}]<");
|
||||
// TODO: Don't link to the local player because it lets you do invalid things
|
||||
msg.Add(new PlayerPayload(chatMsg.SenderName, (uint)chatMsg.SenderHomeWorldId));
|
||||
msg.Add(new PlayerPayload(chatMsg.SenderName, chatMsg.SenderHomeWorldId));
|
||||
msg.AddText("> ");
|
||||
msg.Append(SeString.Parse(message.ChatMsg.PayloadContent));
|
||||
msg.AddUiForegroundOff();
|
||||
if (color != 0)
|
||||
msg.AddUiForegroundOff();
|
||||
|
||||
_chatGui.Print(new XivChatEntry{
|
||||
Message = msg.Build(),
|
||||
Name = chatMsg.SenderName,
|
||||
Type = XivChatType.StandardEmote
|
||||
Type = logKind
|
||||
});
|
||||
}
|
||||
|
||||
// Print an example message to the configured global chat channel
|
||||
public void PrintChannelExample(string message, string gid = "")
|
||||
{
|
||||
int chatType = _mareConfig.Current.ChatLogKind;
|
||||
|
||||
foreach (var group in _pairManager.Groups)
|
||||
{
|
||||
if (group.Key.GID == gid)
|
||||
{
|
||||
int shellChatType = _serverConfigurationManager.GetShellConfigForGid(gid).LogKind;
|
||||
if (shellChatType != 0)
|
||||
chatType = shellChatType;
|
||||
}
|
||||
}
|
||||
|
||||
_chatGui.Print(new XivChatEntry{
|
||||
Message = message,
|
||||
Name = "",
|
||||
Type = (XivChatType)chatType
|
||||
});
|
||||
}
|
||||
|
||||
@@ -94,7 +153,8 @@ public class ChatService : DisposableMediatorSubscriberBase
|
||||
|
||||
foreach (var group in _pairManager.Groups)
|
||||
{
|
||||
if (_serverConfigurationManager.GetShellNumberForGid(group.Key.GID) == shellNumber)
|
||||
var shellConfig = _serverConfigurationManager.GetShellConfigForGid(group.Key.GID);
|
||||
if (shellConfig.Enabled && shellConfig.ShellNumber == shellNumber)
|
||||
{
|
||||
if (_gameChatHooks.IsValueCreated && _gameChatHooks.Value.ChatChannelOverride != null)
|
||||
{
|
||||
@@ -113,7 +173,8 @@ public class ChatService : DisposableMediatorSubscriberBase
|
||||
|
||||
foreach (var group in _pairManager.Groups)
|
||||
{
|
||||
if (_serverConfigurationManager.GetShellNumberForGid(group.Key.GID) == shellNumber)
|
||||
var shellConfig = _serverConfigurationManager.GetShellConfigForGid(group.Key.GID);
|
||||
if (shellConfig.Enabled && shellConfig.ShellNumber == shellNumber)
|
||||
{
|
||||
var name = _serverConfigurationManager.GetNoteForGid(group.Key.GID) ?? group.Key.AliasOrGID;
|
||||
// BUG: This doesn't always update the chat window e.g. when renaming a group
|
||||
@@ -136,7 +197,8 @@ public class ChatService : DisposableMediatorSubscriberBase
|
||||
|
||||
foreach (var group in _pairManager.Groups)
|
||||
{
|
||||
if (_serverConfigurationManager.GetShellNumberForGid(group.Key.GID) == shellNumber)
|
||||
var shellConfig = _serverConfigurationManager.GetShellConfigForGid(group.Key.GID);
|
||||
if (shellConfig.Enabled && shellConfig.ShellNumber == shellNumber)
|
||||
{
|
||||
Task.Run(async () => {
|
||||
// TODO: Cache the name and home world instead of fetching it every time
|
||||
|
||||
@@ -19,7 +19,6 @@ public sealed class CommandManagerService : IDisposable
|
||||
private const string _commandName2 = "/loporrit";
|
||||
|
||||
private const string _ssCommandPrefix = "/ss";
|
||||
private const int _ssCommandMaxNumber = 50;
|
||||
|
||||
private readonly ApiController _apiController;
|
||||
private readonly ICommandManager _commandManager;
|
||||
@@ -52,7 +51,7 @@ public sealed class CommandManagerService : IDisposable
|
||||
});
|
||||
|
||||
// Lazy registration of all possible /ss# commands which tbf is what the game does for linkshells anyway
|
||||
for (int i = 1; i <= _ssCommandMaxNumber; ++i)
|
||||
for (int i = 1; i <= ChatService.CommandMaxNumber; ++i)
|
||||
{
|
||||
_commandManager.AddHandler($"{_ssCommandPrefix}{i}", new CommandInfo(OnChatCommand)
|
||||
{
|
||||
@@ -66,7 +65,7 @@ public sealed class CommandManagerService : IDisposable
|
||||
_commandManager.RemoveHandler(_commandName);
|
||||
_commandManager.RemoveHandler(_commandName2);
|
||||
|
||||
for (int i = 1; i <= _ssCommandMaxNumber; ++i)
|
||||
for (int i = 1; i <= ChatService.CommandMaxNumber; ++i)
|
||||
_commandManager.RemoveHandler($"{_ssCommandPrefix}{i}");
|
||||
}
|
||||
|
||||
|
||||
@@ -70,6 +70,12 @@ public class DalamudUtilService : IHostedService, IMediatorSubscriber
|
||||
.Where(w => w.Name.ByteLength > 0 && w.DataCenter.RowId != 0 && (w.IsPublic || char.IsUpper((char)w.Name.Data.Span[0])))
|
||||
.ToDictionary(w => (ushort)w.RowId, w => w.Name.ToString());
|
||||
});
|
||||
UiColors = new(() =>
|
||||
{
|
||||
return gameData.GetExcelSheet<Lumina.Excel.Sheets.UIColor>(Dalamud.Game.ClientLanguage.English)!
|
||||
.Where(x => x.RowId != 0 && !(x.RowId >= 500 && (x.UIForeground & 0xFFFFFF00) == 0))
|
||||
.ToDictionary(x => (int)x.RowId);
|
||||
});
|
||||
mediator.Subscribe<TargetPairMessage>(this, async (msg) =>
|
||||
{
|
||||
if (clientState.IsPvP) return;
|
||||
@@ -99,6 +105,7 @@ public class DalamudUtilService : IHostedService, IMediatorSubscriber
|
||||
public bool IsInCombat { get; private set; } = false;
|
||||
|
||||
public Lazy<Dictionary<ushort, string>> WorldData { get; private set; }
|
||||
public Lazy<Dictionary<int, Lumina.Excel.Sheets.UIColor>> UiColors { get; private set; }
|
||||
|
||||
public MareMediator Mediator { get; }
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using MareSynchronos.MareConfiguration;
|
||||
using MareSynchronos.MareConfiguration.Models;
|
||||
using MareSynchronos.Services.Mediator;
|
||||
using MareSynchronos.Utils;
|
||||
using MareSynchronos.WebAPI;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System.Diagnostics;
|
||||
@@ -246,16 +247,27 @@ public class ServerConfigurationManager
|
||||
return CurrentServerTagStorage().ServerAvailablePairTags;
|
||||
}
|
||||
|
||||
internal int GetShellNumberForGid(string gid)
|
||||
internal ShellConfig GetShellConfigForGid(string gid)
|
||||
{
|
||||
if (CurrentSyncshellStorage().GidShellConfig.TryGetValue(gid, out var config))
|
||||
{
|
||||
return config.ShellNumber;
|
||||
}
|
||||
return config;
|
||||
|
||||
int newNumber = CurrentSyncshellStorage().GidShellConfig.Count > 0 ? CurrentSyncshellStorage().GidShellConfig.Select(x => x.Value.ShellNumber).Max() + 1 : 1;
|
||||
SetShellNumberForGid(gid, newNumber, false);
|
||||
return newNumber;
|
||||
// Pick the next higher syncshell number that is available
|
||||
int newShellNumber = CurrentSyncshellStorage().GidShellConfig.Count > 0 ? CurrentSyncshellStorage().GidShellConfig.Select(x => x.Value.ShellNumber).Max() + 1 : 1;
|
||||
|
||||
var shellConfig = new ShellConfig{
|
||||
ShellNumber = newShellNumber
|
||||
};
|
||||
|
||||
// Save config to avoid auto-generated numbers shuffling around
|
||||
SaveShellConfigForGid(gid, shellConfig);
|
||||
|
||||
return CurrentSyncshellStorage().GidShellConfig[gid];
|
||||
}
|
||||
|
||||
internal int GetShellNumberForGid(string gid)
|
||||
{
|
||||
return GetShellConfigForGid(gid).ShellNumber;
|
||||
}
|
||||
|
||||
internal Dictionary<string, List<string>> GetUidServerPairedUserTags()
|
||||
@@ -359,23 +371,14 @@ public class ServerConfigurationManager
|
||||
_notesConfig.Save();
|
||||
}
|
||||
|
||||
internal void SetShellNumberForGid(string gid, int number, bool save = true)
|
||||
internal void SaveShellConfigForGid(string gid, ShellConfig config)
|
||||
{
|
||||
if (string.IsNullOrEmpty(gid)) return;
|
||||
|
||||
if (CurrentSyncshellStorage().GidShellConfig.TryGetValue(gid, out var config))
|
||||
{
|
||||
config.ShellNumber = number;
|
||||
}
|
||||
else
|
||||
{
|
||||
CurrentSyncshellStorage().GidShellConfig.Add(gid, new(){
|
||||
ShellNumber = number
|
||||
});
|
||||
}
|
||||
// This is somewhat pointless because ShellConfig is a ref type we returned to the caller anyway...
|
||||
CurrentSyncshellStorage().GidShellConfig[gid] = config;
|
||||
|
||||
if (save)
|
||||
_syncshellConfig.Save();
|
||||
_syncshellConfig.Save();
|
||||
}
|
||||
|
||||
private ServerNotesStorage CurrentNotesStorage()
|
||||
|
||||
@@ -239,7 +239,8 @@ internal sealed class GroupPanel
|
||||
|
||||
if (!string.Equals(_editGroupEntry, groupDto.GID, StringComparison.Ordinal))
|
||||
{
|
||||
if (!_mareConfig.Current.DisableSyncshellChat)
|
||||
var shellConfig = _serverConfigurationManager.GetShellConfigForGid(groupDto.GID);
|
||||
if (!_mareConfig.Current.DisableSyncshellChat && shellConfig.Enabled)
|
||||
{
|
||||
ImGui.TextUnformatted($"[{shellNumber}]");
|
||||
UiSharedService.AttachToolTip("Chat command prefix: /ss" + shellNumber);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Dalamud.Interface;
|
||||
using Dalamud.Game.Text;
|
||||
using Dalamud.Interface;
|
||||
using Dalamud.Interface.Colors;
|
||||
using Dalamud.Interface.Utility;
|
||||
using Dalamud.Interface.Utility.Raii;
|
||||
@@ -38,11 +39,13 @@ public class SettingsUi : WindowMediatorSubscriberBase
|
||||
private readonly MareConfigService _configService;
|
||||
private readonly ConcurrentDictionary<GameObjectHandler, Dictionary<string, FileDownloadStatus>> _currentDownloads = new();
|
||||
private readonly FileCompactor _fileCompactor;
|
||||
private readonly DalamudUtilService _dalamudUtilService;
|
||||
private readonly FileUploadManager _fileTransferManager;
|
||||
private readonly FileTransferOrchestrator _fileTransferOrchestrator;
|
||||
private readonly FileCacheManager _fileCacheManager;
|
||||
private readonly MareCharaFileManager _mareCharaFileManager;
|
||||
private readonly PairManager _pairManager;
|
||||
private readonly ChatService _chatService;
|
||||
private readonly PerformanceCollectorService _performanceCollector;
|
||||
private readonly ServerConfigurationManager _serverConfigurationManager;
|
||||
private readonly UiSharedService _uiShared;
|
||||
@@ -67,9 +70,10 @@ public class SettingsUi : WindowMediatorSubscriberBase
|
||||
|
||||
public SettingsUi(ILogger<SettingsUi> logger,
|
||||
UiSharedService uiShared, MareConfigService configService,
|
||||
MareCharaFileManager mareCharaFileManager, PairManager pairManager,
|
||||
MareCharaFileManager mareCharaFileManager, PairManager pairManager, ChatService chatService,
|
||||
ServerConfigurationManager serverConfigurationManager,
|
||||
MareMediator mediator, PerformanceCollectorService performanceCollector,
|
||||
DalamudUtilService dalamudUtilService,
|
||||
FileUploadManager fileTransferManager,
|
||||
FileTransferOrchestrator fileTransferOrchestrator,
|
||||
FileCacheManager fileCacheManager,
|
||||
@@ -78,8 +82,10 @@ public class SettingsUi : WindowMediatorSubscriberBase
|
||||
_configService = configService;
|
||||
_mareCharaFileManager = mareCharaFileManager;
|
||||
_pairManager = pairManager;
|
||||
_chatService = chatService;
|
||||
_serverConfigurationManager = serverConfigurationManager;
|
||||
_performanceCollector = performanceCollector;
|
||||
_dalamudUtilService = dalamudUtilService;
|
||||
_fileTransferManager = fileTransferManager;
|
||||
_fileTransferOrchestrator = fileTransferOrchestrator;
|
||||
_fileCacheManager = fileCacheManager;
|
||||
@@ -369,6 +375,233 @@ public class SettingsUi : WindowMediatorSubscriberBase
|
||||
}
|
||||
}
|
||||
|
||||
private static readonly List<(XivChatType, string)> _syncshellChatTypes = [
|
||||
(0, "(use global setting)"),
|
||||
(XivChatType.Debug, "Debug"),
|
||||
(XivChatType.Echo, "Echo"),
|
||||
(XivChatType.StandardEmote, "Standard Emote"),
|
||||
(XivChatType.CustomEmote, "Custom Emote"),
|
||||
(XivChatType.SystemMessage, "System Message"),
|
||||
(XivChatType.SystemError, "System Error"),
|
||||
(XivChatType.GatheringSystemMessage, "Gathering Message"),
|
||||
(XivChatType.ErrorMessage, "Error message"),
|
||||
];
|
||||
|
||||
private void DrawChatConfig()
|
||||
{
|
||||
_lastTab = "Chat";
|
||||
|
||||
UiSharedService.FontText("Chat Settings", _uiShared.UidFont);
|
||||
|
||||
var disableSyncshellChat = _configService.Current.DisableSyncshellChat;
|
||||
|
||||
if (ImGui.Checkbox("Disable chat globally", ref disableSyncshellChat))
|
||||
{
|
||||
_configService.Current.DisableSyncshellChat = disableSyncshellChat;
|
||||
_configService.Save();
|
||||
}
|
||||
UiSharedService.DrawHelpText("Global setting to disable chat for all syncshells.");
|
||||
|
||||
using var pushDisableGlobal = ImRaii.Disabled(disableSyncshellChat);
|
||||
|
||||
var uiColors = _dalamudUtilService.UiColors.Value;
|
||||
int globalChatColor = _configService.Current.ChatColor;
|
||||
|
||||
if (globalChatColor != 0 && !uiColors.ContainsKey(globalChatColor))
|
||||
{
|
||||
globalChatColor = 0;
|
||||
_configService.Current.ChatColor = 0;
|
||||
_configService.Save();
|
||||
}
|
||||
|
||||
ImGui.SetNextItemWidth(200 * ImGuiHelpers.GlobalScale);
|
||||
_uiShared.DrawColorCombo("Chat text color", Enumerable.Concat([0], uiColors.Keys),
|
||||
i => i switch
|
||||
{
|
||||
0 => (uiColors[ChatService.DefaultColor].UIForeground, "Plugin Default"),
|
||||
_ => (uiColors[i].UIForeground, $"[{i}] Sample Text")
|
||||
},
|
||||
i => {
|
||||
_configService.Current.ChatColor = i;
|
||||
_configService.Save();
|
||||
}, globalChatColor);
|
||||
|
||||
int globalChatType = _configService.Current.ChatLogKind;
|
||||
int globalChatTypeIdx = _syncshellChatTypes.FindIndex(x => globalChatType == (int)x.Item1);
|
||||
|
||||
if (globalChatTypeIdx == -1)
|
||||
globalChatTypeIdx = 0;
|
||||
|
||||
ImGui.SetNextItemWidth(200 * ImGuiHelpers.GlobalScale);
|
||||
_uiShared.DrawCombo("Chat channel", Enumerable.Range(1, _syncshellChatTypes.Count - 1), i => $"{_syncshellChatTypes[i].Item2}",
|
||||
i => {
|
||||
if (_configService.Current.ChatLogKind == (int)_syncshellChatTypes[i].Item1)
|
||||
return;
|
||||
_configService.Current.ChatLogKind = (int)_syncshellChatTypes[i].Item1;
|
||||
_chatService.PrintChannelExample($"Selected channel: {_syncshellChatTypes[i].Item2}");
|
||||
_configService.Save();
|
||||
}, globalChatTypeIdx);
|
||||
UiSharedService.DrawHelpText("FFXIV chat channel to output chat messages on.");
|
||||
|
||||
ImGui.SetWindowFontScale(0.6f);
|
||||
UiSharedService.FontText("\"Chat 2\" Plugin Integration", _uiShared.UidFont);
|
||||
ImGui.SetWindowFontScale(1.0f);
|
||||
|
||||
// TODO: ExtraChat API impersonation
|
||||
/*
|
||||
var extraChatAPI = _configService.Current.ExtraChatAPI;
|
||||
if (ImGui.Checkbox("ExtraChat replacement mode", ref extraChatAPI))
|
||||
{
|
||||
_configService.Current.ExtraChatAPI = extraChatAPI;
|
||||
if (extraChatAPI)
|
||||
_configService.Current.ExtraChatTags = true;
|
||||
_configService.Save();
|
||||
}
|
||||
ImGui.EndDisabled();
|
||||
UiSharedService.DrawHelpText("Enable ExtraChat APIs for full Chat 2 plugin integration.\n\nDo not enable this if ExtraChat is also installed and running.");
|
||||
*/
|
||||
|
||||
var extraChatTags = _configService.Current.ExtraChatTags;
|
||||
if (ImGui.Checkbox("Tag messages as ExtraChat", ref extraChatTags))
|
||||
{
|
||||
_configService.Current.ExtraChatTags = extraChatTags;
|
||||
if (!extraChatTags)
|
||||
_configService.Current.ExtraChatAPI = false;
|
||||
_configService.Save();
|
||||
}
|
||||
UiSharedService.DrawHelpText("If enabled, messages will be filtered under the category \"ExtraChat channels: All\".\n\nThis works even if ExtraChat is also installed and enabled.");
|
||||
|
||||
ImGui.Separator();
|
||||
|
||||
UiSharedService.FontText("Syncshell Settings", _uiShared.UidFont);
|
||||
|
||||
if (!ApiController.ServerAlive)
|
||||
{
|
||||
ImGui.Text("Connect to the server to configure individual syncshell settings.");
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var group in _pairManager.Groups.OrderBy(k => k.Key.GID))
|
||||
{
|
||||
var gid = group.Key.GID;
|
||||
using var pushId = ImRaii.PushId(gid);
|
||||
|
||||
var shellConfig = _serverConfigurationManager.GetShellConfigForGid(gid);
|
||||
var shellNumber = shellConfig.ShellNumber;
|
||||
var shellEnabled = shellConfig.Enabled;
|
||||
var shellName = _serverConfigurationManager.GetNoteForGid(gid) ?? group.Key.AliasOrGID;
|
||||
|
||||
if (shellEnabled)
|
||||
shellName = $"[{shellNumber}] {shellName}";
|
||||
|
||||
ImGui.SetWindowFontScale(0.6f);
|
||||
UiSharedService.FontText(shellName, _uiShared.UidFont);
|
||||
ImGui.SetWindowFontScale(1.0f);
|
||||
|
||||
using var pushIndent = ImRaii.PushIndent();
|
||||
|
||||
if (ImGui.Checkbox($"Enable chat for this syncshell##{gid}", ref shellEnabled))
|
||||
{
|
||||
// If there is an active group with the same syncshell number, pick a new one
|
||||
int nextNumber = 1;
|
||||
bool conflict = false;
|
||||
foreach (var otherGroup in _pairManager.Groups)
|
||||
{
|
||||
if (gid == otherGroup.Key.GID) continue;
|
||||
var otherShellConfig = _serverConfigurationManager.GetShellConfigForGid(otherGroup.Key.GID);
|
||||
if (otherShellConfig.Enabled && otherShellConfig.ShellNumber == shellNumber)
|
||||
conflict = true;
|
||||
nextNumber = System.Math.Max(nextNumber, otherShellConfig.ShellNumber) + 1;
|
||||
}
|
||||
if (conflict)
|
||||
shellConfig.ShellNumber = nextNumber;
|
||||
shellConfig.Enabled = shellEnabled;
|
||||
_serverConfigurationManager.SaveShellConfigForGid(gid, shellConfig);
|
||||
}
|
||||
|
||||
using var pushDisabled = ImRaii.Disabled(!shellEnabled);
|
||||
|
||||
ImGui.SetNextItemWidth(50 * ImGuiHelpers.GlobalScale);
|
||||
|
||||
var setSyncshellNumberFn = (int i) => {
|
||||
// Find an active group with the same syncshell number as selected, and swap it
|
||||
// This logic can leave duplicate IDs present in the config but its not critical
|
||||
foreach (var otherGroup in _pairManager.Groups)
|
||||
{
|
||||
if (gid == otherGroup.Key.GID) continue;
|
||||
var otherShellConfig = _serverConfigurationManager.GetShellConfigForGid(otherGroup.Key.GID);
|
||||
if (otherShellConfig.Enabled && otherShellConfig.ShellNumber == i)
|
||||
{
|
||||
otherShellConfig.ShellNumber = shellNumber;
|
||||
_serverConfigurationManager.SaveShellConfigForGid(otherGroup.Key.GID, otherShellConfig);
|
||||
break;
|
||||
}
|
||||
}
|
||||
shellConfig.ShellNumber = i;
|
||||
_serverConfigurationManager.SaveShellConfigForGid(gid, shellConfig);
|
||||
};
|
||||
|
||||
// _uiShared.DrawCombo() remembers the selected option -- we don't want that, because the value can change
|
||||
if (ImGui.BeginCombo("Syncshell number##{gid}", $"{shellNumber}"))
|
||||
{
|
||||
// Same hard-coded number in CommandManagerService
|
||||
for (int i = 1; i <= ChatService.CommandMaxNumber; ++i)
|
||||
{
|
||||
if (ImGui.Selectable($"{i}", i == shellNumber))
|
||||
{
|
||||
// Find an active group with the same syncshell number as selected, and swap it
|
||||
// This logic can leave duplicate IDs present in the config but its not critical
|
||||
foreach (var otherGroup in _pairManager.Groups)
|
||||
{
|
||||
if (gid == otherGroup.Key.GID) continue;
|
||||
var otherShellConfig = _serverConfigurationManager.GetShellConfigForGid(otherGroup.Key.GID);
|
||||
if (otherShellConfig.Enabled && otherShellConfig.ShellNumber == i)
|
||||
{
|
||||
otherShellConfig.ShellNumber = shellNumber;
|
||||
_serverConfigurationManager.SaveShellConfigForGid(otherGroup.Key.GID, otherShellConfig);
|
||||
break;
|
||||
}
|
||||
}
|
||||
shellConfig.ShellNumber = i;
|
||||
_serverConfigurationManager.SaveShellConfigForGid(gid, shellConfig);
|
||||
}
|
||||
}
|
||||
ImGui.EndCombo();
|
||||
}
|
||||
|
||||
if (shellConfig.Color != 0 && !uiColors.ContainsKey(shellConfig.Color))
|
||||
{
|
||||
shellConfig.Color = 0;
|
||||
_serverConfigurationManager.SaveShellConfigForGid(gid, shellConfig);
|
||||
}
|
||||
|
||||
ImGui.SetNextItemWidth(200 * ImGuiHelpers.GlobalScale);
|
||||
_uiShared.DrawColorCombo($"Chat text color##{gid}", Enumerable.Concat([0], uiColors.Keys),
|
||||
i => i switch
|
||||
{
|
||||
0 => (uiColors[globalChatColor > 0 ? globalChatColor : ChatService.DefaultColor].UIForeground, "(use global setting)"),
|
||||
_ => (uiColors[i].UIForeground, $"[{i}] Sample Text")
|
||||
},
|
||||
i => {
|
||||
shellConfig.Color = i;
|
||||
_serverConfigurationManager.SaveShellConfigForGid(gid, shellConfig);
|
||||
}, shellConfig.Color);
|
||||
|
||||
int shellChatTypeIdx = _syncshellChatTypes.FindIndex(x => shellConfig.LogKind == (int)x.Item1);
|
||||
|
||||
if (shellChatTypeIdx == -1)
|
||||
shellChatTypeIdx = 0;
|
||||
|
||||
ImGui.SetNextItemWidth(200 * ImGuiHelpers.GlobalScale);
|
||||
_uiShared.DrawCombo($"Chat channel##{gid}", Enumerable.Range(0, _syncshellChatTypes.Count), i => $"{_syncshellChatTypes[i].Item2}",
|
||||
i => {
|
||||
shellConfig.LogKind = (int)_syncshellChatTypes[i].Item1;
|
||||
_serverConfigurationManager.SaveShellConfigForGid(gid, shellConfig);
|
||||
}, shellChatTypeIdx);
|
||||
UiSharedService.DrawHelpText("Override the FFXIV chat channel used for this syncshell.");
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawDebug()
|
||||
{
|
||||
_lastTab = "Debug";
|
||||
@@ -651,18 +884,6 @@ public class SettingsUi : WindowMediatorSubscriberBase
|
||||
}
|
||||
UiSharedService.DrawHelpText("This will open a popup that allows you to set the notes for a user after successfully adding them to your individual pairs.");
|
||||
|
||||
ImGui.Separator();
|
||||
UiSharedService.FontText("Chat", _uiShared.UidFont);
|
||||
|
||||
var disableSyncshellChat = _configService.Current.DisableSyncshellChat;
|
||||
|
||||
if (ImGui.Checkbox("Disable Syncshell Chat", ref disableSyncshellChat))
|
||||
{
|
||||
_configService.Current.DisableSyncshellChat = disableSyncshellChat;
|
||||
_configService.Save();
|
||||
}
|
||||
UiSharedService.DrawHelpText("Disable sending/receiving all syncshell chat messages.");
|
||||
|
||||
ImGui.Separator();
|
||||
UiSharedService.FontText("UI", _uiShared.UidFont);
|
||||
var showCharacterNames = _configService.Current.ShowCharacterNames;
|
||||
@@ -1230,6 +1451,12 @@ public class SettingsUi : WindowMediatorSubscriberBase
|
||||
ImGui.EndDisabled(); // _registrationInProgress
|
||||
}
|
||||
|
||||
if (ImGui.BeginTabItem("Chat"))
|
||||
{
|
||||
DrawChatConfig();
|
||||
ImGui.EndTabItem();
|
||||
}
|
||||
|
||||
if (ImGui.BeginTabItem("Debug"))
|
||||
{
|
||||
DrawDebug();
|
||||
|
||||
@@ -688,6 +688,51 @@ public partial class UiSharedService : DisposableMediatorSubscriberBase
|
||||
return (T)_selectedComboItems[comboName];
|
||||
}
|
||||
|
||||
public T? DrawColorCombo<T>(string comboName, IEnumerable<T> comboItems, Func<T, (uint Color, string Name)> toEntry,
|
||||
Action<T>? onSelected = null, T? initialSelectedItem = default)
|
||||
{
|
||||
if (!comboItems.Any()) return default;
|
||||
|
||||
if (!_selectedComboItems.TryGetValue(comboName, out var selectedItem) && selectedItem == null)
|
||||
{
|
||||
if (!EqualityComparer<T>.Default.Equals(initialSelectedItem, default))
|
||||
{
|
||||
selectedItem = initialSelectedItem;
|
||||
_selectedComboItems[comboName] = selectedItem!;
|
||||
if (!EqualityComparer<T>.Default.Equals(initialSelectedItem, default))
|
||||
onSelected?.Invoke(initialSelectedItem);
|
||||
}
|
||||
else
|
||||
{
|
||||
selectedItem = comboItems.First();
|
||||
_selectedComboItems[comboName] = selectedItem!;
|
||||
}
|
||||
}
|
||||
|
||||
var entry = toEntry((T)selectedItem!);
|
||||
ImGui.PushStyleColor(ImGuiCol.Text, ColorHelpers.RgbaUintToVector4(ColorHelpers.SwapEndianness(entry.Color)));
|
||||
if (ImGui.BeginCombo(comboName, entry.Name))
|
||||
{
|
||||
foreach (var item in comboItems)
|
||||
{
|
||||
entry = toEntry(item);
|
||||
ImGui.PushStyleColor(ImGuiCol.Text, ColorHelpers.RgbaUintToVector4(ColorHelpers.SwapEndianness(entry.Color)));
|
||||
bool isSelected = EqualityComparer<T>.Default.Equals(item, (T)selectedItem);
|
||||
if (ImGui.Selectable(entry.Name, isSelected))
|
||||
{
|
||||
_selectedComboItems[comboName] = item!;
|
||||
onSelected?.Invoke(item!);
|
||||
}
|
||||
ImGui.PopStyleColor();
|
||||
}
|
||||
|
||||
ImGui.EndCombo();
|
||||
}
|
||||
ImGui.PopStyleColor();
|
||||
|
||||
return (T)_selectedComboItems[comboName];
|
||||
}
|
||||
|
||||
public void DrawFileScanState()
|
||||
{
|
||||
ImGui.TextUnformatted("File Scanner Status");
|
||||
|
||||
34
MareSynchronos/Utils/ChatUtils.cs
Normal file
34
MareSynchronos/Utils/ChatUtils.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
using Dalamud.Game.Text.SeStringHandling.Payloads;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
|
||||
namespace MareSynchronos.Utils;
|
||||
|
||||
public static class ChatUtils
|
||||
{
|
||||
// Based on https://git.anna.lgbt/anna/ExtraChat/src/branch/main/client/ExtraChat/Util/PayloadUtil.cs
|
||||
// This must store a Guid (16 bytes), as Chat 2 converts the data back to one
|
||||
|
||||
public static RawPayload CreateExtraChatTagPayload(Guid guid)
|
||||
{
|
||||
var header = (byte[])[
|
||||
0x02, // Payload.START_BYTE
|
||||
0x27, // SeStringChunkType.Interactable
|
||||
2 + 16, // remaining length: ExtraChat sends 19 here but I think its an error
|
||||
0x20 // Custom ExtraChat InfoType
|
||||
];
|
||||
|
||||
var footer = (byte)0x03; // Payload.END_BYTE
|
||||
|
||||
return new RawPayload([..header, ..guid.ToByteArray(), footer]);
|
||||
}
|
||||
|
||||
// We have a unique identifier in the form of a GID, which can be consistently mapped to the same GUID
|
||||
public static RawPayload CreateExtraChatTagPayload(string gid)
|
||||
{
|
||||
var gidBytes = UTF8Encoding.UTF8.GetBytes(gid);
|
||||
var hashedBytes = MD5.HashData(gidBytes);
|
||||
var guid = new Guid(hashedBytes);
|
||||
return CreateExtraChatTagPayload(guid);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user