using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;
///
/// Extending MonoBehaviour to add some extra functionality
/// Exception handling from: http://twistedoakstudios.com/blog/Post83_coroutines-more-than-you-want-to-know
///
/// 2013 Tim Tregubov
///
public class TTMonoBehaviour : MonoBehaviour
{
private LockQueue LockedCoroutineQueue { get; set; }
///
/// Coroutine with return value AND exception handling on the return value.
///
public Coroutine StartCoroutine(IEnumerator coroutine)
{
Coroutine coroutineObj = new Coroutine();
coroutineObj.coroutine = base.StartCoroutine(coroutineObj.InternalRoutine(coroutine));
return coroutineObj;
}
///
/// Lockable coroutine. Can either wait for a previous coroutine to finish or a timeout or just bail if previous one isn't done.
/// Caution: the default timeout is 10 seconds. Coroutines that timeout just drop so if its essential increase this timeout.
/// Set waitTime to 0 for no wait
///
public Coroutine StartCoroutine(IEnumerator coroutine, string lockID, float waitTime = 10f)
{
if (LockedCoroutineQueue == null) LockedCoroutineQueue = new LockQueue();
Coroutine coroutineObj = new Coroutine(lockID, waitTime, LockedCoroutineQueue);
coroutineObj.coroutine = base.StartCoroutine(coroutineObj.InternalRoutine(coroutine));
return coroutineObj;
}
///
/// Coroutine with return value AND exception handling AND lockable
///
public class Coroutine
{
private T returnVal;
private Exception e;
private string lockID;
private float waitTime;
private LockQueue lockedCoroutines; //reference to objects lockdict
private bool lockable;
public Coroutine coroutine;
public T Value
{
get
{
if (e != null)
{
throw e;
}
return returnVal;
}
}
public Coroutine() { lockable = false; }
public Coroutine(string lockID, float waitTime, LockQueue lockedCoroutines)
{
this.lockable = true;
this.lockID = lockID;
this.lockedCoroutines = lockedCoroutines;
this.waitTime = waitTime;
}
public IEnumerator InternalRoutine(IEnumerator coroutine)
{
if (lockable && lockedCoroutines != null)
{
if (lockedCoroutines.Contains(lockID))
{
if (waitTime == 0f)
{
//Debug.Log(this.GetType().Name + ": coroutine already running and wait not requested so exiting: " + lockID);
yield break;
}
else
{
//Debug.Log(this.GetType().Name + ": previous coroutine already running waiting max " + waitTime + " for my turn: " + lockID);
float starttime = Time.time;
float counter = 0f;
lockedCoroutines.Add(lockID, coroutine);
while (!lockedCoroutines.First(lockID, coroutine) && (Time.time - starttime) < waitTime)
{
yield return null;
counter += Time.deltaTime;
}
if (counter >= waitTime)
{
string error = this.GetType().Name + ": coroutine " + lockID + " bailing! due to timeout: " + counter;
Debug.LogError(error);
this.e = new Exception(error);
lockedCoroutines.Remove(lockID, coroutine);
yield break;
}
}
}
else
{
lockedCoroutines.Add(lockID, coroutine);
}
}
while (true)
{
try
{
if (!coroutine.MoveNext())
{
if (lockable) lockedCoroutines.Remove(lockID, coroutine);
yield break;
}
}
catch (Exception e)
{
this.e = e;
Debug.LogError(this.GetType().Name + ": caught Coroutine exception! " + e.Message + "\n" + e.StackTrace);
if (lockable) lockedCoroutines.Remove(lockID, coroutine);
yield break;
}
object yielded = coroutine.Current;
if (yielded != null && yielded.GetType() == typeof(T))
{
returnVal = (T)yielded;
if (lockable) lockedCoroutines.Remove(lockID, coroutine);
yield break;
}
else
{
yield return coroutine.Current;
}
}
}
}
///
/// coroutine lock and queue
///
public class LockQueue
{
private Dictionary> LockedCoroutines { get; set; }
public LockQueue()
{
LockedCoroutines = new Dictionary>();
}
///
/// check if LockID is locked
///
public bool Contains(string lockID)
{
return LockedCoroutines.ContainsKey(lockID);
}
///
/// check if given coroutine is first in the queue
///
public bool First(string lockID, IEnumerator coroutine)
{
bool ret = false;
if (Contains(lockID))
{
if (LockedCoroutines[lockID].Count > 0)
{
ret = LockedCoroutines[lockID][0] == coroutine;
}
}
return ret;
}
///
/// Add the specified lockID and coroutine to the coroutine lockqueue
///
public void Add(string lockID, IEnumerator coroutine)
{
if (!LockedCoroutines.ContainsKey(lockID))
{
LockedCoroutines.Add(lockID, new List());
}
if (!LockedCoroutines[lockID].Contains(coroutine))
{
LockedCoroutines[lockID].Add(coroutine);
}
}
///
/// Remove the specified coroutine and queue if empty
///
public bool Remove(string lockID, IEnumerator coroutine)
{
bool ret = false;
if (LockedCoroutines.ContainsKey(lockID))
{
if (LockedCoroutines[lockID].Contains(coroutine))
{
ret = LockedCoroutines[lockID].Remove(coroutine);
}
if (LockedCoroutines[lockID].Count == 0)
{
ret = LockedCoroutines.Remove(lockID);
}
}
return ret;
}
}
}