Skip to content

Commit f61b626

Browse files
committed
Refactor script starting logic
1 parent 670b1d6 commit f61b626

File tree

6 files changed

+114
-72
lines changed

6 files changed

+114
-72
lines changed

include/scratchcpp/iengine.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ class LIBSCRATCHCPP_EXPORT IEngine
5454
virtual void stop() = 0;
5555

5656
/*! Starts a script with the given top level block as the given Target (a sprite or the stage). */
57-
virtual void startScript(std::shared_ptr<Block> topLevelBlock, std::shared_ptr<Target> target) = 0;
57+
virtual VirtualMachine *startScript(std::shared_ptr<Block> topLevelBlock, Target *) = 0;
5858

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

src/engine/internal/engine.cpp

Lines changed: 78 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ void Engine::start()
159159
for (auto target : m_targets) {
160160
auto gfBlocks = target->greenFlagBlocks();
161161
for (auto block : gfBlocks)
162-
startScript(block, target);
162+
startScript(block, target.get());
163163
}
164164

165165
m_eventLoopMutex.unlock();
@@ -171,23 +171,27 @@ void Engine::stop()
171171
deleteClones();
172172
}
173173

174-
void Engine::startScript(std::shared_ptr<Block> topLevelBlock, std::shared_ptr<Target> target)
174+
VirtualMachine *Engine::startScript(std::shared_ptr<Block> topLevelBlock, Target *target)
175175
{
176176
if (!topLevelBlock) {
177177
std::cout << "warning: starting a script with a null top level block (nothing will happen)" << std::endl;
178-
return;
178+
return nullptr;
179179
}
180180

181181
if (!target) {
182182
std::cout << "error: scripts must be started by a target";
183183
assert(false);
184-
return;
184+
return nullptr;
185185
}
186186

187187
if (topLevelBlock->next()) {
188188
auto script = m_scripts[topLevelBlock];
189-
addRunningScript(script->start());
189+
std::shared_ptr<VirtualMachine> vm = script->start(target);
190+
addRunningScript(vm);
191+
return vm.get();
190192
}
193+
194+
return nullptr;
191195
}
192196

