Skip to content

Commit 4d00b3f

Browse files
authored
Merge pull request #573 from scratchcpp/broadcast_promises
Fix #563: Refactor broadcasts using promise API
2 parents 96800f9 + 2a56cb4 commit 4d00b3f

File tree

12 files changed

+274
-545
lines changed

12 files changed

+274
-545
lines changed

include/scratchcpp/iengine.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,13 +61,13 @@ class LIBSCRATCHCPP_EXPORT IEngine
6161
virtual VirtualMachine *startScript(std::shared_ptr<Block> topLevelBlock, Target *) = 0;
6262

6363
/*! Starts the scripts of the broadcast with the given index. */
64-
virtual void broadcast(int index) = 0;
64+
virtual void broadcast(int index, VirtualMachine *sender) = 0;
6565

6666
/*! Starts the scripts of the given broadcast. */
67-
virtual void broadcastByPtr(Broadcast *broadcast) = 0;
67+
virtual void broadcastByPtr(Broadcast *broadcast, VirtualMachine *sender) = 0;
6868

6969
/*! Starts the "when backdrop switches to" scripts for the given backdrop broadcast. */
70-
virtual void startBackdropScripts(Broadcast *broadcast) = 0;
70+
virtual void startBackdropScripts(Broadcast *broadcast, VirtualMachine *sender) = 0;
7171

7272
/*! Stops the given script. */
7373
virtual void stopScript(VirtualMachine *vm) = 0;

src/blocks/eventblocks.cpp

Lines changed: 7 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -96,43 +96,14 @@ void EventBlocks::compileWhenStageClicked(Compiler *compiler)
9696

9797
void EventBlocks::compileBroadcast(Compiler *compiler)
9898
{
99-
auto input = compiler->input(BROADCAST_INPUT);
100-
101-
if (input->type() != Input::Type::ObscuredShadow) {
102-
std::vector<int> broadcasts = compiler->engine()->findBroadcasts(input->primaryValue()->value().toString());
103-
104-
for (int index : broadcasts) {
105-
compiler->addConstValue(index);
106-
compiler->addFunctionCall(&broadcastByIndex);
107-
}
108-
} else {
109-
compiler->addInput(input);
110-
compiler->addFunctionCall(&broadcast);
111-
}
99+
compiler->addInput(BROADCAST_INPUT);
100+
compiler->addFunctionCall(&broadcast);
112101
}
113102

114103
void EventBlocks::compileBroadcastAndWait(Compiler *compiler)
115104
{
116-
auto input = compiler->input(BROADCAST_INPUT);
117-
118-
if (input->type() != Input::Type::ObscuredShadow) {
119-
std::vector<int> broadcasts = compiler->engine()->findBroadcasts(input->primaryValue()->value().toString());
120-
121-
for (int index : broadcasts) {
122-
compiler->addConstValue(index);
123-
compiler->addFunctionCall(&broadcastByIndexAndWait);
124-
}
125-
126-
for (int index : broadcasts) {
127-
compiler->addConstValue(index);
128-
compiler->addFunctionCall(&checkBroadcastByIndex);
129-
}
130-
} else {
131-
compiler->addInput(input);
132-
compiler->addFunctionCall(&broadcastAndWait);
133-
compiler->addInput(input);
134-
compiler->addFunctionCall(&checkBroadcast);
135-
}
105+
compiler->addInput(BROADCAST_INPUT);
106+
compiler->addFunctionCall(&broadcastAndWait);
136107
}
137108

138109
void EventBlocks::compileWhenBroadcastReceived(Compiler *compiler)
@@ -210,59 +181,23 @@ unsigned int EventBlocks::broadcast(VirtualMachine *vm)
210181
std::vector<int> broadcasts = vm->engine()->findBroadcasts(vm->getInput(0, 1)->toString());
211182

212183
for (int index : broadcasts)
213-
vm->engine()->broadcast(index);
184+
vm->engine()->broadcast(index, vm);
214185

215186
return 1;
216187
}
217188

