Skip to content

Commit a9084f0

Browse files
committed
Refactor broadcasts
They now behave (almost) the same as in Scratch, but there are some details which will be fixed later.
1 parent 24925ad commit a9084f0

File tree

6 files changed

+85
-23
lines changed

6 files changed

+85
-23
lines changed

include/scratchcpp/iengine.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ class LIBSCRATCHCPP_EXPORT IEngine
5858
virtual void startScript(std::shared_ptr<Block> topLevelBlock, std::shared_ptr<Target> target) = 0;
5959

6060
/*! Starts the script of the broadcast with the given index. */
61-
virtual void broadcast(unsigned int index, VirtualMachine *sourceScript) = 0;
61+
virtual void broadcast(unsigned int index, VirtualMachine *sourceScript, bool wait = false) = 0;
6262

6363
/*! Stops the given script. */
6464
virtual void stopScript(VirtualMachine *vm) = 0;
@@ -79,7 +79,7 @@ class LIBSCRATCHCPP_EXPORT IEngine
7979
virtual void run() = 0;
8080

8181
/*! Returns true if there are any running script of the broadcast with the given index. */
82-
virtual bool broadcastRunning(unsigned int index) = 0;
82+
virtual bool broadcastRunning(unsigned int index, VirtualMachine *sourceScript) = 0;
8383

