mirror of
https://github.com/BlackMATov/ecs.hpp.git
synced 2025-12-16 22:19:21 +07:00
3
.codecov.yml
Normal file
3
.codecov.yml
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
ignore:
|
||||||
|
- catch.hpp
|
||||||
|
- catch_main.hpp
|
||||||
@@ -62,6 +62,8 @@ matrix:
|
|||||||
- os: osx
|
- os: osx
|
||||||
osx_image: xcode10
|
osx_image: xcode10
|
||||||
compiler: clang
|
compiler: clang
|
||||||
|
addons: { homebrew: { packages: ["lcov"] } }
|
||||||
|
after_success: ./scripts/upload_coverage.sh
|
||||||
before_install:
|
before_install:
|
||||||
- eval "${MATRIX_EVAL}"
|
- eval "${MATRIX_EVAL}"
|
||||||
script:
|
script:
|
||||||
|
|||||||
@@ -1,6 +1,36 @@
|
|||||||
cmake_minimum_required(VERSION 3.5 FATAL_ERROR)
|
cmake_minimum_required(VERSION 3.5 FATAL_ERROR)
|
||||||
project(ecs)
|
project(ecs)
|
||||||
|
|
||||||
|
#
|
||||||
|
# coverage mode
|
||||||
|
#
|
||||||
|
|
||||||
|
option(ECS_BUILD_WITH_COVERAGE "Build with coverage" OFF)
|
||||||
|
if(ECS_BUILD_WITH_COVERAGE AND (CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang"))
|
||||||
|
add_definitions(-DECS_BUILD_WITH_COVERAGE)
|
||||||
|
set(ECS_COVERAGE_FLAGS "--coverage")
|
||||||
|
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} ${ECS_COVERAGE_FLAGS}")
|
||||||
|
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${ECS_COVERAGE_FLAGS}")
|
||||||
|
set(CMAKE_LINKER_FLAGS_DEBUG "${CMAKE_LINKER_FLAGS_DEBUG} ${ECS_COVERAGE_FLAGS}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
#
|
||||||
|
# sanitizer mode
|
||||||
|
#
|
||||||
|
|
||||||
|
option(ECS_BUILD_WITH_SANITIZER "Build with sanitizer" OFF)
|
||||||
|
if(ECS_BUILD_WITH_SANITIZER AND (CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang"))
|
||||||
|
add_definitions(-DECS_BUILD_WITH_SANITIZER)
|
||||||
|
set(ECS_SANITIZER_FLAGS "-fno-omit-frame-pointer -fsanitize=address")
|
||||||
|
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} ${ECS_SANITIZER_FLAGS}")
|
||||||
|
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${ECS_SANITIZER_FLAGS}")
|
||||||
|
set(CMAKE_LINKER_FLAGS_DEBUG "${CMAKE_LINKER_FLAGS_DEBUG} ${ECS_SANITIZER_FLAGS}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
#
|
||||||
|
# tests executable
|
||||||
|
#
|
||||||
|
|
||||||
file(GLOB test_sources "*.cpp" "*.hpp")
|
file(GLOB test_sources "*.cpp" "*.hpp")
|
||||||
add_executable(${PROJECT_NAME} ${test_sources})
|
add_executable(${PROJECT_NAME} ${test_sources})
|
||||||
|
|
||||||
|
|||||||
13
README.md
13
README.md
@@ -2,18 +2,21 @@
|
|||||||
|
|
||||||
[![travis][badge.travis]][travis]
|
[![travis][badge.travis]][travis]
|
||||||
[![appveyor][badge.appveyor]][appveyor]
|
[![appveyor][badge.appveyor]][appveyor]
|
||||||
|
[![codecov][badge.codecov]][codecov]
|
||||||
[![language][badge.language]][language]
|
[![language][badge.language]][language]
|
||||||
[![license][badge.license]][license]
|
[![license][badge.license]][license]
|
||||||
[![paypal][badge.paypal]][paypal]
|
[![paypal][badge.paypal]][paypal]
|
||||||
|
|
||||||
[badge.travis]: https://img.shields.io/travis/BlackMATov/ecs.hpp/master.svg?logo=travis&style=for-the-badge
|
[badge.travis]: https://img.shields.io/travis/BlackMATov/ecs.hpp/master.svg?logo=travis
|
||||||
[badge.appveyor]: https://img.shields.io/appveyor/ci/BlackMATov/ecs-hpp/master.svg?logo=appveyor&style=for-the-badge
|
[badge.appveyor]: https://img.shields.io/appveyor/ci/BlackMATov/ecs-hpp/master.svg?logo=appveyor
|
||||||
[badge.language]: https://img.shields.io/badge/language-C%2B%2B14-red.svg?style=for-the-badge
|
[badge.codecov]: https://img.shields.io/codecov/c/github/BlackMATov/ecs-hpp/master.svg?logo=codecov
|
||||||
[badge.license]: https://img.shields.io/badge/license-MIT-blue.svg?style=for-the-badge
|
[badge.language]: https://img.shields.io/badge/language-C%2B%2B14-red.svg
|
||||||
[badge.paypal]: https://img.shields.io/badge/donate-PayPal-orange.svg?logo=paypal&colorA=00457C&style=for-the-badge
|
[badge.license]: https://img.shields.io/badge/license-MIT-blue.svg
|
||||||
|
[badge.paypal]: https://img.shields.io/badge/donate-PayPal-orange.svg?logo=paypal&colorA=00457C
|
||||||
|
|
||||||
[travis]: https://travis-ci.org/BlackMATov/ecs.hpp
|
[travis]: https://travis-ci.org/BlackMATov/ecs.hpp
|
||||||
[appveyor]: https://ci.appveyor.com/project/BlackMATov/ecs-hpp
|
[appveyor]: https://ci.appveyor.com/project/BlackMATov/ecs-hpp
|
||||||
|
[codecov]: https://codecov.io/gh/BlackMATov/ecs.hpp
|
||||||
[language]: https://en.wikipedia.org/wiki/C%2B%2B14
|
[language]: https://en.wikipedia.org/wiki/C%2B%2B14
|
||||||
[license]: https://en.wikipedia.org/wiki/MIT_License
|
[license]: https://en.wikipedia.org/wiki/MIT_License
|
||||||
[paypal]: https://www.paypal.me/matov
|
[paypal]: https://www.paypal.me/matov
|
||||||
|
|||||||
145
ecs.hpp
145
ecs.hpp
@@ -6,6 +6,151 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
#include <mutex>
|
||||||
|
#include <limits>
|
||||||
|
#include <functional>
|
||||||
|
#include <unordered_set>
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// config
|
||||||
|
//
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
namespace ecs_hpp
|
namespace ecs_hpp
|
||||||
{
|
{
|
||||||
|
class world;
|
||||||
|
class entity;
|
||||||
|
|
||||||
|
using entity_id = std::uint64_t;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// entity
|
||||||
|
//
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
namespace ecs_hpp
|
||||||
|
{
|
||||||
|
class entity final {
|
||||||
|
public:
|
||||||
|
entity(world& owner);
|
||||||
|
entity(world& owner, entity_id id);
|
||||||
|
|
||||||
|
const world& owner() const noexcept;
|
||||||
|
entity_id id() const noexcept;
|
||||||
|
|
||||||
|
bool destroy();
|
||||||
|
private:
|
||||||
|
world& owner_;
|
||||||
|
entity_id id_{0u};
|
||||||
|
};
|
||||||
|
|
||||||
|
bool operator==(const entity& l, const entity& r) noexcept;
|
||||||
|
bool operator!=(const entity& l, const entity& r) noexcept;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace std
|
||||||
|
{
|
||||||
|
template <>
|
||||||
|
struct hash<ecs_hpp::entity>
|
||||||
|
: std::unary_function<const ecs_hpp::entity&, std::size_t>
|
||||||
|
{
|
||||||
|
std::size_t operator()(const ecs_hpp::entity& ent) const noexcept {
|
||||||
|
return std::hash<ecs_hpp::entity_id>()(ent.id());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// world
|
||||||
|
//
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
namespace ecs_hpp
|
||||||
|
{
|
||||||
|
class world final {
|
||||||
|
public:
|
||||||
|
world();
|
||||||
|
~world() noexcept;
|
||||||
|
|
||||||
|
entity create_entity();
|
||||||
|
bool destroy_entity(const entity& ent);
|
||||||
|
bool is_entity_alive(const entity& ent) const noexcept;
|
||||||
|
private:
|
||||||
|
mutable std::mutex mutex_;
|
||||||
|
entity_id last_entity_id_{0u};
|
||||||
|
std::unordered_set<entity> entities_;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// entity impl
|
||||||
|
//
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
namespace ecs_hpp
|
||||||
|
{
|
||||||
|
inline entity::entity(world& owner)
|
||||||
|
: owner_(owner) {}
|
||||||
|
|
||||||
|
inline entity::entity(world& owner, entity_id id)
|
||||||
|
: owner_(owner)
|
||||||
|
, id_(id) {}
|
||||||
|
|
||||||
|
inline const world& entity::owner() const noexcept {
|
||||||
|
return owner_;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline entity_id entity::id() const noexcept {
|
||||||
|
return id_;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool entity::destroy() {
|
||||||
|
return owner_.destroy_entity(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool operator==(const entity& l, const entity& r) noexcept {
|
||||||
|
return l.id() == r.id();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool operator!=(const entity& l, const entity& r) noexcept {
|
||||||
|
return !(l == r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// world impl
|
||||||
|
//
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
namespace ecs_hpp
|
||||||
|
{
|
||||||
|
inline world::world() = default;
|
||||||
|
inline world::~world() noexcept = default;
|
||||||
|
|
||||||
|
inline entity world::create_entity() {
|
||||||
|
std::lock_guard<std::mutex> guard(mutex_);
|
||||||
|
assert(last_entity_id_ < std::numeric_limits<entity_id>::max());
|
||||||
|
auto ent = entity(*this, ++last_entity_id_);
|
||||||
|
entities_.insert(ent);
|
||||||
|
return ent;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool world::destroy_entity(const entity& ent) {
|
||||||
|
std::lock_guard<std::mutex> guard(mutex_);
|
||||||
|
return entities_.erase(ent) > 0u;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool world::is_entity_alive(const entity& ent) const noexcept {
|
||||||
|
std::lock_guard<std::mutex> guard(mutex_);
|
||||||
|
return entities_.count(ent) > 0u;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,60 @@ namespace ecs = ecs_hpp;
|
|||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
|
struct position {
|
||||||
|
int x{0};
|
||||||
|
int y{0};
|
||||||
|
|
||||||
|
position() = default;
|
||||||
|
position(int nx, int ny) : x(nx), y(ny) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct velocity {
|
||||||
|
int dx{0};
|
||||||
|
int dy{0};
|
||||||
|
|
||||||
|
velocity() = default;
|
||||||
|
velocity(int ndx, int ndy) : dx(ndx), dy(ndy) {}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("ecs") {
|
TEST_CASE("world") {
|
||||||
|
SECTION("entities") {
|
||||||
|
{
|
||||||
|
ecs::world w;
|
||||||
|
|
||||||
|
ecs::entity e1{w};
|
||||||
|
ecs::entity e2{w};
|
||||||
|
|
||||||
|
REQUIRE(e1 == e2);
|
||||||
|
REQUIRE_FALSE(w.is_entity_alive(e1));
|
||||||
|
REQUIRE_FALSE(w.is_entity_alive(e2));
|
||||||
|
|
||||||
|
REQUIRE_FALSE(w.destroy_entity(e1));
|
||||||
|
REQUIRE_FALSE(w.destroy_entity(e2));
|
||||||
|
}
|
||||||
|
{
|
||||||
|
ecs::world w;
|
||||||
|
|
||||||
|
auto e1 = w.create_entity();
|
||||||
|
auto e2 = w.create_entity();
|
||||||
|
|
||||||
|
REQUIRE(e1 != e2);
|
||||||
|
REQUIRE(w.is_entity_alive(e1));
|
||||||
|
REQUIRE(w.is_entity_alive(e2));
|
||||||
|
|
||||||
|
REQUIRE(w.destroy_entity(e1));
|
||||||
|
REQUIRE_FALSE(w.is_entity_alive(e1));
|
||||||
|
REQUIRE(w.is_entity_alive(e2));
|
||||||
|
|
||||||
|
REQUIRE(w.destroy_entity(e2));
|
||||||
|
REQUIRE_FALSE(w.is_entity_alive(e1));
|
||||||
|
REQUIRE_FALSE(w.is_entity_alive(e2));
|
||||||
|
|
||||||
|
REQUIRE_FALSE(w.destroy_entity(e1));
|
||||||
|
REQUIRE_FALSE(w.destroy_entity(e2));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SECTION("components") {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
17
scripts/upload_coverage.sh
Executable file
17
scripts/upload_coverage.sh
Executable file
@@ -0,0 +1,17 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
BUILD_DIR=`dirname "$BASH_SOURCE"`/../build
|
||||||
|
mkdir -p $BUILD_DIR/coverage
|
||||||
|
cd $BUILD_DIR/coverage
|
||||||
|
cmake -DCMAKE_BUILD_TYPE=Debug -DECS_BUILD_WITH_COVERAGE=ON ../..
|
||||||
|
cmake --build . -- -j8
|
||||||
|
|
||||||
|
lcov -d . -z
|
||||||
|
ctest --verbose
|
||||||
|
|
||||||
|
lcov -d . -c -o "coverage.info"
|
||||||
|
lcov -r "coverage.info" "*/usr/*" "*/catch.hpp" "*/catch_main.cpp" -o "coverage.info"
|
||||||
|
lcov -l "coverage.info"
|
||||||
|
|
||||||
|
bash <(curl -s https://codecov.io/bash) || echo "Codecov did not collect coverage reports"
|
||||||
Reference in New Issue
Block a user