Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions include/scratchcpp/iengine.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,13 +61,13 @@ class LIBSCRATCHCPP_EXPORT IEngine
virtual VirtualMachine *startScript(std::shared_ptr<Block> topLevelBlock, Target *) = 0;

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

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

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

/*! Stops the given script. */
virtual void stopScript(VirtualMachine *vm) = 0;
Expand Down
79 changes: 7 additions & 72 deletions src/blocks/eventblocks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -96,43 +96,14 @@ void EventBlocks::compileWhenStageClicked(Compiler *compiler)

void EventBlocks::compileBroadcast(Compiler *compiler)
{
auto input = compiler->input(BROADCAST_INPUT);

if (input->type() != Input::Type::ObscuredShadow) {
std::vector<int> broadcasts = compiler->engine()->findBroadcasts(input->primaryValue()->value().toString());

for (int index : broadcasts) {
compiler->addConstValue(index);
compiler->addFunctionCall(&broadcastByIndex);
}
} else {
compiler->addInput(input);
compiler->addFunctionCall(&broadcast);
}
compiler->addInput(BROADCAST_INPUT);
compiler->addFunctionCall(&broadcast);
}

void EventBlocks::compileBroadcastAndWait(Compiler *compiler)
{
auto input = compiler->input(BROADCAST_INPUT);

if (input->type() != Input::Type::ObscuredShadow) {
std::vector<int> broadcasts = compiler->engine()->findBroadcasts(input->primaryValue()->value().toString());

for (int index : broadcasts) {
compiler->addConstValue(index);
compiler->addFunctionCall(&broadcastByIndexAndWait);
}

for (int index : broadcasts) {
compiler->addConstValue(index);
compiler->addFunctionCall(&checkBroadcastByIndex);
}
} else {
compiler->addInput(input);
compiler->addFunctionCall(&broadcastAndWait);
compiler->addInput(input);
compiler->addFunctionCall(&checkBroadcast);
}
compiler->addInput(BROADCAST_INPUT);
compiler->addFunctionCall(&broadcastAndWait);
}

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

for (int index : broadcasts)
vm->engine()->broadcast(index);
vm->engine()->broadcast(index, vm);

return 1;
}

unsigned int EventBlocks::broadcastByIndex(VirtualMachine *vm)
{
vm->engine()->broadcast(vm->getInput(0, 1)->toLong());
return 1;
}

unsigned int EventBlocks::broadcastAndWait(VirtualMachine *vm)
{
std::vector<int> broadcasts = vm->engine()->findBroadcasts(vm->getInput(0, 1)->toString());

for (int index : broadcasts)
vm->engine()->broadcast(index);
vm->engine()->broadcast(index, vm);

return 1;
}

unsigned int EventBlocks::broadcastByIndexAndWait(VirtualMachine *vm)
{
vm->engine()->broadcast(vm->getInput(0, 1)->toLong());
return 1;
}

unsigned int EventBlocks::checkBroadcast(VirtualMachine *vm)
{
bool running = false;

std::vector<int> broadcasts = vm->engine()->findBroadcasts(vm->getInput(0, 1)->toString());

for (int index : broadcasts) {
if (vm->engine()->broadcastRunning(index)) {
running = true;
break;
}
}

if (running)
vm->stop(true, true, true);
vm->promise();

return 1;
}

unsigned int EventBlocks::checkBroadcastByIndex(VirtualMachine *vm)
{
if (vm->engine()->broadcastRunning(vm->getInput(0, 1)->toLong()))
vm->stop(true, true, true);
return 1;
}