8484
/*!
8585
* Call this from a block implementation to force a "screen refresh".

src/blocks/eventblocks.cpp

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,11 +48,11 @@ void EventBlocks::compileBroadcastAndWait(Compiler *compiler)
4848
compiler->addInput(input);
4949
if (input->type() != Input::Type::ObscuredShadow) {
5050
input->primaryValue()->setValue(compiler->engine()->findBroadcast(input->primaryValue()->value().toString()));
51-
compiler->addFunctionCall(&broadcastByIndex);
51+
compiler->addFunctionCall(&broadcastByIndexAndWait);
5252
compiler->addInput(input);
5353
compiler->addFunctionCall(&checkBroadcastByIndex);
5454
} else {
55-
compiler->addFunctionCall(&broadcast);
55+
compiler->addFunctionCall(&broadcastAndWait);
5656
compiler->addInput(input);
5757
compiler->addFunctionCall(&checkBroadcast);
5858
}
@@ -77,16 +77,28 @@ unsigned int EventBlocks::broadcastByIndex(VirtualMachine *vm)
7777
return 1;
7878
}
7979

80+
unsigned int EventBlocks::broadcastAndWait(VirtualMachine *vm)
81+
{
82+
vm->engine()->broadcast(vm->engine()->findBroadcast(vm->getInput(0, 1)->toString()), vm, true);
83+
return 1;
84+
}
85+
86+
unsigned int EventBlocks::broadcastByIndexAndWait(VirtualMachine *vm)
87+
{
88+
vm->engine()->broadcast(vm->getInput(0, 1)->toLong(), vm, true);
89+
return 1;
90+
}
91+
8092
unsigned int EventBlocks::checkBroadcast(VirtualMachine *vm)
8193
{
82-
if (vm->engine()->broadcastRunning(vm->engine()->findBroadcast(vm->getInput(0, 1)->toString())))
94+
if (vm->engine()->broadcastRunning(vm->engine()->findBroadcast(vm->getInput(0, 1)->toString()), vm))
8395
vm->stop(true, true, true);
8496
return 1;
8597
}
8698

8799
unsigned int EventBlocks::checkBroadcastByIndex(VirtualMachine *vm)
88100
{
89-
if (vm->engine()->broadcastRunning(vm->getInput(0, 1)->toLong()))
101+
if (vm->engine()->broadcastRunning(vm->getInput(0, 1)->toLong(), vm))
90102
vm->stop(true, true, true);
91103
return 1;
92104
}

src/blocks/eventblocks.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ class EventBlocks : public IBlockSection
3535
private:
3636
static unsigned int broadcast(VirtualMachine *vm);
3737
static unsigned int broadcastByIndex(VirtualMachine *vm);
38+
static unsigned int broadcastAndWait(VirtualMachine *vm);
39+
static unsigned int broadcastByIndexAndWait(VirtualMachine *vm);
3840
static unsigned int checkBroadcast(VirtualMachine *vm);
3941
static unsigned int checkBroadcastByIndex(VirtualMachine *vm);
4042
};

src/engine/engine.cpp

Lines changed: 60 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -126,8 +126,19 @@ void Engine::frame()
126126
do {
127127
script->run();
128128
if (script->atEnd()) {
129-
for (auto &[key, value] : m_broadcastMap)
130-
value.erase(std::remove(value.begin(), value.end(), script->script()), value.end());
129+
for (auto &[key, value] : m_runningBroadcastMap) {
130+
size_t index = 0;
131+
132+
for (const auto &pair : value) {
133+
if (pair.second == script.get()) {
134+
value.erase(value.begin() + index);
135+
break;
136+
}
137+
138+
index++;
139+
}
140+
}
141+
131142
m_scriptsToRemove.push_back(script.get());
132143
}
133144
} while (!script->atEnd() && !m_breakFrame);
@@ -180,24 +191,48 @@ void Engine::startScript(std::shared_ptr<Block> topLevelBlock, std::shared_ptr<T
180191
}
181192
}
182193

183-
void libscratchcpp::Engine::broadcast(unsigned int index, VirtualMachine *sourceScript)
194+
void libscratchcpp::Engine::broadcast(unsigned int index, VirtualMachine *sourceScript, bool wait)
184195
{
196+
bool previousSkipFrame = m_skipFrame;
197+
skipFrame();
185198
const std::vector<Script *> &scripts = m_broadcastMap[index];
199+
186200
for (auto script : scripts) {
187-
size_t index = -1;
188-
for (size_t i = 0; i < m_runningScripts.size(); i++) {
201+
long scriptIndex = -1;
202+
for (long i = 0; i < m_runningScripts.size(); i++) {
189203
if (m_runningScripts[i]->script() == script) {
190-
index = i;
204+
scriptIndex = i;
191205
break;
192206
}
193207
}
194-
if (index != -1) {
208+
209+
if (scriptIndex != -1) {
195210
// Reset the script if it's already running
196-
m_runningScripts[index]->reset();
197-
if (script == sourceScript->script())
211+
auto vm = m_runningScripts[scriptIndex];
212+
vm->reset();
213+
214+
// Remove the script from scripts to remove because it's going to run again
215+
m_scriptsToRemove.erase(std::remove(m_scriptsToRemove.begin(), m_scriptsToRemove.end(), vm.get()), m_scriptsToRemove.end());
216+
assert(std::find(m_scriptsToRemove.begin(), m_scriptsToRemove.end(), m_runningScripts[scriptIndex].get()) == m_scriptsToRemove.end());
217+
218+
auto &scripts = m_runningBroadcastMap[index];
219+
220+
for (auto &pair : scripts) {
221+
if (pair.second->script() == script)
222+
pair.first = sourceScript;
223+
}
224+
225+
if (script == sourceScript->script()) {
198226
sourceScript->stop(false, true);
227+
228+
if (!previousSkipFrame && !wait)
229+
m_skipFrame = false;
230+
} else
231+
sourceScript->stop(true, true);
199232
} else {
200-
m_runningScripts.push_back(script->start());
233+
auto vm = script->start();
234+
m_runningScripts.push_back(vm);
235+
m_runningBroadcastMap[index].push_back({ sourceScript, vm.get() });
201236
}
202237
}
203238
}
@@ -251,9 +286,16 @@ void Engine::run()
251286
}
252287
}
253288

254-
bool Engine::broadcastRunning(unsigned int index)
289+
bool Engine::broadcastRunning(unsigned int index, VirtualMachine *sourceScript)
255290
{
256-
return !m_broadcastMap[index].empty();
291+
const auto &scripts = m_runningBroadcastMap[index];
292+
293+
for (const auto &pair : scripts) {
294+
if (pair.first == sourceScript)
295+
return true;
296+
}
297+
298+
return false;
257299
}
258300

259301
void Engine::breakFrame()
@@ -377,8 +419,13 @@ void libscratchcpp::Engine::addBroadcastScript(std::shared_ptr<Block> whenReceiv
377419
if (m_broadcastMap.count(id) == 1) {
378420
std::vector<Script *> &scripts = m_broadcastMap[id];
379421
scripts.push_back(m_scripts[whenReceivedBlock].get());
380-
} else
422+
} else {
381423
m_broadcastMap[id] = { m_scripts[whenReceivedBlock].get() };
424+
425+
// Create a vector of running scripts for this broadcast
426+
// so we don't need to check if it's there
427+
m_runningBroadcastMap[id] = {};
428+
}
382429
}
383430

384431
const std::vector<std::shared_ptr<Target>> &Engine::targets() const

src/engine/engine.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,12 @@ class Engine : public IEngine
3030
void start() override;
3131
void stop() override;
3232
void startScript(std::shared_ptr<Block> topLevelBlock, std::shared_ptr<Target> target) override;
33-
void broadcast(unsigned int index, VirtualMachine *sourceScript) override;
33+
void broadcast(unsigned int index, VirtualMachine *sourceScript, bool wait = false) override;
3434
void stopScript(VirtualMachine *vm) override;
3535
void stopTarget(Target *target, VirtualMachine *exceptScript) override;
3636
void run() override;
3737

38-
bool broadcastRunning(unsigned int index) override;
38+
bool broadcastRunning(unsigned int index, VirtualMachine *sourceScript) override;
3939

4040
void breakFrame() override;
4141
bool breakingCurrentFrame() override;
@@ -83,6 +83,7 @@ class Engine : public IEngine
8383
std::vector<std::shared_ptr<Target>> m_targets;
8484
std::vector<std::shared_ptr<Broadcast>> m_broadcasts;
8585
std::unordered_map<unsigned int, std::vector<Script *>> m_broadcastMap;
86+
std::unordered_map<unsigned int, std::vector<std::pair<VirtualMachine *, VirtualMachine *>>> m_runningBroadcastMap; // source script, "when received" script
8687
std::vector<std::string> m_extensions;
8788
std::vector<std::shared_ptr<VirtualMachine>> m_runningScripts;
8889
std::vector<VirtualMachine *> m_scriptsToRemove;

test/mocks/enginemock.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,12 @@ class EngineMock : public IEngine
1818
MOCK_METHOD(void, start, (), (override));
1919
MOCK_METHOD(void, stop, (), (override));
2020
MOCK_METHOD(void, startScript, (std::shared_ptr<Block>, std::shared_ptr<Target>), (override));
21-
MOCK_METHOD(void, broadcast, (unsigned int, VirtualMachine *), (override));
21+
MOCK_METHOD(void, broadcast, (unsigned int, VirtualMachine *, bool), (override));
2222
MOCK_METHOD(void, stopScript, (VirtualMachine *), (override));
2323
MOCK_METHOD(void, stopTarget, (Target *, VirtualMachine *), (override));
2424
MOCK_METHOD(void, run, (), (override));
2525

26-
MOCK_METHOD(bool, broadcastRunning, (unsigned int), (override));
26+
MOCK_METHOD(bool, broadcastRunning, (unsigned int, VirtualMachine *), (override));
2727

2828
MOCK_METHOD(void, breakFrame, (), (override));
2929
MOCK_METHOD(bool, breakingCurrentFrame, (), (override));

0 commit comments

Comments
 (0)