Files
2023-01-18 17:25:32 -05:00

175 lines
5.6 KiB
C#

// Copyright (c) CypherCore <http://github.com/CypherCore> All rights reserved.
// Licensed under the GNU GENERAL PUBLIC LICENSE. See LICENSE file in the project root for full license information.
using System;
using System.Collections.Generic;
using System.Linq;
namespace Framework.Dynamic
{
public class EventSystem
{
public EventSystem()
{
m_time = 0;
}
public void Update(uint p_time)
{
// update time
m_time += p_time;
// main event loop
KeyValuePair<ulong, BasicEvent> i;
while ((i = m_events.FirstOrDefault()).Value != null && i.Key <= m_time)
{
var Event = i.Value;
m_events.Remove(i);
if (Event.IsRunning())
{
Event.Execute(m_time, p_time);
continue;
}
if (Event.IsAbortScheduled())
{
Event.Abort(m_time);
// Mark the event as aborted
Event.SetAborted();
}
if (Event.IsDeletable())
continue;
// Reschedule non deletable events to be checked at
// the next update tick
AddEvent(Event, CalculateTime(TimeSpan.FromMilliseconds(1)), false);
}
}
public void KillAllEvents(bool force)
{
foreach (var pair in m_events.KeyValueList)
{
// Abort events which weren't aborted already
if (!pair.Value.IsAborted())
{
pair.Value.SetAborted();
pair.Value.Abort(m_time);
}
// Skip non-deletable events when we are
// not forcing the event cancellation.
if (!force && !pair.Value.IsDeletable())
continue;
if (!force)
m_events.Remove(pair);
}
// fast clear event list (in force case)
if (force)
m_events.Clear();
}
public void AddEvent(BasicEvent Event, TimeSpan e_time, bool set_addtime = true)
{
if (set_addtime)
Event.m_addTime = m_time;
Event.m_execTime = (ulong)e_time.TotalMilliseconds;
m_events.Add((ulong)e_time.TotalMilliseconds, Event);
}
public void AddEvent(Action action, TimeSpan e_time, bool set_addtime = true) { AddEvent(new LambdaBasicEvent(action), e_time, set_addtime); }
public void AddEventAtOffset(BasicEvent Event, TimeSpan offset) { AddEvent(Event, CalculateTime(offset)); }
public void AddEventAtOffset(BasicEvent Event, TimeSpan offset, TimeSpan offset2) { AddEvent(Event, CalculateTime(RandomHelper.RandTime(offset, offset2))); }
public void AddEventAtOffset(Action action, TimeSpan offset) { AddEventAtOffset(new LambdaBasicEvent(action), offset); }
public void ModifyEventTime(BasicEvent Event, TimeSpan newTime)
{
foreach (var pair in m_events)
{
if (pair.Value != Event)
continue;
Event.m_execTime = (ulong)newTime.TotalMilliseconds;
m_events.Remove(pair);
m_events.Add((ulong)newTime.TotalMilliseconds, Event);
break;
}
}
public TimeSpan CalculateTime(TimeSpan t_offset)
{
return TimeSpan.FromMilliseconds(m_time) + t_offset;
}
public SortedMultiMap<ulong, BasicEvent> GetEvents() { return m_events; }
ulong m_time;
SortedMultiMap<ulong, BasicEvent> m_events = new();
}
public class BasicEvent
{
public BasicEvent() { m_abortState = AbortState.Running; }
public void ScheduleAbort()
{
Cypher.Assert(IsRunning(), "Tried to scheduled the abortion of an event twice!");
m_abortState = AbortState.Scheduled;
}
public void SetAborted()
{
Cypher.Assert(!IsAborted(), "Tried to abort an already aborted event!");
m_abortState = AbortState.Aborted;
}
// this method executes when the event is triggered
// return false if event does not want to be deleted
// e_time is execution time, p_time is update interval
public virtual bool Execute(ulong e_time, uint p_time) { return true; }
public virtual bool IsDeletable() { return true; } // this event can be safely deleted
public virtual void Abort(ulong e_time) { } // this method executes when the event is aborted
public bool IsRunning() { return m_abortState == AbortState.Running; }
public bool IsAbortScheduled() { return m_abortState == AbortState.Scheduled; }
public bool IsAborted() { return m_abortState == AbortState.Aborted; }
AbortState m_abortState; // set by externals when the event is aborted, aborted events don't execute
public ulong m_addTime; // time when the event was added to queue, filled by event handler
public ulong m_execTime; // planned time of next execution, filled by event handler
}
class LambdaBasicEvent : BasicEvent
{
Action _callback;
public LambdaBasicEvent(Action callback) : base()
{
_callback = callback;
}
public override bool Execute(ulong e_time, uint p_time)
{
_callback();
return true;
}
}
enum AbortState
{
Running,
Scheduled,
Aborted
}
}