Skip to content

Commit 1405c37

Browse files
authored
Merge pull request #183 from scratchcpp/clones_api
Add API for clones
2 parents 0485474 + 846043d commit 1405c37

File tree

24 files changed

+973
-43
lines changed

24 files changed

+973
-43
lines changed

include/scratchcpp/iengine.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ class IBlockSection;
1515
class Broadcast;
1616
class Block;
1717
class Target;
18+
class Sprite;
19+
class Variable;
20+
class List;
1821
class Script;
1922

2023
/*!
@@ -70,6 +73,9 @@ class LIBSCRATCHCPP_EXPORT IEngine
7073
*/
7174
virtual void stopTarget(Target *target, VirtualMachine *exceptScript) = 0;
7275

76+
/*! Calls the "when I start as a clone" blocks of the given sprite. */
77+
virtual void initClone(Sprite *clone) = 0;
78+
7379
/*!
7480
* Runs the event loop and calls "when green flag clicked" blocks.
7581
* \note This function returns when all scripts finish.\n
@@ -157,6 +163,9 @@ class LIBSCRATCHCPP_EXPORT IEngine
157163
/*! Registers the broadcast script. */
158164
virtual void addBroadcastScript(std::shared_ptr<Block> whenReceivedBlock, std::shared_ptr<Broadcast> broadcast) = 0;
159165

166+
/* Registers the given "when I start as clone" script. */
167+
virtual void addCloneInitScript(std::shared_ptr<Block> hatBlock) = 0;
168+
160169
/*! Returns the list of targets. */
161170
virtual const std::vector<std::shared_ptr<Target>> &targets() const = 0;
162171

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

181+
/*! Returns the target which owns the given variable. If it is the stage, the variable is global. */
182+
virtual Target *variableOwner(Variable *variable) const = 0;
183+
184+
/*! Returns the target which owns the given list. If it is the stage, the list is global. */
185+
virtual Target *listOwner(List *list) const = 0;
186+
172187
/*! Returns the list of extension names. */
173188
virtual const std::vector<std::string> &extensions() const = 0;
174189

include/scratchcpp/script.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ class Target;
1515
class IEngine;
1616
class Value;
1717
class VirtualMachine;
18+
class Variable;
1819
class List;
1920
class ScriptPrivate;
2021

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

29+
Target *target() const;
30+
2831
unsigned int *bytecode() const;
2932
const std::vector<unsigned int> &bytecodeVector() const;
3033
void setBytecode(const std::vector<unsigned int> &code);
3134

3235
void setProcedures(const std::vector<unsigned int *> &procedures);
3336
void setFunctions(const std::vector<BlockFunc> &functions);
3437
void setConstValues(const std::vector<Value> &values);
35-
void setVariables(const std::vector<Value *> &variables);
38+
void setVariables(const std::vector<Variable *> &variables);
3639
void setLists(const std::vector<List *> &lists);
3740

3841
std::shared_ptr<VirtualMachine> start();
42+
std::shared_ptr<VirtualMachine> start(Target *target);
3943

4044
private:
4145
spimpl::unique_impl_ptr<ScriptPrivate> impl;

include/scratchcpp/sprite.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,19 @@ class LIBSCRATCHCPP_EXPORT Sprite : public Target
2222
};
2323

2424
Sprite();
25+
~Sprite();
2526

2627
void setInterface(ISpriteHandler *newInterface);
2728

29+
std::shared_ptr<Sprite> clone();
30+
31+
bool isClone() const;
32+
33+
Sprite *cloneRoot() const;
34+
Sprite *cloneParent() const;
35+
const std::vector<std::shared_ptr<Sprite>> &children() const;
36+
std::vector<std::shared_ptr<Sprite>> allChildren() const;
37+
2838
bool visible() const;
2939
void setVisible(bool newVisible);
3040

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

5262
private:
63+
Target *dataSource() const override;
64+
5365
spimpl::unique_impl_ptr<SpritePrivate> impl;
5466
};
5567

include/scratchcpp/target.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
namespace libscratchcpp
1111
{
1212

13+
class IEngine;
1314
class Variable;
1415
class List;
1516
class Block;
@@ -67,6 +68,13 @@ class LIBSCRATCHCPP_EXPORT Target
6768
int volume() const;
6869
void setVolume(int newVolume);
6970

71+
IEngine *engine() const;
72+
void setEngine(IEngine *engine);
73+
74+
protected:
75+
/*! Override this method to set a custom data source for blocks and assets. */
76+
virtual Target *dataSource() const { return nullptr; }
77+
7078
private:
7179
spimpl::unique_impl_ptr<TargetPrivate> impl;
7280
};

