Skip to content

Commit 7fa848d

Browse files
authored
Merge pull request #383 from scratchcpp/refactor_clones
Refactor clone API
2 parents d3eef68 + e81c1c3 commit 7fa848d

File tree

14 files changed

+191
-220
lines changed

14 files changed

+191
-220
lines changed

include/scratchcpp/iengine.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,10 +73,10 @@ class LIBSCRATCHCPP_EXPORT IEngine
7373
virtual void stopTarget(Target *target, VirtualMachine *exceptScript) = 0;
7474

7575
/*! Calls the "when I start as a clone" blocks of the given sprite. */
76-
virtual void initClone(Sprite *clone) = 0;
76+
virtual void initClone(std::shared_ptr<Sprite> clone) = 0;
7777

7878
/*! Automatically called from clones that are being deleted. */
79-
virtual void deinitClone(Sprite *clone) = 0;
79+
virtual void deinitClone(std::shared_ptr<Sprite> clone) = 0;
8080

8181
/*!
8282
* Calls and runs "when green flag clicked" blocks.

include/scratchcpp/sprite.h

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@ class IGraphicsEffect;
1313
class SpritePrivate;
1414

1515
/*! \brief The Sprite class represents a Scratch sprite. */
16-
class LIBSCRATCHCPP_EXPORT Sprite : public Target
16+
class LIBSCRATCHCPP_EXPORT Sprite
17+
: public Target
18+
, public std::enable_shared_from_this<Sprite>
1719
{
1820
public:
1921
enum class RotationStyle
@@ -25,18 +27,16 @@ class LIBSCRATCHCPP_EXPORT Sprite : public Target
2527

2628
Sprite();
2729
Sprite(const Sprite &) = delete;
28-
~Sprite();
2930

3031
void setInterface(ISpriteHandler *newInterface);
3132

3233
std::shared_ptr<Sprite> clone();
34+
void deleteClone();
3335

3436
bool isClone() const;
3537

36-
Sprite *cloneRoot() const;
37-
Sprite *cloneParent() const;
38-
const std::vector<std::shared_ptr<Sprite>> &children() const;
39-
std::vector<std::shared_ptr<Sprite>> allChildren() const;
38+
Sprite *cloneSprite() const;
39+
const std::vector<std::shared_ptr<Sprite>> &clones() const;
4040

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

src/blocks/controlblocks.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -286,11 +286,11 @@ unsigned int ControlBlocks::createCloneOfMyself(VirtualMachine *vm)
286286

287287
unsigned int ControlBlocks::deleteThisClone(VirtualMachine *vm)
288288
{
289-
Target *target = vm->target();
289+
Sprite *sprite = dynamic_cast<Sprite *>(vm->target());
290290

291-
if (target) {
292-
vm->engine()->stopTarget(target, nullptr);
293-
target->~Target();
291+
if (sprite && sprite->isClone()) {
292+
vm->engine()->stopTarget(sprite, nullptr);
293+
sprite->deleteClone();
294294
}
295295

296296
return 0;

src/engine/internal/engine.cpp

Lines changed: 17 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,8 @@ void Engine::broadcastByPtr(Broadcast *broadcast, VirtualMachine *sourceScript,
239239
if (!root->isStage()) {
240240
Sprite *sprite = dynamic_cast<Sprite *>(root);
241241
assert(sprite);
242-
auto children = sprite->allChildren();
242+
assert(!sprite->isClone());
243+
const auto &children = sprite->clones();
243244

244245
for (auto child : children)
245246
targets.push_back(child.get());
@@ -278,17 +279,15 @@ void Engine::stopTarget(Target *target, VirtualMachine *exceptScript)
278279
stopScript(script);
279280
}
280281

281-
void Engine::initClone(Sprite *clone)
282+
void Engine::initClone(std::shared_ptr<Sprite> clone)
282283
{
283284
if (!clone || ((m_cloneLimit >= 0) && (m_clones.size() >= m_cloneLimit)))
284285
return;
285286

286-
Sprite *source = clone->cloneParent();
287-
Target *root = clone->cloneRoot();
288-
assert(source);
287+
Target *root = clone->cloneSprite();
289288
assert(root);
290289

291-
if (!source || !root)
290+
if (!root)
292291
return;
293292

294293
auto it = m_cloneInitScriptsMap.find(root);
@@ -300,26 +299,26 @@ void Engine::initClone(Sprite *clone)
300299
// Since we're initializing the clone, it shouldn't have any running scripts
301300
for (const auto &[target, targetScripts] : m_runningScripts) {
302301
for (const auto script : targetScripts)
303-
assert((target != clone) || (std::find(m_scriptsToRemove.begin(), m_scriptsToRemove.end(), script.get()) != m_scriptsToRemove.end()));
302+
assert((target != clone.get()) || (std::find(m_scriptsToRemove.begin(), m_scriptsToRemove.end(), script.get()) != m_scriptsToRemove.end()));
304303
}
305304
#endif
306305

307306
for (auto script : scripts) {
308-
auto vm = script->start(clone);
307+
auto vm = script->start(clone.get());
309308
addRunningScript(vm);
310309
}
311310
}
312311

313312
assert(std::find(m_clones.begin(), m_clones.end(), clone) == m_clones.end());
314-
assert(std::find(m_executableTargets.begin(), m_executableTargets.end(), clone) == m_executableTargets.end());
315-
m_clones.push_back(clone);
316-
m_executableTargets.push_back(clone); // execution order needs to be updated after this
313+
assert(std::find(m_executableTargets.begin(), m_executableTargets.end(), clone.get()) == m_executableTargets.end());
314+
m_clones.insert(clone);
315+
m_executableTargets.push_back(clone.get()); // execution order needs to be updated after this
317316
}
318317

319-
void Engine::deinitClone(Sprite *clone)
318+
void Engine::deinitClone(std::shared_ptr<Sprite> clone)
320319
{
321-
m_clones.erase(std::remove(m_clones.begin(), m_clones.end(), clone), m_clones.end());
322-
m_executableTargets.erase(std::remove(m_executableTargets.begin(), m_executableTargets.end(), clone), m_executableTargets.end());
320+
m_clones.erase(clone);
321+
m_executableTargets.erase(std::remove(m_executableTargets.begin(), m_executableTargets.end(), clone.get()), m_executableTargets.end());
323322
}
324323

325324
void Engine::run()
@@ -1204,11 +1203,11 @@ void Engine::deleteClones()
12041203
Sprite *sprite = dynamic_cast<Sprite *>(target.get());
12051204

12061205
if (sprite) {
1207-
std::vector<std::shared_ptr<Sprite>> clones = sprite->children();
1206+
std::vector<std::shared_ptr<Sprite>> clones = sprite->clones();
12081207

12091208
for (auto clone : clones) {
12101209
assert(clone);
1211-
clone->~Sprite();
1210+
clone->deleteClone();
12121211
}
12131212
}
12141213
}
@@ -1219,8 +1218,8 @@ void Engine::deleteClones()
12191218
void Engine::removeExecutableClones()
12201219
{
12211220
// Remove clones from the executable targets
1222-
for (Target *target : m_clones)
1223-
m_executableTargets.erase(std::remove(m_executableTargets.begin(), m_executableTargets.end(), target), m_executableTargets.end());
1221+
for (std::shared_ptr<Sprite> clone : m_clones)
1222+
m_executableTargets.erase(std::remove(m_executableTargets.begin(), m_executableTargets.end(), clone.get()), m_executableTargets.end());
12241223
}
12251224

12261225
void Engine::updateFrameDuration()

src/engine/internal/engine.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include <memory>
1010
#include <chrono>
1111
#include <mutex>
12+
#include <set>
1213

1314
#include "blocksectioncontainer.h"
1415

@@ -36,8 +37,8 @@ class Engine : public IEngine
3637
void broadcastByPtr(Broadcast *broadcast, VirtualMachine *sourceScript, bool wait = false) override;
3738
void stopScript(VirtualMachine *vm) override;
3839
void stopTarget(Target *target, VirtualMachine *exceptScript) override;
39-
void initClone(libscratchcpp::Sprite *clone) override;
40-
void deinitClone(libscratchcpp::Sprite *clone) override;
40+
void initClone(std::shared_ptr<Sprite> clone) override;
41+
void deinitClone(std::shared_ptr<Sprite> clone) override;
4142

4243
void run() override;
4344
void runEventLoop() override;
@@ -183,7 +184,7 @@ class Engine : public IEngine
183184
unsigned int m_stageWidth = 480;
184185
unsigned int m_stageHeight = 360;
185186
int m_cloneLimit = 300;
186-
std::vector<Sprite *> m_clones;
187+
std::set<std::shared_ptr<Sprite>> m_clones;
187188
bool m_spriteFencingEnabled = true;
188189

189190
bool m_running = false;

src/engine/script.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ std::shared_ptr<VirtualMachine> Script::start(Target *target)
6363
sprite = dynamic_cast<Sprite *>(target);
6464

6565
if (impl->target && sprite && sprite->isClone() && impl->engine) {
66-
Target *root = sprite->cloneRoot();
66+
Target *root = sprite->cloneSprite();
6767

6868
if (root != impl->target) {
6969
std::cout << "warning: a clone tried to start a script of another target (this is a bug in libscratchcpp or in your code!)" << std::endl;

src/scratch/sprite.cpp

Lines changed: 36 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include <scratchcpp/costume.h>
99
#include <scratchcpp/rect.h>
1010
#include <cassert>
11+
#include <iostream>
1112

1213
#include "sprite_p.h"
1314

@@ -20,25 +21,6 @@ Sprite::Sprite() :
2021
{
2122
}
2223

23-
/*! Destroys the Sprite object. */
24-
Sprite::~Sprite()
25-
{
26-
if (isClone()) {
27-
IEngine *eng = engine();
28-
29-
if (eng) {
30-
eng->deinitClone(this);
31-
32-
auto children = allChildren();
33-
for (auto child : children)
34-
eng->deinitClone(child.get());
35-
}
36-
37-
assert(impl->cloneParent);
38-
impl->cloneParent->impl->removeClone(this);
39-
}
40-
}
41-
4224
/*! Sets the sprite interface. */
4325
void Sprite::setInterface(ISpriteHandler *newInterface)
4426
{
@@ -55,13 +37,13 @@ std::shared_ptr<Sprite> Sprite::clone()
5537
if (eng && (eng->cloneLimit() == -1 || eng->cloneCount() < eng->cloneLimit())) {
5638
std::shared_ptr<Sprite> clone = std::make_shared<Sprite>();
5739

58-
if (impl->cloneRoot == nullptr)
59-
clone->impl->cloneRoot = this;
60-
else
61-
clone->impl->cloneRoot = impl->cloneRoot;
62-
63-
clone->impl->cloneParent = this;
64-
impl->childClones.push_back(clone);
40+
if (impl->cloneSprite == nullptr) {
41+
clone->impl->cloneSprite = this;
42+
impl->clones.push_back(clone);
43+
} else {
44+
clone->impl->cloneSprite = impl->cloneSprite;
45+
impl->cloneSprite->impl->clones.push_back(clone);
46+
}
6547

6648
// Copy data
6749
clone->setName(name());
@@ -91,7 +73,7 @@ std::shared_ptr<Sprite> Sprite::clone()
9173
clone->setEngine(engine());
9274

9375
// Call "when I start as clone" scripts
94-
eng->initClone(clone.get());
76+
eng->initClone(clone);
9577

9678
if (impl->visible)
9779
eng->requestRedraw();
@@ -108,42 +90,43 @@ std::shared_ptr<Sprite> Sprite::clone()
10890
return nullptr;
10991
}
11092

111-
/*! Returns true if this is a clone. */
112-
bool Sprite::isClone() const
93+
/*! Deletes this clone (if the sprite is a clone). */
94+
void Sprite::deleteClone()
11395
{
114-
return (impl->cloneParent != nullptr);
115-
}
96+
assert(isClone());
11697

117-
/*! Returns the sprite this clone was created from, or nullptr if this isn't a clone. */
118-
Sprite *Sprite::cloneRoot() const
119-
{
120-
return impl->cloneRoot;
98+
if (isClone()) {
99+
IEngine *eng = engine();
100+
101+
if (eng)
102+
eng->deinitClone(shared_from_this());
103+
104+
assert(impl->cloneSprite);
105+
impl->cloneDeleted = true;
106+
impl->cloneSprite->impl->removeClone(this);
107+
}
121108
}
122109

123-
/*! Returns the sprite or clone this clone was created from, or nullptr if this isn't a clone. */
124-
Sprite *Sprite::cloneParent() const
110+
/*! Returns true if this is a clone. */
111+
bool Sprite::isClone() const
125112
{
126-
return impl->cloneParent;
113+
return (impl->cloneSprite != nullptr);
127114
}
128115

129-
/*! Returns list of child clones. */
130-
const std::vector<std::shared_ptr<Sprite>> &Sprite::children() const
116+
/*! Returns the sprite this clone was created from, or nullptr if this isn't a clone. */
117+
Sprite *Sprite::cloneSprite() const
131118
{
132-
return impl->childClones;
119+
return impl->cloneSprite;
133120
}
134121

135-
/*! Returns list of child clones and their children (recursive). */
136-
std::vector<std::shared_ptr<Sprite>> Sprite::allChildren() const
122+
/*! Returns list of clones of the sprite. */
123+
const std::vector<std::shared_ptr<Sprite>> &Sprite::clones() const
137124
{
138-
std::vector<std::shared_ptr<Sprite>> ret;
139-
140-
for (auto clone : impl->childClones) {
141-
ret.push_back(clone);
142-
auto children = clone->allChildren();
143-
ret.insert(ret.end(), children.begin(), children.end());
144-
}
145-
146-
return ret;
125+
if (isClone()) {
126+
assert(impl->cloneSprite);
127+
return impl->cloneSprite->impl->clones;
128+
} else
129+
return impl->clones;
147130
}
148131

149132
/*! Returns true if the sprite is visible. */
@@ -419,7 +402,7 @@ void Sprite::clearGraphicsEffects()
419402

420403
Target *Sprite::dataSource() const
421404
{
422-
return impl->cloneRoot;
405+
return impl->cloneSprite;
423406
}
424407

425408
void Sprite::setXY(double x, double y)

src/scratch/sprite_p.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,9 @@ SpritePrivate::SpritePrivate(Sprite *sprite) :
2121
void SpritePrivate::removeClone(Sprite *clone)
2222
{
2323
int index = 0;
24-
for (const auto &child : childClones) {
24+
for (const auto &child : clones) {
2525
if (child.get() == clone) {
26-
childClones.erase(childClones.begin() + index);
26+
clones.erase(clones.begin() + index);
2727
return;
2828
}
2929

src/scratch/sprite_p.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,9 @@ struct SpritePrivate
2121

2222
Sprite *sprite = nullptr;
2323
ISpriteHandler *iface = nullptr;
24-
Sprite *cloneRoot = nullptr;
25-
Sprite *cloneParent = nullptr;
26-
std::vector<std::shared_ptr<Sprite>> childClones;
24+
Sprite *cloneSprite = nullptr;
25+
std::vector<std::shared_ptr<Sprite>> clones;
26+
bool cloneDeleted = false;
2727
bool visible = true;
2828
double x = 0;
2929
double y = 0;

0 commit comments

Comments
 (0)