unsigned int EventBlocks::whenLoudnessGreaterThanPredicate(VirtualMachine *vm)
{
if (!audioInput)
Expand Down
4 changes: 0 additions & 4 deletions src/blocks/eventblocks.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,7 @@ class EventBlocks : public IBlockSection
static unsigned int whenTouchingObjectPredicate(VirtualMachine *vm);

static unsigned int broadcast(VirtualMachine *vm);
static unsigned int broadcastByIndex(VirtualMachine *vm);
static unsigned int broadcastAndWait(VirtualMachine *vm);
static unsigned int broadcastByIndexAndWait(VirtualMachine *vm);
static unsigned int checkBroadcast(VirtualMachine *vm);
static unsigned int checkBroadcastByIndex(VirtualMachine *vm);

static unsigned int whenLoudnessGreaterThanPredicate(VirtualMachine *vm);
static unsigned int whenTimerGreaterThanPredicate(VirtualMachine *vm);
Expand Down
42 changes: 2 additions & 40 deletions src/blocks/looksblocks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -366,8 +366,6 @@ void LooksBlocks::compileSwitchBackdropToAndWait(Compiler *compiler)
{
compiler->addInput(BACKDROP);
compiler->addFunctionCall(&switchBackdropToAndWait);
compiler->addFunctionCall(&backdropNumber);
compiler->addFunctionCall(&checkBackdropScripts);
}

void LooksBlocks::compileNextBackdrop(Compiler *compiler)
Expand Down Expand Up @@ -894,7 +892,7 @@ void LooksBlocks::startBackdropScripts(VirtualMachine *vm, bool wait)
{
if (Stage *stage = vm->engine()->stage()) {
if (stage->costumes().size() > 0)
vm->engine()->startBackdropScripts(stage->currentCostume()->broadcast());
vm->engine()->startBackdropScripts(stage->currentCostume()->broadcast(), vm);
}
}

Expand Down Expand Up @@ -976,6 +974,7 @@ unsigned int LooksBlocks::switchBackdropToAndWait(VirtualMachine *vm)
{
switchBackdropToImpl(vm);
startBackdropScripts(vm, true);
vm->promise();

return 1;
}
Expand All @@ -988,14 +987,6 @@ unsigned int LooksBlocks::nextBackdrop(VirtualMachine *vm)
return 0;
}

unsigned int LooksBlocks::nextBackdropAndWait(VirtualMachine *vm)
{
nextBackdropImpl(vm);
startBackdropScripts(vm, true);

return 0;
}

unsigned int LooksBlocks::previousBackdrop(VirtualMachine *vm)
{
previousBackdropImpl(vm);
Expand All @@ -1004,14 +995,6 @@ unsigned int LooksBlocks::previousBackdrop(VirtualMachine *vm)
return 0;
}

unsigned int LooksBlocks::previousBackdropAndWait(VirtualMachine *vm)
{
previousBackdropImpl(vm);
startBackdropScripts(vm, true);

return 0;
}

unsigned int LooksBlocks::randomBackdrop(VirtualMachine *vm)
{
randomBackdropImpl(vm);
Expand All @@ -1020,27 +1003,6 @@ unsigned int LooksBlocks::randomBackdrop(VirtualMachine *vm)
return 0;
}

unsigned int LooksBlocks::randomBackdropAndWait(VirtualMachine *vm)
{
randomBackdropImpl(vm);
startBackdropScripts(vm, true);

return 0;
}

unsigned int LooksBlocks::checkBackdropScripts(VirtualMachine *vm)
{
if (Stage *stage = vm->engine()->stage()) {
long index = vm->getInput(0, 1)->toLong() - 1;
assert(stage->costumes().size() == 0 || index >= 0);

if ((stage->costumes().size() > 0) && vm->engine()->broadcastByPtrRunning(stage->costumeAt(index)->broadcast()))
vm->stop(true, true, true);
}

return 1;
}

