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
33 changes: 33 additions & 0 deletions src/engine/EngineRuntime.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,26 @@ EngineConfig normalizeConfig(EngineConfig config) {

EngineRuntime::EngineRuntime(EngineConfig config)
: config_(normalizeConfig(config)),
world_(core_, buffer_),
frameClock_(config_) {
}

void EngineRuntime::addSystem(std::unique_ptr<EngineSystem> system) {
systems_.push_back(std::move(system));
}

void EngineRuntime::initialize() {
frameClock_.reset();
core_ = EcsCore{};
buffer_ = CommandBuffer{};
stats_ = {};
stats_.state = EngineState::Ready;
++runIndex_;

for (auto& system : systems_) {
system->configure(world_);
buffer_.flush(core_);
}
}

void EngineRuntime::play() {
Expand All @@ -49,6 +61,8 @@ void EngineRuntime::pause() {

void EngineRuntime::stop() {
frameClock_.reset();
core_ = EcsCore{};
buffer_ = CommandBuffer{};
stats_ = {};
stats_.state = EngineState::Stopped;
}
Expand All @@ -58,6 +72,11 @@ void EngineRuntime::stepFrame(double deltaSeconds) {
initialize();
}

if (stats_.state == EngineState::Paused) {
stats_.fixedStepsThisFrame = 0;
return;
}

frameClock_.beginFrame(deltaSeconds);

++stats_.frameIndex;
Expand All @@ -67,6 +86,20 @@ void EngineRuntime::stepFrame(double deltaSeconds) {
frameClock_.consumeFixedStep();
++stats_.fixedStepIndex;
++stats_.fixedStepsThisFrame;

const EngineStepContext ctx{
.frameIndex = stats_.frameIndex,
.fixedStepIndex = stats_.fixedStepIndex,
.alpha = frameClock_.alpha(),
.runIndex = runIndex_,
.derivedSeed = 0,
};

for (auto& system : systems_) {
system->update(world_, ctx);
}

buffer_.flush(core_);
}

stats_.alpha = frameClock_.alpha();
Expand Down
9 changes: 9 additions & 0 deletions src/engine/EngineRuntime.h
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
#pragma once

#include <cstdint>
#include <memory>
#include <vector>

#include "engine/CommandBuffer.h"
#include "engine/EcsCore.h"
#include "engine/EngineConfig.h"
#include "engine/EngineStats.h"
#include "engine/EngineSystem.h"
Expand All @@ -13,6 +17,8 @@ class EngineRuntime {
public:
explicit EngineRuntime(EngineConfig config = {});

void addSystem(std::unique_ptr<EngineSystem> system);

void initialize();
void play();
void pause();
Expand All @@ -29,9 +35,12 @@ class EngineRuntime {
private:
EngineConfig config_;
EngineStats stats_;
EcsCore core_;
CommandBuffer buffer_;
EngineWorld world_;
FrameClock frameClock_;
std::uint64_t runIndex_{0};
std::vector<std::unique_ptr<EngineSystem>> systems_;
};

} // namespace safecrowd::engine
14 changes: 14 additions & 0 deletions src/engine/EngineSystem.h
Original file line number Diff line number Diff line change
@@ -1,10 +1,24 @@
#pragma once

#include "engine/CommandBuffer.h"
#include "engine/EngineStepContext.h"
#include "engine/WorldQuery.h"

namespace safecrowd::engine {

class EngineWorld {
public:
EngineWorld() = delete;
explicit EngineWorld(EcsCore& core, CommandBuffer& buffer)
: query_(core), commands_(buffer) {}

[[nodiscard]] WorldQuery& query() noexcept { return query_; }
[[nodiscard]] const WorldQuery& query() const noexcept { return query_; }
[[nodiscard]] WorldCommands& commands() noexcept { return commands_; }

private:
WorldQuery query_;
WorldCommands commands_;
};

class EngineSystem {
Expand Down
146 changes: 146 additions & 0 deletions tests/EngineRuntimeTests.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,56 @@
#include "TestSupport.h"

#include <cstddef>
#include <memory>

#include "engine/EngineRuntime.h"

namespace {

struct Marker {};

class UpdateCounterSystem : public safecrowd::engine::EngineSystem {
public:
int& count;
explicit UpdateCounterSystem(int& c) : count(c) {}
void update(safecrowd::engine::EngineWorld&, const safecrowd::engine::EngineStepContext&) override {
++count;
}
};

class SpawnMarkerSystem : public safecrowd::engine::EngineSystem {
public:
void update(safecrowd::engine::EngineWorld& world, const safecrowd::engine::EngineStepContext&) override {
world.commands().spawnEntity(Marker{});
}
};

class ConfigureSpawnMarkerSystem : public safecrowd::engine::EngineSystem {
public:
void configure(safecrowd::engine::EngineWorld& world) override {
world.commands().spawnEntity(Marker{});
}

void update(safecrowd::engine::EngineWorld&, const safecrowd::engine::EngineStepContext&) override {
}
};

class ConfigureObserveMarkerSystem : public safecrowd::engine::EngineSystem {
public:
std::size_t& count;

explicit ConfigureObserveMarkerSystem(std::size_t& c) : count(c) {}

void configure(safecrowd::engine::EngineWorld& world) override {
count = world.query().view<Marker>().size();
}

void update(safecrowd::engine::EngineWorld&, const safecrowd::engine::EngineStepContext&) override {
}
};

} // namespace

SC_TEST(EngineRuntimePlayAndStepUpdatesStats) {
safecrowd::engine::EngineRuntime runtime({
.fixedDeltaTime = 0.25,
Expand All @@ -20,6 +69,103 @@ SC_TEST(EngineRuntimePlayAndStepUpdatesStats) {
SC_EXPECT_NEAR(stats.alpha, 0.0, 1e-9);
}

SC_TEST(EngineRuntime_RegisteredSystem_UpdateCalledOnFixedStep) {
int count = 0;
safecrowd::engine::EngineRuntime runtime({
.fixedDeltaTime = 0.25,
.maxCatchUpSteps = 4,
.baseSeed = 1,
});

runtime.addSystem(std::make_unique<UpdateCounterSystem>(count));
runtime.play();
runtime.stepFrame(0.50); // 2 fixed steps

SC_EXPECT_EQ(count, 2);
}

SC_TEST(EngineRuntime_WorldCommands_FlushedAfterEachFixedStep) {
safecrowd::engine::EngineRuntime runtime({
.fixedDeltaTime = 0.25,
.maxCatchUpSteps = 4,
.baseSeed = 1,
});

runtime.addSystem(std::make_unique<SpawnMarkerSystem>());
runtime.play();
runtime.stepFrame(0.25); // 1 fixed step

const auto entities = runtime.world().query().view<Marker>();
SC_EXPECT_EQ(entities.size(), std::size_t{1});
}

SC_TEST(EngineRuntime_ConfigureCommands_AreVisibleToLaterSystems) {
std::size_t configuredMarkerCount = 0;

safecrowd::engine::EngineRuntime runtime({
.fixedDeltaTime = 0.25,
.maxCatchUpSteps = 4,
.baseSeed = 1,
});

runtime.addSystem(std::make_unique<ConfigureSpawnMarkerSystem>());
runtime.addSystem(std::make_unique<ConfigureObserveMarkerSystem>(configuredMarkerCount));

runtime.play();

SC_EXPECT_EQ(configuredMarkerCount, std::size_t{1});
SC_EXPECT_EQ(runtime.world().query().view<Marker>().size(), std::size_t{1});
}

SC_TEST(EngineRuntime_Stop_ClearsWorldAndPendingCommandsBeforeNextRun) {
safecrowd::engine::EngineRuntime runtime({
.fixedDeltaTime = 0.25,
.maxCatchUpSteps = 4,
.baseSeed = 1,
});

runtime.addSystem(std::make_unique<SpawnMarkerSystem>());
runtime.play();
runtime.stepFrame(0.25);
SC_EXPECT_EQ(runtime.world().query().view<Marker>().size(), std::size_t{1});

runtime.world().commands().spawnEntity(Marker{});
runtime.stop();

SC_EXPECT_EQ(runtime.world().query().view<Marker>().size(), std::size_t{0});

runtime.play();
runtime.stepFrame(0.25);

SC_EXPECT_EQ(runtime.world().query().view<Marker>().size(), std::size_t{1});
}

SC_TEST(EngineRuntime_PausedRuntime_DoesNotAdvanceSimulation) {
int count = 0;

safecrowd::engine::EngineRuntime runtime({
.fixedDeltaTime = 0.25,
.maxCatchUpSteps = 4,
.baseSeed = 1,
});

runtime.addSystem(std::make_unique<UpdateCounterSystem>(count));
runtime.play();
runtime.stepFrame(0.25);
runtime.pause();
runtime.stepFrame(1.00);

const auto& pausedStats = runtime.stats();
SC_EXPECT_EQ(count, 1);
SC_EXPECT_EQ(pausedStats.frameIndex, 1ULL);
SC_EXPECT_EQ(pausedStats.fixedStepIndex, 1ULL);
SC_EXPECT_EQ(pausedStats.fixedStepsThisFrame, 0U);

runtime.play();
runtime.stepFrame(0.25);
SC_EXPECT_EQ(count, 2);
}

SC_TEST(EngineRuntimePauseAndStopResetLifecycleState) {
safecrowd::engine::EngineRuntime runtime({
.fixedDeltaTime = 0.25,
Expand Down
Loading