193197
void Engine::broadcast(unsigned int index, VirtualMachine *sourceScript, bool wait)
@@ -201,61 +205,22 @@ void Engine::broadcast(unsigned int index, VirtualMachine *sourceScript, bool wa
201205
void Engine::broadcastByPtr(Broadcast *broadcast, VirtualMachine *sourceScript, bool wait)
202206
{
203207
const std::vector<Script *> &scripts = m_broadcastMap[broadcast];
208+
auto &runningBroadcasts = m_runningBroadcastMap[broadcast];
204209

205210
for (auto script : scripts) {
206-
std::vector<VirtualMachine *> runningBroadcastScripts;
207-
208-
for (const auto &[target, targetScripts] : m_runningScripts) {
209-
for (auto vm : targetScripts) {
210-
if (vm->script() == script) {
211-
runningBroadcastScripts.push_back(vm.get());
212-
}
213-
}
211+
for (auto &pair : runningBroadcasts) {
212+
if (pair.second->script() == script)
213+
pair.first = sourceScript;
214214
}
215215

216-
// Reset running scripts
217-
for (VirtualMachine *vm : runningBroadcastScripts) {
218-
vm->reset();
219-
220-
// Remove the script from scripts to remove because it's going to run again
221-
m_scriptsToRemove.erase(std::remove(m_scriptsToRemove.begin(), m_scriptsToRemove.end(), vm), m_scriptsToRemove.end());
222-
assert(std::find(m_scriptsToRemove.begin(), m_scriptsToRemove.end(), vm) == m_scriptsToRemove.end());
223-
224-
auto &scripts = m_runningBroadcastMap[broadcast];
225-
226-
for (auto &pair : scripts) {
227-
if (pair.second->script() == script)
228-
pair.first = sourceScript;
229-
}
230-
231-
if (script == sourceScript->script())
232-
sourceScript->stop(false, !wait); // source script is the broadcast script
233-
}
234-
235-
// Start scripts which are not running
236-
Target *root = script->target();
237-
std::vector<Target *> targets = { root };
238-
239-
if (!root->isStage()) {
240-
Sprite *sprite = dynamic_cast<Sprite *>(root);
241-
assert(sprite);
242-
assert(!sprite->isClone());
243-
const auto &children = sprite->clones();
244-
245-
for (auto child : children)
246-
targets.push_back(child.get());
247-
}
216+
if (script == sourceScript->script())
217+
sourceScript->stop(false, !wait); // source script is the broadcast script
218+
}
248219

249-
for (Target *target : targets) {
250-
auto it = std::find_if(runningBroadcastScripts.begin(), runningBroadcastScripts.end(), [target](VirtualMachine *vm) { return vm->target() == target; });
220+
std::vector<VirtualMachine *> startedScripts = startHats(scripts);
251221

252-
if (it == runningBroadcastScripts.end()) {
253-
auto vm = script->start(target);
254-
addRunningScript(vm);
255-
m_runningBroadcastMap[broadcast].push_back({ sourceScript, vm.get() });
256-
}
257-
}
258-
}
222+
for (VirtualMachine *vm : startedScripts)
223+
runningBroadcasts.push_back({ sourceScript, vm });
259224
}
260225

261226
void Engine::stopScript(VirtualMachine *vm)
@@ -565,12 +530,12 @@ void Engine::setKeyState(const KeyEvent &event, bool pressed)
565530
auto it = m_whenKeyPressedScripts.find(event.name());
566531

567532
if (it != m_whenKeyPressedScripts.cend())
568-
startWhenKeyPressedScripts(it->second);
533+
startHats(it->second);
569534

570535
it = m_whenKeyPressedScripts.find("any");
571536

572537
if (it != m_whenKeyPressedScripts.cend())
573-
startWhenKeyPressedScripts(it->second);
538+
startHats(it->second);
574539
}
575540
}
576541

@@ -583,7 +548,7 @@ void Engine::setAnyKeyPressed(bool pressed)
583548
auto it = m_whenKeyPressedScripts.find("any");
584549

585550
if (it != m_whenKeyPressedScripts.cend())
586-
startWhenKeyPressedScripts(it->second);
551+
startHats(it->second);
587552
}
588553
}
589554

@@ -1249,18 +1214,66 @@ void Engine::addRunningScript(std::shared_ptr<VirtualMachine> vm)
12491214
}
12501215
}
12511216

