Convert movement, actions (lift/use), combat, and spells to use Core.TickCount and avoid DateTime caveats (performance, system time dependency, etc) Refactor DateTime.Now to DateTime.UtcNow Add LOS check for Iron Maiden addon
679 lines
No EOL
15 KiB
C#
679 lines
No EOL
15 KiB
C#
/***************************************************************************
|
|
* Timer.cs
|
|
* -------------------
|
|
* begin : May 1, 2002
|
|
* copyright : (C) The RunUO Software Team
|
|
* email : info@runuo.com
|
|
*
|
|
* $Id$
|
|
*
|
|
***************************************************************************/
|
|
|
|
/***************************************************************************
|
|
*
|
|
* 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 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
***************************************************************************/
|
|
|
|
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.IO;
|
|
using System.Threading;
|
|
using Server.Diagnostics;
|
|
|
|
namespace Server
|
|
{
|
|
public enum TimerPriority
|
|
{
|
|
EveryTick,
|
|
TenMS,
|
|
TwentyFiveMS,
|
|
FiftyMS,
|
|
TwoFiftyMS,
|
|
OneSecond,
|
|
FiveSeconds,
|
|
OneMinute
|
|
}
|
|
|
|
public delegate void TimerCallback();
|
|
public delegate void TimerStateCallback( object state );
|
|
public delegate void TimerStateCallback<T>( T state );
|
|
|
|
public class Timer
|
|
{
|
|
private DateTime m_Next;
|
|
private TimeSpan m_Delay;
|
|
private TimeSpan m_Interval;
|
|
private bool m_Running;
|
|
private int m_Index, m_Count;
|
|
private TimerPriority m_Priority;
|
|
private List<Timer> m_List;
|
|
private bool m_PrioritySet;
|
|
|
|
private static string FormatDelegate( Delegate callback )
|
|
{
|
|
if ( callback == null )
|
|
return "null";
|
|
|
|
return String.Format( "{0}.{1}", callback.Method.DeclaringType.FullName, callback.Method.Name );
|
|
}
|
|
|
|
public static void DumpInfo( TextWriter tw )
|
|
{
|
|
TimerThread.DumpInfo( tw );
|
|
}
|
|
|
|
public TimerPriority Priority
|
|
{
|
|
get
|
|
{
|
|
return m_Priority;
|
|
}
|
|
set
|
|
{
|
|
if ( !m_PrioritySet )
|
|
m_PrioritySet = true;
|
|
|
|
if ( m_Priority != value )
|
|
{
|
|
m_Priority = value;
|
|
|
|
if ( m_Running )
|
|
TimerThread.PriorityChange( this, (int)m_Priority );
|
|
}
|
|
}
|
|
}
|
|
|
|
public DateTime Next
|
|
{
|
|
get { return m_Next; }
|
|
}
|
|
|
|
public TimeSpan Delay
|
|
{
|
|
get { return m_Delay; }
|
|
set { m_Delay = value; }
|
|
}
|
|
|
|
public TimeSpan Interval
|
|
{
|
|
get { return m_Interval; }
|
|
set { m_Interval = value; }
|
|
}
|
|
|
|
public bool Running
|
|
{
|
|
get { return m_Running; }
|
|
set {
|
|
if ( value ) {
|
|
Start();
|
|
} else {
|
|
Stop();
|
|
}
|
|
}
|
|
}
|
|
|
|
public TimerProfile GetProfile()
|
|
{
|
|
if ( !Core.Profiling ) {
|
|
return null;
|
|
}
|
|
|
|
string name = ToString();
|
|
|
|
if ( name == null ) {
|
|
name = "null";
|
|
}
|
|
|
|
return TimerProfile.Acquire( name );
|
|
}
|
|
|
|
public class TimerThread
|
|
{
|
|
private static Queue m_ChangeQueue = Queue.Synchronized( new Queue() );
|
|
|
|
private static DateTime[] m_NextPriorities = new DateTime[8];
|
|
private static TimeSpan[] m_PriorityDelays = new TimeSpan[8]
|
|
{
|
|
TimeSpan.Zero,
|
|
TimeSpan.FromMilliseconds( 10.0 ),
|
|
TimeSpan.FromMilliseconds( 25.0 ),
|
|
TimeSpan.FromMilliseconds( 50.0 ),
|
|
TimeSpan.FromMilliseconds( 250.0 ),
|
|
TimeSpan.FromSeconds( 1.0 ),
|
|
TimeSpan.FromSeconds( 5.0 ),
|
|
TimeSpan.FromMinutes( 1.0 )
|
|
};
|
|
|
|
private static List<Timer>[] m_Timers = new List<Timer>[8]
|
|
{
|
|
new List<Timer>(),
|
|
new List<Timer>(),
|
|
new List<Timer>(),
|
|
new List<Timer>(),
|
|
new List<Timer>(),
|
|
new List<Timer>(),
|
|
new List<Timer>(),
|
|
new List<Timer>(),
|
|
};
|
|
|
|
public static void DumpInfo( TextWriter tw )
|
|
{
|
|
for ( int i = 0; i < 8; ++i )
|
|
{
|
|
tw.WriteLine( "Priority: {0}", (TimerPriority)i );
|
|
tw.WriteLine();
|
|
|
|
Dictionary<string, List<Timer>> hash = new Dictionary<string, List<Timer>>();
|
|
|
|
for ( int j = 0; j < m_Timers[i].Count; ++j )
|
|
{
|
|
Timer t = m_Timers[i][j];
|
|
|
|
string key = t.ToString();
|
|
|
|
List<Timer> list;
|
|
hash.TryGetValue( key, out list );
|
|
|
|
if ( list == null )
|
|
hash[key] = list = new List<Timer>();
|
|
|
|
list.Add( t );
|
|
}
|
|
|
|
foreach ( KeyValuePair<string, List<Timer>> kv in hash )
|
|
{
|
|
string key = kv.Key;
|
|
List<Timer> list = kv.Value;
|
|
|
|
tw.WriteLine( "Type: {0}; Count: {1}; Percent: {2}%", key, list.Count, (int)(100 * (list.Count / (double)m_Timers[i].Count)) );
|
|
}
|
|
|
|
tw.WriteLine();
|
|
tw.WriteLine();
|
|
}
|
|
}
|
|
|
|
private class TimerChangeEntry
|
|
{
|
|
public Timer m_Timer;
|
|
public int m_NewIndex;
|
|
public bool m_IsAdd;
|
|
|
|
private TimerChangeEntry( Timer t, int newIndex, bool isAdd )
|
|
{
|
|
m_Timer = t;
|
|
m_NewIndex = newIndex;
|
|
m_IsAdd = isAdd;
|
|
}
|
|
|
|
public void Free()
|
|
{
|
|
//m_InstancePool.Enqueue( this );
|
|
}
|
|
|
|
private static Queue<TimerChangeEntry> m_InstancePool = new Queue<TimerChangeEntry>();
|
|
|
|
public static TimerChangeEntry GetInstance( Timer t, int newIndex, bool isAdd )
|
|
{
|
|
TimerChangeEntry e;
|
|
|
|
if ( m_InstancePool.Count > 0 )
|
|
{
|
|
e = m_InstancePool.Dequeue();
|
|
|
|
if ( e == null )
|
|
e = new TimerChangeEntry( t, newIndex, isAdd );
|
|
else
|
|
{
|
|
e.m_Timer = t;
|
|
e.m_NewIndex = newIndex;
|
|
e.m_IsAdd = isAdd;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
e = new TimerChangeEntry( t, newIndex, isAdd );
|
|
}
|
|
|
|
return e;
|
|
}
|
|
}
|
|
|
|
public TimerThread()
|
|
{
|
|
}
|
|
|
|
public static void Change( Timer t, int newIndex, bool isAdd )
|
|
{
|
|
m_ChangeQueue.Enqueue( TimerChangeEntry.GetInstance( t, newIndex, isAdd ) );
|
|
m_Signal.Set();
|
|
}
|
|
|
|
public static void AddTimer( Timer t )
|
|
{
|
|
Change( t, (int)t.Priority, true );
|
|
}
|
|
|
|
public static void PriorityChange( Timer t, int newPrio )
|
|
{
|
|
Change( t, newPrio, false );
|
|
}
|
|
|
|
public static void RemoveTimer( Timer t )
|
|
{
|
|
Change( t, -1, false );
|
|
}
|
|
|
|
private static void ProcessChangeQueue()
|
|
{
|
|
while ( m_ChangeQueue.Count > 0 )
|
|
{
|
|
TimerChangeEntry tce = (TimerChangeEntry)m_ChangeQueue.Dequeue();
|
|
Timer timer = tce.m_Timer;
|
|
int newIndex = tce.m_NewIndex;
|
|
|
|
if ( timer.m_List != null )
|
|
timer.m_List.Remove( timer );
|
|
|
|
if ( tce.m_IsAdd )
|
|
{
|
|
timer.m_Next = DateTime.UtcNow + timer.m_Delay;
|
|
timer.m_Index = 0;
|
|
}
|
|
|
|
if ( newIndex >= 0 )
|
|
{
|
|
timer.m_List = m_Timers[newIndex];
|
|
timer.m_List.Add( timer );
|
|
}
|
|
else
|
|
{
|
|
timer.m_List = null;
|
|
}
|
|
|
|
tce.Free();
|
|
}
|
|
}
|
|
|
|
private static AutoResetEvent m_Signal = new AutoResetEvent( false );
|
|
public static void Set() { m_Signal.Set(); }
|
|
|
|
public void TimerMain()
|
|
{
|
|
DateTime now;
|
|
int i, j;
|
|
bool loaded;
|
|
|
|
while ( !Core.Closing )
|
|
{
|
|
ProcessChangeQueue();
|
|
|
|
loaded = false;
|
|
|
|
for ( i = 0; i < m_Timers.Length; i++)
|
|
{
|
|
now = DateTime.UtcNow;
|
|
if ( now < m_NextPriorities[i] )
|
|
break;
|
|
|
|
m_NextPriorities[i] = now + m_PriorityDelays[i];
|
|
|
|
for ( j = 0; j < m_Timers[i].Count; j++)
|
|
{
|
|
Timer t = m_Timers[i][j];
|
|
|
|
if ( !t.m_Queued && now > t.m_Next )
|
|
{
|
|
t.m_Queued = true;
|
|
|
|
lock ( m_Queue )
|
|
m_Queue.Enqueue( t );
|
|
|
|
loaded = true;
|
|
|
|
if ( t.m_Count != 0 && (++t.m_Index >= t.m_Count) )
|
|
{
|
|
t.Stop();
|
|
}
|
|
else
|
|
{
|
|
t.m_Next = now + t.m_Interval;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( loaded )
|
|
Core.Set();
|
|
|
|
m_Signal.WaitOne( 10, false );
|
|
}
|
|
}
|
|
}
|
|
|
|
private static Queue<Timer> m_Queue = new Queue<Timer>();
|
|
private static int m_BreakCount = 20000;
|
|
|
|
public static int BreakCount{ get{ return m_BreakCount; } set{ m_BreakCount = value; } }
|
|
|
|
private static int m_QueueCountAtSlice;
|
|
|
|
private bool m_Queued;
|
|
|
|
public static void Slice()
|
|
{
|
|
lock ( m_Queue )
|
|
{
|
|
m_QueueCountAtSlice = m_Queue.Count;
|
|
|
|
int index = 0;
|
|
|
|
while ( index < m_BreakCount && m_Queue.Count != 0 )
|
|
{
|
|
Timer t = m_Queue.Dequeue();
|
|
TimerProfile prof = t.GetProfile();
|
|
|
|
if ( prof != null ) {
|
|
prof.Start();
|
|
}
|
|
|
|
t.OnTick();
|
|
t.m_Queued = false;
|
|
++index;
|
|
|
|
if ( prof != null ) {
|
|
prof.Finish();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public Timer( TimeSpan delay ) : this( delay, TimeSpan.Zero, 1 )
|
|
{
|
|
}
|
|
|
|
public Timer( TimeSpan delay, TimeSpan interval ) : this( delay, interval, 0 )
|
|
{
|
|
}
|
|
|
|
public virtual bool DefRegCreation
|
|
{
|
|
get{ return true; }
|
|
}
|
|
|
|
public virtual void RegCreation()
|
|
{
|
|
TimerProfile prof = GetProfile();
|
|
|
|
if ( prof != null ) {
|
|
prof.Created++;
|
|
}
|
|
}
|
|
|
|
public Timer( TimeSpan delay, TimeSpan interval, int count )
|
|
{
|
|
m_Delay = delay;
|
|
m_Interval = interval;
|
|
m_Count = count;
|
|
|
|
if ( !m_PrioritySet ) {
|
|
if ( count == 1 ) {
|
|
m_Priority = ComputePriority( delay );
|
|
} else {
|
|
m_Priority = ComputePriority( interval );
|
|
}
|
|
m_PrioritySet = true;
|
|
}
|
|
|
|
if ( DefRegCreation )
|
|
RegCreation();
|
|
}
|
|
|
|
public override string ToString()
|
|
{
|
|
return GetType().FullName;
|
|
}
|
|
|
|
public static TimerPriority ComputePriority( TimeSpan ts )
|
|
{
|
|
if ( ts >= TimeSpan.FromMinutes( 1.0 ) )
|
|
return TimerPriority.FiveSeconds;
|
|
|
|
if ( ts >= TimeSpan.FromSeconds( 10.0 ) )
|
|
return TimerPriority.OneSecond;
|
|
|
|
if ( ts >= TimeSpan.FromSeconds( 5.0 ) )
|
|
return TimerPriority.TwoFiftyMS;
|
|
|
|
if ( ts >= TimeSpan.FromSeconds( 2.5 ) )
|
|
return TimerPriority.FiftyMS;
|
|
|
|
if ( ts >= TimeSpan.FromSeconds( 1.0 ) )
|
|
return TimerPriority.TwentyFiveMS;
|
|
|
|
if ( ts >= TimeSpan.FromSeconds( 0.5 ) )
|
|
return TimerPriority.TenMS;
|
|
|
|
return TimerPriority.EveryTick;
|
|
}
|
|
|
|
#region DelayCall(..)
|
|
|
|
public static Timer DelayCall( TimerCallback callback )
|
|
{
|
|
return DelayCall( TimeSpan.Zero, TimeSpan.Zero, 1, callback );
|
|
}
|
|
|
|
public static Timer DelayCall( TimeSpan delay, TimerCallback callback )
|
|
{
|
|
return DelayCall( delay, TimeSpan.Zero, 1, callback );
|
|
}
|
|
|
|
public static Timer DelayCall( TimeSpan delay, TimeSpan interval, TimerCallback callback )
|
|
{
|
|
return DelayCall( delay, interval, 0, callback );
|
|
}
|
|
|
|
public static Timer DelayCall( TimeSpan delay, TimeSpan interval, int count, TimerCallback callback )
|
|
{
|
|
Timer t = new DelayCallTimer( delay, interval, count, callback );
|
|
|
|
if ( count == 1 )
|
|
t.Priority = ComputePriority( delay );
|
|
else
|
|
t.Priority = ComputePriority( interval );
|
|
|
|
t.Start();
|
|
|
|
return t;
|
|
}
|
|
|
|
public static Timer DelayCall( TimerStateCallback callback, object state )
|
|
{
|
|
return DelayCall( TimeSpan.Zero, TimeSpan.Zero, 1, callback, state );
|
|
}
|
|
|
|
public static Timer DelayCall( TimeSpan delay, TimerStateCallback callback, object state )
|
|
{
|
|
return DelayCall( delay, TimeSpan.Zero, 1, callback, state );
|
|
}
|
|
|
|
public static Timer DelayCall( TimeSpan delay, TimeSpan interval, TimerStateCallback callback, object state )
|
|
{
|
|
return DelayCall( delay, interval, 0, callback, state );
|
|
}
|
|
|
|
public static Timer DelayCall( TimeSpan delay, TimeSpan interval, int count, TimerStateCallback callback, object state )
|
|
{
|
|
Timer t = new DelayStateCallTimer( delay, interval, count, callback, state );
|
|
|
|
if ( count == 1 )
|
|
t.Priority = ComputePriority( delay );
|
|
else
|
|
t.Priority = ComputePriority( interval );
|
|
|
|
t.Start();
|
|
|
|
return t;
|
|
}
|
|
#endregion
|
|
|
|
#region DelayCall<T>(..)
|
|
public static Timer DelayCall<T>( TimerStateCallback<T> callback, T state )
|
|
{
|
|
return DelayCall( TimeSpan.Zero, TimeSpan.Zero, 1, callback, state );
|
|
}
|
|
|
|
public static Timer DelayCall<T>( TimeSpan delay, TimerStateCallback<T> callback, T state )
|
|
{
|
|
return DelayCall( delay, TimeSpan.Zero, 1, callback, state );
|
|
}
|
|
|
|
public static Timer DelayCall<T>( TimeSpan delay, TimeSpan interval, TimerStateCallback<T> callback, T state )
|
|
{
|
|
return DelayCall( delay, interval, 0, callback, state );
|
|
}
|
|
|
|
public static Timer DelayCall<T>( TimeSpan delay, TimeSpan interval, int count, TimerStateCallback<T> callback, T state )
|
|
{
|
|
Timer t = new DelayStateCallTimer<T>( delay, interval, count, callback, state );
|
|
|
|
if( count == 1 )
|
|
t.Priority = ComputePriority( delay );
|
|
else
|
|
t.Priority = ComputePriority( interval );
|
|
|
|
t.Start();
|
|
|
|
return t;
|
|
}
|
|
#endregion
|
|
|
|
#region DelayCall Timers
|
|
private class DelayCallTimer : Timer
|
|
{
|
|
private TimerCallback m_Callback;
|
|
|
|
public TimerCallback Callback{ get{ return m_Callback; } }
|
|
|
|
public override bool DefRegCreation{ get{ return false; } }
|
|
|
|
public DelayCallTimer( TimeSpan delay, TimeSpan interval, int count, TimerCallback callback ) : base( delay, interval, count )
|
|
{
|
|
m_Callback = callback;
|
|
RegCreation();
|
|
}
|
|
|
|
protected override void OnTick()
|
|
{
|
|
if ( m_Callback != null )
|
|
m_Callback();
|
|
}
|
|
|
|
public override string ToString()
|
|
{
|
|
return String.Format( "DelayCallTimer[{0}]", FormatDelegate( m_Callback ) );
|
|
}
|
|
}
|
|
|
|
private class DelayStateCallTimer : Timer
|
|
{
|
|
private TimerStateCallback m_Callback;
|
|
private object m_State;
|
|
|
|
public TimerStateCallback Callback{ get{ return m_Callback; } }
|
|
|
|
public override bool DefRegCreation{ get{ return false; } }
|
|
|
|
public DelayStateCallTimer( TimeSpan delay, TimeSpan interval, int count, TimerStateCallback callback, object state ) : base( delay, interval, count )
|
|
{
|
|
m_Callback = callback;
|
|
m_State = state;
|
|
|
|
RegCreation();
|
|
}
|
|
|
|
protected override void OnTick()
|
|
{
|
|
if ( m_Callback != null )
|
|
m_Callback( m_State );
|
|
}
|
|
|
|
public override string ToString()
|
|
{
|
|
return String.Format( "DelayStateCall[{0}]", FormatDelegate( m_Callback ) );
|
|
}
|
|
}
|
|
|
|
private class DelayStateCallTimer<T> : Timer
|
|
{
|
|
private TimerStateCallback<T> m_Callback;
|
|
private T m_State;
|
|
|
|
public TimerStateCallback<T> Callback { get { return m_Callback; } }
|
|
|
|
public override bool DefRegCreation { get { return false; } }
|
|
|
|
public DelayStateCallTimer( TimeSpan delay, TimeSpan interval, int count, TimerStateCallback<T> callback, T state )
|
|
: base( delay, interval, count )
|
|
{
|
|
m_Callback = callback;
|
|
m_State = state;
|
|
|
|
RegCreation();
|
|
}
|
|
|
|
protected override void OnTick()
|
|
{
|
|
if( m_Callback != null )
|
|
m_Callback( m_State );
|
|
}
|
|
|
|
public override string ToString()
|
|
{
|
|
return String.Format( "DelayStateCall[{0}]", FormatDelegate( m_Callback ) );
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
public void Start()
|
|
{
|
|
if ( !m_Running )
|
|
{
|
|
m_Running = true;
|
|
TimerThread.AddTimer( this );
|
|
|
|
TimerProfile prof = GetProfile();
|
|
|
|
if ( prof != null ) {
|
|
prof.Started++;
|
|
}
|
|
}
|
|
}
|
|
|
|
public void Stop()
|
|
{
|
|
if ( m_Running )
|
|
{
|
|
m_Running = false;
|
|
TimerThread.RemoveTimer( this );
|
|
|
|
TimerProfile prof = GetProfile();
|
|
|
|
if ( prof != null ) {
|
|
prof.Stopped++;
|
|
}
|
|
}
|
|
}
|
|
|
|
protected virtual void OnTick()
|
|
{
|
|
}
|
|
}
|
|
} |