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
|
--
-- coil
--
-- Copyright (c) 2014 rxi
--
-- This library is free software; you can redistribute it and/or modify it
-- under the terms of the MIT license. See LICENSE for details.
--
local coil = { _version = "0.1.0" }
coil.__index = coil
coil.tasks = {}
local unpack = unpack or table.unpack
local _assert = function(cond, msg, lvl)
if cond then return cond, msg, lvl end
error(msg, lvl + 1)
end
local callback_mt = {
__call = function(t, ...)
t.args = {...}
t.ready = true
end}
local task = {}
task.__index = task
function task.new(fn, parent)
local self = setmetatable({}, task)
self.routine = coroutine.wrap(fn)
self.parent = parent
self.pausecount = 0
return self
end
function task:pause()
self.pausecount = self.pausecount + 1
end
function task:resume()
_assert(self.pausecount > 0, "unbalanced resume()", 2)
self.pausecount = self.pausecount - 1
end
function task:stop()
coil.remove(self.parent, self)
end
function coil:update(dt)
if #self == 0 then return end
coil.deltatime = dt
for i = #self, 1, -1 do
local task = self[i]
if task.wait then
-- Handle wait
if type(task.wait) == "number" then
-- Handle numerical wait
task.wait = task.wait - dt
if task.wait <= 0 then
task.waitrem = task.wait
task.wait = nil
end
elseif type(task.wait) == "table" then
-- Handle callback object
if task.wait.ready then
task.wait = nil
end
end
end
if not task.wait and task.pausecount == 0 then
-- Run task
coil.current = task
if not task.routine() then
coil.remove(self, i)
end
end
end
coil.current = nil
end
function coil:add(fn)
local t = task.new(fn, self)
table.insert(self, t)
return t
end
function coil:remove(t)
if type(t) == "number" then
self[t] = self[#self]
table.remove(self)
return
end
for i, task in ipairs(self) do
if task == t then
return coil.remove(self, i)
end
end
end
function coil.wait(x, y)
-- Discard first argument if its a coil group
x = getmetatable(x) == coil and y or x
local c = coil.current
_assert(c, "wait() called from outside a coroutine", 2)
if type(x) == "number" then
-- Handle numerical wait
c.wait = (c.waitrem or 0) + x
if c.wait <= 0 then
c.waitrem = c.wait
return
else
c.waitrem = nil
end
else
-- Handle next-frame / callback wait
_assert(x == nil or getmetatable(x) == callback_mt,
"wait() expected number, callback object or nothing as argument",
2)
c.waitrem = nil
c.wait = x
end
coroutine.yield(true)
-- Return args if wait was a callback object
if type(x) == "table" then
return unpack(x.args)
end
-- Return delta time if wait had no args
if x == nil then
return coil.deltatime
end
end
function coil.callback()
return setmetatable({ ready = false }, callback_mt)
end
function coil.group()
return setmetatable({}, coil)
end
local bound = {
update = function(...) return coil.update(coil.tasks, ...) end,
add = function(...) return coil.add(coil.tasks, ...) end,
remove = function(...) return coil.remove(coil.tasks, ...) end,
}
setmetatable(bound, coil)
return bound
|