Skip to content

Commit 400ac54

Browse files
committed
Add updateMonitors() method to IEngine
1 parent 2886f2e commit 400ac54

File tree

5 files changed

+56
-4
lines changed

5 files changed

+56
-4
lines changed

include/scratchcpp/iengine.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,9 @@ class LIBSCRATCHCPP_EXPORT IEngine
8686
/*! Stops all currently playing sounds. */
8787
virtual void stopSounds() = 0;
8888

89+
/*! Updates the values of stage monitors. */
90+
virtual void updateMonitors() = 0;
91+
8992
/*! Steps all currently running threads. Use this to implement a custom event loop. */
9093
virtual void step() = 0;
9194

src/engine/internal/engine.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,12 @@ void Engine::compile()
236236
else
237237
std::cout << "warning: monitor block doesn't have a compile function: " << block->opcode() << std::endl;
238238

239+
// Workaround for register leak warning spam: pause the script after getting the monitor value
240+
compiler.addFunctionCall([](VirtualMachine *vm) -> unsigned int {
241+
vm->stop(false, false, false);
242+
return 0;
243+
});
244+
239245
compiler.end();
240246

241247
script->setBytecode(compiler.bytecode());
@@ -367,6 +373,22 @@ void Engine::stopSounds()
367373
}
368374
}
369375

376+
void Engine::updateMonitors()
377+
{
378+
// Execute the "script" of each visible monitor
379+
for (auto monitor : m_monitors) {
380+
if (monitor->visible()) {
381+
auto script = monitor->script();
382+
383+
if (script) {
384+
auto vm = script->start();
385+
vm->run();
386+
monitor->updateValue(vm.get());
387+
}
388+
}
389+
}
390+
}
391+
370392
void Engine::step()
371393
{
372394
// https://github.com/scratchfoundation/scratch-vm/blob/f1aa92fad79af17d9dd1c41eeeadca099339a9f1/src/engine/runtime.js#L2087C6-L2155
@@ -485,6 +507,7 @@ void Engine::eventLoop(bool untilProjectStops)
485507
auto tickStart = m_clock->currentSteadyTime();
486508
m_eventLoopMutex.lock();
487509
step();
510+
updateMonitors();
488511

489512
// Stop the event loop if the project has finished running (and untilProjectStops is set to true)
490513
if (untilProjectStops && m_threads.empty()) {

src/engine/internal/engine.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ class Engine : public IEngine
4343
void deinitClone(std::shared_ptr<Sprite> clone) override;
4444
void stopSounds() override;
4545

46+
void updateMonitors() override;
4647
void step() override;
4748
void run() override;
4849
void runEventLoop() override;

test/engine/engine_test.cpp

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ using namespace libscratchcpp;
3232

3333
using ::testing::Return;
3434
using ::testing::SaveArg;
35+
using ::testing::WithArgs;
36+
using ::testing::Invoke;
3537
using ::testing::_;
3638

3739
// NOTE: resolveIds() and compile() are tested in load_project_test
@@ -102,7 +104,7 @@ TEST(EngineTest, Clear)
102104
ASSERT_TRUE(engine.monitors().empty());
103105
}
104106

105-
TEST(EngineTest, CompileMonitors)
107+
TEST(EngineTest, CompileAndExecuteMonitors)
106108
{
107109
Engine engine;
108110
auto stage = std::make_shared<Stage>();
@@ -111,6 +113,8 @@ TEST(EngineTest, CompileMonitors)
111113

112114
auto m1 = std::make_shared<Monitor>("a", "monitor_test1");
113115
auto m2 = std::make_shared<Monitor>("b", "monitor_test2");
116+
auto m3 = std::make_shared<Monitor>("c", "monitor_test3");
117+
m1->setVisible(false);
114118
m2->setSprite(sprite.get());
115119
engine.setMonitors({ m1, m2 });
116120

@@ -119,18 +123,38 @@ TEST(EngineTest, CompileMonitors)
119123
engine.addCompileFunction(section.get(), m1->opcode(), [](Compiler *compiler) { compiler->addConstValue(5.4); });
120124
engine.addCompileFunction(section.get(), m2->opcode(), [](Compiler *compiler) { compiler->addConstValue("test"); });
121125

126+
// Compile the monitor blocks
122127
engine.compile();
123128
auto script1 = m1->script();
124129
auto script2 = m2->script();
125-
ASSERT_TRUE(script1 && script2);
130+
auto script3 = m3->script();
131+
ASSERT_TRUE(script1 && script2 && !script3);
126132

127-
ASSERT_EQ(script1->bytecodeVector(), std::vector<unsigned int>({ vm::OP_START, vm::OP_CONST, 0, vm::OP_HALT }));
133+
ASSERT_EQ(script1->bytecodeVector(), std::vector<unsigned int>({ vm::OP_START, vm::OP_CONST, 0, vm::OP_EXEC, 0, vm::OP_HALT }));
128134
ASSERT_EQ(script1->target(), stage.get());
129135
ASSERT_EQ(script1->topBlock(), m1->block());
130136

131-
ASSERT_EQ(script2->bytecodeVector(), std::vector<unsigned int>({ vm::OP_START, vm::OP_CONST, 0, vm::OP_HALT }));
137+
ASSERT_EQ(script2->bytecodeVector(), std::vector<unsigned int>({ vm::OP_START, vm::OP_CONST, 0, vm::OP_EXEC, 0, vm::OP_HALT }));
132138
ASSERT_EQ(script2->target(), sprite.get());
133139
ASSERT_EQ(script2->topBlock(), m2->block());
140+
141+
// Execute the monitor blocks
142+
MonitorHandlerMock iface1, iface2, iface3;
143+
EXPECT_CALL(iface1, init);
144+
EXPECT_CALL(iface2, init);
145+
EXPECT_CALL(iface3, init);
146+
m1->setInterface(&iface1);
147+
m2->setInterface(&iface2);
148+
m3->setInterface(&iface3);
149+
150+
EXPECT_CALL(iface1, onValueChanged).Times(0);
151+
EXPECT_CALL(iface2, onValueChanged(_)).WillOnce(WithArgs<0>(Invoke([](const VirtualMachine *vm) {
152+
ASSERT_EQ(vm->registerCount(), 1);
153+
ASSERT_EQ(vm->getInput(0, 1)->toString(), "test");
154+
ASSERT_FALSE(vm->atEnd()); // the script shouldn't end because that would spam the console with leak warnings
155+
})));
156+
EXPECT_CALL(iface3, onValueChanged).Times(0);
157+
engine.updateMonitors();
134158
}
135159

136160
TEST(EngineTest, IsRunning)

test/mocks/enginemock.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ class EngineMock : public IEngine
2626
MOCK_METHOD(void, deinitClone, (std::shared_ptr<Sprite>), (override));
2727
MOCK_METHOD(void, stopSounds, (), (override));
2828

29+
MOCK_METHOD(void, updateMonitors, (), (override));
2930
MOCK_METHOD(void, step, (), (override));
3031
MOCK_METHOD(void, run, (), (override));
3132
MOCK_METHOD(void, runEventLoop, (), (override));

0 commit comments

Comments
 (0)