@@ -5,6 +5,14 @@ local class = require("ccClass")
55--- @field eventArgs table | nil
66--- @field receivedBy table
77
8+ --- @class _pullEventWrapper
9+ --- @field expectedEventName string
10+ --- @field firstStart boolean
11+
12+ --- @class Promise
13+ --- @field fullFilled boolean
14+ --- @field currentValue any
15+
816--- @class timer
917--- @field id number
1018--- @field triggerAfter number
@@ -16,16 +24,16 @@ local class = require("ccClass")
1624--- @class subThread
1725--- @field originalFunction function
1826--- @field thread thread
19- --- @field waiting boolean
27+ --- @field promise Promise
28+ --- @field waiting boolean To confirm if a function was actually called or is just wrapped.
29+ -- Relevant for Events.run, as it resumes ALL coroutines - therefore invoking each method if not for this check
2030
21- --- @class ccEvent
31+ --- @class ccEvent : subThread
2232--- @field FIFOEventList Event[]
2333--- @field TimerList timerList
24- --- @field thread thread
2534--- @field subThreads table<string , subThread>
2635--- @field time number eq. os.time (" ingame" ) from ccTweaked )
2736--- @field epoch number eq. os.epoch (" ingame" ) from ccTweaked )
28- --- @field private run thread
2937local Events = class (
3038 function (baseClass )
3139 --- @cast baseClass ccEvent
@@ -34,32 +42,36 @@ local Events = class(
3442 baseClass .time = 0
3543 baseClass .epoch = 0
3644 baseClass .subThreads = {}
37- baseClass .run = coroutine.create (
38- function ()
39- while true do
40- while # baseClass .FIFOEventList > 0 do -- TODO if a DID trigger something, stop?
41- local event = table.remove (baseClass .FIFOEventList , 1 )
42- --- @cast event Event
43- if coroutine.status (baseClass .thread ) == " suspended" then -- Modules should be "dead"
44- coroutine.resume (baseClass .thread , event .eventName , table.unpack (event .eventArgs ))
45- -- just empty the list until an event was valid OR no Events are left
46- end
47- for key , value in pairs (baseClass .subThreads ) do
48- if value .waiting then
49- assert (coroutine.resume (value .thread , event .eventName , table.unpack (event .eventArgs )))
50- if coroutine.status (value .thread ) == " dead" then
51- value .waiting = false
52- end
53- end
54- end
55- end
56- coroutine.yield (" tick" )
57- end
58- end
59- )
6045 end
6146)
6247
48+ --- comment
49+ --- @param threadHolder subThread
50+ --- @param ... any
51+ function Events :resumeThread (threadHolder , ...)
52+ local ok
53+ local wrapper
54+ local result
55+
56+ ok , result = coroutine.resume (threadHolder .thread , ... )
57+ --- @cast result _pullEventWrapper | any
58+
59+ wrapper = type (result ) == " table" and (result .firstStart or result .firstStart == false )
60+ local status = coroutine.status (threadHolder .thread )
61+ threadHolder .promise .currentValue = wrapper and result .expectedEventName or result
62+ threadHolder .promise .fullFilled = status == " dead"
63+ threadHolder .waiting = not threadHolder .promise .fullFilled
64+ assert (ok , debug.traceback (" coroutine Error: " .. tostring (result ))) -- TODO: Checkout current behaviour
65+ if wrapper and result .firstStart and status == " suspended" then
66+ result .firstStart = false
67+ -- TODO: If there is a coroutine.yield with a table containing this value -> I need a better way to guard...
68+ -- this is required bc. the test-script would normaly be in the "main" thread, therefore it would resume instantly
69+ -- on os.pullEvent, assuming there is an Event already waiting prior
70+ self :checkForUpdates ()
71+ end
72+ return ok , threadHolder .promise .currentValue
73+ end
74+
6375--- @generic T
6476--- @param func function | T
6577--- @param wrapModule ? boolean This will modify the Module !
@@ -73,11 +85,12 @@ function Events:wrap(func, wrapModule, ...)
7385 env .os = setmetatable ({
7486
7587 pullEvent = function (expectedEventName )
76- local firstStart = true
88+ --- @type _pullEventWrapper
89+ local _pullEventWrapper = {expectedEventName = expectedEventName , firstStart = true }
7790 local event
78- while firstStart or (event [1 ] ~= expectedEventName ) do
79- event = {coroutine.yield (expectedEventName )}
80- firstStart = false
91+ while _pullEventWrapper . firstStart or (expectedEventName ~= nil and ( event [1 ] ~= expectedEventName ) ) do
92+ event = {coroutine.yield (_pullEventWrapper )}
93+ --- @cast event Event
8194 end
8295 return table.unpack (event )
8396 end ,
@@ -97,39 +110,53 @@ function Events:wrap(func, wrapModule, ...)
97110 setfenv (func , env )
98111
99112
100- self .thread = coroutine.create (func )
101113
102114 if (not wrapModule ) then
115+ self .originalFunction = func
103116 return function (...)
104- local ok , result = coroutine.resume (self .thread , ... )
117+ if self .thread == nil or coroutine.status (self .thread ) == " dead" then
118+ self .waiting = true
119+ self .promise = {
120+ currentValue = nil ,
121+ fullFilled = false
122+ }
123+ -- "restart" function => create new Thread
124+ self .thread = coroutine.create (self .originalFunction )
125+
126+ self .waiting = false
127+ end
128+ local ok , result = self :resumeThread (self , ... )
105129 assert (ok , " coroutine Error: " .. tostring (result ))
106130 return result
107131 end
108132 end
109-
110- local ok , result = coroutine.resume (self . thread , ... )
133+ local thread = coroutine.create ( func )
134+ local ok , result = coroutine.resume (thread , ... )
111135 assert (ok , " Could not load Module" )
112- assert (type (result ) == " table" )
136+ assert (type (result ) == " table" , " Module could not be loaded " )
113137 for k ,v in pairs (result ) do
114138 if type (v ) == " function" and (self .subThreads [k ] == nil )then
115139 local thread = coroutine.create (v )
116- self .subThreads [k ] = {
117- thread = thread ,
118- originalFunction = v ,
119- waiting = false
140+ self .subThreads [k ] = {
141+ thread = thread ,
142+ originalFunction = v ,
143+ waiting = false ,
144+ promise = {
145+ currentValue = nil ,
146+ fullFilled = false
120147 }
148+ }
121149 result [k ] = function (...)
122- local ok , result = coroutine.resume ( self . subThreads [ k ]. thread , ... )
150+
123151 local status = coroutine.status (self .subThreads [k ].thread )
124152 if status == " dead" then
125153 -- "restart" function => create new Thread
126154 self .subThreads [k ].thread = coroutine.create (self .subThreads [k ].originalFunction )
127155 self .subThreads [k ].waiting = false
128- else
129- self .subThreads [k ].waiting = true
130156 end
131- assert (ok , " coroutine Error: " .. tostring (result ))
132- return result
157+ self :resumeThread (self .subThreads [k ], ... )
158+
159+ return self .subThreads [k ].promise
133160 end
134161 end
135162 end
164191function Events :passTime (time )
165192 assert (type (time ) == " number" )
166193 time = time * 1000
167- self .time = (self .time + (time / 60 / 24 )) % 24 -- TODO: Test
194+ self .time = (self .time + (time / 60 / 24 )) % 24
168195 self .epoch = self .epoch + time
169196
170197 for key , value in pairs (self .TimerList .timers ) do
@@ -180,8 +207,40 @@ function Events:invoke(eventName, ...)
180207 --- @type Event
181208 local event = {eventName = eventName , receivedBy = {}, eventArgs = {... }}
182209 table.insert (self .FIFOEventList , event )
183- self .newEventAdded = true
184- assert (coroutine.resume (self .run , " tick" ))
210+ self :checkForUpdates ()
185211end
186212
213+ function Events :checkForUpdates ()
214+ local modifier = 0
215+
216+ local removeEvent = function (i )
217+ table.remove (self .FIFOEventList , i + modifier )
218+ modifier = modifier - 1 ;
219+ end
220+
221+ for i = 1 ,# self .FIFOEventList do
222+ local read = false
223+ local event = self .FIFOEventList [i + modifier ]
224+ --- @cast event Event
225+ if self .waiting and coroutine.status (self .thread ) == " suspended" then -- On it Modules should be "dead"
226+
227+ removeEvent (i )
228+ assert (self :resumeThread (self , event .eventName , table.unpack (event .eventArgs )))
229+
230+ end
231+ for key , value in pairs (self .subThreads ) do -- should be empty on Function
232+ -- if status = running -> The thread making the call,- obvious skip
233+ if value .waiting and coroutine.status (value .thread ) ~= " normal" then
234+ removeEvent (i )
235+ assert (self :resumeThread (value , event .eventName , table.unpack (event .eventArgs )))
236+ if coroutine.status (value .thread ) == " dead" then
237+ value .waiting = false
238+ end
239+ break ;
240+ end
241+ end
242+ end
243+ end
244+
245+
187246return Events
0 commit comments