using System;
using System.Collections.Generic;
namespace RSG {
public class PromiseCancelledException : Exception {
///
/// Just create the exception
///
public PromiseCancelledException() {
}
///
/// Create the exception with description
///
/// Exception description
public PromiseCancelledException(string message) : base(message) {
}
}
///
/// A class that wraps a pending promise with it's predicate and time data
///
class PredicateWait {
///
/// Predicate for resolving the promise
///
public Func predicate;
///
/// The time the promise was started
///
public float timeStarted;
///
/// The pending promise which is an interface for a promise that can be rejected or resolved.
///
public IPendingPromise pendingPromise;
///
/// The time data specific to this pending promise. Includes elapsed time and delta time.
///
public TimeData timeData;
///
/// The frame the promise was started
///
public int frameStarted;
}
///
/// Time data specific to a particular pending promise.
///
public struct TimeData {
///
/// The amount of time that has elapsed since the pending promise started running
///
public float elapsedTime;
///
/// The amount of time since the last time the pending promise was updated.
///
public float deltaTime;
///
/// The amount of times that update has been called since the pending promise started running
///
public int elapsedUpdates;
}
public interface IPromiseTimer {
///
/// Resolve the returned promise once the time has elapsed
///
IPromise WaitFor(float seconds);
///
/// Resolve the returned promise once the predicate evaluates to true
///
IPromise WaitUntil(Func predicate);
///
/// Resolve the returned promise once the predicate evaluates to false
///
IPromise WaitWhile(Func predicate);
///
/// Update all pending promises. Must be called for the promises to progress and resolve at all.
///
void Update(float deltaTime);
///
/// Cancel a waiting promise and reject it immediately.
///
bool Cancel(IPromise promise);
}
public class PromiseTimer : IPromiseTimer {
///
/// The current running total for time that this PromiseTimer has run for
///
float curTime;
///
/// The current running total for the amount of frames the PromiseTimer has run for
///
int curFrame;
///
/// Currently pending promises
///
readonly LinkedList waiting = new LinkedList();
///
/// Resolve the returned promise once the time has elapsed
///
public IPromise WaitFor(float seconds) {
return this.WaitUntil(t => t.elapsedTime >= seconds);
}
///
/// Resolve the returned promise once the predicate evaluates to false
///
public IPromise WaitWhile(Func predicate) {
return this.WaitUntil(t => !predicate(t));
}
///
/// Resolve the returned promise once the predicate evalutes to true
///
public IPromise WaitUntil(Func predicate) {
var promise = new Promise();
var wait = new PredicateWait() {
timeStarted = this.curTime,
pendingPromise = promise,
timeData = new TimeData(),
predicate = predicate,
frameStarted = this.curFrame
};
this.waiting.AddLast(wait);
return promise;
}
public bool Cancel(IPromise promise) {
var node = this.FindInWaiting(promise);
if (node == null) {
return false;
}
node.Value.pendingPromise.Reject(new PromiseCancelledException("Promise was cancelled by user."));
this.waiting.Remove(node);
return true;
}
LinkedListNode FindInWaiting(IPromise promise) {
for (var node = this.waiting.First; node != null; node = node.Next) {
if (node.Value.pendingPromise.Id.Equals(promise.Id)) {
return node;
}
}
return null;
}
///
/// Update all pending promises. Must be called for the promises to progress and resolve at all.
///
public void Update(float deltaTime) {
this.curTime += deltaTime;
this.curFrame += 1;
var node = this.waiting.First;
while (node != null) {
var wait = node.Value;
var newElapsedTime = this.curTime - wait.timeStarted;
wait.timeData.deltaTime = newElapsedTime - wait.timeData.elapsedTime;
wait.timeData.elapsedTime = newElapsedTime;
var newElapsedUpdates = this.curFrame - wait.frameStarted;
wait.timeData.elapsedUpdates = newElapsedUpdates;
bool result;
try {
result = wait.predicate(wait.timeData);
} catch (Exception ex) {
wait.pendingPromise.Reject(ex);
node = this.RemoveNode(node);
continue;
}
if (result) {
wait.pendingPromise.Resolve();
node = this.RemoveNode(node);
} else {
node = node.Next;
}
}
}
///
/// Removes the provided node and returns the next node in the list.
///
LinkedListNode RemoveNode(LinkedListNode node) {
var currentNode = node;
node = node.Next;
this.waiting.Remove(currentNode);
return node;
}
}
}