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
15 changes: 15 additions & 0 deletions include/scratchcpp/iengine.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ class IBlockSection;
class Broadcast;
class Block;
class Target;
class Sprite;
class Variable;
class List;
class Script;

/*!
Expand Down Expand Up @@ -70,6 +73,9 @@ class LIBSCRATCHCPP_EXPORT IEngine
*/
virtual void stopTarget(Target *target, VirtualMachine *exceptScript) = 0;

/*! Calls the "when I start as a clone" blocks of the given sprite. */
virtual void initClone(Sprite *clone) = 0;

/*!
* Runs the event loop and calls "when green flag clicked" blocks.
* \note This function returns when all scripts finish.\n
Expand Down Expand Up @@ -157,6 +163,9 @@ class LIBSCRATCHCPP_EXPORT IEngine
/*! Registers the broadcast script. */
virtual void addBroadcastScript(std::shared_ptr<Block> whenReceivedBlock, std::shared_ptr<Broadcast> broadcast) = 0;

/* Registers the given "when I start as clone" script. */
virtual void addCloneInitScript(std::shared_ptr<Block> hatBlock) = 0;

/*! Returns the list of targets. */
virtual const std::vector<std::shared_ptr<Target>> &targets() const = 0;

Expand All @@ -169,6 +178,12 @@ class LIBSCRATCHCPP_EXPORT IEngine
/*! Returns the index of the target with the given name. */
virtual int findTarget(const std::string &targetName) const = 0;

/*! Returns the target which owns the given variable. If it is the stage, the variable is global. */
virtual Target *variableOwner(Variable *variable) const = 0;

/*! Returns the target which owns the given list. If it is the stage, the list is global. */
virtual Target *listOwner(List *list) const = 0;

/*! Returns the list of extension names. */
virtual const std::vector<std::string> &extensions() const = 0;

Expand Down
6 changes: 5 additions & 1 deletion include/scratchcpp/script.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ class Target;
class IEngine;
class Value;
class VirtualMachine;
class Variable;
class List;
class ScriptPrivate;

Expand All @@ -25,17 +26,20 @@ class LIBSCRATCHCPP_EXPORT Script
Script(Target *target, IEngine *engine);
Script(const Script &) = delete;

Target *target() const;

unsigned int *bytecode() const;
const std::vector<unsigned int> &bytecodeVector() const;
void setBytecode(const std::vector<unsigned int> &code);

void setProcedures(const std::vector<unsigned int *> &procedures);
void setFunctions(const std::vector<BlockFunc> &functions);
void setConstValues(const std::vector<Value> &values);
void setVariables(const std::vector<Value *> &variables);
void setVariables(const std::vector<Variable *> &variables);
void setLists(const std::vector<List *> &lists);

std::shared_ptr<VirtualMachine> start();
std::shared_ptr<VirtualMachine> start(Target *target);

private:
spimpl::unique_impl_ptr<ScriptPrivate> impl;
Expand Down
12 changes: 12 additions & 0 deletions include/scratchcpp/sprite.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,19 @@ class LIBSCRATCHCPP_EXPORT Sprite : public Target
};

Sprite();
~Sprite();

void setInterface(ISpriteHandler *newInterface);

std::shared_ptr<Sprite> clone();

bool isClone() const;

Sprite *cloneRoot() const;
Sprite *cloneParent() const;
const std::vector<std::shared_ptr<Sprite>> &children() const;
std::vector<std::shared_ptr<Sprite>> allChildren() const;

bool visible() const;
void setVisible(bool newVisible);

Expand All @@ -50,6 +60,8 @@ class LIBSCRATCHCPP_EXPORT Sprite : public Target
void setRotationStyle(const char *newRotationStyle);

private:
Target *dataSource() const override;

spimpl::unique_impl_ptr<SpritePrivate> impl;
};

Expand Down
8 changes: 8 additions & 0 deletions include/scratchcpp/target.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
namespace libscratchcpp
{

class IEngine;
class Variable;
class List;
class Block;
Expand Down Expand Up @@ -67,6 +68,13 @@ class LIBSCRATCHCPP_EXPORT Target
int volume() const;
void setVolume(int newVolume);

IEngine *engine() const;
void setEngine(IEngine *engine);

protected:
/*! Override this method to set a custom data source for blocks and assets. */
virtual Target *dataSource() const { return nullptr; }

private:
spimpl::unique_impl_ptr<TargetPrivate> impl;
};
Expand Down
2 changes: 2 additions & 0 deletions include/scratchcpp/virtualmachine.h
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,8 @@ class LIBSCRATCHCPP_EXPORT VirtualMachine
void setConstValues(const Value *values);
void setVariables(Value **variables);
void setLists(List **lists);
void setVariablesVector(const std::vector<Value *> &variables);
void setListsVector(const std::vector<List *> &lists);