include/scratchcpp/virtualmachine.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,8 @@ class LIBSCRATCHCPP_EXPORT VirtualMachine
102102
void setConstValues(const Value *values);
103103
void setVariables(Value **variables);
104104
void setLists(List **lists);
105+
void setVariablesVector(const std::vector<Value *> &variables);
106+
void setListsVector(const std::vector<List *> &lists);
105107

106108
void setBytecode(unsigned int *code);
107109

src/engine/internal/engine.cpp

Lines changed: 120 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include <scratchcpp/scratchconfiguration.h>
44
#include <scratchcpp/iblocksection.h>
55
#include <scratchcpp/script.h>
6+
#include <scratchcpp/sprite.h>
67
#include <scratchcpp/broadcast.h>
78
#include <scratchcpp/compiler.h>
89
#include <scratchcpp/input.h>
@@ -111,7 +112,7 @@ void Engine::compile()
111112
if (m_scripts.count(block) == 1) {
112113
m_scripts[block]->setProcedures(procedureBytecodes);
113114
m_scripts[block]->setConstValues(compiler.constValues());
114-
m_scripts[block]->setVariables(compiler.variablePtrs());
115+
m_scripts[block]->setVariables(compiler.variables());
115116
m_scripts[block]->setLists(compiler.lists());
116117
}
117118
}
@@ -201,22 +202,21 @@ void Engine::broadcast(unsigned int index, VirtualMachine *sourceScript, bool wa
201202
const std::vector<Script *> &scripts = m_broadcastMap[index];
202203

203204
for (auto script : scripts) {
204-
long scriptIndex = -1;
205-
for (long i = 0; i < m_runningScripts.size(); i++) {
206-
if (m_runningScripts[i]->script() == script) {
207-
scriptIndex = i;
208-
break;
205+
std::vector<VirtualMachine *> runningBroadcastScripts;
206+
207+
for (auto vm : m_runningScripts) {
208+
if (vm->script() == script) {
209+
runningBroadcastScripts.push_back(vm.get());
209210
}
210211
}
211212

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

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

221221
auto &scripts = m_runningBroadcastMap[index];
222222

@@ -232,10 +232,29 @@ void Engine::broadcast(unsigned int index, VirtualMachine *sourceScript, bool wa
232232
m_skipFrame = false;
233233
} else
234234
sourceScript->stop(true, true);
235-
} else {
236-
auto vm = script->start();
237-
m_runningScripts.push_back(vm);
238-
m_runningBroadcastMap[index].push_back({ sourceScript, vm.get() });
235+
}
236+
237+
// Start scripts which are not running
238+
Target *root = script->target();
239+
std::vector<Target *> targets = { root };
240+
241+
if (!root->isStage()) {
242+
Sprite *sprite = dynamic_cast<Sprite *>(root);
243+
assert(sprite);
244+
auto children = sprite->allChildren();
245+
246+
for (auto child : children)
247+
targets.push_back(child.get());
248+
}
249+
250+
for (Target *target : targets) {
251+
auto it = std::find_if(runningBroadcastScripts.begin(), runningBroadcastScripts.end(), [target](VirtualMachine *vm) { return vm->target() == target; });
252+
253+
if (it == runningBroadcastScripts.end()) {
254+
auto vm = script->start(target);
255+
m_runningScripts.push_back(vm);
256+
m_runningBroadcastMap[index].push_back({ sourceScript, vm.get() });
257+
}
239258
}
240259
}
241260
}
@@ -258,6 +277,37 @@ void Engine::stopTarget(Target *target, VirtualMachine *exceptScript)
258277
stopScript(script);
259278
}
260279

