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: 6 additions & 0 deletions include/scratchcpp/iengine.h
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,12 @@ class LIBSCRATCHCPP_EXPORT IEngine
/*! Sets the list of monitors. */
virtual void setMonitors(const std::vector<std::shared_ptr<Monitor>> &newMonitors) = 0;

/*! Creates a monitor for the given variable if it's missing and returns it. */
virtual Monitor *createVariableMonitor(std::shared_ptr<Variable> var, const std::string &opcode, const std::string &varFieldName, int varFieldId, BlockComp compileFunction) = 0;

/*! Creates a monitor for the given list if it's missing and returns it. */
virtual Monitor *createListMonitor(std::shared_ptr<List> list, const std::string &opcode, const std::string &listFieldName, int listFieldId, BlockComp compileFunction) = 0;

/*! Emits when a monitor is added. */
virtual sigslot::signal<Monitor *> &monitorAdded() = 0;

Expand Down
22 changes: 13 additions & 9 deletions src/blocks/listblocks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -142,19 +142,23 @@ void ListBlocks::compileHideList(Compiler *compiler)
compiler->addFunctionCall(&hideList);
}

void ListBlocks::setListVisible(std::shared_ptr<List> list, bool visible)
void ListBlocks::setListVisible(std::shared_ptr<List> list, bool visible, IEngine *engine)
{
if (list) {
assert(list->monitor());
list->monitor()->setVisible(visible);
Monitor *monitor = list->monitor();

if (!monitor)
monitor = engine->createListMonitor(list, "data_listcontents", "LIST", LIST, &compileListContents);

monitor->setVisible(visible);
}
}

unsigned int ListBlocks::showGlobalList(VirtualMachine *vm)
{
if (Stage *target = vm->engine()->stage()) {
int index = target->findListById(vm->getInput(0, 1)->toString());
setListVisible(target->listAt(index), true);
setListVisible(target->listAt(index), true, vm->engine());
}

return 1;
Expand All @@ -166,10 +170,10 @@ unsigned int ListBlocks::showList(VirtualMachine *vm)
if (!target->isStage() && static_cast<Sprite *>(target)->isClone()) {
Sprite *sprite = static_cast<Sprite *>(target)->cloneSprite(); // use clone root list
int index = sprite->findListById(vm->getInput(0, 1)->toString());
setListVisible(sprite->listAt(index), true);
setListVisible(sprite->listAt(index), true, vm->engine());
} else {
int index = target->findListById(vm->getInput(0, 1)->toString());
setListVisible(target->listAt(index), true);
setListVisible(target->listAt(index), true, vm->engine());
}
}

