diff --git a/Blish HUD/GameServices/ArcDps/V2/ArcDpsClient.cs b/Blish HUD/GameServices/ArcDps/V2/ArcDpsClient.cs index 782854f88..14ff66287 100644 --- a/Blish HUD/GameServices/ArcDps/V2/ArcDpsClient.cs +++ b/Blish HUD/GameServices/ArcDps/V2/ArcDpsClient.cs @@ -3,11 +3,13 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; +using System.Linq; using System.Net; using System.Net.Sockets; using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; +using Blish_HUD.GameServices.ArcDps.Models.UnofficialExtras; using Blish_HUD.GameServices.ArcDps.V2; using Blish_HUD.GameServices.ArcDps.V2.Processors; @@ -19,49 +21,56 @@ internal class ArcDpsClient : IArcDpsClient { #endif private static readonly Logger _logger = Logger.GetLogger(); - private readonly BlockingCollection[] messageQueues; - private readonly Dictionary processors = new Dictionary(); - private readonly ArcDpsBridgeVersion arcDpsBridgeVersion; - private bool isConnected = false; - private NetworkStream networkStream; - private CancellationToken ct; - private bool disposedValue; + private readonly BlockingCollection[] _messageQueues; + private readonly Dictionary _processors = new Dictionary(); + private readonly ArcDpsBridgeVersion _arcDpsBridgeVersion; + private bool _isConnected = false; + private NetworkStream _networkStream; + private CancellationTokenSource _cancellationTokenSource; + private CancellationTokenSource _linkedTokenSource; + private CancellationToken _linkedToken; + private bool _disposedValue; + private CancellationToken _ct; public event EventHandler Error; - public bool IsConnected => this.isConnected && this.Client.Connected; + public bool IsConnected => _isConnected && (Client?.Connected ?? false); - public TcpClient Client { get; } + public TcpClient Client { get; private set; } public event Action Disconnected; public ArcDpsClient(ArcDpsBridgeVersion arcDpsBridgeVersion) { - this.arcDpsBridgeVersion = arcDpsBridgeVersion; + this._arcDpsBridgeVersion = arcDpsBridgeVersion; - processors.Add(1, new ImGuiProcessor()); + _processors.Add(1, new ImGuiProcessor()); - if (this.arcDpsBridgeVersion == ArcDpsBridgeVersion.V1) { - processors.Add(2, new LegacyCombatProcessor()); - processors.Add(3, new LegacyCombatProcessor()); + if (arcDpsBridgeVersion == ArcDpsBridgeVersion.V1) { + _processors.Add((int)MessageType.CombatEventArea, new LegacyCombatProcessor()); + _processors.Add((int)MessageType.CombatEventLocal, new LegacyCombatProcessor()); } else { - processors.Add(2, new CombatEventProcessor()); - processors.Add(3, new CombatEventProcessor()); + _processors.Add((int)MessageType.CombatEventArea, new CombatEventProcessor()); + _processors.Add((int)MessageType.CombatEventLocal, new CombatEventProcessor()); + _processors.Add((int)MessageType.UserInfo, new UnofficialExtrasUserInfoProcessor()); + _processors.Add((int)MessageType.ChatMessage, new UnofficialExtrasMessageInfoProcessor()); } // hardcoded message queue size. One Collection per message type. This is done just for optimizations - this.messageQueues = new BlockingCollection[4]; + _messageQueues = new BlockingCollection[byte.MaxValue]; - this.Client = new TcpClient(); } + public bool IsMessageTypeAvailable(MessageType type) + => this._processors.ContainsKey((int)type); + public void RegisterMessageTypeListener(int type, Func listener) where T : struct { - var processor = (MessageProcessor)this.processors[type]; - if (messageQueues[type] == null) { - messageQueues[type] = new BlockingCollection(); + var processor = (MessageProcessor)_processors[type]; + if (_messageQueues[type] == null) { + _messageQueues[type] = new BlockingCollection(); try { - Task.Run(() => this.ProcessMessage(processor, messageQueues[type])); + Task.Run(() => ProcessMessage(processor, _messageQueues[type])); } catch (OperationCanceledException) { // NOP } @@ -71,17 +80,17 @@ public void RegisterMessageTypeListener(int type, Func messageQueue) { - while (!ct.IsCancellationRequested) { - ct.ThrowIfCancellationRequested(); + while (!_linkedToken.IsCancellationRequested) { + _linkedToken.ThrowIfCancellationRequested(); Task.Delay(1).Wait(); foreach (var item in messageQueue.GetConsumingEnumerable()) { - ct.ThrowIfCancellationRequested(); - processor.Process(item, ct); + _linkedToken.ThrowIfCancellationRequested(); + processor.Process(item, _linkedToken); ArrayPool.Shared.Return(item); } } - ct.ThrowIfCancellationRequested(); + _linkedToken.ThrowIfCancellationRequested(); } /// @@ -90,18 +99,25 @@ private void ProcessMessage(MessageProcessor processor, BlockingCollection /// CancellationToken to cancel the whole client public void Initialize(IPEndPoint endpoint, CancellationToken ct) { - this.ct = ct; - this.Client.Connect(endpoint); + this._ct = ct; + _cancellationTokenSource?.Cancel(); + _cancellationTokenSource = new CancellationTokenSource(); + _linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(ct, this._cancellationTokenSource.Token); + _linkedToken = _linkedTokenSource.Token; + Client?.Dispose(); + Client = new TcpClient(); + Client.ReceiveBufferSize = 4096; + Client.Connect(endpoint); _logger.Info("Connected to arcdps endpoint on: " + endpoint.ToString()); - this.networkStream = this.Client.GetStream(); - this.isConnected = true; + _networkStream = Client.GetStream(); + _isConnected = true; try { - if (this.arcDpsBridgeVersion == ArcDpsBridgeVersion.V1) { - Task.Run(async () => await this.LegacyReceive(ct), ct); + if (_arcDpsBridgeVersion == ArcDpsBridgeVersion.V1) { + Task.Run(async () => await LegacyReceive(_linkedToken), _linkedToken); } else { - Task.Run(async () => await this.Receive(ct), ct); + Task.Run(async () => await Receive(_linkedToken), _linkedToken); } } catch (OperationCanceledException) { // NOP @@ -109,40 +125,36 @@ public void Initialize(IPEndPoint endpoint, CancellationToken ct) { } public void Disconnect() { - if (isConnected) { - if (this.Client.Connected) { - this.Client.Close(); - this.Client.Dispose(); + if (_isConnected) { + if (Client?.Connected ?? false) { + Client.Close(); + Client.Dispose(); _logger.Info("Disconnected from arcdps endpoint"); } - this.isConnected = false; - this.Disconnected?.Invoke(); + _isConnected = false; + Disconnected?.Invoke(); } } private async Task LegacyReceive(CancellationToken ct) { - _logger.Info($"Start Legacy Receive Task for {this.Client.Client.RemoteEndPoint?.ToString()}"); + _logger.Info($"Start Legacy Receive Task for {Client?.Client.RemoteEndPoint?.ToString()}"); try { var messageHeaderBuffer = new byte[9]; ArrayPool pool = ArrayPool.Shared; - while (this.Client.Connected) { + while (Client?.Connected ?? false) { ct.ThrowIfCancellationRequested(); - if (this.Client.Available == 0) { + if (Client.Available == 0) { await Task.Delay(1, ct); } - ReadFromStream(this.networkStream, messageHeaderBuffer, 9); + ReadFromStream(_networkStream, messageHeaderBuffer, 9); - // In V1 the message type is part of the message and therefor included in message length, so we subtract it here var messageLength = Unsafe.ReadUnaligned(ref messageHeaderBuffer[0]) - 1; var messageType = messageHeaderBuffer[8]; - var messageBuffer = pool.Rent(messageLength); - ReadFromStream(this.networkStream, messageBuffer, messageLength); - - this.messageQueues[messageType]?.Add(messageBuffer); + ReadMessage(pool, messageLength, _networkStream, _messageQueues, messageType); #if DEBUG Interlocked.Increment(ref Counter); #endif @@ -150,44 +162,58 @@ private async Task LegacyReceive(CancellationToken ct) { } } catch (Exception ex) { _logger.Error(ex.ToString()); - this.Error?.Invoke(this, SocketError.SocketError); - this.Disconnect(); + Error?.Invoke(this, SocketError.SocketError); + Disconnect(); } - _logger.Info($"Legacy Receive Task for {this.Client.Client?.RemoteEndPoint?.ToString()} stopped"); + _logger.Info($"Legacy Receive Task for {Client?.Client.RemoteEndPoint?.ToString()} stopped"); } private async Task Receive(CancellationToken ct) { - _logger.Info($"Start Receive Task for {this.Client.Client.RemoteEndPoint?.ToString()}"); + _logger.Info($"Start Receive Task for {Client?.Client.RemoteEndPoint?.ToString()}"); try { var messageHeaderBuffer = new byte[5]; ArrayPool pool = ArrayPool.Shared; - while (this.Client.Connected) { + while (Client?.Connected ?? false) { ct.ThrowIfCancellationRequested(); - if (this.Client.Available == 0) { + if (Client.Available == 0) { await Task.Delay(1, ct); } - ReadFromStream(this.networkStream, messageHeaderBuffer, 5); + ReadFromStream(_networkStream, messageHeaderBuffer, 5); - var messageLength = Unsafe.ReadUnaligned(ref messageHeaderBuffer[0]); + var messageLength = Unsafe.ReadUnaligned(ref messageHeaderBuffer[0]) - 1; var messageType = messageHeaderBuffer[4]; - var messageBuffer = pool.Rent(messageLength); - ReadFromStream(this.networkStream, messageBuffer, messageLength); - this.messageQueues[messageType]?.Add(messageBuffer); + ReadMessage(pool, messageLength, _networkStream, _messageQueues, messageType); #if DEBUG Interlocked.Increment(ref Counter); #endif } + + // Reconnect if the bridge closes the connection. + // Pass on the cancellationToken from the creator of this class + this.Initialize((IPEndPoint)this.Client.Client.RemoteEndPoint, this._ct); } catch (Exception ex) { _logger.Error(ex.ToString()); - this.Error?.Invoke(this, SocketError.SocketError); - this.Disconnect(); + Error?.Invoke(this, SocketError.SocketError); + Disconnect(); } - _logger.Info($"Receive Task for {this.Client.Client?.RemoteEndPoint?.ToString()} stopped"); + _logger.Info($"Receive Task for {Client?.Client.RemoteEndPoint?.ToString()} stopped"); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void ReadMessage(ArrayPool pool, int messageLength, Stream networkStream, BlockingCollection[] messageQueues, byte messageType) { + var messageBuffer = pool.Rent(messageLength); + ReadFromStream(networkStream, messageBuffer, messageLength); + + if (messageQueues[messageType] != null) { + messageQueues[messageType]?.Add(messageBuffer); + } else { + pool.Return(messageBuffer); + } } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -199,20 +225,21 @@ private static void ReadFromStream(Stream stream, byte[] buffer, int length) { } protected virtual void Dispose(bool disposing) { - if (!disposedValue) { + if (!_disposedValue) { if (disposing) { - Client.Dispose(); - foreach (var item in messageQueues) { + _cancellationTokenSource.Cancel(); + Client?.Dispose(); + foreach (var item in _messageQueues) { if (item.Count != 0) { foreach (var message in item) { ArrayPool.Shared.Return(message); } } } - networkStream.Dispose(); + _networkStream?.Dispose(); } - disposedValue = true; + _disposedValue = true; } } diff --git a/Blish HUD/GameServices/ArcDps/V2/CommonFields.cs b/Blish HUD/GameServices/ArcDps/V2/CommonFields.cs index d34eae326..623ce0a18 100644 --- a/Blish HUD/GameServices/ArcDps/V2/CommonFields.cs +++ b/Blish HUD/GameServices/ArcDps/V2/CommonFields.cs @@ -41,7 +41,7 @@ public void Activate() { if (_enabled) return; _enabled = true; - GameService.ArcDpsV2.RegisterMessageType(2, CombatHandler); + GameService.ArcDpsV2.RegisterMessageType(MessageType.CombatEventArea, CombatHandler); } private Task CombatHandler(CombatCallback combatEvent, CancellationToken ct) { diff --git a/Blish HUD/GameServices/ArcDps/V2/Extensions/BincodeBinaryReaderExtensions.cs b/Blish HUD/GameServices/ArcDps/V2/Extensions/BincodeBinaryReaderExtensions.cs index 1ee91fd48..cbe3c2dc1 100644 --- a/Blish HUD/GameServices/ArcDps/V2/Extensions/BincodeBinaryReaderExtensions.cs +++ b/Blish HUD/GameServices/ArcDps/V2/Extensions/BincodeBinaryReaderExtensions.cs @@ -1,15 +1,16 @@ using Blish_HUD.GameServices.ArcDps.Models.UnofficialExtras; using Blish_HUD.GameServices.ArcDps.V2.Models; using Blish_HUD.GameServices.ArcDps.V2.Processors; +using System; namespace Blish_HUD.GameServices.ArcDps.V2.Extensions { public static class BincodeBinaryReaderExtensions { public static CombatCallback ParseCombatCallback(this BincodeBinaryReader reader) { var result = default(CombatCallback); - result.Event = reader.ParseCombatEvent(); - result.Source = reader.ParseAgent(); - result.Destination = reader.ParseAgent(); - result.SkillName = reader.Convert.ParseString(); + result.Event = ParseOptional(reader, reader.ParseCombatEvent); + result.Source = ParseOptional(reader, reader.ParseAgent); + result.Destination = ParseOptional(reader, reader.ParseAgent); + result.SkillName = ParseOptional(reader, reader.Convert.ParseString); result.Id = reader.Convert.ParseULong(); result.Revision = reader.Convert.ParseULong(); @@ -50,7 +51,7 @@ public static CombatEvent ParseCombatEvent(this BincodeBinaryReader reader) { public static Agent ParseAgent(this BincodeBinaryReader reader) { var result = default(Agent); - result.Name = reader.Convert.ParseString(); + result.Name = ParseOptional(reader, reader.Convert.ParseString); result.Id = reader.Convert.ParseUSize(); result.Profession = reader.Convert.ParseUInt(); result.Elite = reader.Convert.ParseUInt(); @@ -61,7 +62,7 @@ public static Agent ParseAgent(this BincodeBinaryReader reader) { public static UserInfo ParseUserInfo(this BincodeBinaryReader reader) { var result = default(UserInfo); - result.AccountName = reader.Convert.ParseString(); + result.AccountName = ParseOptional(reader, reader.Convert.ParseString); result.JoinTime = reader.Convert.ParseULong(); result.Role = ParseEnum((byte)reader.Convert.ParseUInt(), (int)UserRole.None, UserRole.None); result.Subgroup = reader.Convert.ParseByte(); @@ -71,14 +72,13 @@ public static UserInfo ParseUserInfo(this BincodeBinaryReader reader) { return result; } - public static ChatMessageInfo ParseChatMessageInfo(BincodeBinaryReader reader) { + public static ChatMessageInfo ParseChatMessageInfo(this BincodeBinaryReader reader) { var result = default(ChatMessageInfo); result.ChannelId = reader.Convert.ParseUInt(); result.ChannelType = ParseEnum((byte)reader.Convert.ParseUInt(), (int)ChannelType.Invalid, ChannelType.Invalid); result.Subgroup = reader.Convert.ParseByte(); result.IsBroadcast = reader.Convert.ParseBool(); - result._unused1 = reader.Convert.ParseByte(); - result.TimeStamp = reader.Convert.ParseString(); + result.TimeStamp = DateTime.Parse(reader.Convert.ParseString()); result.AccountName = reader.Convert.ParseString(); result.CharacterName = reader.Convert.ParseString(); result.Text = reader.Convert.ParseString(); @@ -94,6 +94,13 @@ private static T ParseEnum(byte enumByteValue, int maxValue, T unknown) return (T)(object)enumByteValue; } + + private static T ParseOptional(BincodeBinaryReader reader, Func parse) { + if (reader.ReadByte() == 1) { + return parse(); + } + return default; + } } } diff --git a/Blish HUD/GameServices/ArcDps/V2/IArcDpsClient.cs b/Blish HUD/GameServices/ArcDps/V2/IArcDpsClient.cs index 0198504ce..463922ea0 100644 --- a/Blish HUD/GameServices/ArcDps/V2/IArcDpsClient.cs +++ b/Blish HUD/GameServices/ArcDps/V2/IArcDpsClient.cs @@ -14,9 +14,11 @@ internal interface IArcDpsClient : IDisposable { event EventHandler Error; void Disconnect(); - - void Initialize(IPEndPoint endpoint, CancellationToken ct); + void Initialize(IPEndPoint endpoint, CancellationToken ct); + + bool IsMessageTypeAvailable(MessageType type); + void RegisterMessageTypeListener(int type, Func listener) where T : struct; } } diff --git a/Blish HUD/GameServices/ArcDps/V2/MessageType.cs b/Blish HUD/GameServices/ArcDps/V2/MessageType.cs new file mode 100644 index 000000000..986648fe9 --- /dev/null +++ b/Blish HUD/GameServices/ArcDps/V2/MessageType.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Blish_HUD.GameServices.ArcDps.V2 { + public enum MessageType { + // ArcDPS + ImGui = 1, + CombatEventArea = 2, + CombatEventLocal = 3, + // Unofficial Extras + UserInfo = 4, + ChatMessage = 5, + } +} diff --git a/Blish HUD/GameServices/ArcDps/V2/Models/Unofficial Extras/ChannelType.cs b/Blish HUD/GameServices/ArcDps/V2/Models/Unofficial Extras/ChannelType.cs index 068b84c19..70b232cb7 100644 --- a/Blish HUD/GameServices/ArcDps/V2/Models/Unofficial Extras/ChannelType.cs +++ b/Blish HUD/GameServices/ArcDps/V2/Models/Unofficial Extras/ChannelType.cs @@ -1,5 +1,5 @@ namespace Blish_HUD.GameServices.ArcDps.Models.UnofficialExtras { - public enum ChannelType { + public enum ChannelType : byte { Party = 0, Squad = 1, _Reserved = 2, diff --git a/Blish HUD/GameServices/ArcDps/V2/Models/Unofficial Extras/ChatMessageInfo.cs b/Blish HUD/GameServices/ArcDps/V2/Models/Unofficial Extras/ChatMessageInfo.cs index fb8afc7eb..ed0b45e31 100644 --- a/Blish HUD/GameServices/ArcDps/V2/Models/Unofficial Extras/ChatMessageInfo.cs +++ b/Blish HUD/GameServices/ArcDps/V2/Models/Unofficial Extras/ChatMessageInfo.cs @@ -1,4 +1,6 @@ -namespace Blish_HUD.GameServices.ArcDps.Models.UnofficialExtras { +using System; + +namespace Blish_HUD.GameServices.ArcDps.Models.UnofficialExtras { public struct ChatMessageInfo { public uint ChannelId { get; set; } @@ -8,9 +10,7 @@ public struct ChatMessageInfo { public bool IsBroadcast { get; set; } - public byte _unused1 { get; set; } - - public string TimeStamp { get; set; } + public DateTime TimeStamp { get; set; } public string AccountName { get; set; } diff --git a/Blish HUD/GameServices/ArcDps/V2/Models/Unofficial Extras/UserRole.cs b/Blish HUD/GameServices/ArcDps/V2/Models/Unofficial Extras/UserRole.cs index 5eec2238c..25e4fdfae 100644 --- a/Blish HUD/GameServices/ArcDps/V2/Models/Unofficial Extras/UserRole.cs +++ b/Blish HUD/GameServices/ArcDps/V2/Models/Unofficial Extras/UserRole.cs @@ -1,5 +1,5 @@ namespace Blish_HUD.GameServices.ArcDps.Models.UnofficialExtras { - public enum UserRole { + public enum UserRole : byte { SquadLeader = 0, Lieutenant = 1, Member = 2, diff --git a/Blish HUD/GameServices/ArcDps/V2/Processors/BincodeSerializer.cs b/Blish HUD/GameServices/ArcDps/V2/Processors/BincodeSerializer.cs index 5e1e9af9d..4e28b216b 100644 --- a/Blish HUD/GameServices/ArcDps/V2/Processors/BincodeSerializer.cs +++ b/Blish HUD/GameServices/ArcDps/V2/Processors/BincodeSerializer.cs @@ -69,9 +69,6 @@ public static sbyte Convert(BinaryReader reader) { } public static byte ConvertUnsigned(BinaryReader reader) { - if (UseVarint) { - return (byte)VarintEncoding.Instance.ConvertUnsigned(reader); - } return reader.ReadByte(); } } diff --git a/Blish HUD/GameServices/ArcDps/V2/CombatEventProcessor.cs b/Blish HUD/GameServices/ArcDps/V2/Processors/CombatEventProcessor.cs similarity index 88% rename from Blish HUD/GameServices/ArcDps/V2/CombatEventProcessor.cs rename to Blish HUD/GameServices/ArcDps/V2/Processors/CombatEventProcessor.cs index 47a6e4b0f..cf13cc65f 100644 --- a/Blish HUD/GameServices/ArcDps/V2/CombatEventProcessor.cs +++ b/Blish HUD/GameServices/ArcDps/V2/Processors/CombatEventProcessor.cs @@ -1,11 +1,10 @@ using Blish_HUD.GameServices.ArcDps.V2.Extensions; using Blish_HUD.GameServices.ArcDps.V2.Models; -using Blish_HUD.GameServices.ArcDps.V2.Processors; using SharpDX; using System; using System.IO; -namespace Blish_HUD.GameServices.ArcDps.V2 { +namespace Blish_HUD.GameServices.ArcDps.V2.Processors { internal class CombatEventProcessor : MessageProcessor { internal override bool TryInternalProcess(byte[] message, out CombatCallback result) { try { diff --git a/Blish HUD/GameServices/ArcDps/V2/Processors/UnofficialExtrasMessageInfoProcessor.cs b/Blish HUD/GameServices/ArcDps/V2/Processors/UnofficialExtrasMessageInfoProcessor.cs new file mode 100644 index 000000000..6e3bf2390 --- /dev/null +++ b/Blish HUD/GameServices/ArcDps/V2/Processors/UnofficialExtrasMessageInfoProcessor.cs @@ -0,0 +1,25 @@ +using Blish_HUD.GameServices.ArcDps.Models.UnofficialExtras; +using Blish_HUD.GameServices.ArcDps.V2.Extensions; +using Blish_HUD.GameServices.ArcDps.V2.Models; +using Blish_HUD.GameServices.ArcDps.V2.Processors; +using SharpDX; +using System; +using System.IO; + +namespace Blish_HUD.GameServices.ArcDps.V2 { + internal class UnofficialExtrasMessageInfoProcessor : MessageProcessor { + internal override bool TryInternalProcess(byte[] message, out ChatMessageInfo result) { + try { + using var memoryStream = new MemoryStream(message); + using var binaryReader = new BincodeBinaryReader(memoryStream); + result = binaryReader.ParseChatMessageInfo(); + return true; + + } catch (Exception) { + result = default; + return false; + } + + } + } +} diff --git a/Blish HUD/GameServices/ArcDps/V2/Processors/UnofficialExtrasUserInfoProcessor.cs b/Blish HUD/GameServices/ArcDps/V2/Processors/UnofficialExtrasUserInfoProcessor.cs new file mode 100644 index 000000000..c79f5660b --- /dev/null +++ b/Blish HUD/GameServices/ArcDps/V2/Processors/UnofficialExtrasUserInfoProcessor.cs @@ -0,0 +1,25 @@ +using Blish_HUD.GameServices.ArcDps.Models.UnofficialExtras; +using Blish_HUD.GameServices.ArcDps.V2.Extensions; +using Blish_HUD.GameServices.ArcDps.V2.Models; +using Blish_HUD.GameServices.ArcDps.V2.Processors; +using SharpDX; +using System; +using System.IO; + +namespace Blish_HUD.GameServices.ArcDps.V2 { + internal class UnofficialExtrasUserInfoProcessor : MessageProcessor { + internal override bool TryInternalProcess(byte[] message, out UserInfo result) { + try { + using var memoryStream = new MemoryStream(message); + using var binaryReader = new BincodeBinaryReader(memoryStream); + result = binaryReader.ParseUserInfo(); + return true; + + } catch (Exception) { + result = default; + return false; + } + + } + } +} diff --git a/Blish HUD/GameServices/ArcDpsService.cs b/Blish HUD/GameServices/ArcDpsService.cs index 64546765c..a97b0b3bf 100644 --- a/Blish HUD/GameServices/ArcDpsService.cs +++ b/Blish HUD/GameServices/ArcDpsService.cs @@ -67,11 +67,11 @@ public class ArcDpsService : GameService { public void SubscribeToCombatEventId(Action func, params uint[] skillIds) { if (!_subscribed) { - GameService.ArcDpsV2.RegisterMessageType(2, async (combatEvent, ct) => { + GameService.ArcDpsV2.RegisterMessageType(GameServices.ArcDps.V2.MessageType.CombatEventArea, async (combatEvent, ct) => { DispatchSkillSubscriptions(combatEvent, RawCombatEventArgs.CombatEventType.Area); await System.Threading.Tasks.Task.CompletedTask; }); - GameService.ArcDpsV2.RegisterMessageType(3, async (combatEvent, ct) => { + GameService.ArcDpsV2.RegisterMessageType(GameServices.ArcDps.V2.MessageType.CombatEventLocal, async (combatEvent, ct) => { DispatchSkillSubscriptions(combatEvent, RawCombatEventArgs.CombatEventType.Local); await System.Threading.Tasks.Task.CompletedTask; }); @@ -117,13 +117,13 @@ protected override void Initialize() { this.RawCombatEvent += (a, b) => { Interlocked.Increment(ref Counter); }; #endif - GameService.ArcDpsV2.RegisterMessageType(2, async (combatEvent, ct) => { + GameService.ArcDpsV2.RegisterMessageType(GameServices.ArcDps.V2.MessageType.CombatEventArea, async (combatEvent, ct) => { var rawCombat = ConvertFrom(combatEvent, RawCombatEventArgs.CombatEventType.Area); this.RawCombatEvent?.Invoke(this, rawCombat); await System.Threading.Tasks.Task.CompletedTask; }); - GameService.ArcDpsV2.RegisterMessageType(3, async (combatEvent, ct) => { + GameService.ArcDpsV2.RegisterMessageType(GameServices.ArcDps.V2.MessageType.CombatEventLocal, async (combatEvent, ct) => { var rawCombat = ConvertFrom(combatEvent, RawCombatEventArgs.CombatEventType.Local); this.RawCombatEvent?.Invoke(this, rawCombat); await System.Threading.Tasks.Task.CompletedTask; diff --git a/Blish HUD/GameServices/ArcDpsServiceV2.cs b/Blish HUD/GameServices/ArcDpsServiceV2.cs index 43d96291f..67a5aaacb 100644 --- a/Blish HUD/GameServices/ArcDpsServiceV2.cs +++ b/Blish HUD/GameServices/ArcDpsServiceV2.cs @@ -2,15 +2,20 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; +using System.IO; using System.Net; using System.Net.Sockets; using System.Threading; using System.Threading.Tasks; using Blish_HUD.ArcDps; using Blish_HUD.GameServices.ArcDps; +using Blish_HUD.GameServices.ArcDps.Models.UnofficialExtras; using Blish_HUD.GameServices.ArcDps.V2; +using Blish_HUD.GameServices.ArcDps.V2.Extensions; using Blish_HUD.GameServices.ArcDps.V2.Models; +using Blish_HUD.GameServices.ArcDps.V2.Processors; using Microsoft.Xna.Framework; +using SharpDX; namespace Blish_HUD { @@ -50,7 +55,7 @@ public class ArcDpsServiceV2 : GameService { /// /// Indicates if the socket listener for the arcdps service is running and arcdps sent an update in the last second. /// - public bool Running => (this._arcDpsClient?.Client.Connected ?? false) && this.RenderPresent; + public bool Running => (this._arcDpsClient?.Client?.Connected ?? false) && this.RenderPresent; /// /// Indicates if arcdps currently draws its HUD (not in character select, cut scenes or loading screens) @@ -69,6 +74,14 @@ private set { } } + public bool IsMessageTypeAvailable(MessageType type) + => _arcDpsClient.IsMessageTypeAvailable(type); + + public void RegisterMessageType(MessageType type, Func listener) + where T : struct { + RegisterMessageType((int)type, listener); + } + public void RegisterMessageType(int type, Func listener) where T : struct { Action action = () => _arcDpsClient.RegisterMessageTypeListener(type, listener); @@ -120,7 +133,7 @@ private void Start(uint processId) { _arcDpsClient.Error += SocketErrorHandler; _arcDpsClient.Initialize(new IPEndPoint(IPAddress.Loopback, GetPort(processId, version)), _arcDpsClientCancellationTokenSource.Token); - RegisterMessageType(1, async (imGuiCallback, ct) => { + RegisterMessageType(MessageType.ImGui, async (imGuiCallback, ct) => { this.HudIsActive = imGuiCallback.NotCharacterSelectOrLoading != 0; }); } @@ -148,7 +161,7 @@ protected override void Unload() { _arcDpsClientCancellationTokenSource?.Dispose(); _stopwatch?.Stop(); _arcDpsClient?.Disconnect(); - + if (_arcDpsClient != null) { _arcDpsClient.Error -= SocketErrorHandler; }