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
9 changes: 9 additions & 0 deletions include/scratchcpp/iengine.h
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,9 @@ class LIBSCRATCHCPP_EXPORT IEngine
/*! Calls the "when I start as a clone" blocks of the given sprite. */
virtual void initClone(Sprite *clone) = 0;

/*! Automatically called from clones that are being deleted. */
virtual void deinitClone(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 @@ -137,6 +140,12 @@ class LIBSCRATCHCPP_EXPORT IEngine
/*! Sets the stage height. */
virtual void setStageHeight(unsigned int height) = 0;

/*! Returns the maximum number of clones (or -1 if the limit is disabled). */
virtual int cloneLimit() const = 0;

/*! Sets the maximum number of clones (use -1 or any negative number to disable the limit). */
virtual void setCloneLimit(int limit) = 0;

/*! Returns true if there are any running script of the broadcast with the given index. */
virtual bool broadcastRunning(unsigned int index, VirtualMachine *sourceScript) = 0;

Expand Down
63 changes: 58 additions & 5 deletions src/engine/internal/engine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,17 @@ Engine::Engine() :
{
}

Engine::~Engine()
{
m_clones.clear();
}

void Engine::clear()
{
m_sections.clear();
m_targets.clear();
m_broadcasts.clear();
m_clones.clear();
}

// Resolves ID references and sets pointers of entities.
Expand Down Expand Up @@ -172,6 +178,11 @@ void Engine::frame()

void Engine::start()
{
if (m_running)
finalize();

deleteClones();

m_timer->reset();
m_running = true;

Expand All @@ -184,9 +195,8 @@ void Engine::start()

void Engine::stop()
{
m_runningScripts.clear();
m_scriptsToRemove.clear();
m_running = false;
finalize();
deleteClones();
}

void Engine::startScript(std::shared_ptr<Block> topLevelBlock, std::shared_ptr<Target> target)
Expand Down Expand Up @@ -301,7 +311,7 @@ void Engine::stopTarget(Target *target, VirtualMachine *exceptScript)

void Engine::initClone(Sprite *clone)
{
if (!clone)
if (!clone || ((m_cloneLimit >= 0) && (m_clones.size() >= m_cloneLimit)))
return;

Sprite *source = clone->cloneParent();
Expand All @@ -328,6 +338,14 @@ void Engine::initClone(Sprite *clone)
m_runningScripts.push_back(vm);
}
}

assert(std::find(m_clones.begin(), m_clones.end(), clone) == m_clones.end());
m_clones.push_back(clone);
}

void Engine::deinitClone(Sprite *clone)
{
m_clones.erase(std::remove(m_clones.begin(), m_clones.end(), clone), m_clones.end());
}

void Engine::run()
Expand Down Expand Up @@ -360,7 +378,7 @@ void Engine::run()
lastFrameTime = currentTime;
}

stop();
finalize();
}

bool Engine::isRunning() const
Expand Down Expand Up @@ -463,6 +481,16 @@ void Engine::setStageHeight(unsigned int height)
m_stageHeight = height;
}

int Engine::cloneLimit() const
{
return m_cloneLimit;
}

void Engine::setCloneLimit(int limit)
{
m_cloneLimit = limit < 0 ? -1 : limit;
}

bool Engine::broadcastRunning(unsigned int index, VirtualMachine *sourceScript)
{
if (index < 0 || index >= m_broadcasts.size())
Expand Down Expand Up @@ -879,6 +907,31 @@ BlockSectionContainer *Engine::blockSectionContainer(IBlockSection *section) con
return nullptr;
}

void Engine::finalize()
{
m_runningScripts.clear();
m_scriptsToRemove.clear();
m_running = false;
}

void Engine::deleteClones()
{
m_clones.clear();

for (auto target : m_targets) {
Sprite *sprite = dynamic_cast<Sprite *>(target.get());

if (sprite) {
std::vector<std::shared_ptr<Sprite>> clones = sprite->children();

for (auto clone : clones) {
assert(clone);
clone->~Sprite();
}
}
}
}