280+
void Engine::initClone(Sprite *clone)
281+
{
282+
if (!clone)
283+
return;
284+
285+
Sprite *source = clone->cloneParent();
286+
Target *root = clone->cloneRoot();
287+
assert(source);
288+
assert(root);
289+
290+
if (!source || !root)
291+
return;
292+
293+
auto it = m_cloneInitScriptsMap.find(root);
294+
295+
if (it != m_cloneInitScriptsMap.cend()) {
296+
const auto &scripts = it->second;
297+
298+
#ifndef NDEBUG
299+
// Since we're initializing the clone, it shouldn't have any running scripts
300+
for (const auto script : m_runningScripts)
301+
assert(script->target() != clone);
302+
#endif
303+
304+
for (auto script : scripts) {
305+
auto vm = script->start(clone);
306+
m_runningScripts.push_back(vm);
307+
}
308+
}
309+
}
310+
261311
void Engine::run()
262312
{
263313
auto frameDuration = std::chrono::milliseconds(33);
@@ -442,6 +492,7 @@ void Engine::addBroadcastScript(std::shared_ptr<Block> whenReceivedBlock, std::s
442492
auto id = findBroadcast(broadcast->name());
443493
if (m_broadcastMap.count(id) == 1) {
444494
std::vector<Script *> &scripts = m_broadcastMap[id];
495+
// TODO: Do not allow adding existing scripts
445496
scripts.push_back(m_scripts[whenReceivedBlock].get());
446497
} else {
447498
m_broadcastMap[id] = { m_scripts[whenReceivedBlock].get() };
@@ -452,6 +503,22 @@ void Engine::addBroadcastScript(std::shared_ptr<Block> whenReceivedBlock, std::s
452503
}
453504
}
454505

506+
void Engine::addCloneInitScript(std::shared_ptr<Block> hatBlock)
507+
{
508+
Target *target = hatBlock->target();
509+
Script *script = m_scripts[hatBlock].get();
510+
auto it = m_cloneInitScriptsMap.find(target);
511+
512+
if (it == m_cloneInitScriptsMap.cend())
513+
m_cloneInitScriptsMap[target] = { m_scripts[hatBlock].get() };
514+
else {
515+
auto &scripts = it->second;
516+
517+
if (std::find(scripts.begin(), scripts.end(), script) == scripts.cend())
518+
scripts.push_back(script);
519+
}
520+
}
521+
455522
const std::vector<std::shared_ptr<Target>> &Engine::targets() const
456523
{
457524
return m_targets;
@@ -460,14 +527,31 @@ const std::vector<std::shared_ptr<Target>> &Engine::targets() const
460527
void Engine::setTargets(const std::vector<std::shared_ptr<Target>> &newTargets)
461528
{
462529
m_targets = newTargets;
530+
m_variableOwners.clear();
531+
m_listOwners.clear();
463532

464-
// Set engine and target in all blocks
465533
for (auto target : m_targets) {
534+
// Set engine in the target
535+
target->setEngine(this);
466536
auto blocks = target->blocks();
537+
467538
for (auto block : blocks) {
539+
// Set engine and target in the block
468540
block->setEngine(this);
469541
block->setTarget(target.get());
470542
}
543+
544+
// Add variables to owner map
545+
const auto &variables = target->variables();
546+
547+
for (auto variable : variables)
548+
m_variableOwners[variable.get()] = target.get();
549+
550+
// Add lists to owner map
551+
const auto &lists = target->lists();
552+
553+
for (auto list : lists)
554+
m_listOwners[list.get()] = target.get();
471555
}
472556
}
473557

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

577+
Target *Engine::variableOwner(Variable *variable) const
578+
{
579+
auto it = m_variableOwners.find(variable);
580+
581+
if (it == m_variableOwners.cend())
582+
return nullptr;
583+
584+
return it->second;
585+
}
586+
587+
Target *Engine::listOwner(List *list) const
588+
{
589+
auto it = m_listOwners.find(list);
590+
591+
if (it == m_listOwners.cend())
592+
return nullptr;
593+
594+
return it->second;
595+
}
596+
493597
const std::vector<std::string> &Engine::extensions() const
494598
{
495599
return m_extensions;

src/engine/internal/engine.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ class Engine : public IEngine
3232
void broadcast(unsigned int index, VirtualMachine *sourceScript, bool wait = false) override;
3333
void stopScript(VirtualMachine *vm) override;
3434
void stopTarget(Target *target, VirtualMachine *exceptScript) override;
35+
void initClone(libscratchcpp::Sprite *clone) override;
3536
void run() override;
3637

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

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

64+
void addCloneInitScript(std::shared_ptr<Block> hatBlock) override;
65+
6366
const std::vector<std::shared_ptr<Target>> &targets() const override;
6467
void setTargets(const std::vector<std::shared_ptr<Target>> &newTargets) override;
6568
Target *targetAt(int index) const override;
6669
int findTarget(const std::string &targetName) const override;
6770

71+
Target *variableOwner(Variable *variable) const override;
72+
Target *listOwner(List *list) const override;
73+
6874
const std::vector<std::string> &extensions() const override;
6975
void setExtensions(const std::vector<std::string> &newExtensions) override;
7076

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

95104
bool m_breakFrame = false;
96105
bool m_skipFrame = false;

0 commit comments

Comments
 (0)