diff --git a/Blish HUD/GameServices/ArcDps/V2/ArcDpsClient.cs b/Blish HUD/GameServices/ArcDps/V2/ArcDpsClient.cs index 782854f8..e61da74b 100644 --- a/Blish HUD/GameServices/ArcDps/V2/ArcDpsClient.cs +++ b/Blish HUD/GameServices/ArcDps/V2/ArcDpsClient.cs @@ -3,6 +3,7 @@ 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; @@ -29,7 +30,7 @@ internal class ArcDpsClient : IArcDpsClient { public event EventHandler Error; - public bool IsConnected => this.isConnected && this.Client.Connected; + public bool IsConnected => isConnected && Client.Connected; public TcpClient Client { get; } @@ -40,7 +41,7 @@ public ArcDpsClient(ArcDpsBridgeVersion arcDpsBridgeVersion) { processors.Add(1, new ImGuiProcessor()); - if (this.arcDpsBridgeVersion == ArcDpsBridgeVersion.V1) { + if (arcDpsBridgeVersion == ArcDpsBridgeVersion.V1) { processors.Add(2, new LegacyCombatProcessor()); processors.Add(3, new LegacyCombatProcessor()); } else { @@ -49,19 +50,19 @@ public ArcDpsClient(ArcDpsBridgeVersion arcDpsBridgeVersion) { } // 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(); + Client = new TcpClient(); } public void RegisterMessageTypeListener(int type, Func listener) where T : struct { - var processor = (MessageProcessor)this.processors[type]; + 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 } @@ -91,17 +92,17 @@ private void ProcessMessage(MessageProcessor processor, BlockingCollectionCancellationToken to cancel the whole client public void Initialize(IPEndPoint endpoint, CancellationToken ct) { this.ct = ct; - this.Client.Connect(endpoint); + 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(ct), ct); } else { - Task.Run(async () => await this.Receive(ct), ct); + Task.Run(async () => await Receive(ct), ct); } } catch (OperationCanceledException) { // NOP @@ -110,39 +111,43 @@ public void Initialize(IPEndPoint endpoint, CancellationToken ct) { public void Disconnect() { if (isConnected) { - if (this.Client.Connected) { - this.Client.Close(); - this.Client.Dispose(); + if (Client.Connected) { + 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) { 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); + ReadFromStream(networkStream, messageBuffer, messageLength); - this.messageQueues[messageType]?.Add(messageBuffer); + if (messageQueues[messageType] != null) { + messageQueues[messageType]?.Add(messageBuffer); + } else { + pool.Return(messageBuffer); + } #if DEBUG Interlocked.Increment(ref Counter); #endif @@ -150,44 +155,49 @@ 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) { 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); + ReadFromStream(networkStream, messageBuffer, messageLength); + + if (messageQueues[messageType] != null) { + messageQueues[messageType]?.Add(messageBuffer); + } else { + pool.Return(messageBuffer); + } #if DEBUG Interlocked.Increment(ref Counter); #endif } } 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)] diff --git a/Blish HUD/GameServices/ArcDps/V2/CommonFields.cs b/Blish HUD/GameServices/ArcDps/V2/CommonFields.cs index d34eae32..623ce0a1 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 1ee91fd4..966ba598 100644 --- a/Blish HUD/GameServices/ArcDps/V2/Extensions/BincodeBinaryReaderExtensions.cs +++ b/Blish HUD/GameServices/ArcDps/V2/Extensions/BincodeBinaryReaderExtensions.cs @@ -6,10 +6,19 @@ 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(); + if (reader.Convert.ParseByte() == 1) { + result.Event = reader.ParseCombatEvent(); + } + if (reader.Convert.ParseByte() == 1) { + result.Source = reader.ParseAgent(); + } + if (reader.Convert.ParseByte() == 1) { + result.Destination = reader.ParseAgent(); + } + if (reader.Convert.ParseByte() == 1) { + result.SkillName = reader.Convert.ParseString(); + } + result.Id = reader.Convert.ParseULong(); result.Revision = reader.Convert.ParseULong(); @@ -50,7 +59,9 @@ public static CombatEvent ParseCombatEvent(this BincodeBinaryReader reader) { public static Agent ParseAgent(this BincodeBinaryReader reader) { var result = default(Agent); - result.Name = reader.Convert.ParseString(); + if (reader.Convert.ParseByte() == 1) { + result.Name = reader.Convert.ParseString(); + } result.Id = reader.Convert.ParseUSize(); result.Profession = reader.Convert.ParseUInt(); result.Elite = reader.Convert.ParseUInt(); diff --git a/Blish HUD/GameServices/ArcDps/V2/MessageType.cs b/Blish HUD/GameServices/ArcDps/V2/MessageType.cs new file mode 100644 index 00000000..5decd01d --- /dev/null +++ b/Blish HUD/GameServices/ArcDps/V2/MessageType.cs @@ -0,0 +1,13 @@ +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 { + ImGui = 1, + CombatEventArea = 2, + CombatEventLocal = 3, + } +} diff --git a/Blish HUD/GameServices/ArcDps/V2/Processors/BincodeSerializer.cs b/Blish HUD/GameServices/ArcDps/V2/Processors/BincodeSerializer.cs index 5e1e9af9..f92d8b9e 100644 --- a/Blish HUD/GameServices/ArcDps/V2/Processors/BincodeSerializer.cs +++ b/Blish HUD/GameServices/ArcDps/V2/Processors/BincodeSerializer.cs @@ -23,7 +23,7 @@ public static double Convert(BinaryReader reader) { } public static class IntConverter { - public static bool UseVarint { get; set; } = true; + public static bool UseVarint { get; set; } = false; public class VarintEncoding { public static readonly VarintEncoding Instance = new VarintEncoding(); diff --git a/Blish HUD/GameServices/ArcDpsService.cs b/Blish HUD/GameServices/ArcDpsService.cs index 64546765..a97b0b3b 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 43d96291..c794aa7d 100644 --- a/Blish HUD/GameServices/ArcDpsServiceV2.cs +++ b/Blish HUD/GameServices/ArcDpsServiceV2.cs @@ -2,6 +2,7 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; +using System.IO; using System.Net; using System.Net.Sockets; using System.Threading; @@ -9,8 +10,11 @@ using Blish_HUD.ArcDps; using Blish_HUD.GameServices.ArcDps; 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 { @@ -69,6 +73,11 @@ private set { } } + 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 +129,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; }); }