1252-
void Engine::startWhenKeyPressedScripts(const std::vector<Script *> &scripts)
1217+
std::vector<VirtualMachine *> Engine::startHats(const std::vector<Script *> &scripts)
12531218
{
1219+
std::vector<VirtualMachine *> startedScripts;
1220+
12541221
for (auto script : scripts) {
1255-
std::shared_ptr<Block> block = nullptr;
1222+
std::vector<VirtualMachine *> runningScripts;
1223+
1224+
for (const auto &[target, targetScripts] : m_runningScripts) {
1225+
for (auto vm : targetScripts) {
1226+
if (vm->script() == script) {
1227+
runningScripts.push_back(vm.get());
1228+
}
1229+
}
1230+
}
1231+
1232+
// Reset running scripts
1233+
for (VirtualMachine *vm : runningScripts) {
1234+
vm->reset();
1235+
1236+
// Remove the script from scripts to remove because it's going to run again
1237+
m_scriptsToRemove.erase(std::remove(m_scriptsToRemove.begin(), m_scriptsToRemove.end(), vm), m_scriptsToRemove.end());
1238+
assert(std::find(m_scriptsToRemove.begin(), m_scriptsToRemove.end(), vm) == m_scriptsToRemove.end());
1239+
}
1240+
1241+
// Start scripts which are not running
1242+
Target *root = script->target();
1243+
std::vector<Target *> targets = { root };
1244+
1245+
if (!root->isStage()) {
1246+
Sprite *sprite = dynamic_cast<Sprite *>(root);
1247+
assert(sprite);
1248+
assert(!sprite->isClone());
1249+
const auto &children = sprite->clones();
12561250

1257-
for (const auto &[b, s] : m_scripts) {
1258-
if (s.get() == script)
1259-
block = b;
1251+
for (auto child : children)
1252+
targets.push_back(child.get());
12601253
}
12611254

1262-
assert(block);
1263-
assert(std::find_if(m_targets.begin(), m_targets.end(), [script](std::shared_ptr<Target> target) { return script->target() == target.get(); }) != m_targets.end());
1264-
startScript(block, *std::find_if(m_targets.begin(), m_targets.end(), [script](std::shared_ptr<Target> target) { return script->target() == target.get(); }));
1255+
for (Target *target : targets) {
1256+
std::shared_ptr<Block> block = nullptr;
1257+
1258+
if (!runningScripts.empty()) {
1259+
auto it = std::find_if(runningScripts.begin(), runningScripts.end(), [target, script](VirtualMachine *vm) { return (vm->target() == target) && (vm->script() == script); });
1260+
1261+
if (it != runningScripts.end())
1262+
continue; // skip the script because it was already running and was reset
1263+
}
1264+
1265+
for (const auto &[b, s] : m_scripts) {
1266+
if (s.get() == script) {
1267+
block = b;
1268+
break;
1269+
}
1270+
}
1271+
1272+
assert(block);
1273+
assert(std::find_if(m_targets.begin(), m_targets.end(), [script](std::shared_ptr<Target> target) { return script->target() == target.get(); }) != m_targets.end());
1274+
startedScripts.push_back(startScript(block, target));
1275+
}
12651276
}
1277+
1278+
return startedScripts;
12661279
}

src/engine/internal/engine.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ class Engine : public IEngine
3232

3333
void start() override;
3434
void stop() override;
35-
void startScript(std::shared_ptr<Block> topLevelBlock, std::shared_ptr<Target> target) override;
35+
VirtualMachine *startScript(std::shared_ptr<Block> topLevelBlock, Target *target) override;
3636
void broadcast(unsigned int index, VirtualMachine *sourceScript, bool wait = false) override;
3737
void broadcastByPtr(Broadcast *broadcast, VirtualMachine *sourceScript, bool wait = false) override;
3838
void stopScript(VirtualMachine *vm) override;
@@ -153,7 +153,7 @@ class Engine : public IEngine
153153

154154
void updateFrameDuration();
155155
void addRunningScript(std::shared_ptr<VirtualMachine> vm);
156-
void startWhenKeyPressedScripts(const std::vector<Script *> &scripts);
156+
std::vector<VirtualMachine *> startHats(const std::vector<Script *> &scripts);
157157

158158
std::unordered_map<std::shared_ptr<IBlockSection>, std::unique_ptr<BlockSectionContainer>> m_sections;
159159
std::vector<std::shared_ptr<Target>> m_targets;

test/engine/engine_test.cpp

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,7 @@ TEST(EngineTest, ExecutionOrder)
183183
ASSERT_EQ((*list)[9].toString(), "Sprite1 1 msg");
184184
ASSERT_EQ((*list)[10].toString(), "Sprite1 2 msg");
185185
ASSERT_EQ((*list)[11].toString(), "Sprite1 3 msg");
186+
ASSERT_EQ((*list)[12].toString(), "Stage msg");
186187
}
187188