Expand All @@ -180,7 +184,7 @@ unsigned int ListBlocks::hideGlobalList(VirtualMachine *vm)
{
if (Stage *target = vm->engine()->stage()) {
int index = target->findListById(vm->getInput(0, 1)->toString());
setListVisible(target->listAt(index), false);
setListVisible(target->listAt(index), false, vm->engine());
}

return 1;
Expand All @@ -192,10 +196,10 @@ unsigned int ListBlocks::hideList(VirtualMachine *vm)
if (!target->isStage() && static_cast<Sprite *>(target)->isClone()) {
Sprite *sprite = static_cast<Sprite *>(target)->cloneSprite(); // use clone root list
int index = sprite->findListById(vm->getInput(0, 1)->toString());
setListVisible(sprite->listAt(index), false);
setListVisible(sprite->listAt(index), false, vm->engine());
} else {
int index = target->findListById(vm->getInput(0, 1)->toString());
setListVisible(target->listAt(index), false);
setListVisible(target->listAt(index), false, vm->engine());
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/blocks/listblocks.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ class ListBlocks : public IBlockSection
static void compileShowList(Compiler *compiler);
static void compileHideList(Compiler *compiler);

static void setListVisible(std::shared_ptr<List> list, bool visible);
static void setListVisible(std::shared_ptr<List> list, bool visible, IEngine *engine);

static unsigned int showGlobalList(VirtualMachine *vm);
static unsigned int showList(VirtualMachine *vm);
Expand Down
22 changes: 13 additions & 9 deletions src/blocks/variableblocks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -88,19 +88,23 @@ void VariableBlocks::compileHideVariable(Compiler *compiler)
compiler->addFunctionCall(&hideVariable);
}

void VariableBlocks::setVarVisible(std::shared_ptr<Variable> var, bool visible)
void VariableBlocks::setVarVisible(std::shared_ptr<Variable> var, bool visible, IEngine *engine)
{
if (var) {
assert(var->monitor());
var->monitor()->setVisible(visible);
Monitor *monitor = var->monitor();

if (!monitor)
monitor = engine->createVariableMonitor(var, "data_variable", "VARIABLE", VARIABLE, &compileVariable);

monitor->setVisible(visible);
}
}

unsigned int VariableBlocks::showGlobalVariable(VirtualMachine *vm)
{
if (Stage *target = vm->engine()->stage()) {
int index = target->findVariableById(vm->getInput(0, 1)->toString());
setVarVisible(target->variableAt(index), true);
setVarVisible(target->variableAt(index), true, vm->engine());
}

return 1;
Expand All @@ -112,10 +116,10 @@ unsigned int VariableBlocks::showVariable(VirtualMachine *vm)
if (!target->isStage() && static_cast<Sprite *>(target)->isClone()) {
Sprite *sprite = static_cast<Sprite *>(target)->cloneSprite(); // use clone root variable
int index = sprite->findVariableById(vm->getInput(0, 1)->toString());
setVarVisible(sprite->variableAt(index), true);
setVarVisible(sprite->variableAt(index), true, vm->engine());
} else {
int index = target->findVariableById(vm->getInput(0, 1)->toString());
setVarVisible(target->variableAt(index), true);
setVarVisible(target->variableAt(index), true, vm->engine());
}
}

Expand All @@ -126,7 +130,7 @@ unsigned int VariableBlocks::hideGlobalVariable(VirtualMachine *vm)
{
if (Stage *target = vm->engine()->stage()) {
int index = target->findVariableById(vm->getInput(0, 1)->toString());
setVarVisible(target->variableAt(index), false);
setVarVisible(target->variableAt(index), false, vm->engine());
}

return 1;
Expand All @@ -138,10 +142,10 @@ unsigned int VariableBlocks::hideVariable(VirtualMachine *vm)
if (!target->isStage() && static_cast<Sprite *>(target)->isClone()) {
Sprite *sprite = static_cast<Sprite *>(target)->cloneSprite(); // use clone root variable
int index = sprite->findVariableById(vm->getInput(0, 1)->toString());
setVarVisible(sprite->variableAt(index), false);
setVarVisible(sprite->variableAt(index), false, vm->engine());
} else {
int index = target->findVariableById(vm->getInput(0, 1)->toString());
setVarVisible(target->variableAt(index), false);
setVarVisible(target->variableAt(index), false, vm->engine());
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/blocks/variableblocks.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ class VariableBlocks : public IBlockSection
static void compileShowVariable(Compiler *compiler);
static void compileHideVariable(Compiler *compiler);

static void setVarVisible(std::shared_ptr<Variable> var, bool visible);
static void setVarVisible(std::shared_ptr<Variable> var, bool visible, IEngine *engine);

static unsigned int showGlobalVariable(VirtualMachine *vm);
static unsigned int showVariable(VirtualMachine *vm);
Expand Down
200 changes: 89 additions & 111 deletions src/engine/internal/engine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,6 @@
#include "clock.h"
#include "audio/iaudioengine.h"
#include "blocks/standardblocks.h"
#include "blocks/variableblocks.h"
#include "blocks/listblocks.h"
#include "scratch/monitor_p.h"

using namespace libscratchcpp;
Expand Down Expand Up @@ -314,56 +312,8 @@ void Engine::compile()
// Compile monitor blocks to bytecode
std::cout << "Compiling stage monitors..." << std::endl;

for (auto monitor : m_monitors) {
Target *target = monitor->sprite() ? dynamic_cast<Target *>(monitor->sprite()) : stage();
Compiler compiler(this, target);
auto block = monitor->block();
auto section = blockSection(block->opcode());
auto container = blockSectionContainer(block->opcode());

if (container) {
MonitorNameFunc nameFunc = container->resolveMonitorNameFunc(block->opcode());

if (nameFunc)
monitor->setName(nameFunc(block.get()));

MonitorChangeFunc changeFunc = container->resolveMonitorChangeFunc(block->opcode());
monitor->setValueChangeFunction(changeFunc);
}

if (section) {
auto script = std::make_shared<Script>(target, block, this);
monitor->setScript(script);
compiler.init();
compiler.setBlock(block);

if (block->compileFunction())
block->compile(&compiler);
else
std::cout << "warning: monitor block doesn't have a compile function: " << block->opcode() << std::endl;

// Workaround for register leak warning spam: pause the script after getting the monitor value
compiler.addFunctionCall([](VirtualMachine *vm) -> unsigned int {
vm->stop(false, false, false);
return 0;
});

compiler.end();

script->setBytecode(compiler.bytecode());
script->setConstValues(compiler.constValues());
script->setVariables(compiler.variables());
script->setLists(compiler.lists());
} else {
std::cout << "warning: unsupported monitor block: " << block->opcode() << std::endl;
m_unsupportedBlocks.insert(block->opcode());
}

const auto &unsupportedBlocks = compiler.unsupportedBlocks();

for (const std::string &opcode : unsupportedBlocks)
m_unsupportedBlocks.insert(opcode);
}
for (auto monitor : m_monitors)
compileMonitor(monitor);
}

void Engine::start()
Expand Down Expand Up @@ -1231,9 +1181,6 @@ void Engine::setTargets(const std::vector<std::shared_ptr<Target>> &newTargets)

// Sort the executable targets by layer order
std::sort(m_executableTargets.begin(), m_executableTargets.end(), [](Target *t1, Target *t2) { return t1->layerOrder() < t2->layerOrder(); });

// Create missing monitors
createMissingMonitors();
}

Target *Engine::targetAt(int index) const
Expand Down Expand Up @@ -1401,9 +1348,42 @@ void Engine::setMonitors(const std::vector<std::shared_ptr<Monitor>> &newMonitor
m_monitors.push_back(monitor);
m_monitorAdded(monitor.get());
}
}

