| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414 |
- #include <gtest/gtest.h>
- #include <type_traits>
- #include <cassert>
- #include <map>
- #include <string>
- #include <duktape.h>
- #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 ®istry) {
- const auto 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 ®istry) {
- const auto 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 ®istry) {
- const auto 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 ®istry) {
- [[maybe_unused]] const auto 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 {
- // I'm pretty sure I won't have more than 99 components in the example
- static constexpr entt::registry::component_type udef = 100;
- 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[registry.type<Comp>()] = {
- &::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 ®istry = dreg.registry;
- auto type = duk_require_uint(ctx, 1);
- if(type >= udef) {
- type = registry.type<duktape_runtime>();
- }
- assert(func.find(type) != func.cend());
- return (func[type].*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 auto next = udef;
- duk_push_uint(ctx, next++);
- return 1;
- }
- static duk_ret_t create(duk_context *ctx) {
- auto &dreg = instance(ctx);
- duk_push_uint(ctx, dreg.registry.create());
- 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_uarridx_t pos = 0;
- duk_push_array(ctx);
- std::vector<typename entt::registry::component_type> components;
- std::vector<typename entt::registry::component_type> runtime;
- for(duk_idx_t arg = 0; arg < nargs; arg++) {
- auto type = duk_require_uint(ctx, arg);
- if(type < udef) {
- components.push_back(type);
- } else {
- if(runtime.empty()) {
- components.push_back(dreg.registry.type<duktape_runtime>());
- }
- runtime.push_back(type);
- }
- }
- auto view = dreg.registry.runtime_view(components.cbegin(), components.cend());
- for(const auto entity: view) {
- if(runtime.empty()) {
- duk_push_uint(ctx, 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, entity);
- duk_put_prop_index(ctx, -2, pos++);
- }
- }
- }
- return 1;
- }
- private:
- std::map<duk_uint_t, func_map> func;
- entt::registry ®istry;
- };
- 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, entt::registry ®istry) {
- auto export_type = [](auto *ctx, auto ®, auto idx, auto type, const auto *name) {
- duk_push_string(ctx, name);
- duk_push_uint(ctx, reg.template type<typename decltype(type)::type>());
- duk_def_prop(ctx, idx, DUK_DEFPROP_HAVE_VALUE | DUK_DEFPROP_CLEAR_WRITABLE);
- };
- auto idx = duk_push_object(context);
- export_type(context, registry, idx, tag<position>{}, "position");
- export_type(context, registry, idx, 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, registry);
- 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([®istry](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([](auto, 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([](auto, const position &position, const 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);
- }
|