unsigned int LooksBlocks::goToFront(VirtualMachine *vm)
{
Sprite *sprite = dynamic_cast<Sprite *>(vm->target());
Expand Down
4 changes: 0 additions & 4 deletions src/blocks/looksblocks.h
Original file line number Diff line number Diff line change
Expand Up @@ -142,12 +142,8 @@ class LooksBlocks : public IBlockSection
static unsigned int switchBackdropTo(VirtualMachine *vm);
static unsigned int switchBackdropToAndWait(VirtualMachine *vm);
static unsigned int nextBackdrop(VirtualMachine *vm);
static unsigned int nextBackdropAndWait(VirtualMachine *vm);
static unsigned int previousBackdrop(VirtualMachine *vm);
static unsigned int previousBackdropAndWait(VirtualMachine *vm);
static unsigned int randomBackdrop(VirtualMachine *vm);
static unsigned int randomBackdropAndWait(VirtualMachine *vm);
static unsigned int checkBackdropScripts(VirtualMachine *vm);

static unsigned int goToFront(VirtualMachine *vm);
static unsigned int goToBack(VirtualMachine *vm);
Expand Down
89 changes: 85 additions & 4 deletions src/engine/internal/engine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -433,22 +433,24 @@ VirtualMachine *Engine::startScript(std::shared_ptr<Block> topLevelBlock, Target
return pushThread(topLevelBlock, target).get();
}

void Engine::broadcast(int index)
void Engine::broadcast(int index, VirtualMachine *sender)
{
if (index < 0 || index >= m_broadcasts.size())
return;

broadcastByPtr(m_broadcasts[index].get());
broadcastByPtr(m_broadcasts[index].get(), sender);
}

void Engine::broadcastByPtr(Broadcast *broadcast)
void Engine::broadcastByPtr(Broadcast *broadcast, VirtualMachine *sender)
{
startHats(HatType::BroadcastReceived, { { HatField::BroadcastOption, broadcast } }, nullptr);
addBroadcastPromise(broadcast, sender);
}

void Engine::startBackdropScripts(Broadcast *broadcast)
void Engine::startBackdropScripts(Broadcast *broadcast, VirtualMachine *sender)
{
startHats(HatType::BackdropChanged, { { HatField::Backdrop, broadcast->name() } }, nullptr);
addBroadcastPromise(broadcast, sender);
}

void Engine::stopScript(VirtualMachine *vm)
Expand Down Expand Up @@ -583,6 +585,48 @@ void Engine::step()
// Check running threads (must be done here)
m_frameActivity = !m_threads.empty();

// Resolve stopped broadcast scripts
std::vector<Broadcast *> resolved;

for (const auto &[broadcast, senderThread] : m_broadcastSenders) {
std::unordered_map<Broadcast *, std::vector<Script *>> *broadcastMap = nullptr;

if (broadcast->isBackdropBroadcast()) {
// This broadcast belongs to a backdrop
assert(m_broadcastMap.find(broadcast) == m_broadcastMap.cend());
broadcastMap = &m_backdropBroadcastMap;
} else {
// This is a regular broadcast
assert(m_backdropBroadcastMap.find(broadcast) == m_backdropBroadcastMap.cend());
broadcastMap = &m_broadcastMap;
}

bool found = false;

if (broadcastMap->find(broadcast) != broadcastMap->cend()) {
const auto &scripts = (*broadcastMap)[broadcast];

for (auto script : scripts) {
if (std::find_if(m_threads.begin(), m_threads.end(), [script](std::shared_ptr<VirtualMachine> thread) { return thread->script() == script; }) != m_threads.end()) {
found = true;
break;
}
}
}

if (!found) {
VirtualMachine *th = senderThread;

if (std::find_if(m_threads.begin(), m_threads.end(), [th](std::shared_ptr<VirtualMachine> thread) { return thread.get() == th; }) != m_threads.end())
th->resolvePromise();

resolved.push_back(broadcast);
}
}

for (Broadcast *broadcast : resolved)
m_broadcastSenders.erase(broadcast);

m_redrawRequested = false;

// Step threads
Expand Down Expand Up @@ -1160,7 +1204,30 @@ void Engine::addBroadcastScript(std::shared_ptr<Block> whenReceivedBlock, int fi

void Engine::addBackdropChangeScript(std::shared_ptr<Block> hatBlock, int fieldId)
{
Stage *stage = this->stage();

if (!stage)
return;

// 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)
assert(hatBlock->fieldAt(0));
const std::string &backdropName = hatBlock->fieldAt(0)->value().toString();
auto backdrop = stage->costumeAt(stage->findCostume(backdropName));
Broadcast *broadcast = backdrop->broadcast();
assert(broadcast->isBackdropBroadcast());

Script *script = m_scripts[hatBlock].get();
auto it = m_backdropBroadcastMap.find(broadcast);

if (it != m_backdropBroadcastMap.cend()) {
auto &scripts = it->second;
auto scriptIt = std::find(scripts.begin(), scripts.end(), script);

if (scriptIt == scripts.end())
scripts.push_back(script);
} else
m_backdropBroadcastMap[broadcast] = { script };

addHatToMap(m_backdropChangeHats, script);
addHatField(script, HatField::Backdrop, fieldId);
}
Expand Down Expand Up @@ -1873,6 +1940,20 @@ void Engine::addRunningScript(std::shared_ptr<VirtualMachine> vm)
m_threads.push_back(vm);
}

void Engine::addBroadcastPromise(Broadcast *broadcast, VirtualMachine *sender)
{
assert(broadcast);
assert(sender);

// Resolve broadcast promise if it's already running
auto it = m_broadcastSenders.find(broadcast);

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())
it->second->resolvePromise();

m_broadcastSenders[broadcast] = sender;
}