188189
TEST(EngineTest, KeyState)
@@ -479,7 +480,7 @@ TEST(EngineTest, WhenKeyPressed)
479480
ASSERT_VAR(stage, "right_arrow_pressed");
480481
ASSERT_EQ(GET_VAR(stage, "right_arrow_pressed")->value().toInt(), 2);
481482
ASSERT_VAR(stage, "any_key_pressed");
482-
ASSERT_EQ(GET_VAR(stage, "any_key_pressed")->value().toInt(), 9);
483+
ASSERT_EQ(GET_VAR(stage, "any_key_pressed")->value().toInt(), 8);
483484
ASSERT_VAR(stage, "a_pressed");
484485
ASSERT_EQ(GET_VAR(stage, "a_pressed")->value().toInt(), 1);
485486
ASSERT_VAR(stage, "x_pressed");
@@ -1144,7 +1145,6 @@ TEST(EngineTest, CloneLimit)
11441145
ASSERT_EQ(engine->cloneCount(), 0);
11451146
}
11461147

1147-
// TODO: Uncomment this after fixing #256 and #257
11481148
TEST(EngineTest, BackdropBroadcasts)
11491149
{
11501150
// TODO: Set "infinite" FPS (#254)
@@ -1169,7 +1169,6 @@ TEST(EngineTest, BackdropBroadcasts)
11691169
ASSERT_EQ(GET_VAR(stage, "test5")->value().toString(), "2 2 0 0");
11701170
}
11711171

1172-
// TODO: Uncomment this after fixing #256 and #257
11731172
TEST(EngineTest, BroadcastsProject)
11741173
{
11751174
// TODO: Set "infinite" FPS (#254)
@@ -1287,3 +1286,33 @@ TEST(EngineTest, NoStopWhenCallingRunningBroadcastFromCustomBlock)
12871286
ASSERT_VAR(stage, "passed2");
12881287
ASSERT_TRUE(GET_VAR(stage, "passed2")->value().toBool());
12891288
}
1289+
1290+
TEST(EngineTest, ResetRunningHats)
1291+
{
1292+
// Regtest for #395
1293+
Project p("regtest_projects/395_reset_running_hats.sb3");
1294+
ASSERT_TRUE(p.load());
1295+
1296+
auto engine = p.engine();
1297+
1298+
Stage *stage = engine->stage();
1299+
ASSERT_TRUE(stage);
1300+
1301+
engine->setKeyState(KeyEvent(KeyEvent::Type::Space), true);
1302+
engine->setKeyState(KeyEvent(KeyEvent::Type::Space), false);
1303+
engine->setKeyState(KeyEvent(KeyEvent::Type::Space), true);
1304+
engine->setKeyState(KeyEvent(KeyEvent::Type::Space), false);
1305+
engine->run();
1306+
1307+
ASSERT_VAR(stage, "test");
1308+
ASSERT_EQ(GET_VAR(stage, "test")->value().toInt(), 1);
1309+
1310+
engine->run();
1311+
ASSERT_EQ(GET_VAR(stage, "test")->value().toInt(), 1);
1312+
1313+
engine->setKeyState(KeyEvent(KeyEvent::Type::Space), true);
1314+
engine->setKeyState(KeyEvent(KeyEvent::Type::Space), false);
1315+
engine->run();
1316+
1317+
ASSERT_EQ(GET_VAR(stage, "test")->value().toInt(), 2);
1318+
}

test/mocks/enginemock.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ class EngineMock : public IEngine
1616

1717
MOCK_METHOD(void, start, (), (override));
1818
MOCK_METHOD(void, stop, (), (override));
19-
MOCK_METHOD(void, startScript, (std::shared_ptr<Block>, std::shared_ptr<Target>), (override));
19+
MOCK_METHOD(VirtualMachine *, startScript, (std::shared_ptr<Block>, Target *), (override));
2020
MOCK_METHOD(void, broadcast, (unsigned int, VirtualMachine *, bool), (override));
2121
MOCK_METHOD(void, broadcastByPtr, (Broadcast *, VirtualMachine *, bool), (override));
2222
MOCK_METHOD(void, stopScript, (VirtualMachine *), (override));
1017 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)