#region License // Copyright (C) 2020 Reetus // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . #endregion using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using ClassicAssist.Shared; using ClassicAssist.Data; using ClassicAssist.Data.Macros; using ClassicAssist.Misc; using ClassicAssist.Shared.Resources; using ClassicAssist.Shared.UO; using ClassicAssist.UO.Network.Packets; using ClassicAssist.UO.Objects; namespace ClassicAssist.UO.Network { public static class ActionPacketQueue { private const int DRAG_DROP_DISTANCE = 3; private static readonly ThreadPriorityQueue _actionPacketQueue = new ThreadPriorityQueue( ProcessActionPacketQueue ); private static readonly object _actionPacketQueueLock = new object(); private static void ProcessActionPacketQueue( BaseQueueItem queueItem ) { // Lock here so wait don't set the WaitHandle before we've called ToTask(?) lock ( _actionPacketQueueLock ) { if ( queueItem is PacketQueueItem actionQueueItem ) { if ( actionQueueItem.DelaySend ) { while ( Engine.LastActionPacket + TimeSpan.FromMilliseconds( Options.CurrentOptions.ActionDelayMS ) > DateTime.Now ) { Thread.Sleep( 1 ); } } byte[] data = actionQueueItem.Packet; int length = actionQueueItem.Length; Engine.LastActionPacket = DateTime.Now; Engine.SendPacketToServer( data, length ); actionQueueItem.WaitHandle.Set(); } else if ( queueItem is ActionQueueItem actionItem ) { if ( actionItem.DelaySend ) { while ( Engine.LastActionPacket + TimeSpan.FromMilliseconds( Options.CurrentOptions.ActionDelayMS ) > DateTime.Now ) { Thread.Sleep( 1 ); } } bool? result = actionItem.Action?.Invoke( actionItem.CheckRange ); if ( result.HasValue && result.Value ) { Engine.LastActionPacket = DateTime.Now; } actionItem.WaitHandle.Set(); } } } // ReSharper disable once UnusedMember.Global public static Task EnqueueActionPacket( byte[] packet, int length, QueuePriority priority, bool delaySend ) { return EnqueueActionPacket( new PacketQueueItem( packet, length, delaySend ), priority ); } public static Task EnqueueActionPacket( BasePacket packet, QueuePriority priority = QueuePriority.Low, bool delaySend = true ) { byte[] data = packet.ToArray(); return EnqueueActionPacket( new PacketQueueItem( data, data.Length, delaySend ), priority ); } public static Task EnqueueActionPacket( PacketQueueItem packetQueueItem, QueuePriority priority ) { lock ( _actionPacketQueueLock ) { _actionPacketQueue.Enqueue( packetQueueItem, priority ); return packetQueueItem.WaitHandle.ToTask(); } } public static Task EnqueueDragDropGround( int serial, int amount, int x, int y, int z, QueuePriority priority = QueuePriority.Low, bool delaySend = true ) { lock ( _actionPacketQueueLock ) { byte[] dragPacket = new DragItem( serial, amount ).ToArray(); byte[] dropItem = new DropItem( serial, -1, x, y, z ).ToArray(); PacketQueueItem dragPacketQueueItem = new PacketQueueItem( dragPacket, dragPacket.Length, delaySend ); _actionPacketQueue.Enqueue( dragPacketQueueItem, priority ); PacketQueueItem dropPacketQueueItem = new PacketQueueItem( dropItem, dropItem.Length, delaySend ); _actionPacketQueue.Enqueue( dropPacketQueueItem, priority ); return new[] { dragPacketQueueItem.WaitHandle, dropPacketQueueItem.WaitHandle }.ToTask(); } } public static Task EnqueueActionPackets( IEnumerable packets, QueuePriority priority = QueuePriority.Low, bool delaySend = true ) { lock ( _actionPacketQueueLock ) { List handles = new List(); foreach ( BasePacket packet in packets ) { byte[] data = packet.ToArray(); PacketQueueItem packetQueueItem = new PacketQueueItem( data, data.Length, delaySend ); handles.Add( packetQueueItem.WaitHandle ); _actionPacketQueue.Enqueue( packetQueueItem, priority ); } return handles.ToTask(); } } public static Task EnqueueDragDrop( int serial, int amount, int containerSerial, QueuePriority priority = QueuePriority.Low, bool checkRange = false, bool checkExisting = false, bool delaySend = true, int x = -1, int y = -1 ) { lock ( _actionPacketQueueLock ) { if ( checkExisting && _actionPacketQueue.Contains( e => e is ActionQueueItem aqi && aqi.Serial == serial ) ) { return Task.CompletedTask; } ActionQueueItem actionQueueItem = new ActionQueueItem( check => { if ( check ) { Item item = Engine.Items.GetItem( serial ); if ( item == null || item.Distance >= DRAG_DROP_DISTANCE ) { if ( !MacroManager.QuietMode ) { Commands.SystemMessage( Strings.Item_out_of_range___ ); } return false; } } Engine.SendPacketToServer( new DragItem( serial, amount ) ); Thread.Sleep( 50 ); Engine.SendPacketToServer( new DropItem( serial, containerSerial, x, y, 0 ) ); return true; } ) { CheckRange = checkRange, DelaySend = delaySend, Serial = serial }; _actionPacketQueue.Enqueue( actionQueueItem, priority ); return actionQueueItem.WaitHandle.ToTask(); } } public static int Count() { lock ( _actionPacketQueueLock ) { return _actionPacketQueue.Count(); } } public static void Clear() { lock ( _actionPacketQueueLock ) { _actionPacketQueue.Clear(); } } } }