Basic syncshell chat impl with game hooks
This commit is contained in:
23
MareSynchronos/Services/ChatIntegrationService.cs
Normal file
23
MareSynchronos/Services/ChatIntegrationService.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
using MareSynchronos.Services.Mediator;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace MareSynchronos.Services;
|
||||
|
||||
public sealed class ChatIntegrationService : MediatorSubscriberBase, IDisposable
|
||||
{
|
||||
public bool Activated { get; private set; } = false;
|
||||
|
||||
public ChatIntegrationService(ILogger<ChatIntegrationService> logger, MareMediator mediator) : base(logger, mediator)
|
||||
{
|
||||
}
|
||||
|
||||
public void Activate()
|
||||
{
|
||||
if (Activated)
|
||||
return;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
}
|
||||
157
MareSynchronos/Services/ChatService.cs
Normal file
157
MareSynchronos/Services/ChatService.cs
Normal file
@@ -0,0 +1,157 @@
|
||||
using Dalamud.Game.Text;
|
||||
using Dalamud.Game.Text.SeStringHandling;
|
||||
using Dalamud.Game.Text.SeStringHandling.Payloads;
|
||||
using Dalamud.Plugin.Services;
|
||||
using MareSynchronos.API.Data;
|
||||
using MareSynchronos.Interop;
|
||||
using MareSynchronos.MareConfiguration;
|
||||
using MareSynchronos.PlayerData.Pairs;
|
||||
using MareSynchronos.Services.Mediator;
|
||||
using MareSynchronos.Services.ServerConfiguration;
|
||||
using MareSynchronos.WebAPI;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace MareSynchronos.Services;
|
||||
|
||||
public class ChatService : DisposableMediatorSubscriberBase
|
||||
{
|
||||
private readonly ILogger<ChatService> _logger;
|
||||
private readonly IChatGui _chatGui;
|
||||
private readonly DalamudUtilService _dalamudUtil;
|
||||
private readonly MareConfigService _mareConfig;
|
||||
private readonly ApiController _apiController;
|
||||
private readonly PairManager _pairManager;
|
||||
private readonly ServerConfigurationManager _serverConfigurationManager;
|
||||
|
||||
private readonly Lazy<GameChatHooks> _gameChatHooks;
|
||||
|
||||
public ChatService(ILogger<ChatService> logger, DalamudUtilService dalamudUtil, MareMediator mediator, ApiController apiController,
|
||||
PairManager pairManager, ILogger<GameChatHooks> logger2, IGameInteropProvider gameInteropProvider, IChatGui chatGui,
|
||||
MareConfigService mareConfig, ServerConfigurationManager serverConfigurationManager) : base(logger, mediator)
|
||||
{
|
||||
_logger = logger;
|
||||
_dalamudUtil = dalamudUtil;
|
||||
_chatGui = chatGui;
|
||||
_mareConfig = mareConfig;
|
||||
_apiController = apiController;
|
||||
_pairManager = pairManager;
|
||||
_serverConfigurationManager = serverConfigurationManager;
|
||||
|
||||
Mediator.Subscribe<UserChatMsgMessage>(this, HandleUserChat);
|
||||
Mediator.Subscribe<GroupChatMsgMessage>(this, HandleGroupChat);
|
||||
|
||||
_gameChatHooks = new(() => new GameChatHooks(logger2, gameInteropProvider));
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (_gameChatHooks.IsValueCreated)
|
||||
_gameChatHooks.Value!.Dispose();
|
||||
}
|
||||
|
||||
private void HandleUserChat(UserChatMsgMessage message)
|
||||
{
|
||||
var chatMsg = message.ChatMsg;
|
||||
var prefix = new SeStringBuilder();
|
||||
prefix.AddText("[BnnuyChat] ");
|
||||
_chatGui.Print(new XivChatEntry{
|
||||
MessageBytes = [..prefix.Build().Encode(), ..message.ChatMsg.PayloadContent],
|
||||
Name = chatMsg.SenderName,
|
||||
Type = XivChatType.TellIncoming
|
||||
});
|
||||
}
|
||||
|
||||
private void HandleGroupChat(GroupChatMsgMessage message)
|
||||
{
|
||||
if (_mareConfig.Current.DisableSyncshellChat)
|
||||
return;
|
||||
|
||||
var chatMsg = message.ChatMsg;
|
||||
var shellNumber = _serverConfigurationManager.GetShellNumberForGid(message.GroupInfo.GID);
|
||||
var prefix = new SeStringBuilder();
|
||||
|
||||
// TODO: Configure colors and appearance
|
||||
prefix.AddUiForeground(710);
|
||||
prefix.AddText($"[SS{shellNumber}]<");
|
||||
// TODO: Don't link to the local player because it lets you do invalid things
|
||||
prefix.Add(new PlayerPayload(chatMsg.SenderName, (uint)chatMsg.SenderHomeWorldId));
|
||||
prefix.AddText("> ");
|
||||
|
||||
_chatGui.Print(new XivChatEntry{
|
||||
MessageBytes = [..prefix.Build().Encode(), ..message.ChatMsg.PayloadContent],
|
||||
Name = chatMsg.SenderName,
|
||||
Type = XivChatType.Debug
|
||||
});
|
||||
}
|
||||
|
||||
// Called to update the active chat shell name if its renamed
|
||||
public void MaybeUpdateShellName(int shellNumber)
|
||||
{
|
||||
if (_mareConfig.Current.DisableSyncshellChat)
|
||||
return;
|
||||
|
||||
foreach (var group in _pairManager.Groups)
|
||||
{
|
||||
if (_serverConfigurationManager.GetShellNumberForGid(group.Key.GID) == shellNumber)
|
||||
{
|
||||
if (_gameChatHooks.IsValueCreated && _gameChatHooks.Value.ChatChannelOverride != null)
|
||||
{
|
||||
// Very dumb and won't handle re-numbering -- need to identify the active chat channel more reliably later
|
||||
if (_gameChatHooks.Value.ChatChannelOverride.ChannelName.StartsWith($"SS [{shellNumber}]"))
|
||||
SwitchChatShell(shellNumber);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void SwitchChatShell(int shellNumber)
|
||||
{
|
||||
if (_mareConfig.Current.DisableSyncshellChat)
|
||||
return;
|
||||
|
||||
foreach (var group in _pairManager.Groups)
|
||||
{
|
||||
if (_serverConfigurationManager.GetShellNumberForGid(group.Key.GID) == 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
|
||||
_gameChatHooks.Value.ChatChannelOverride = new()
|
||||
{
|
||||
ChannelName = $"SS [{shellNumber}]: {name}",
|
||||
ChatMessageHandler = chatBytes => SendChatShell(shellNumber, chatBytes)
|
||||
};
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
_chatGui.PrintError($"[LoporritSync] Syncshell number #{shellNumber} not found");
|
||||
}
|
||||
|
||||
public void SendChatShell(int shellNumber, byte[] chatBytes)
|
||||
{
|
||||
if (_mareConfig.Current.DisableSyncshellChat)
|
||||
return;
|
||||
|
||||
foreach (var group in _pairManager.Groups)
|
||||
{
|
||||
if (_serverConfigurationManager.GetShellNumberForGid(group.Key.GID) == shellNumber)
|
||||
{
|
||||
Task.Run(async () => {
|
||||
// TODO: Cache the name and home world instead of fetching it every time
|
||||
var chatMsg = await _dalamudUtil.RunOnFrameworkThread(() => {
|
||||
return new ChatMessage()
|
||||
{
|
||||
SenderName = _dalamudUtil.GetPlayerName(),
|
||||
SenderHomeWorldId = _dalamudUtil.GetHomeWorldId(),
|
||||
PayloadContent = chatBytes
|
||||
};
|
||||
});
|
||||
await _apiController.GroupChatSendMsg(new(group.Key), chatMsg);
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
_chatGui.PrintError($"[LoporritSync] Syncshell number #{shellNumber} not found");
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using Dalamud.Game.Command;
|
||||
using Dalamud.Game.Text.SeStringHandling.Payloads;
|
||||
using Dalamud.Interface.ImGuiNotification;
|
||||
using Dalamud.Plugin.Services;
|
||||
using MareSynchronos.FileCache;
|
||||
@@ -8,6 +9,7 @@ using MareSynchronos.Services.ServerConfiguration;
|
||||
using MareSynchronos.UI;
|
||||
using MareSynchronos.WebAPI;
|
||||
using System.Globalization;
|
||||
using System.Text;
|
||||
|
||||
namespace MareSynchronos.Services;
|
||||
|
||||
@@ -16,22 +18,27 @@ public sealed class CommandManagerService : IDisposable
|
||||
private const string _commandName = "/sync";
|
||||
private const string _commandName2 = "/loporrit";
|
||||
|
||||
private const string _ssCommandPrefix = "/ss";
|
||||
private const int _ssCommandMaxNumber = 50;
|
||||
|
||||
private readonly ApiController _apiController;
|
||||
private readonly ICommandManager _commandManager;
|
||||
private readonly MareMediator _mediator;
|
||||
private readonly MareConfigService _mareConfigService;
|
||||
private readonly PerformanceCollectorService _performanceCollectorService;
|
||||
private readonly PeriodicFileScanner _periodicFileScanner;
|
||||
private readonly ChatService _chatService;
|
||||
private readonly ServerConfigurationManager _serverConfigurationManager;
|
||||
|
||||
public CommandManagerService(ICommandManager commandManager, PerformanceCollectorService performanceCollectorService,
|
||||
ServerConfigurationManager serverConfigurationManager, PeriodicFileScanner periodicFileScanner,
|
||||
ServerConfigurationManager serverConfigurationManager, PeriodicFileScanner periodicFileScanner, ChatService chatService,
|
||||
ApiController apiController, MareMediator mediator, MareConfigService mareConfigService)
|
||||
{
|
||||
_commandManager = commandManager;
|
||||
_performanceCollectorService = performanceCollectorService;
|
||||
_serverConfigurationManager = serverConfigurationManager;
|
||||
_periodicFileScanner = periodicFileScanner;
|
||||
_chatService = chatService;
|
||||
_apiController = apiController;
|
||||
_mediator = mediator;
|
||||
_mareConfigService = mareConfigService;
|
||||
@@ -43,12 +50,24 @@ public sealed class CommandManagerService : IDisposable
|
||||
{
|
||||
HelpMessage = "Opens the Loporrit UI"
|
||||
});
|
||||
|
||||
// Lazy registration of all possible /ss# commands which tbf is what the game does for linkshells anyway
|
||||
for (int i = 1; i <= _ssCommandMaxNumber; ++i)
|
||||
{
|
||||
_commandManager.AddHandler($"{_ssCommandPrefix}{i}", new CommandInfo(OnChatCommand)
|
||||
{
|
||||
ShowInHelp = false
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_commandManager.RemoveHandler(_commandName);
|
||||
_commandManager.RemoveHandler(_commandName2);
|
||||
|
||||
for (int i = 1; i <= _ssCommandMaxNumber; ++i)
|
||||
_commandManager.RemoveHandler($"{_ssCommandPrefix}{i}");
|
||||
}
|
||||
|
||||
private void OnCommand(string command, string args)
|
||||
@@ -116,4 +135,23 @@ public sealed class CommandManagerService : IDisposable
|
||||
_mediator.Publish(new UiToggleMessage(typeof(DataAnalysisUi)));
|
||||
}
|
||||
}
|
||||
|
||||
private void OnChatCommand(string command, string args)
|
||||
{
|
||||
if (_mareConfigService.Current.DisableSyncshellChat)
|
||||
return;
|
||||
|
||||
int shellNumber = int.Parse(command[_ssCommandPrefix.Length..]);
|
||||
|
||||
if (args.Length == 0)
|
||||
{
|
||||
_chatService.SwitchChatShell(shellNumber);
|
||||
}
|
||||
else
|
||||
{
|
||||
// FIXME: Chat content seems to already be stripped of any special characters here?
|
||||
byte[] chatBytes = Encoding.UTF8.GetBytes(args);
|
||||
_chatService.SendChatShell(shellNumber, chatBytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -79,5 +79,7 @@ public record TargetPairMessage(Pair Pair) : MessageBase;
|
||||
public record CombatStartMessage : MessageBase;
|
||||
public record CombatEndMessage : MessageBase;
|
||||
|
||||
public record UserChatMsgMessage(SignedChatMessage ChatMsg) : MessageBase;
|
||||
public record GroupChatMsgMessage(GroupDto GroupInfo, SignedChatMessage ChatMsg) : MessageBase;
|
||||
#pragma warning restore S2094
|
||||
#pragma warning restore MA0048 // File name must match type name
|
||||
@@ -15,14 +15,16 @@ public class ServerConfigurationManager
|
||||
private readonly MareMediator _mareMediator;
|
||||
private readonly NotesConfigService _notesConfig;
|
||||
private readonly ServerTagConfigService _serverTagConfig;
|
||||
private readonly SyncshellConfigService _syncshellConfig;
|
||||
|
||||
public ServerConfigurationManager(ILogger<ServerConfigurationManager> logger, ServerConfigService configService,
|
||||
ServerTagConfigService serverTagConfig, NotesConfigService notesConfig, DalamudUtilService dalamudUtil,
|
||||
MareMediator mareMediator)
|
||||
ServerTagConfigService serverTagConfig, SyncshellConfigService syncshellConfig, NotesConfigService notesConfig,
|
||||
DalamudUtilService dalamudUtil, MareMediator mareMediator)
|
||||
{
|
||||
_logger = logger;
|
||||
_configService = configService;
|
||||
_serverTagConfig = serverTagConfig;
|
||||
_syncshellConfig = syncshellConfig;
|
||||
_notesConfig = notesConfig;
|
||||
_dalamudUtil = dalamudUtil;
|
||||
_mareMediator = mareMediator;
|
||||
@@ -244,6 +246,18 @@ public class ServerConfigurationManager
|
||||
return CurrentServerTagStorage().ServerAvailablePairTags;
|
||||
}
|
||||
|
||||
internal int GetShellNumberForGid(string gid)
|
||||
{
|
||||
if (CurrentSyncshellStorage().GidShellConfig.TryGetValue(gid, out var config))
|
||||
{
|
||||
return config.ShellNumber;
|
||||
}
|
||||
|
||||
int newNumber = CurrentSyncshellStorage().GidShellConfig.Count > 0 ? CurrentSyncshellStorage().GidShellConfig.Select(x => x.Value.ShellNumber).Max() + 1 : 1;
|
||||
SetShellNumberForGid(gid, newNumber, false);
|
||||
return newNumber;
|
||||
}
|
||||
|
||||
internal Dictionary<string, List<string>> GetUidServerPairedUserTags()
|
||||
{
|
||||
return CurrentServerTagStorage().UidServerPairedUserTags;
|
||||
@@ -345,6 +359,25 @@ public class ServerConfigurationManager
|
||||
_notesConfig.Save();
|
||||
}
|
||||
|
||||
internal void SetShellNumberForGid(string gid, int number, bool save = true)
|
||||
{
|
||||
if (string.IsNullOrEmpty(gid)) return;
|
||||
|
||||
if (CurrentSyncshellStorage().GidShellConfig.TryGetValue(gid, out var config))
|
||||
{
|
||||
config.ShellNumber = number;
|
||||
}
|
||||
else
|
||||
{
|
||||
CurrentSyncshellStorage().GidShellConfig.Add(gid, new(){
|
||||
ShellNumber = number
|
||||
});
|
||||
}
|
||||
|
||||
if (save)
|
||||
_syncshellConfig.Save();
|
||||
}
|
||||
|
||||
private ServerNotesStorage CurrentNotesStorage()
|
||||
{
|
||||
TryCreateCurrentNotesStorage();
|
||||
@@ -357,6 +390,12 @@ public class ServerConfigurationManager
|
||||
return _serverTagConfig.Current.ServerTagStorage[CurrentApiUrl];
|
||||
}
|
||||
|
||||
private ServerShellStorage CurrentSyncshellStorage()
|
||||
{
|
||||
TryCreateCurrentSyncshellStorage();
|
||||
return _syncshellConfig.Current.ServerShellStorage[CurrentApiUrl];
|
||||
}
|
||||
|
||||
private void EnsureMainExists()
|
||||
{
|
||||
bool lopExists = false;
|
||||
@@ -406,4 +445,12 @@ public class ServerConfigurationManager
|
||||
_serverTagConfig.Current.ServerTagStorage[CurrentApiUrl] = new();
|
||||
}
|
||||
}
|
||||
|
||||
private void TryCreateCurrentSyncshellStorage()
|
||||
{
|
||||
if (!_syncshellConfig.Current.ServerShellStorage.ContainsKey(CurrentApiUrl))
|
||||
{
|
||||
_syncshellConfig.Current.ServerShellStorage[CurrentApiUrl] = new();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user