Skip to content

Commit 100c7a8

Browse files
authored
Associations implementation (#13)
1 parent d6f1c3b commit 100c7a8

13 files changed

+627
-191
lines changed

.vscode/launch.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"version": "0.2.0",
33
"configurations": [
44
{
5-
"name": "test //test:test",
5+
"name": "test //runtime/test:test",
66
"request": "launch",
77
"program": "${workspaceFolder}/bazel-bin/runtime/test/test",
88
"cwd": "${workspaceFolder}",

README.md

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,57 @@
11
# EnTT Ecsact Runtime Implementation
22

33
[Ecsact](https://ecsact.dev) runtime built with [EnTT](https://github.com/skypjack/entt).
4+
5+
## System Views
6+
7+
In the simpliest cases an Ecsact system lines up 1:1 with an EnTT view.
8+
9+
```ecsact
10+
package example;
11+
component Health { f32 value; }
12+
component Invincible;
13+
system Gravity { readwrite Position; exclude Weightless; }
14+
```
15+
16+
```cpp
17+
entt::basic_view<entt::entity, entt::get_t<example::Health>, entt::exclude_t<example::Invincible>>
18+
```
19+
20+
Since `adds` implies `exclude` and `removes` implies `include` even `adds`/`removes` systems are _almost_ 1:1.
21+
22+
```ecsact
23+
package example;
24+
component Health { f32 value; }
25+
component Healing;
26+
action StartHealing { include Health; adds Healing; }
27+
action StopHealing { include Health; removes Healing; }
28+
```
29+
30+
```cpp
31+
// EnTT view for StartHealing
32+
entt::basic_view<entt::entity, entt::get_t<example::Health>, entt::exclude_t<example::Healing>>
33+
// EnTT view for StopHealing
34+
entt::basic_view<entt::entity, entt::get_t<example::Health, example::Healing>, entt::exclude_t<>>
35+
```
36+
37+
As soon as system association occurs there now requires multiple views to be iterated at the same time.
38+
39+
```ecsact
40+
package example;
41+
component Health { f32 value; }
42+
component Attacking { entity target; }
43+
action Attack {
44+
entity target;
45+
adds Attacking;
46+
}
47+
system AttackDamage {
48+
readonly Attacking with target {
49+
readwrite Health;
50+
}
51+
}
52+
```
53+
54+
```cpp
55+
entt::basic_view<entt::entity, entt:get_t<example::Attacking>, entt::exclude_t<>>
56+
entt::basic_view<entt::entity, entt:get_t<example::Health, ecsact::entt::association<example::Attacking, 0/*target*/>>, entt::exclude_t<>>
57+
```
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
#pragma once
2+
3+
#include <cstdint>
4+
#include <type_traits>
5+
6+
namespace ecsact::entt::detail {
7+
template<typename C, std::size_t FieldOffset>
8+
struct association {
9+
using component = C;
10+
static constexpr auto field_offset = FieldOffset;
11+
};
12+
13+
template<typename C>
14+
struct temp_storage;
15+
16+
template<typename C> requires(std::is_empty_v<C>)
17+
struct temp_storage<C> { };
18+
19+
template<typename C> requires(!std::is_empty_v<C>)
20+
struct temp_storage<C> { C value; };
21+
22+
template<typename C> requires(!std::is_empty_v<C>)
23+
struct beforechange_storage { C value; bool set = false; };
24+
25+
template<typename C>
26+
struct pending_add;
27+
28+
template<typename C> requires(std::is_empty_v<C>)
29+
struct pending_add<C> { };
30+
31+
template<typename C> requires(!std::is_empty_v<C>)
32+
struct pending_add<C> { C value; };
33+
34+
template<typename C>
35+
struct pending_remove {};
36+
}

ecsact/entt/detail/registry_info.hh

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include "ecsact/runtime/core.h"
1111

1212
#include "ecsact/entt/event_markers.hh"
13+
#include "ecsact/entt/detail/internal_markers.hh"
1314

1415
namespace ecsact_entt_rt {
1516
using entity_id_map_t = std::unordered_map
@@ -57,6 +58,38 @@ namespace ecsact_entt_rt {
5758
});
5859
}
5960

61+
template<typename C>
62+
void _add_association
63+
( const C& component
64+
, const ecsact::field_info& field
65+
)
66+
{
67+
using ecsact::entt::detail::association;
68+
using boost::mp11::mp_with_index;
69+
70+
auto entity_field = field.template get<ecsact_entity_id>(component);
71+
auto entity_field_entt = entities_map.at(entity_field);
72+
mp_with_index<64>(field.offset, [&](auto I) {
73+
registry.emplace<association<C, I>>(entity_field_entt);
74+
});
75+
}
76+
77+
template<typename C>
78+
void _remove_association
79+
( const C& component
80+
, const ecsact::field_info& field
81+
)
82+
{
83+
using ecsact::entt::detail::association;
84+
using boost::mp11::mp_with_index;
85+
86+
auto entity_field = field.template get<ecsact_entity_id>(component);
87+
auto entity_field_entt = entities_map.at(entity_field);
88+
mp_with_index<64>(field.offset, [&](auto I) {
89+
registry.erase<association<C, I>>(entity_field_entt);
90+
});
91+
}
92+
6093
template<typename C> requires(std::is_empty_v<C>)
6194
void add_component
6295
( ::entt::entity entity
@@ -72,8 +105,9 @@ namespace ecsact_entt_rt {
72105
)
73106
{
74107
using boost::mp11::mp_for_each;
108+
using boost::mp11::mp_with_index;
75109

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

78112
mp_for_each<typename package::components>([&]<typename O>(O) {
79113
if constexpr(std::is_same_v<std::remove_cvref_t<C>, O>) {
@@ -88,6 +122,15 @@ namespace ecsact_entt_rt {
88122
);
89123
}
90124
});
125+
126+
constexpr auto fields_info = ecsact::fields_info<C>();
127+
if constexpr(!fields_info.empty()) {
128+
for(auto& field : fields_info) {
129+
if(field.storage_type == ECSACT_ENTITY_TYPE) {
130+
_add_association(comp, field);
131+
}
132+
}
133+
}
91134
}
92135

93136
template<typename C>
@@ -97,6 +140,16 @@ namespace ecsact_entt_rt {
97140
{
98141
using boost::mp11::mp_for_each;
99142

143+
constexpr auto fields_info = ecsact::fields_info<C>();
144+
if constexpr(!fields_info.empty()) {
145+
auto& comp = registry.get<C>(entity);
146+
for(auto& field : fields_info) {
147+
if(field.storage_type == ECSACT_ENTITY_TYPE) {
148+
_remove_association(comp, field);
149+
}
150+
}
151+
}
152+
100153
registry.erase<C>(entity);
101154

102155
if constexpr(!std::is_empty_v<C>) {

0 commit comments

Comments
 (0)