void setBytecode(unsigned int *code);

Expand Down
136 changes: 120 additions & 16 deletions src/engine/internal/engine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include <scratchcpp/scratchconfiguration.h>
#include <scratchcpp/iblocksection.h>
#include <scratchcpp/script.h>
#include <scratchcpp/sprite.h>
#include <scratchcpp/broadcast.h>
#include <scratchcpp/compiler.h>
#include <scratchcpp/input.h>
Expand Down Expand Up @@ -111,7 +112,7 @@ void Engine::compile()
if (m_scripts.count(block) == 1) {
m_scripts[block]->setProcedures(procedureBytecodes);
m_scripts[block]->setConstValues(compiler.constValues());
m_scripts[block]->setVariables(compiler.variablePtrs());
m_scripts[block]->setVariables(compiler.variables());
m_scripts[block]->setLists(compiler.lists());
}
}
Expand Down Expand Up @@ -201,22 +202,21 @@ void Engine::broadcast(unsigned int index, VirtualMachine *sourceScript, bool wa
const std::vector<Script *> &scripts = m_broadcastMap[index];

for (auto script : scripts) {
long scriptIndex = -1;
for (long i = 0; i < m_runningScripts.size(); i++) {
if (m_runningScripts[i]->script() == script) {
scriptIndex = i;
break;
std::vector<VirtualMachine *> runningBroadcastScripts;

for (auto vm : m_runningScripts) {
if (vm->script() == script) {
runningBroadcastScripts.push_back(vm.get());
}
}

if (scriptIndex != -1) {
// Reset the script if it's already running
auto vm = m_runningScripts[scriptIndex];
// Reset running scripts
for (VirtualMachine *vm : runningBroadcastScripts) {
vm->reset();

// Remove the script from scripts to remove because it's going to run again
m_scriptsToRemove.erase(std::remove(m_scriptsToRemove.begin(), m_scriptsToRemove.end(), vm.get()), m_scriptsToRemove.end());
assert(std::find(m_scriptsToRemove.begin(), m_scriptsToRemove.end(), m_runningScripts[scriptIndex].get()) == m_scriptsToRemove.end());
m_scriptsToRemove.erase(std::remove(m_scriptsToRemove.begin(), m_scriptsToRemove.end(), vm), m_scriptsToRemove.end());
assert(std::find(m_scriptsToRemove.begin(), m_scriptsToRemove.end(), vm) == m_scriptsToRemove.end());

auto &scripts = m_runningBroadcastMap[index];

Expand All @@ -232,10 +232,29 @@ void Engine::broadcast(unsigned int index, VirtualMachine *sourceScript, bool wa
m_skipFrame = false;
} else
sourceScript->stop(true, true);
} else {
auto vm = script->start();
m_runningScripts.push_back(vm);
m_runningBroadcastMap[index].push_back({ sourceScript, vm.get() });
}

// Start scripts which are not running
Target *root = script->target();
std::vector<Target *> targets = { root };

if (!root->isStage()) {
Sprite *sprite = dynamic_cast<Sprite *>(root);
assert(sprite);
auto children = sprite->allChildren();

for (auto child : children)
targets.push_back(child.get());
}

for (Target *target : targets) {
auto it = std::find_if(runningBroadcastScripts.begin(), runningBroadcastScripts.end(), [target](VirtualMachine *vm) { return vm->target() == target; });

if (it == runningBroadcastScripts.end()) {
auto vm = script->start(target);
m_runningScripts.push_back(vm);
m_runningBroadcastMap[index].push_back({ sourceScript, vm.get() });
}
}
}
}
Expand All @@ -258,6 +277,37 @@ void Engine::stopTarget(Target *target, VirtualMachine *exceptScript)
stopScript(script);
}

void Engine::initClone(Sprite *clone)
{
if (!clone)
return;

Sprite *source = clone->cloneParent();
Target *root = clone->cloneRoot();
assert(source);
assert(root);

if (!source || !root)
return;

auto it = m_cloneInitScriptsMap.find(root);

if (it != m_cloneInitScriptsMap.cend()) {
const auto &scripts = it->second;

#ifndef NDEBUG
// Since we're initializing the clone, it shouldn't have any running scripts
for (const auto script : m_runningScripts)
assert(script->target() != clone);
#endif

for (auto script : scripts) {
auto vm = script->start(clone);
m_runningScripts.push_back(vm);
}
}
}

void Engine::run()
{
auto frameDuration = std::chrono::milliseconds(33);
Expand Down Expand Up @@ -442,6 +492,7 @@ void Engine::addBroadcastScript(std::shared_ptr<Block> whenReceivedBlock, std::s
auto id = findBroadcast(broadcast->name());
if (m_broadcastMap.count(id) == 1) {
std::vector<Script *> &scripts = m_broadcastMap[id];
// TODO: Do not allow adding existing scripts
scripts.push_back(m_scripts[whenReceivedBlock].get());
} else {
m_broadcastMap[id] = { m_scripts[whenReceivedBlock].get() };
Expand All @@ -452,6 +503,22 @@ void Engine::addBroadcastScript(std::shared_ptr<Block> whenReceivedBlock, std::s
}
}

void Engine::addCloneInitScript(std::shared_ptr<Block> hatBlock)
{
Target *target = hatBlock->target();
Script *script = m_scripts[hatBlock].get();
auto it = m_cloneInitScriptsMap.find(target);

if (it == m_cloneInitScriptsMap.cend())
m_cloneInitScriptsMap[target] = { m_scripts[hatBlock].get() };
else {
auto &scripts = it->second;

if (std::find(scripts.begin(), scripts.end(), script) == scripts.cend())
scripts.push_back(script);
}
}

const std::vector<std::shared_ptr<Target>> &Engine::targets() const
{
return m_targets;
Expand All @@ -460,14 +527,31 @@ const std::vector<std::shared_ptr<Target>> &Engine::targets() const
void Engine::setTargets(const std::vector<std::shared_ptr<Target>> &newTargets)
{
m_targets = newTargets;
m_variableOwners.clear();
m_listOwners.clear();

// Set engine and target in all blocks
for (auto target : m_targets) {
// Set engine in the target
target->setEngine(this);
auto blocks = target->blocks();

for (auto block : blocks) {
// Set engine and target in the block
block->setEngine(this);
block->setTarget(target.get());
}

// Add variables to owner map
const auto &variables = target->variables();

for (auto variable : variables)
m_variableOwners[variable.get()] = target.get();

// Add lists to owner map
const auto &lists = target->lists();

for (auto list : lists)
m_listOwners[list.get()] = target.get();
}
}

Expand All @@ -490,6 +574,26 @@ int Engine::findTarget(const std::string &targetName) const
return -1;
}

Target *Engine::variableOwner(Variable *variable) const
{
auto it = m_variableOwners.find(variable);

if (it == m_variableOwners.cend())
return nullptr;

return it->second;
}

Target *Engine::listOwner(List *list) const
{
auto it = m_listOwners.find(list);

if (it == m_listOwners.cend())
return nullptr;

return it->second;
}

const std::vector<std::string> &Engine::extensions() const
{
return m_extensions;
Expand Down
9 changes: 9 additions & 0 deletions src/engine/internal/engine.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ class Engine : public IEngine
void broadcast(unsigned int index, VirtualMachine *sourceScript, bool wait = false) override;
void stopScript(VirtualMachine *vm) override;
void stopTarget(Target *target, VirtualMachine *exceptScript) override;
void initClone(libscratchcpp::Sprite *clone) override;
void run() override;

bool broadcastRunning(unsigned int index, VirtualMachine *sourceScript) override;
Expand Down Expand Up @@ -60,11 +61,16 @@ class Engine : public IEngine

void addBroadcastScript(std::shared_ptr<Block> whenReceivedBlock, std::shared_ptr<Broadcast> broadcast) override;

void addCloneInitScript(std::shared_ptr<Block> hatBlock) override;

const std::vector<std::shared_ptr<Target>> &targets() const override;
void setTargets(const std::vector<std::shared_ptr<Target>> &newTargets) override;
Target *targetAt(int index) const override;
int findTarget(const std::string &targetName) const override;

Target *variableOwner(Variable *variable) const override;
Target *listOwner(List *list) const override;

const std::vector<std::string> &extensions() const override;
void setExtensions(const std::vector<std::string> &newExtensions) override;

Expand All @@ -86,11 +92,14 @@ class Engine : public IEngine
std::vector<std::shared_ptr<Broadcast>> m_broadcasts;
std::unordered_map<unsigned int, std::vector<Script *>> m_broadcastMap;
std::unordered_map<unsigned int, std::vector<std::pair<VirtualMachine *, VirtualMachine *>>> m_runningBroadcastMap; // source script, "when received" script
std::unordered_map<Target *, std::vector<Script *>> m_cloneInitScriptsMap; // target (no clones), "when I start as a clone" scripts
std::vector<std::string> m_extensions;
std::vector<std::shared_ptr<VirtualMachine>> m_runningScripts;
std::vector<VirtualMachine *> m_scriptsToRemove;
std::unordered_map<std::shared_ptr<Block>, std::shared_ptr<Script>> m_scripts;
std::vector<BlockFunc> m_functions;
std::unordered_map<Variable *, Target *> m_variableOwners;
std::unordered_map<List *, Target *> m_listOwners;

bool m_breakFrame = false;
bool m_skipFrame = false;
Expand Down
Loading