aob: removed mod stuff

This commit is contained in:
Michele Caini
2019-12-29 15:53:52 +01:00
parent b6f9ca0021
commit 8aacd4d022
6 changed files with 27 additions and 499 deletions

View File

@@ -160,7 +160,6 @@ if(BUILD_TESTING)
option(BUILD_BENCHMARK "Build benchmark." OFF)
option(BUILD_LIB "Build lib example." OFF)
option(BUILD_MOD "Build mod example." OFF)
option(BUILD_SNAPSHOT "Build snapshot example." OFF)
enable_testing()

1
TODO
View File

@@ -17,5 +17,4 @@
* observer: user defined filters (eg .replace<T, &function> or .group<T, U, &func>)
* any-of rule for views/groups (eg entity has A and any of B/C/D)
- get -> all, exclude -> none
* remove duktape example (and eventually provide a new one)?
* extract only the type within type_info, hash its name (more portable)

View File

@@ -1,20 +0,0 @@
project(duktape-download NONE)
cmake_minimum_required(VERSION 3.2)
include(ExternalProject)
ExternalProject_Add(
duktape
GIT_REPOSITORY https://github.com/svaarala/duktape-releases.git
GIT_TAG v2.2.0
GIT_SHALLOW 1
DOWNLOAD_DIR ${DUKTAPE_DEPS_DIR}
TMP_DIR ${DUKTAPE_DEPS_DIR}/tmp
STAMP_DIR ${DUKTAPE_DEPS_DIR}/stamp
SOURCE_DIR ${DUKTAPE_DEPS_DIR}/src
BINARY_DIR ${DUKTAPE_DEPS_DIR}/build
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND ""
TEST_COMMAND ""
)

View File