218-
unsigned int EventBlocks::broadcastByIndex(VirtualMachine *vm)
219-
{
220-
vm->engine()->broadcast(vm->getInput(0, 1)->toLong());
221-
return 1;
222-
}
223-
224189
unsigned int EventBlocks::broadcastAndWait(VirtualMachine *vm)
225190
{
226191
std::vector<int> broadcasts = vm->engine()->findBroadcasts(vm->getInput(0, 1)->toString());
227192

228193
for (int index : broadcasts)
229-
vm->engine()->broadcast(index);
194+
vm->engine()->broadcast(index, vm);
230195

231-
return 1;
232-
}
233-
234-
unsigned int EventBlocks::broadcastByIndexAndWait(VirtualMachine *vm)
235-
{
236-
vm->engine()->broadcast(vm->getInput(0, 1)->toLong());
237-
return 1;
238-
}
239-
240-
unsigned int EventBlocks::checkBroadcast(VirtualMachine *vm)
241-
{
242-
bool running = false;
243-
244-
std::vector<int> broadcasts = vm->engine()->findBroadcasts(vm->getInput(0, 1)->toString());
245-
246-
for (int index : broadcasts) {
247-
if (vm->engine()->broadcastRunning(index)) {
248-
running = true;
249-
break;
250-
}
251-
}
252-
253-
if (running)
254-
vm->stop(true, true, true);
196+
vm->promise();
255197

256198
return 1;
257199
}
258200

259-
unsigned int EventBlocks::checkBroadcastByIndex(VirtualMachine *vm)
260-
{
261-
if (vm->engine()->broadcastRunning(vm->getInput(0, 1)->toLong()))
262-
vm->stop(true, true, true);
263-
return 1;
264-
}
265-
266201
unsigned int EventBlocks::whenLoudnessGreaterThanPredicate(VirtualMachine *vm)
267202
{
268203
if (!audioInput)

src/blocks/eventblocks.h

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,11 +56,7 @@ class EventBlocks : public IBlockSection
5656
static unsigned int whenTouchingObjectPredicate(VirtualMachine *vm);
5757

5858
static unsigned int broadcast(VirtualMachine *vm);
59-
static unsigned int broadcastByIndex(VirtualMachine *vm);
6059
static unsigned int broadcastAndWait(VirtualMachine *vm);
61-
static unsigned int broadcastByIndexAndWait(VirtualMachine *vm);
62-
static unsigned int checkBroadcast(VirtualMachine *vm);
63-
static unsigned int checkBroadcastByIndex(VirtualMachine *vm);
6460

6561
static unsigned int whenLoudnessGreaterThanPredicate(VirtualMachine *vm);
6662
static unsigned int whenTimerGreaterThanPredicate(VirtualMachine *vm);

src/blocks/looksblocks.cpp

Lines changed: 2 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -366,8 +366,6 @@ void LooksBlocks::compileSwitchBackdropToAndWait(Compiler *compiler)
366366
{
367367
compiler->addInput(BACKDROP);
368368
compiler->addFunctionCall(&switchBackdropToAndWait);
369-
compiler->addFunctionCall(&backdropNumber);
370-
compiler->addFunctionCall(&checkBackdropScripts);
371369
}
372370

373371
void LooksBlocks::compileNextBackdrop(Compiler *compiler)
@@ -894,7 +892,7 @@ void LooksBlocks::startBackdropScripts(VirtualMachine *vm, bool wait)
894892
{
895893
if (Stage *stage = vm->engine()->stage()) {
896894
if (stage->costumes().size() > 0)
897-
vm->engine()->startBackdropScripts(stage->currentCostume()->broadcast());
895+
vm->engine()->startBackdropScripts(stage->currentCostume()->broadcast(), vm);
898896
}
899897
}
900898

@@ -976,6 +974,7 @@ unsigned int LooksBlocks::switchBackdropToAndWait(VirtualMachine *vm)
976974
{
977975
switchBackdropToImpl(vm);
978976
startBackdropScripts(vm, true);
977+
vm->promise();
979978

980979
return 1;
981980
}
@@ -988,14 +987,6 @@ unsigned int LooksBlocks::nextBackdrop(VirtualMachine *vm)
988987
return 0;
989988
}
990989

991-
unsigned int LooksBlocks::nextBackdropAndWait(VirtualMachine *vm)
992-
{
993-
nextBackdropImpl(vm);
994-
startBackdropScripts(vm, true);
995-
996-
return 0;
997-
}
998-
999990
unsigned int LooksBlocks::previousBackdrop(VirtualMachine *vm)
1000991
{
1001992
previousBackdropImpl(vm);
@@ -1004,14 +995,6 @@ unsigned int LooksBlocks::previousBackdrop(VirtualMachine *vm)
1004995
return 0;
1005996
}
1006997

1007-
unsigned int LooksBlocks::previousBackdropAndWait(VirtualMachine *vm)
1008-
{
1009-
previousBackdropImpl(vm);
1010-
startBackdropScripts(vm, true);
1011-
1012-
return 0;
1013-
}
1014-
1015998
unsigned int LooksBlocks::randomBackdrop(VirtualMachine *vm)
1016999
{
10171000
randomBackdropImpl(vm);
@@ -1020,27 +1003,6 @@ unsigned int LooksBlocks::randomBackdrop(VirtualMachine *vm)
10201003
return 0;
10211004
}
10221005