Monitor *Engine::createVariableMonitor(std::shared_ptr<Variable> var, const std::string &opcode, const std::string &varFieldName, int varFieldId, BlockComp compileFunction)
{
if (var->monitor())
return var->monitor();
else {
auto monitor = std::make_shared<Monitor>(var->id(), opcode);
auto field = std::make_shared<Field>(varFieldName, var->name(), var);
field->setFieldId(varFieldId);
monitor->block()->addField(field);
monitor->block()->setCompileFunction(compileFunction);

addVarOrListMonitor(monitor, var->target());
var->setMonitor(monitor.get());
compileMonitor(monitor);
return monitor.get();
}
}

// Create missing monitors
createMissingMonitors();
Monitor *Engine::createListMonitor(std::shared_ptr<List> list, const std::string &opcode, const std::string &listFieldName, int listFieldId, BlockComp compileFunction)
{
if (list->monitor())
return list->monitor();
else {
auto monitor = std::make_shared<Monitor>(list->id(), opcode);
auto field = std::make_shared<Field>(listFieldName, list->name(), list);
field->setFieldId(listFieldId);
monitor->block()->addField(field);
monitor->block()->setCompileFunction(compileFunction);

addVarOrListMonitor(monitor, list->target());
list->setMonitor(monitor.get());
compileMonitor(monitor);
return monitor.get();
}
}

sigslot::signal<Monitor *> &Engine::monitorAdded()
Expand Down Expand Up @@ -1759,6 +1739,58 @@ const std::unordered_set<std::string> &Engine::unsupportedBlocks() const
return m_unsupportedBlocks;
}