@@ -15,8 +15,6 @@
* [The Registry, the Entity and the Component](#the-registry-the-entity-and-the-component)
* [Observe changes](#observe-changes)
* [They call me Reactive System](#they-call-me-reactive-system)
* [Runtime components](#runtime-components)
* [A journey through a plugin](#a-journey-through-a-plugin)
* [Sorting: is it possible?](#sorting-is-it-possible)
* [Helpers](#helpers)
* [Null entity](#null-entity)
@@ -30,6 +28,7 @@
* [Continuous loader](#continuous-loader)
* [Archives](#archives)
* [One example to rule them all](#one-example-to-rule-them-all)
* [Runtime components](#runtime-components)
* [Views and Groups](#views-and-groups)
* [Views](#views)
* [Runtime views](#runtime-views)
@@ -551,54 +550,6 @@ multiple elements in the exclusion list. Moreover, every matcher can have it's
own clause and multiple clauses for the same matcher are combined in a single
one.
## Runtime components
Defining components at runtime is useful to support plugin systems and mods in
general. However, it seems impossible with a tool designed around a bunch of
templates. Indeed it's not that difficult.<br/>
Of course, some features cannot be easily exported into a runtime
environment. As an example, sorting a group of components defined at runtime
isn't for free if compared to most of the other operations. However, the basic
functionalities of an entity-component system such as `EnTT` fit the problem
perfectly and can also be used to manage runtime components if required.<br/>
All that is necessary to do it is to know the identifiers of the components. An
identifier is nothing more than a number or similar that can be used at runtime
to work with the type system.
In `EnTT`, identifiers are easily accessible:
```cpp
entt::registry registry;
// component identifier
auto type = registry.type<position>();
```
Once the identifiers are made available, almost everything becomes pretty
simple.
### A journey through a plugin
`EnTT` comes with an example (actually a test) that shows how to integrate
compile-time and runtime components in a stack based JavaScript environment. It
uses [`Duktape`](https://github.com/svaarala/duktape) under the hood, mainly
because I wanted to learn how it works at the time I was writing the code.
The code is not production-ready and overall performance can be highly improved.
However, I sacrificed optimizations in favor of a more readable piece of code. I
hope I succeeded.<br/>
Note also that this isn't neither the only nor (probably) the best way to do it.
In fact, the right way depends on the scripting language and the problem one is
facing in general.<br/>
That being said, feel free to use it at your own risk.
The basic idea is that of creating a compile-time component aimed to map all the
runtime components assigned to an entity.<br/>
Identifiers come in use to address the right function from a map when invoked
from the runtime environment and to filter entities when iterating.<br/>
With a bit of gymnastic, one can narrow views and improve the performance to
some extent but it was not the goal of the example.
## Sorting: is it possible?
It goes without saying that sorting entities and components is possible with
@@ -1035,6 +986,30 @@ the best way to do it. However, feel free to use it at your own risk.
The basic idea is to store everything in a group of queues in memory, then bring
everything back to the registry with different loaders.
## Runtime components
Defining components at runtime is useful to support plugin systems and mods in
general. However, it seems impossible with a tool designed around a bunch of
templates. Indeed it's not that difficult.<br/>
Of course, some features cannot be easily exported into a runtime environment.
As an example, sorting a group of components defined at runtime isn't for free
if compared to most of the other operations. However, the basic functionalities
of an entity-component system such as `EnTT` fit the problem perfectly and can
also be used to manage runtime components if required.<br/>
All that is necessary to do it is to know the identifiers of the components. An
identifier is nothing more than a number or similar that can be used at runtime
to work with the type system.
In `EnTT`, identifiers are easily accessible:
```cpp
// component identifier
const auto type = entt::type_info<position>::id();
```
Once the identifiers are made available, almost everything becomes pretty
simple.
# Views and Groups
First of all, it is worth answering an obvious question: why views and
@@ -1192,7 +1167,7 @@ thrown away. The reasons for this go far beyond the scope of this document.<br/>
To iterate a runtime view, either use it in a range-for loop:
```cpp
entt::component types[] = { registry.type<position>(), registry.type<velocity>() };
entt::component types[] = { entt::type_info<position>::id(), entt::type_info<velocity>::id() };
auto view = registry.runtime_view(std::cbegin(types), std::cend(types));
for(auto entity: view) {
@@ -1210,7 +1185,7 @@ for(auto entity: view) {
Or rely on the `each` member function to iterate entities:
```cpp
entt::component types[] = { registry.type<position>(), registry.type<velocity>() };
entt::component types[] = { entt::type_info<position>::id(), entt::type_info<velocity>::id() };
registry.runtime_view(std::cbegin(types), std::cend(types)).each([](auto entity) {
// ...

View File

@@ -102,22 +102,6 @@ if(BUILD_LIB)
SETUP_PLUGIN_TEST(registry_plugin_std ENTT_STANDARD_CPP)
endif()
# Test mod
if(BUILD_MOD)
enable_language(C)
set(DUKTAPE_DEPS_DIR ${EnTT_SOURCE_DIR}/deps/duktape)
configure_file(${EnTT_SOURCE_DIR}/cmake/in/duktape.in ${DUKTAPE_DEPS_DIR}/CMakeLists.txt)
execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" . WORKING_DIRECTORY ${DUKTAPE_DEPS_DIR})
execute_process(COMMAND ${CMAKE_COMMAND} --build . WORKING_DIRECTORY ${DUKTAPE_DEPS_DIR})
set(DUKTAPE_SRC_DIR ${DUKTAPE_DEPS_DIR}/src/src)
set(MOD_TEST_SOURCE ${DUKTAPE_SRC_DIR}/duktape.c mod/mod.cpp)
SETUP_BASIC_TEST(mod "${MOD_TEST_SOURCE}")
target_include_directories(mod PRIVATE ${DUKTAPE_SRC_DIR})
endif()
# Test snapshot
if(BUILD_SNAPSHOT)

View File

@@ -1,409 +0,0 @@
#include <type_traits>
#include <cassert>
#include <map>
#include <string>
#include <duktape.h>
#include <gtest/gtest.h>
#include <entt/core/type_info.hpp>
#include <entt/entity/registry.hpp>
template<typename Type>
struct tag { using type = Type; };
struct position {
double x;
double y;
};
struct renderable {};
struct duktape_runtime {
std::map<duk_uint_t, std::string> components;
};
template<typename Comp>
duk_ret_t set(duk_context *ctx, entt::registry &registry) {
const entt::entity entity{duk_require_uint(ctx, 0)};
if constexpr(std::is_same_v<Comp, position>) {
const auto x = duk_require_number(ctx, 2);
const auto y = duk_require_number(ctx, 3);
registry.assign_or_replace<position>(entity, x, y);
} else if constexpr(std::is_same_v<Comp, duktape_runtime>) {
const auto type = duk_require_uint(ctx, 1);
duk_dup(ctx, 2);
if(!registry.has<duktape_runtime>(entity)) {
registry.assign<duktape_runtime>(entity).components[type] = duk_json_encode(ctx, -1);
} else {
registry.get<duktape_runtime>(entity).components[type] = duk_json_encode(ctx, -1);
}
duk_pop(ctx);
} else {
registry.assign_or_replace<Comp>(entity);
}
return 0;
}
template<typename Comp>
duk_ret_t unset(duk_context *ctx, entt::registry &registry) {
const entt::entity entity{duk_require_uint(ctx, 0)};
if constexpr(std::is_same_v<Comp, duktape_runtime>) {
const auto type = duk_require_uint(ctx, 1);
auto &components = registry.get<duktape_runtime>(entity).components;
assert(components.find(type) != components.cend());
components.erase(type);
if(components.empty()) {
registry.remove<duktape_runtime>(entity);
}
} else {
registry.remove<Comp>(entity);
}
return 0;
}
template<typename Comp>
duk_ret_t has(duk_context *ctx, entt::registry &registry) {
const entt::entity entity{duk_require_uint(ctx, 0)};
if constexpr(std::is_same_v<Comp, duktape_runtime>) {
duk_push_boolean(ctx, registry.has<duktape_runtime>(entity));
if(registry.has<duktape_runtime>(entity)) {
const auto type = duk_require_uint(ctx, 1);
const auto &components = registry.get<duktape_runtime>(entity).components;
duk_push_boolean(ctx, components.find(type) != components.cend());
} else {
duk_push_false(ctx);
}
} else {
duk_push_boolean(ctx, registry.has<Comp>(entity));
}
return 1;
}
template<typename Comp>
duk_ret_t get(duk_context *ctx, entt::registry &registry) {
[[maybe_unused]] const entt::entity entity{duk_require_uint(ctx, 0)};
if constexpr(std::is_same_v<Comp, position>) {
const auto &pos = registry.get<position>(entity);
const auto idx = duk_push_object(ctx);
duk_push_string(ctx, "x");
duk_push_number(ctx, pos.x);
duk_def_prop(ctx, idx, DUK_DEFPROP_HAVE_VALUE);
duk_push_string(ctx, "y");
duk_push_number(ctx, pos.y);
duk_def_prop(ctx, idx, DUK_DEFPROP_HAVE_VALUE);
} if constexpr(std::is_same_v<Comp, duktape_runtime>) {
const auto type = duk_require_uint(ctx, 1);
auto &runtime = registry.get<duktape_runtime>(entity);
assert(runtime.components.find(type) != runtime.components.cend());
duk_push_string(ctx, runtime.components[type].c_str());
duk_json_decode(ctx, -1);
} else {
assert(registry.has<Comp>(entity));
duk_push_object(ctx);
}
return 1;
}
class duktape_registry {
struct func_map {
using func_type = duk_ret_t(*)(duk_context *, entt::registry &);
func_type set;
func_type unset;
func_type has;
func_type get;
};
template<typename... Comp>
void reg() {
((func[entt::type_info<Comp>::id()] = {
&::set<Comp>,
&::unset<Comp>,
&::has<Comp>,
&::get<Comp>
}), ...);
}
static duktape_registry & instance(duk_context *ctx) {
duk_push_this(ctx);
duk_push_string(ctx, DUK_HIDDEN_SYMBOL("dreg"));
duk_get_prop(ctx, -2);
auto &dreg = *static_cast<duktape_registry *>(duk_require_pointer(ctx, -1));
duk_pop_2(ctx);
return dreg;
}
template<func_map::func_type func_map::*Op>
static duk_ret_t invoke(duk_context *ctx) {
auto &dreg = instance(ctx);
auto &func = dreg.func;
auto &registry = dreg.registry;
auto type = duk_require_uint(ctx, 1);
const auto it = func.find(type);
return (it == func.cend())
? (func[entt::type_info<duktape_runtime>::id()].*Op)(ctx, registry)
: (it->second.*Op)(ctx, registry);
}
public:
duktape_registry(entt::registry &ref)
: registry{ref}
{
reg<position, renderable, duktape_runtime>();
}
static duk_ret_t identifier(duk_context *ctx) {
static ENTT_ID_TYPE next{1000u};
duk_push_uint(ctx, next++);
return 1;
}
static duk_ret_t create(duk_context *ctx) {
auto &dreg = instance(ctx);
const auto entity = dreg.registry.create();
duk_push_uint(ctx, static_cast<std::underlying_type_t<entt::entity>>(entity));
return 1;
}
static duk_ret_t set(duk_context *ctx) {
return invoke<&func_map::set>(ctx);
}
static duk_ret_t unset(duk_context *ctx) {
return invoke<&func_map::unset>(ctx);
}
static duk_ret_t has(duk_context *ctx) {
return invoke<&func_map::has>(ctx);
}
static duk_ret_t get(duk_context *ctx) {
return invoke<&func_map::get>(ctx);
}
static duk_ret_t entities(duk_context *ctx) {
const duk_idx_t nargs = duk_get_top(ctx);
auto &dreg = instance(ctx);
duk_push_array(ctx);
std::vector<ENTT_ID_TYPE> components;
std::vector<ENTT_ID_TYPE> runtime;
for(duk_idx_t arg = 0; arg < nargs; arg++) {
auto type = duk_require_uint(ctx, arg);
if(dreg.func.find(type) == dreg.func.cend()) {
if(runtime.empty()) {
components.push_back(entt::type_info<duktape_runtime>::id());
}
runtime.push_back(type);
} else {
components.push_back(type);
}
}
auto view = dreg.registry.runtime_view(components.cbegin(), components.cend());
duk_uarridx_t pos = 0;
for(const auto entity: view) {
if(runtime.empty()) {
duk_push_uint(ctx, static_cast<std::underlying_type_t<entt::entity>>(entity));
duk_put_prop_index(ctx, -2, pos++);
} else {
const auto &others = dreg.registry.get<duktape_runtime>(entity).components;
const auto match = std::all_of(runtime.cbegin(), runtime.cend(), [&others](const auto type) {
return others.find(type) != others.cend();
});
if(match) {
duk_push_uint(ctx, static_cast<std::underlying_type_t<entt::entity>>(entity));
duk_put_prop_index(ctx, -2, pos++);
}
}
}
return 1;
}
private:
std::map<duk_uint_t, func_map> func;
entt::registry &registry;
};
const duk_function_list_entry js_duktape_registry_methods[] = {
{ "identifier", &duktape_registry::identifier, 0 },
{ "create", &duktape_registry::create, 0 },
{ "set", &duktape_registry::set, DUK_VARARGS },
{ "unset", &duktape_registry::unset, 2 },
{ "has", &duktape_registry::has, 2 },
{ "get", &duktape_registry::get, 2 },
{ "entities", &duktape_registry::entities, DUK_VARARGS },
{ nullptr, nullptr, 0 }
};
void export_types(duk_context *context) {
auto export_type = [idx = duk_push_object(context)](auto *ctx, auto type, const auto *name) {
duk_push_string(ctx, name);
duk_push_uint(ctx, entt::type_info<typename decltype(type)::type>::id());
duk_def_prop(ctx, idx, DUK_DEFPROP_HAVE_VALUE | DUK_DEFPROP_CLEAR_WRITABLE);
};
export_type(context, tag<position>{}, "position");
export_type(context, tag<renderable>{}, "renderable");
duk_put_global_string(context, "Types");
}
void export_duktape_registry(duk_context *ctx, duktape_registry &dreg) {
auto idx = duk_push_object(ctx);
duk_push_string(ctx, DUK_HIDDEN_SYMBOL("dreg"));
duk_push_pointer(ctx, &dreg);
duk_put_prop(ctx, idx);
duk_put_function_list(ctx, idx, js_duktape_registry_methods);
duk_put_global_string(ctx, "Registry");
}
TEST(Mod, Duktape) {
entt::registry registry;
duktape_registry dreg{registry};
duk_context *ctx = duk_create_heap_default();
if(!ctx) {
FAIL();
}
export_types(ctx);
export_duktape_registry(ctx, dreg);
const char *s0 = ""
"Types[\"PLAYING_CHARACTER\"] = Registry.identifier();"
"Types[\"VELOCITY\"] = Registry.identifier();"
"";
if(duk_peval_string(ctx, s0)) {
FAIL();
}
const auto e0 = registry.create();
registry.assign<position>(e0, 0., 0.);
registry.assign<renderable>(e0);
const auto e1 = registry.create();
registry.assign<position>(e1, 0., 0.);
const char *s1 = ""
"Registry.entities(Types.position, Types.renderable).forEach(function(entity) {"
"Registry.set(entity, Types.position, 100., 100.);"
"});"
"var entity = Registry.create();"
"Registry.set(entity, Types.position, 100., 100.);"
"Registry.set(entity, Types.renderable);"
"";
if(duk_peval_string(ctx, s1)) {
FAIL();
}
ASSERT_EQ(registry.view<duktape_runtime>().size(), 0u);
ASSERT_EQ(registry.view<position>().size(), 3u);
ASSERT_EQ(registry.view<renderable>().size(), 2u);
registry.view<position>().each([&registry](auto entity, const auto &position) {
ASSERT_FALSE(registry.has<duktape_runtime>(entity));
if(registry.has<renderable>(entity)) {
ASSERT_EQ(position.x, 100.);
ASSERT_EQ(position.y, 100.);
} else {
ASSERT_EQ(position.x, 0.);
ASSERT_EQ(position.y, 0.);
}
});
const char *s2 = ""
"Registry.entities(Types.position).forEach(function(entity) {"
"if(!Registry.has(entity, Types.renderable)) {"
"Registry.set(entity, Types.VELOCITY, { \"dx\": -100., \"dy\": -100. });"
"Registry.set(entity, Types.PLAYING_CHARACTER, {});"
"}"
"});"
"";
if(duk_peval_string(ctx, s2)) {
FAIL();
}
ASSERT_EQ(registry.view<duktape_runtime>().size(), 1u);
ASSERT_EQ(registry.view<position>().size(), 3u);
ASSERT_EQ(registry.view<renderable>().size(), 2u);
registry.view<duktape_runtime>().each([](const duktape_runtime &runtime) {
ASSERT_EQ(runtime.components.size(), 2u);
});
const char *s3 = ""
"Registry.entities(Types.position, Types.renderable, Types.VELOCITY, Types.PLAYING_CHARACTER).forEach(function(entity) {"
"var velocity = Registry.get(entity, Types.VELOCITY);"
"Registry.set(entity, Types.position, velocity.dx, velocity.dy)"
"});"
"";
if(duk_peval_string(ctx, s3)) {
FAIL();
}
ASSERT_EQ(registry.view<duktape_runtime>().size(), 1u);
ASSERT_EQ(registry.view<position>().size(), 3u);
ASSERT_EQ(registry.view<renderable>().size(), 2u);
registry.view<position, renderable, duktape_runtime>().each([](const position &position, auto &&...) {
ASSERT_EQ(position.x, -100.);
ASSERT_EQ(position.y, -100.);
});
const char *s4 = ""
"Registry.entities(Types.VELOCITY, Types.PLAYING_CHARACTER).forEach(function(entity) {"
"Registry.unset(entity, Types.VELOCITY);"
"Registry.unset(entity, Types.PLAYING_CHARACTER);"
"});"
"Registry.entities(Types.position).forEach(function(entity) {"
"Registry.unset(entity, Types.position);"
"});"
"";
if(duk_peval_string(ctx, s4)) {
FAIL();
}
ASSERT_EQ(registry.view<duktape_runtime>().size(), 0u);
ASSERT_EQ(registry.view<position>().size(), 0u);
ASSERT_EQ(registry.view<renderable>().size(), 2u);
duk_destroy_heap(ctx);
}