1023-
unsigned int LooksBlocks::randomBackdropAndWait(VirtualMachine *vm)
1024-
{
1025-
randomBackdropImpl(vm);
1026-
startBackdropScripts(vm, true);
1027-
1028-
return 0;
1029-
}
1030-
1031-
unsigned int LooksBlocks::checkBackdropScripts(VirtualMachine *vm)
1032-
{
1033-
if (Stage *stage = vm->engine()->stage()) {
1034-
long index = vm->getInput(0, 1)->toLong() - 1;
1035-
assert(stage->costumes().size() == 0 || index >= 0);
1036-
1037-
if ((stage->costumes().size() > 0) && vm->engine()->broadcastByPtrRunning(stage->costumeAt(index)->broadcast()))
1038-
vm->stop(true, true, true);
1039-
}
1040-
1041-
return 1;
1042-
}
1043-
10441006
unsigned int LooksBlocks::goToFront(VirtualMachine *vm)
10451007
{
10461008
Sprite *sprite = dynamic_cast<Sprite *>(vm->target());

src/blocks/looksblocks.h

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -142,12 +142,8 @@ class LooksBlocks : public IBlockSection
142142
static unsigned int switchBackdropTo(VirtualMachine *vm);
143143
static unsigned int switchBackdropToAndWait(VirtualMachine *vm);
144144
static unsigned int nextBackdrop(VirtualMachine *vm);
145-
static unsigned int nextBackdropAndWait(VirtualMachine *vm);
146145
static unsigned int previousBackdrop(VirtualMachine *vm);
147-
static unsigned int previousBackdropAndWait(VirtualMachine *vm);
148146
static unsigned int randomBackdrop(VirtualMachine *vm);
149-
static unsigned int randomBackdropAndWait(VirtualMachine *vm);
150-
static unsigned int checkBackdropScripts(VirtualMachine *vm);
151147

152148
static unsigned int goToFront(VirtualMachine *vm);
153149
static unsigned int goToBack(VirtualMachine *vm);

src/engine/internal/engine.cpp

Lines changed: 85 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -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

454456
void 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

11611205
void 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+
18761957
std::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

src/engine/internal/engine.h

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,9 @@ class Engine : public IEngine
3535
void start() override;
3636
void stop() override;
3737
VirtualMachine *startScript(std::shared_ptr<Block> topLevelBlock, Target *target) override;
38-
void broadcast(int index) override;
39-
void broadcastByPtr(Broadcast *broadcast) override;
40-
void startBackdropScripts(Broadcast *broadcast) override;
38+
void broadcast(int index, VirtualMachine *sender) override;
39+
void broadcastByPtr(Broadcast *broadcast, VirtualMachine *sender) override;
40+
void startBackdropScripts(Broadcast *broadcast, VirtualMachine *sender) override;
4141
void stopScript(VirtualMachine *vm) override;
4242
void stopTarget(Target *target, VirtualMachine *exceptScript) override;
4343
void initClone(std::shared_ptr<Sprite> clone) override;
@@ -220,6 +220,8 @@ class Engine : public IEngine
220220
void updateFrameDuration();
221221
void addRunningScript(std::shared_ptr<VirtualMachine> vm);
222222

223+
void addBroadcastPromise(Broadcast *broadcast, VirtualMachine *sender);
224+
223225
std::shared_ptr<VirtualMachine> pushThread(std::shared_ptr<Block> block, Target *target);
224226
void stopThread(VirtualMachine *thread);
225227
std::shared_ptr<VirtualMachine> restartThread(std::shared_ptr<VirtualMachine> thread);
@@ -237,6 +239,8 @@ class Engine : public IEngine
237239
std::vector<std::shared_ptr<Target>> m_targets;
238240
std::vector<std::shared_ptr<Broadcast>> m_broadcasts;
239241
std::unordered_map<Broadcast *, std::vector<Script *>> m_broadcastMap;
242+
std::unordered_map<Broadcast *, std::vector<Script *>> m_backdropBroadcastMap;
243+
std::unordered_map<Broadcast *, VirtualMachine *> m_broadcastSenders; // used for resolving broadcast promises
240244
std::vector<std::shared_ptr<Monitor>> m_monitors;
241245
std::vector<std::string> m_extensions;
242246
std::vector<Target *> m_executableTargets; // sorted by layer (reverse order of execution)

0 commit comments

Comments
 (0)