void Engine::updateFrameDuration()
{
m_frameDuration = std::chrono::milliseconds(static_cast<long>(1000 / m_fps));
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 @@ -21,6 +21,7 @@ class Engine : public IEngine
public:
Engine();
Engine(const Engine &) = delete;
~Engine();

void clear() override;
void resolveIds();
Expand All @@ -35,6 +36,7 @@ class Engine : public IEngine
void stopScript(VirtualMachine *vm) override;
void stopTarget(Target *target, VirtualMachine *exceptScript) override;
void initClone(libscratchcpp::Sprite *clone) override;
void deinitClone(libscratchcpp::Sprite *clone) override;
void run() override;

bool isRunning() const override;
Expand All @@ -61,6 +63,9 @@ class Engine : public IEngine
unsigned int stageHeight() const override;
void setStageHeight(unsigned int height) override;

int cloneLimit() const override;
void setCloneLimit(int limit) override;

bool broadcastRunning(unsigned int index, VirtualMachine *sourceScript) override;
bool broadcastByPtrRunning(Broadcast *broadcast, VirtualMachine *sourceScript) override;

Expand Down Expand Up @@ -109,6 +114,8 @@ class Engine : public IEngine
BlockSectionContainer *blockSectionContainer(IBlockSection *section) const;

private:
void finalize();
void deleteClones();
std::shared_ptr<Block> getBlock(const std::string &id);
std::shared_ptr<Variable> getVariable(const std::string &id);
std::shared_ptr<List> getList(const std::string &id);
Expand Down Expand Up @@ -142,6 +149,8 @@ class Engine : public IEngine
bool m_mousePressed = false;
unsigned int m_stageWidth = 480;
unsigned int m_stageHeight = 360;
int m_cloneLimit = 300;
std::vector<Sprite *> m_clones;

bool m_running = false;
bool m_breakFrame = false;
Expand Down
10 changes: 10 additions & 0 deletions src/scratch/sprite.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,16 @@ Sprite::Sprite() :
Sprite::~Sprite()
{
if (isClone()) {
IEngine *eng = engine();

if (eng) {
eng->deinitClone(this);

auto children = allChildren();
for (auto child : children)
eng->deinitClone(child.get());
}

assert(impl->cloneParent);
impl->cloneParent->impl->removeClone(this);
}
Expand Down
Binary file added test/clone_limit.sb3
Binary file not shown.
52 changes: 52 additions & 0 deletions test/engine/engine_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -547,6 +547,58 @@ TEST(EngineTest, Clones)
}
}

TEST(EngineTest, CloneLimit)
{
Project p("clone_limit.sb3");
ASSERT_TRUE(p.load());
auto engine = p.engine();
ASSERT_EQ(engine->cloneLimit(), 300);

// TODO: Set "infinite" FPS and remove this (#254)
engine->setFps(100000);

Stage *stage = engine->stage();
ASSERT_TRUE(stage);

p.run();
ASSERT_VAR(stage, "count");
ASSERT_EQ(GET_VAR(stage, "count")->value().toInt(), 300);
ASSERT_VAR(stage, "delete_passed");
ASSERT_TRUE(GET_VAR(stage, "delete_passed")->value().toBool());

engine->setCloneLimit(475);
ASSERT_EQ(engine->cloneLimit(), 475);
p.run();
ASSERT_VAR(stage, "count");
ASSERT_EQ(GET_VAR(stage, "count")->value().toInt(), 475);
ASSERT_VAR(stage, "delete_passed");
ASSERT_TRUE(GET_VAR(stage, "delete_passed")->value().toBool());

engine->setCloneLimit(0);
ASSERT_EQ(engine->cloneLimit(), 0);
p.run();
ASSERT_VAR(stage, "count");
ASSERT_EQ(GET_VAR(stage, "count")->value().toInt(), 0);
ASSERT_VAR(stage, "delete_passed");
ASSERT_TRUE(GET_VAR(stage, "delete_passed")->value().toBool());

engine->setCloneLimit(-1);
ASSERT_EQ(engine->cloneLimit(), -1);
p.run();
ASSERT_VAR(stage, "count");
ASSERT_GT(GET_VAR(stage, "count")->value().toInt(), 500);
ASSERT_VAR(stage, "delete_passed");
ASSERT_TRUE(GET_VAR(stage, "delete_passed")->value().toBool());

engine->setCloneLimit(-5);
ASSERT_EQ(engine->cloneLimit(), -1);
p.run();
ASSERT_VAR(stage, "count");
ASSERT_GT(GET_VAR(stage, "count")->value().toInt(), 500);
ASSERT_VAR(stage, "delete_passed");
ASSERT_TRUE(GET_VAR(stage, "delete_passed")->value().toBool());
}

// TODO: Uncomment this after fixing #256 and #257
/*TEST(EngineTest, BackdropBroadcasts)
{
Expand Down
4 changes: 4 additions & 0 deletions test/mocks/enginemock.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ class EngineMock : public IEngine
MOCK_METHOD(void, stopScript, (VirtualMachine *), (override));
MOCK_METHOD(void, stopTarget, (Target *, VirtualMachine *), (override));
MOCK_METHOD(void, initClone, (Sprite *), (override));
MOCK_METHOD(void, deinitClone, (Sprite *), (override));
MOCK_METHOD(void, run, (), (override));

MOCK_METHOD(bool, isRunning, (), (const, override));
Expand All @@ -49,6 +50,9 @@ class EngineMock : public IEngine
MOCK_METHOD(unsigned int, stageHeight, (), (const, override));
MOCK_METHOD(void, setStageHeight, (unsigned int), (override));

MOCK_METHOD(int, cloneLimit, (), (const, override));
MOCK_METHOD(void, setCloneLimit, (int), (override));

MOCK_METHOD(bool, broadcastRunning, (unsigned int, VirtualMachine *), (override));
MOCK_METHOD(bool, broadcastByPtrRunning, (Broadcast *, VirtualMachine *), (override));

Expand Down
Loading