#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();
}
}
}
}