Code/BehaviorTree/Clock.cs
using System.Collections.Generic;
using Sandbox;
using Sandbox.Diagnostics;
namespace NPBehave
{
public class Clock
{
private readonly List<System.Action> _updateObservers = new List<System.Action>();
private readonly Dictionary<System.Action, Timer> _timers = new Dictionary<System.Action, Timer>();
private readonly HashSet<System.Action> _removeObservers = new HashSet<System.Action>();
private readonly HashSet<System.Action> _addObservers = new HashSet<System.Action>();
private readonly HashSet<System.Action> _removeTimers = new HashSet<System.Action>();
private readonly Dictionary<System.Action, Timer> _addTimers = new Dictionary<System.Action, Timer>();
private bool _isInUpdate = false;
class Timer
{
public double ScheduledTime = 0f;
public int Repeat = 0;
public bool Used = false;
public double Delay = 0f;
public float RandomVariance = 0.0f;
public void ScheduleAbsoluteTime(double elapsedTime)
{
ScheduledTime = elapsedTime + Delay - RandomVariance * 0.5f + RandomVariance * Game.Random.NextDouble();
}
}
private TimeSince _elapsedTime = 0f;
private readonly List<Timer> _timerPool = new List<Timer>();
private int _currentTimerPoolIndex = 0;
/// <summary>Register a timer function</summary>
/// <param name="time">time in milliseconds</param>
/// <param name="repeat">number of times to repeat, set to -1 to repeat until unregistered.</param>
/// <param name="action">method to invoke</param>
public void AddTimer(float time, int repeat, System.Action action)
{
AddTimer(time, 0f, repeat, action);
}
/// <summary>Register a timer function with random variance</summary>
/// <param name="delay">time in milliseconds</param>
/// <param name="randomVariance">deviate from time on a random basis</param>
/// <param name="repeat">number of times to repeat, set to -1 to repeat until unregistered.</param>
/// <param name="action">method to invoke</param>
public void AddTimer(float delay, float randomVariance, int repeat, System.Action action)
{
Timer timer = null;
if (!_isInUpdate)
{
if (!_timers.ContainsKey(action))
{
_timers[action] = GetTimerFromPool();
}
timer = _timers[action];
}
else
{
if (!_addTimers.TryGetValue( action, out Timer value ) )
{
value = GetTimerFromPool();
_addTimers[action] = value;
}
timer = value;
if (_removeTimers.Contains(action))
{
_removeTimers.Remove(action);
}
}
Assert.True(timer.Used);
timer.Delay = delay;
timer.RandomVariance = randomVariance;
timer.Repeat = repeat;
timer.ScheduleAbsoluteTime(_elapsedTime);
}
public void RemoveTimer(System.Action action)
{
if (!_isInUpdate)
{
if (_timers.TryGetValue( action, out Timer value ) )
{
value.Used = false;
_timers.Remove(action);
}
}
else
{
if (_timers.ContainsKey(action))
{
_removeTimers.Add(action);
}
if (_addTimers.TryGetValue( action, out Timer value ) )
{
Assert.True( value.Used);
value.Used = false;
_addTimers.Remove(action);
}
}
}
public bool HasTimer(System.Action action)
{
if (!_isInUpdate)
{
return _timers.ContainsKey(action);
}
else
{
if (_removeTimers.Contains(action))
{
return false;
}
else if (_addTimers.ContainsKey(action))
{
return true;
}
else
{
return _timers.ContainsKey(action);
}
}
}
/// <summary>Register a function that is called every frame</summary>
/// <param name="action">function to invoke</param>
public void AddUpdateObserver(System.Action action)
{
if (!_isInUpdate)
{
_updateObservers.Add(action);
}
else
{
if (!_updateObservers.Contains(action))
{
_addObservers.Add(action);
}
if (_removeObservers.Contains(action))
{
_removeObservers.Remove(action);
}
}
}
public void RemoveUpdateObserver(System.Action action)
{
if (!_isInUpdate)
{
_updateObservers.Remove(action);
}
else
{
if (_updateObservers.Contains(action))
{
_removeObservers.Add(action);
}
if (_addObservers.Contains(action))
{
_addObservers.Remove(action);
}
}
}
public bool HasUpdateObserver(System.Action action)
{
if (!_isInUpdate)
{
return _updateObservers.Contains(action);
}
else
{
if (_removeObservers.Contains(action))
{
return false;
}
else if (_addObservers.Contains(action))
{
return true;
}
else
{
return _updateObservers.Contains(action);
}
}
}
public void Update(float deltaTime)
{
// _elapsedTime += deltaTime;
_isInUpdate = true;
foreach (System.Action action in _updateObservers)
{
if (!_removeObservers.Contains(action))
{
action.Invoke();
}
}
Dictionary<System.Action, Timer>.KeyCollection keys = _timers.Keys;
foreach (System.Action callback in keys)
{
if (_removeTimers.Contains(callback))
{
continue;
}
Timer timer = _timers[callback];
if (timer.ScheduledTime <= _elapsedTime)
{
if (timer.Repeat == 0)
{
RemoveTimer(callback);
}
else if (timer.Repeat >= 0)
{
timer.Repeat--;
}
callback.Invoke();
timer.ScheduleAbsoluteTime(_elapsedTime);
}
}
foreach (System.Action action in _addObservers)
{
_updateObservers.Add(action);
}
foreach (System.Action action in _removeObservers)
{
_updateObservers.Remove(action);
}
foreach (System.Action action in _addTimers.Keys)
{
if (_timers.TryGetValue( action, out Timer value ) )
{
Assert.AreNotEqual( value, _addTimers[action]);
value.Used = false;
}
Assert.True(_addTimers[action].Used);
_timers[action] = _addTimers[action];
}
foreach (System.Action action in _removeTimers)
{
Assert.True(_timers[action].Used);
_timers[action].Used = false;
_timers.Remove(action);
}
_addObservers.Clear();
_removeObservers.Clear();
_addTimers.Clear();
_removeTimers.Clear();
_isInUpdate = false;
}
public int NumUpdateObservers
{
get
{
return _updateObservers.Count;
}
}
public int NumTimers
{
get
{
return _timers.Count;
}
}
public double ElapsedTime
{
get
{
return _elapsedTime;
}
}
private Timer GetTimerFromPool()
{
int i = 0;
int l = _timerPool.Count;
Timer timer = null;
while (i < l)
{
int timerIndex = (i + _currentTimerPoolIndex) % l;
if (!_timerPool[timerIndex].Used)
{
_currentTimerPoolIndex = timerIndex;
timer = _timerPool[timerIndex];
break;
}
i++;
}
if (timer == null)
{
timer = new Timer();
_currentTimerPoolIndex = 0;
_timerPool.Add(timer);
}
timer.Used = true;
return timer;
}
public int DebugPoolSize
{
get
{
return _timerPool.Count;
}
}
}
}