@@ -119,15 +119,28 @@ void Engine::compile()
119119
120120void Engine::frame ()
121121{
122+ m_lockFrame = false ;
123+
122124 for (int i = 0 ; i < m_runningScripts.size (); i++) {
123125 auto script = m_runningScripts[i];
124126 m_breakFrame = false ;
125127
126128 do {
127129 script->run ();
128130 if (script->atEnd ()) {
129- for (auto &[key, value] : m_broadcastMap)
130- value.erase (std::remove (value.begin (), value.end (), script->script ()), value.end ());
131+ for (auto &[key, value] : m_runningBroadcastMap) {
132+ size_t index = 0 ;
133+
134+ for (const auto &pair : value) {
135+ if (pair.second == script.get ()) {
136+ value.erase (value.begin () + index);
137+ break ;
138+ }
139+
140+ index++;
141+ }
142+ }
143+
131144 m_scriptsToRemove.push_back (script.get ());
132145 }
133146 } while (!script->atEnd () && !m_breakFrame);
@@ -180,24 +193,48 @@ void Engine::startScript(std::shared_ptr<Block> topLevelBlock, std::shared_ptr<T
180193 }
181194}
182195
183- void libscratchcpp::Engine::broadcast (unsigned int index, VirtualMachine *sourceScript)
196+ void libscratchcpp::Engine::broadcast (unsigned int index, VirtualMachine *sourceScript, bool wait )
184197{
198+ bool previousSkipFrame = m_skipFrame;
199+ skipFrame ();
185200 const std::vector<Script *> &scripts = m_broadcastMap[index];
201+
186202 for (auto script : scripts) {
187- size_t index = -1 ;
188- for (size_t i = 0 ; i < m_runningScripts.size (); i++) {
203+ long scriptIndex = -1 ;
204+ for (long i = 0 ; i < m_runningScripts.size (); i++) {
189205 if (m_runningScripts[i]->script () == script) {
190- index = i;
206+ scriptIndex = i;
191207 break ;
192208 }
193209 }
194- if (index != -1 ) {
210+
211+ if (scriptIndex != -1 ) {
195212 // Reset the script if it's already running
196- m_runningScripts[index]->reset ();
197- if (script == sourceScript->script ())
213+ auto vm = m_runningScripts[scriptIndex];
214+ vm->reset ();
215+
216+ // Remove the script from scripts to remove because it's going to run again
217+ m_scriptsToRemove.erase (std::remove (m_scriptsToRemove.begin (), m_scriptsToRemove.end (), vm.get ()), m_scriptsToRemove.end ());
218+ assert (std::find (m_scriptsToRemove.begin (), m_scriptsToRemove.end (), m_runningScripts[scriptIndex].get ()) == m_scriptsToRemove.end ());
219+
220+ auto &scripts = m_runningBroadcastMap[index];
221+
222+ for (auto &pair : scripts) {
223+ if (pair.second ->script () == script)
224+ pair.first = sourceScript;
225+ }
226+
227+ if (script == sourceScript->script ()) {
198228 sourceScript->stop (false , true );
229+
230+ if (!previousSkipFrame && !wait)
231+ m_skipFrame = false ;
232+ } else
233+ sourceScript->stop (true , true );
199234 } else {
200- m_runningScripts.push_back (script->start ());
235+ auto vm = script->start ();
236+ m_runningScripts.push_back (vm);
237+ m_runningBroadcastMap[index].push_back ({ sourceScript, vm.get () });
201238 }
202239 }
203240}
@@ -227,6 +264,8 @@ void Engine::run()
227264
228265 while (true ) {
229266 auto lastFrameTime = std::chrono::steady_clock::now ();
267+ m_skipFrame = false ;
268+
230269 // Execute the frame
231270 frame ();
232271 if (m_runningScripts.size () <= 0 )
@@ -236,17 +275,29 @@ void Engine::run()
236275 auto currentTime = std::chrono::steady_clock::now ();
237276 auto elapsedTime = std::chrono::duration_cast<std::chrono::milliseconds>(currentTime - lastFrameTime);
238277 auto sleepTime = frameDuration - elapsedTime;
278+ bool timeOut = sleepTime <= std::chrono::milliseconds::zero ();
239279
240- if (sleepTime > std::chrono::milliseconds::zero () )
280+ if (!timeOut && !m_skipFrame )
241281 std::this_thread::sleep_for (sleepTime);
242282
283+ if ((m_skipFrame && timeOut) || !m_skipFrame) {
284+ // TODO: Repaint here
285+ }
286+
243287 lastFrameTime = currentTime;
244288 }
245289}
246290
247- bool Engine::broadcastRunning (unsigned int index)
291+ bool Engine::broadcastRunning (unsigned int index, VirtualMachine *sourceScript )
248292{
249- return !m_broadcastMap[index].empty ();
293+ const auto &scripts = m_runningBroadcastMap[index];
294+
295+ for (const auto &pair : scripts) {
296+ if (pair.first == sourceScript)
297+ return true ;
298+ }
299+
300+ return false ;
250301}
251302
252303void Engine::breakFrame ()
@@ -259,6 +310,20 @@ bool libscratchcpp::Engine::breakingCurrentFrame()
259310 return m_breakFrame;
260311}
261312
313+ void Engine::skipFrame ()
314+ {
315+ if (!m_lockFrame) {
316+ breakFrame ();
317+ m_skipFrame = true ;
318+ }
319+ }
320+
321+ void Engine::lockFrame ()
322+ {
323+ m_skipFrame = false ;
324+ m_lockFrame = true ;
325+ }
326+
262327void Engine::registerSection (std::shared_ptr<IBlockSection> section)
263328{
264329 if (section) {
@@ -364,8 +429,13 @@ void libscratchcpp::Engine::addBroadcastScript(std::shared_ptr<Block> whenReceiv
364429 if (m_broadcastMap.count (id) == 1 ) {
365430 std::vector<Script *> &scripts = m_broadcastMap[id];
366431 scripts.push_back (m_scripts[whenReceivedBlock].get ());
367- } else
432+ } else {
368433 m_broadcastMap[id] = { m_scripts[whenReceivedBlock].get () };
434+
435+ // Create a vector of running scripts for this broadcast
436+ // so we don't need to check if it's there
437+ m_runningBroadcastMap[id] = {};
438+ }
369439}
370440
371441const std::vector<std::shared_ptr<Target>> &Engine::targets () const
0 commit comments