using System; using System.ComponentModel; using System.Reflection; using System.Runtime.InteropServices; using System.Windows.Forms; using Decal.Adapter.Support; using Decal.Adapter.Wrappers; using Decal.Interop.Core; namespace Decal.Adapter.IDQueue; /// /// An IDQueue that is fair with respect to plugins and round-robin with respect to ID requests. /// [ComVisible(true)] [ClassInterface(ClassInterfaceType.None)] [ComDefaultInterface(typeof(IIdentifyFilter))] [ProgId("DecalAdapter.FairIDQueue")] [Guid("5CD85A12-3DED-48E7-B440-B41E2A1452D9")] [CLSCompliant(true)] public class FairIDQueue : FairRoundRobinScheduleQueue, IIdentifyFilter { private static int iVal = 1126270821; private const int REQUEST_INTERVAL = 600; private const int MAX_TRY_COUNT = 3; private const int NULL_ACTION_ID = -1; private Timer FireTimer = new Timer(); private DateTime LastFireTime = DateTime.MinValue; private uint CurrentTimerFrame; private uint LastTimerFireFrame; private int UserRequestedID = -1; private int UserRequestedAttempts; private EventHandler mUserIDRequestProcessed; [CLSCompliant(false)] public event EventHandler UserIDRequestProcessed { add { mUserIDRequestProcessed = (EventHandler)Delegate.Combine(mUserIDRequestProcessed, value); } remove { mUserIDRequestProcessed = (EventHandler)Delegate.Remove(mUserIDRequestProcessed, value); } } internal FairIDQueue() : base(3, -1) { FireTimer.Interval = 600; FireTimer.Tick += FireTimer_Tick; CoreManager.Current.Actions.Underlying.SetIDFilter(this); CoreManager.Current.MessageProcessed += Current_MessageProcessed; CoreManager.Current.RenderFrame += Current_RenderFrame; } private void Current_RenderFrame(object sender, EventArgs e) { CurrentTimerFrame++; } private void Current_MessageProcessed(object sender, MessageProcessedEventArgs e) { if (e.Message.Type == 63408 && e.Message.Value("event") == 201) { int num = e.Message.Value("object"); DeleteAction(num); if (UserRequestedID == num) { UserRequestedID = -1; Util.SafeFireEvent(this, mUserIDRequestProcessed, new UserIDRequestProcessedEventArgs(num)); } } } internal void Start() { LastFireTime = DateTime.MinValue; FireTimer.Enabled = true; } internal void Stop() { FireTimer.Enabled = false; ClearAll(); UserRequestedID = -1; } private void FireTimer_Tick(object sender, EventArgs e) { if (FireTimer.Interval != 600) { FireTimer.Enabled = false; FireTimer.Interval = 600; FireTimer.Enabled = true; } if (CurrentTimerFrame != LastTimerFireFrame) { LastTimerFireFrame = CurrentTimerFrame; FireNow(); } } private void FireNow() { Assembly requester = null; int num = -1; if (UserRequestedID != -1) { num = UserRequestedID; UserRequestedAttempts++; if (UserRequestedAttempts > 3) { UserRequestedID = -1; } } if (num == -1) { num = GetNextAction(ref requester); } if (num != -1) { LastFireTime = DateTime.Now; int num2 = num ^ iVal; CoreManager.Current.Actions.Underlying.CallerRefInstanceInternal = num2; iVal = (num2 << 5) ^ (num2 >> 13) ^ iVal; } } protected override bool IsActionValidNow(int action) { WorldObject worldObject = CoreManager.Current.WorldFilter[action]; if (worldObject == null) { return true; } if (worldObject.Container == 0) { WorldObject worldObject2 = CoreManager.Current.WorldFilter[CoreManager.Current.CharacterFilter.Id]; if (worldObject2 != null && worldObject.Coordinates().DistanceToCoords(worldObject2.Coordinates()) > 0.375) { return false; } } return true; } protected override bool IsActionPermanentlyInvalid(int action) { return !CoreManager.Current.Actions.IsValidObject(action); } /// /// Adds a request to the queue. /// /// The game object ID to identify. public void AddToQueue(int lObjectID) { AddToQueue(lObjectID, DateTime.MaxValue); } /// /// Adds a request to the queue with a timeout time. /// Note: if something else requests this ID while this request is still pending, /// the later of the two timeouts will prevail. /// /// The game object ID to identify. /// public void AddToQueue(int lObjectID, DateTime pTimeout) { Assembly assembly = Assembly.GetCallingAssembly(); if (assembly == null) { assembly = Assembly.GetExecutingAssembly(); } AddToQueueForCaller(lObjectID, assembly, pTimeout); } internal void AddToQueueForCaller(int lObjectID, Assembly pCaller, DateTime pTimeout) { try { if (FireTimer.Enabled && lObjectID != -1) { Enqueue(pCaller, lObjectID, DateTime.MaxValue); if ((DateTime.Now - LastFireTime).TotalMilliseconds > 700.0) { FireTimer.Enabled = false; FireTimer.Interval = 1; FireTimer.Enabled = true; } } } catch (Exception ex) { Util.WriteLine("Queue exception in addtoqueue: " + ex.ToString()); } } /// /// Send an ID request which bypasses the queue. /// This is used when the user manually requests an ID. Only one object /// at a time can be pending here. /// /// The game object ID to identify. [CLSCompliant(false)] [EditorBrowsable(EditorBrowsableState.Never)] public void ShortcircuitID(int lObjectID) { if (FireTimer.Enabled && lObjectID != -1) { UserRequestedID = lObjectID; UserRequestedAttempts = 0; if ((DateTime.Now - LastFireTime).TotalMilliseconds > 700.0) { LastFireTime = DateTime.Now; } } } }