@@ -433,22 +433,24 @@ VirtualMachine *Engine::startScript(std::shared_ptr<Block> topLevelBlock, Target
433433 return pushThread (topLevelBlock, target).get ();
434434}
435435
436- void Engine::broadcast (int index)
436+ void Engine::broadcast (int index, VirtualMachine *sender )
437437{
438438 if (index < 0 || index >= m_broadcasts.size ())
439439 return ;
440440
441- broadcastByPtr (m_broadcasts[index].get ());
441+ broadcastByPtr (m_broadcasts[index].get (), sender );
442442}
443443
444- void Engine::broadcastByPtr (Broadcast *broadcast)
444+ void Engine::broadcastByPtr (Broadcast *broadcast, VirtualMachine *sender )
445445{
446446 startHats (HatType::BroadcastReceived, { { HatField::BroadcastOption, broadcast } }, nullptr );
447+ addBroadcastPromise (broadcast, sender);
447448}
448449
449- void Engine::startBackdropScripts (Broadcast *broadcast)
450+ void Engine::startBackdropScripts (Broadcast *broadcast, VirtualMachine *sender )
450451{
451452 startHats (HatType::BackdropChanged, { { HatField::Backdrop, broadcast->name () } }, nullptr );
453+ addBroadcastPromise (broadcast, sender);
452454}
453455
454456void Engine::stopScript (VirtualMachine *vm)
@@ -583,6 +585,48 @@ void Engine::step()
583585 // Check running threads (must be done here)
584586 m_frameActivity = !m_threads.empty ();
585587
588+ // Resolve stopped broadcast scripts
589+ std::vector<Broadcast *> resolved;
590+
591+ for (const auto &[broadcast, senderThread] : m_broadcastSenders) {
592+ std::unordered_map<Broadcast *, std::vector<Script *>> *broadcastMap = nullptr ;
593+
594+ if (broadcast->isBackdropBroadcast ()) {
595+ // This broadcast belongs to a backdrop
596+ assert (m_broadcastMap.find (broadcast) == m_broadcastMap.cend ());
597+ broadcastMap = &m_backdropBroadcastMap;
598+ } else {
599+ // This is a regular broadcast
600+ assert (m_backdropBroadcastMap.find (broadcast) == m_backdropBroadcastMap.cend ());
601+ broadcastMap = &m_broadcastMap;
602+ }
603+
604+ bool found = false ;
605+
606+ if (broadcastMap->find (broadcast) != broadcastMap->cend ()) {
607+ const auto &scripts = (*broadcastMap)[broadcast];
608+
609+ for (auto script : scripts) {
610+ if (std::find_if (m_threads.begin (), m_threads.end (), [script](std::shared_ptr<VirtualMachine> thread) { return thread->script () == script; }) != m_threads.end ()) {
611+ found = true ;
612+ break ;
613+ }
614+ }
615+ }
616+
617+ if (!found) {
618+ VirtualMachine *th = senderThread;
619+
620+ if (std::find_if (m_threads.begin (), m_threads.end (), [th](std::shared_ptr<VirtualMachine> thread) { return thread.get () == th; }) != m_threads.end ())
621+ th->resolvePromise ();
622+
623+ resolved.push_back (broadcast);
624+ }
625+ }
626+
627+ for (Broadcast *broadcast : resolved)
628+ m_broadcastSenders.erase (broadcast);
629+
586630 m_redrawRequested = false ;
587631
588632 // Step threads
@@ -1160,7 +1204,30 @@ void Engine::addBroadcastScript(std::shared_ptr<Block> whenReceivedBlock, int fi
11601204
11611205void Engine::addBackdropChangeScript (std::shared_ptr<Block> hatBlock, int fieldId)
11621206{
1207+ Stage *stage = this ->stage ();
1208+
1209+ if (!stage)
1210+ return ;
1211+
1212+ // TODO: This assumes the first field holds the broadcast pointer, maybe this is not the best way (e. g. if an extension uses this method)
1213+ assert (hatBlock->fieldAt (0 ));
1214+ const std::string &backdropName = hatBlock->fieldAt (0 )->value ().toString ();
1215+ auto backdrop = stage->costumeAt (stage->findCostume (backdropName));
1216+ Broadcast *broadcast = backdrop->broadcast ();
1217+ assert (broadcast->isBackdropBroadcast ());
1218+
11631219 Script *script = m_scripts[hatBlock].get ();
1220+ auto it = m_backdropBroadcastMap.find (broadcast);
1221+
1222+ if (it != m_backdropBroadcastMap.cend ()) {
1223+ auto &scripts = it->second ;
1224+ auto scriptIt = std::find (scripts.begin (), scripts.end (), script);
1225+
1226+ if (scriptIt == scripts.end ())
1227+ scripts.push_back (script);
1228+ } else
1229+ m_backdropBroadcastMap[broadcast] = { script };
1230+
11641231 addHatToMap (m_backdropChangeHats, script);
11651232 addHatField (script, HatField::Backdrop, fieldId);
11661233}
@@ -1873,6 +1940,20 @@ void Engine::addRunningScript(std::shared_ptr<VirtualMachine> vm)
18731940 m_threads.push_back (vm);
18741941}
18751942
1943+ void Engine::addBroadcastPromise (Broadcast *broadcast, VirtualMachine *sender)
1944+ {
1945+ assert (broadcast);
1946+ assert (sender);
1947+
1948+ // Resolve broadcast promise if it's already running
1949+ auto it = m_broadcastSenders.find (broadcast);
1950+
1951+ if (it != m_broadcastSenders.cend () && std::find_if (m_threads.begin (), m_threads.end (), [&it](std::shared_ptr<VirtualMachine> thread) { return thread.get () == it->second ; }) != m_threads.end ())
1952+ it->second ->resolvePromise ();
1953+
1954+ m_broadcastSenders[broadcast] = sender;
1955+ }
1956+
18761957std::shared_ptr<VirtualMachine> Engine::pushThread (std::shared_ptr<Block> block, Target *target)
18771958{
18781959 // https://github.com/scratchfoundation/scratch-vm/blob/f1aa92fad79af17d9dd1c41eeeadca099339a9f1/src/engine/runtime.js#L1649-L1661
0 commit comments