std::shared_ptr<VirtualMachine> Engine::pushThread(std::shared_ptr<Block> block, Target *target)
{
// https://github.com/scratchfoundation/scratch-vm/blob/f1aa92fad79af17d9dd1c41eeeadca099339a9f1/src/engine/runtime.js#L1649-L1661
Expand Down
10 changes: 7 additions & 3 deletions src/engine/internal/engine.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,9 @@ class Engine : public IEngine
void start() override;
void stop() override;
VirtualMachine *startScript(std::shared_ptr<Block> topLevelBlock, Target *target) override;
void broadcast(int index) override;
void broadcastByPtr(Broadcast *broadcast) override;
void startBackdropScripts(Broadcast *broadcast) override;
void broadcast(int index, VirtualMachine *sender) override;
void broadcastByPtr(Broadcast *broadcast, VirtualMachine *sender) override;
void startBackdropScripts(Broadcast *broadcast, VirtualMachine *sender) override;
void stopScript(VirtualMachine *vm) override;
void stopTarget(Target *target, VirtualMachine *exceptScript) override;
void initClone(std::shared_ptr<Sprite> clone) override;
Expand Down Expand Up @@ -220,6 +220,8 @@ class Engine : public IEngine
void updateFrameDuration();
void addRunningScript(std::shared_ptr<VirtualMachine> vm);

void addBroadcastPromise(Broadcast *broadcast, VirtualMachine *sender);

std::shared_ptr<VirtualMachine> pushThread(std::shared_ptr<Block> block, Target *target);
void stopThread(VirtualMachine *thread);
std::shared_ptr<VirtualMachine> restartThread(std::shared_ptr<VirtualMachine> thread);
Expand All @@ -237,6 +239,8 @@ class Engine : public IEngine
std::vector<std::shared_ptr<Target>> m_targets;
std::vector<std::shared_ptr<Broadcast>> m_broadcasts;
std::unordered_map<Broadcast *, std::vector<Script *>> m_broadcastMap;
std::unordered_map<Broadcast *, std::vector<Script *>> m_backdropBroadcastMap;
std::unordered_map<Broadcast *, VirtualMachine *> m_broadcastSenders; // used for resolving broadcast promises
std::vector<std::shared_ptr<Monitor>> m_monitors;
std::vector<std::string> m_extensions;
std::vector<Target *> m_executableTargets; // sorted by layer (reverse order of execution)
Expand Down
Loading