-
Notifications
You must be signed in to change notification settings - Fork 18
Expand file tree
/
Copy pathCoroutineScheduler.cs
More file actions
195 lines (177 loc) · 6.02 KB
/
Copy pathCoroutineScheduler.cs
File metadata and controls
195 lines (177 loc) · 6.02 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
namespace B83.CustomCoroutines
{
using System.Collections;
using System.Collections.Generic;
public class WaitForSeconds
{
public double delay;
public WaitForSeconds(double aDelay) => delay = aDelay;
}
public abstract class CoroutineSchedulerQueue
{
public abstract void ScheduleCoroutine(Coroutine aCoroutine, object aYieldValue);
public abstract void Tick(List<Coroutine> aList);
}
public class CoroutineSchedulerWaitQueue : CoroutineSchedulerQueue
{
public class Node
{
public Coroutine co;
public double timeout;
public Node next = null;
}
public double currentTime;
Node first = null;
public override void ScheduleCoroutine(Coroutine aCoroutine, object aYieldValue)
{
if (aYieldValue is WaitForSeconds wfs)
{
// time sorted linked list
var n = new Node { co = aCoroutine, timeout = currentTime + wfs.delay };
if (first == null || first.timeout > n.timeout)
{
n.next = first;
first = n;
return;
}
// make sure the coroutine is placed in the right order
var a = first;
while (a.next != null && a.next.timeout < n.timeout)
a = a.next;
n.next = a.next;
a.next = n;
}
}
public override void Tick(List<Coroutine> aList)
{
// since the routines are ordered, they always complete in the right order
while (first != null && currentTime >= first.timeout)
{
aList.Add(first.co);
first = first.next;
}
}
}
public class CoroutineSchedulerSimpleQueue : CoroutineSchedulerQueue
{
List<Coroutine> list = new List<Coroutine>();
public System.Type type;
public override void ScheduleCoroutine(Coroutine aCoroutine, object aYieldValue)
{
list.Add(aCoroutine);
}
public override void Tick(List<Coroutine> aList)
{
aList.AddRange(list);
list.Clear();
}
}
public class Coroutine
{
internal Stack<IEnumerator> m_Routine = new Stack<IEnumerator>();
Coroutine m_Child = null;
public bool HasFinished => m_Routine.Count == 0;
public Coroutine(IEnumerator aRoutine)
{
m_Routine.Push(aRoutine);
}
internal bool Tick(out object aYieldVal)
{
aYieldVal = null;
if (m_Routine.Count == 0)
return false;
// This coroutine is waiting for a child coroutine to finish
if (m_Child != null)
{
if (m_Child.HasFinished)
m_Child = null;
else
return true;
}
var it = m_Routine.Peek();
if (it.MoveNext())
{
if (it.Current is IEnumerator subIterator)
{
m_Routine.Push(subIterator);
return Tick(out aYieldVal);
}
else if (it.Current is Coroutine subCoroutine)
{
return subCoroutine.HasFinished;
}
aYieldVal = it.Current;
return true;
}
else
{
m_Routine.Pop();
return Tick(out aYieldVal);
}
}
public void Stop() => m_Routine.Clear();
}
public class CoroutineScheduler
{
Dictionary<System.Type, CoroutineSchedulerQueue> queues = new Dictionary<System.Type, CoroutineSchedulerQueue>();
CoroutineSchedulerSimpleQueue m_DefaultQueue = new CoroutineSchedulerSimpleQueue();
List<Coroutine> m_ProcessList = new List<Coroutine>();
public void AddQueue<T>( CoroutineSchedulerQueue aQueue)
{
AddQueue(typeof(T), aQueue);
}
public void AddQueue(System.Type aType, CoroutineSchedulerQueue aQueue)
{
queues.Add(aType, aQueue);
}
public void TickQueue<T>()
{
TickQueue(typeof(T));
}
public void TickQueue(System.Type aType)
{
if (queues.TryGetValue(aType, out CoroutineSchedulerQueue queue) && queue != null)
TickQueue(queue);
}
private void TickQueue(CoroutineSchedulerQueue aQueue)
{
aQueue.Tick(m_ProcessList);
foreach (var co in m_ProcessList)
{
try
{
TickCoroutine(co);
}
catch (System.Exception e)
{
// Optional: log error here.
// if there's an exception in the coroutine, it would simply terminate the innermost IEnumerator
co.m_Routine.Pop();
if (co.m_Routine.Count > 0)
m_DefaultQueue.ScheduleCoroutine(co, null);
}
}
m_ProcessList.Clear();
}
internal void TickCoroutine(Coroutine aRoutine)
{
if (aRoutine.Tick(out object obj))
{
if (obj == null)
m_DefaultQueue.ScheduleCoroutine(aRoutine, obj);
else if (queues.TryGetValue(obj.GetType(), out CoroutineSchedulerQueue queue))
queue.ScheduleCoroutine(aRoutine, obj);
}
}
public void TickDefault() => TickQueue(m_DefaultQueue);
public Coroutine StartCoroutine(IEnumerator aRoutine, bool aStartImmediately = true)
{
var co = new Coroutine(aRoutine);
if (aStartImmediately)
TickCoroutine(co);
else
m_DefaultQueue.ScheduleCoroutine(co, null);
return co;
}
}
}