using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
namespace Coroutine {
///
/// An object of this class can be used to start, tick and otherwise manage active s as well as their s.
/// Note that a static implementation of this can be found in .
///
public class CoroutineHandlerInstance {
private readonly List tickingCoroutines = new List();
private readonly List eventCoroutines = new List();
private readonly Queue outstandingCoroutines = new Queue();
private readonly ISet eventCoroutinesToRemove = new HashSet();
private readonly Stopwatch stopwatch = new Stopwatch();
///
/// The amount of instances that are currently waiting for a tick (waiting for time to pass)
///
public int TickingCount => this.tickingCoroutines.Count;
///
/// The amount of instances that are currently waiting for an
///
public int EventCount => this.eventCoroutines.Count;
///
/// Starts the given coroutine, returning a object for management.
/// Note that this calls to get the enumerator.
///
/// The coroutine to start
/// The that this coroutine should have. Defaults to an empty string.
/// The that this coroutine should have. The higher the priority, the earlier it is advanced. Defaults to 0.
/// An active coroutine object representing this coroutine
public ActiveCoroutine Start(IEnumerable coroutine, string name = "", int priority = 0) {
return this.Start(coroutine.GetEnumerator(), name, priority);
}
///
/// Starts the given coroutine, returning a object for management.
///
/// The coroutine to start
/// The that this coroutine should have. Defaults to an empty string.
/// The that this coroutine should have. The higher the priority, the earlier it is advanced compared to other coroutines that advance around the same time. Defaults to 0.
/// An active coroutine object representing this coroutine
public ActiveCoroutine Start(IEnumerator coroutine, string name = "", int priority = 0) {
var inst = new ActiveCoroutine(coroutine, name, priority, this.stopwatch);
if (inst.MoveNext())
this.outstandingCoroutines.Enqueue(inst);
return inst;
}
///
/// Causes the given action to be invoked after the given .
/// This is equivalent to a coroutine that waits for the given wait and then executes the given .
///
/// The wait to wait for
/// The action to execute after waiting
/// The that the underlying coroutine should have. Defaults to an empty string.
/// The that the underlying coroutine should have. The higher the priority, the earlier it is advanced compared to other coroutines that advance around the same time. Defaults to 0.
/// An active coroutine object representing this coroutine
public ActiveCoroutine InvokeLater(Wait wait, Action action, string name = "", int priority = 0) {
return this.Start(InvokeLaterImpl(wait, action), name, priority);
}
///
/// Ticks this coroutine handler, causing all time-based s to be ticked.
/// Note that this method needs to be called even if only event-based coroutines are used.
///
/// The amount of seconds that have passed since the last time this method was invoked
public void Tick(double deltaSeconds) {
this.MoveOutstandingCoroutines();
this.tickingCoroutines.RemoveAll(c => {
if (c.Tick(deltaSeconds)) {
return true;
} else if (c.IsWaitingForEvent()) {
this.outstandingCoroutines.Enqueue(c);
return true;
}
return false;
});
}
///
/// Ticks this coroutine handler, causing all time-based s to be ticked.
/// This is a convenience method that calls , but accepts a instead of an amount of seconds.
///
/// The time that has passed since the last time this method was invoked
public void Tick(TimeSpan delta) {
this.Tick(delta.TotalSeconds);
}
///
/// Raises the given event, causing all event-based s to be updated.
///
/// The event to raise
public void RaiseEvent(Event evt) {
for (var i = 0; i < this.eventCoroutines.Count; i++) {
if (this.eventCoroutinesToRemove.Contains(i))
continue;
var c = this.eventCoroutines[i];
if (c.OnEvent(evt)) {
this.eventCoroutinesToRemove.Add(i);
} else if (!c.IsWaitingForEvent()) {
this.outstandingCoroutines.Enqueue(c);
this.eventCoroutinesToRemove.Add(i);
}
}
}
///
/// Returns a list of all currently active objects under this handler.
///
/// All active coroutines
public IEnumerable GetActiveCoroutines() {
return this.tickingCoroutines.Concat(this.eventCoroutines);
}
private void MoveOutstandingCoroutines() {
var i = 0;
this.eventCoroutines.RemoveAll(c => this.eventCoroutinesToRemove.Contains(i++));
this.eventCoroutinesToRemove.Clear();
while (this.outstandingCoroutines.Count > 0) {
var coroutine = this.outstandingCoroutines.Dequeue();
var list = coroutine.IsWaitingForEvent() ? this.eventCoroutines : this.tickingCoroutines;
var position = list.BinarySearch(coroutine);
list.Insert(position < 0 ? ~position : position, coroutine);
}
}
private static IEnumerator InvokeLaterImpl(Wait wait, Action action) {
yield return wait;
action();
}
}
}