void Engine::compileMonitor(std::shared_ptr<Monitor> monitor)
{
Target *target = monitor->sprite() ? dynamic_cast<Target *>(monitor->sprite()) : stage();
Compiler compiler(this, target);
auto block = monitor->block();
auto section = blockSection(block->opcode());
auto container = blockSectionContainer(block->opcode());

if (container) {
MonitorNameFunc nameFunc = container->resolveMonitorNameFunc(block->opcode());

if (nameFunc)
monitor->setName(nameFunc(block.get()));

MonitorChangeFunc changeFunc = container->resolveMonitorChangeFunc(block->opcode());
monitor->setValueChangeFunction(changeFunc);
}

if (section) {
auto script = std::make_shared<Script>(target, block, this);
monitor->setScript(script);
compiler.init();
compiler.setBlock(block);

if (block->compileFunction())
block->compile(&compiler);
else
std::cout << "warning: monitor block doesn't have a compile function: " << block->opcode() << std::endl;

// Workaround for register leak warning spam: pause the script after getting the monitor value
compiler.addFunctionCall([](VirtualMachine *vm) -> unsigned int {
vm->stop(false, false, false);
return 0;
});

compiler.end();

script->setBytecode(compiler.bytecode());
script->setConstValues(compiler.constValues());
script->setVariables(compiler.variables());
script->setLists(compiler.lists());
} else {
std::cout << "warning: unsupported monitor block: " << block->opcode() << std::endl;
m_unsupportedBlocks.insert(block->opcode());
}

const auto &unsupportedBlocks = compiler.unsupportedBlocks();

for (const std::string &opcode : unsupportedBlocks)
m_unsupportedBlocks.insert(opcode);
}

void Engine::finalize()
{
m_eventLoopMutex.lock();
Expand Down Expand Up @@ -1797,60 +1829,6 @@ void Engine::removeExecutableClones()
m_executableTargets.erase(std::remove(m_executableTargets.begin(), m_executableTargets.end(), clone.get()), m_executableTargets.end());
}

void Engine::createMissingMonitors()
{
// This is called when setting targets and monitors because we never know in which order they're set
// If there aren't any targets yet, quit
if (m_targets.empty())
return;

for (auto target : m_targets) {
// Read all variables
const auto &variables = target->variables();

for (auto variable : variables) {
// Find the monitor for this variable
auto it = std::find_if(m_monitors.begin(), m_monitors.end(), [variable](std::shared_ptr<Monitor> monitor) {
// TODO: Move the opcode out of Engine
return monitor->opcode() == "data_variable" && monitor->id() == variable->id();
});

// If it doesn't exist, create it
if (it == m_monitors.end()) {
auto monitor = std::make_shared<Monitor>(variable->id(), "data_variable");
// TODO: Move field information out of Engine
auto field = std::make_shared<Field>("VARIABLE", variable->name(), variable);
field->setFieldId(VariableBlocks::VARIABLE);
monitor->block()->addField(field);

addVarOrListMonitor(monitor, target.get());
}
}

// Read all lists
const auto &lists = target->lists();

for (auto list : lists) {
// Find the monitor for this list
auto it = std::find_if(m_monitors.begin(), m_monitors.end(), [list](std::shared_ptr<Monitor> monitor) {
// TODO: Move the opcode out of Engine
return monitor->opcode() == "data_listcontents" && monitor->id() == list->id();
});

// If it doesn't exist, create it
if (it == m_monitors.end()) {
auto monitor = std::make_shared<Monitor>(list->id(), "data_listcontents");
// TODO: Move field information out of Engine
auto field = std::make_shared<Field>("LIST", list->name(), list);
field->setFieldId(ListBlocks::LIST);
monitor->block()->addField(field);

addVarOrListMonitor(monitor, target.get());
}
}
}
}

void Engine::addVarOrListMonitor(std::shared_ptr<Monitor> monitor, Target *target)
{
if (!target->isStage())
Expand Down
Loading