Skip to content

Associations implementation #13

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Sep 26, 2022
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
2 changes: 1 addition & 1 deletion .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"version": "0.2.0",
"configurations": [
{
"name": "test //test:test",
"name": "test //runtime/test:test",
"request": "launch",
"program": "${workspaceFolder}/bazel-bin/runtime/test/test",
"cwd": "${workspaceFolder}",
Expand Down
54 changes: 54 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,57 @@
# EnTT Ecsact Runtime Implementation

[Ecsact](https://ecsact.dev) runtime built with [EnTT](https://github.com/skypjack/entt).

## System Views

In the simpliest cases an Ecsact system lines up 1:1 with an EnTT view.

```ecsact
package example;
component Health { f32 value; }
component Invincible;
system Gravity { readwrite Position; exclude Weightless; }
```

```cpp
entt::basic_view<entt::entity, entt::get_t<example::Health>, entt::exclude_t<example::Invincible>>
```

Since `adds` implies `exclude` and `removes` implies `include` even `adds`/`removes` systems are _almost_ 1:1.

```ecsact
package example;
component Health { f32 value; }
component Healing;
action StartHealing { include Health; adds Healing; }
action StopHealing { include Health; removes Healing; }
```

```cpp
// EnTT view for StartHealing
entt::basic_view<entt::entity, entt::get_t<example::Health>, entt::exclude_t<example::Healing>>
// EnTT view for StopHealing
entt::basic_view<entt::entity, entt::get_t<example::Health, example::Healing>, entt::exclude_t<>>
```

As soon as system association occurs there now requires multiple views to be iterated at the same time.

```ecsact
package example;
component Health { f32 value; }
component Attacking { entity target; }
action Attack {
entity target;
adds Attacking;
}
system AttackDamage {
readonly Attacking with target {
readwrite Health;
}
}
```

```cpp
entt::basic_view<entt::entity, entt:get_t<example::Attacking>, entt::exclude_t<>>
entt::basic_view<entt::entity, entt:get_t<example::Health, ecsact::entt::association<example::Attacking, 0/*target*/>>, entt::exclude_t<>>
```
36 changes: 36 additions & 0 deletions ecsact/entt/detail/internal_markers.hh
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#pragma once

#include <cstdint>
#include <type_traits>

namespace ecsact::entt::detail {
template<typename C, std::size_t FieldOffset>
struct association {
using component = C;
static constexpr auto field_offset = FieldOffset;
};

template<typename C>
struct temp_storage;

template<typename C> requires(std::is_empty_v<C>)
struct temp_storage<C> { };

template<typename C> requires(!std::is_empty_v<C>)
struct temp_storage<C> { C value; };

template<typename C> requires(!std::is_empty_v<C>)
struct beforechange_storage { C value; bool set = false; };

template<typename C>
struct pending_add;

template<typename C> requires(std::is_empty_v<C>)
struct pending_add<C> { };

template<typename C> requires(!std::is_empty_v<C>)
struct pending_add<C> { C value; };

template<typename C>
struct pending_remove {};
}
55 changes: 54 additions & 1 deletion ecsact/entt/detail/registry_info.hh
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "ecsact/runtime/core.h"

#include "ecsact/entt/event_markers.hh"
#include "ecsact/entt/detail/internal_markers.hh"

namespace ecsact_entt_rt {
using entity_id_map_t = std::unordered_map
Expand Down Expand Up @@ -57,6 +58,38 @@ namespace ecsact_entt_rt {
});
}

template<typename C>
void _add_association
( const C& component
, const ecsact::field_info& field
)
{
using ecsact::entt::detail::association;
using boost::mp11::mp_with_index;

auto entity_field = field.template get<ecsact_entity_id>(component);
auto entity_field_entt = entities_map.at(entity_field);
mp_with_index<64>(field.offset, [&](auto I) {
registry.emplace<association<C, I>>(entity_field_entt);
});
}

template<typename C>
void _remove_association
( const C& component
, const ecsact::field_info& field
)
{
using ecsact::entt::detail::association;
using boost::mp11::mp_with_index;

auto entity_field = field.template get<ecsact_entity_id>(component);
auto entity_field_entt = entities_map.at(entity_field);
mp_with_index<64>(field.offset, [&](auto I) {
registry.erase<association<C, I>>(entity_field_entt);
});
}

template<typename C> requires(std::is_empty_v<C>)
void add_component
( ::entt::entity entity
Expand All @@ -72,8 +105,9 @@ namespace ecsact_entt_rt {
)
{
using boost::mp11::mp_for_each;
using boost::mp11::mp_with_index;

registry.emplace<C>(entity, std::forward<Args>(args)...);
auto& comp = registry.emplace<C>(entity, std::forward<Args>(args)...);

mp_for_each<typename package::components>([&]<typename O>(O) {
if constexpr(std::is_same_v<std::remove_cvref_t<C>, O>) {
Expand All @@ -88,6 +122,15 @@ namespace ecsact_entt_rt {
);
}
});

constexpr auto fields_info = ecsact::fields_info<C>();
if constexpr(!fields_info.empty()) {
for(auto& field : fields_info) {
if(field.storage_type == ECSACT_ENTITY_TYPE) {
_add_association(comp, field);
}
}
}
}

template<typename C>
Expand All @@ -97,6 +140,16 @@ namespace ecsact_entt_rt {
{
using boost::mp11::mp_for_each;

constexpr auto fields_info = ecsact::fields_info<C>();
if constexpr(!fields_info.empty()) {
auto& comp = registry.get<C>(entity);
for(auto& field : fields_info) {
if(field.storage_type == ECSACT_ENTITY_TYPE) {
_remove_association(comp, field);
}
}
}

registry.erase<C>(entity);

if constexpr(!std::is_empty_v<C>) {
Expand Down
Loading