mod.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417
  1. #include <gtest/gtest.h>
  2. #include <type_traits>
  3. #include <cassert>
  4. #include <map>
  5. #include <string>
  6. #include <duktape.h>
  7. #include <entt/entity/registry.hpp>
  8. template<typename Type>
  9. struct tag { using type = Type; };
  10. struct position {
  11. double x;
  12. double y;
  13. };
  14. struct renderable {};
  15. struct duktape_runtime {
  16. std::map<duk_uint_t, std::string> components;
  17. };
  18. template<typename Comp>
  19. duk_ret_t set(duk_context *ctx, entt::registry<> &registry) {
  20. const auto entity = duk_require_uint(ctx, 0);
  21. if constexpr(std::is_same_v<Comp, position>) {
  22. const auto x = duk_require_number(ctx, 2);
  23. const auto y = duk_require_number(ctx, 3);
  24. registry.accommodate<position>(entity, x, y);
  25. } else if constexpr(std::is_same_v<Comp, duktape_runtime>) {
  26. const auto type = duk_require_uint(ctx, 1);
  27. duk_dup(ctx, 2);
  28. if(!registry.has<duktape_runtime>(entity)) {
  29. registry.assign<duktape_runtime>(entity).components[type] = duk_json_encode(ctx, -1);
  30. } else {
  31. registry.get<duktape_runtime>(entity).components[type] = duk_json_encode(ctx, -1);
  32. }
  33. duk_pop(ctx);
  34. } else {
  35. registry.accommodate<Comp>(entity);
  36. }
  37. return 0;
  38. }
  39. template<typename Comp>
  40. duk_ret_t unset(duk_context *ctx, entt::registry<> &registry) {
  41. const auto entity = duk_require_uint(ctx, 0);
  42. if constexpr(std::is_same_v<Comp, duktape_runtime>) {
  43. const auto type = duk_require_uint(ctx, 1);
  44. auto &components = registry.get<duktape_runtime>(entity).components;
  45. assert(components.find(type) != components.cend());
  46. components.erase(type);
  47. if(components.empty()) {
  48. registry.remove<duktape_runtime>(entity);
  49. }
  50. } else {
  51. registry.remove<Comp>(entity);
  52. }
  53. return 0;
  54. }
  55. template<typename Comp>
  56. duk_ret_t has(duk_context *ctx, entt::registry<> &registry) {
  57. const auto entity = duk_require_uint(ctx, 0);
  58. if constexpr(std::is_same_v<Comp, duktape_runtime>) {
  59. duk_push_boolean(ctx, registry.has<duktape_runtime>(entity));
  60. if(registry.has<duktape_runtime>(entity)) {
  61. const auto type = duk_require_uint(ctx, 1);
  62. const auto &components = registry.get<duktape_runtime>(entity).components;
  63. duk_push_boolean(ctx, components.find(type) != components.cend());
  64. } else {
  65. duk_push_false(ctx);
  66. }
  67. } else {
  68. duk_push_boolean(ctx, registry.has<Comp>(entity));
  69. }
  70. return 1;
  71. }
  72. template<typename Comp>
  73. duk_ret_t get(duk_context *ctx, entt::registry<> &registry) {
  74. [[maybe_unused]] const auto entity = duk_require_uint(ctx, 0);
  75. if constexpr(std::is_same_v<Comp, position>) {
  76. const auto &pos = registry.get<position>(entity);
  77. const auto idx = duk_push_object(ctx);
  78. duk_push_string(ctx, "x");
  79. duk_push_number(ctx, pos.x);
  80. duk_def_prop(ctx, idx, DUK_DEFPROP_HAVE_VALUE);
  81. duk_push_string(ctx, "y");
  82. duk_push_number(ctx, pos.y);
  83. duk_def_prop(ctx, idx, DUK_DEFPROP_HAVE_VALUE);
  84. } if constexpr(std::is_same_v<Comp, duktape_runtime>) {
  85. const auto type = duk_require_uint(ctx, 1);
  86. auto &runtime = registry.get<duktape_runtime>(entity);
  87. assert(runtime.components.find(type) != runtime.components.cend());
  88. duk_push_string(ctx, runtime.components[type].c_str());
  89. duk_json_decode(ctx, -1);
  90. } else {
  91. assert(registry.has<Comp>(entity));
  92. duk_push_object(ctx);
  93. }
  94. return 1;
  95. }
  96. class duktape_registry {
  97. // I'm pretty sure I won't have more than 99 components in the example
  98. static constexpr entt::registry<>::component_type udef = 100;
  99. struct func_map {
  100. using func_type = duk_ret_t(*)(duk_context *, entt::registry<> &);
  101. using test_type = bool(entt::registry<>:: *)(entt::registry<>::entity_type) const;
  102. func_type set;
  103. func_type unset;
  104. func_type has;
  105. func_type get;
  106. test_type test;
  107. };
  108. template<typename... Comp>
  109. void reg() {
  110. ((func[registry.type<Comp>()] = {
  111. &::set<Comp>,
  112. &::unset<Comp>,
  113. &::has<Comp>,
  114. &::get<Comp>,
  115. &entt::registry<>::has<Comp>
  116. }), ...);
  117. }
  118. static duktape_registry & instance(duk_context *ctx) {
  119. duk_push_this(ctx);
  120. duk_push_string(ctx, DUK_HIDDEN_SYMBOL("dreg"));
  121. duk_get_prop(ctx, -2);
  122. auto &dreg = *static_cast<duktape_registry *>(duk_require_pointer(ctx, -1));
  123. duk_pop_2(ctx);
  124. return dreg;
  125. }
  126. template<func_map::func_type func_map::*Op>
  127. static duk_ret_t invoke(duk_context *ctx) {
  128. auto &dreg = instance(ctx);
  129. auto &func = dreg.func;
  130. auto &registry = dreg.registry;
  131. auto type = duk_require_uint(ctx, 1);
  132. if(type >= udef) {
  133. type = registry.type<duktape_runtime>();
  134. }
  135. assert(func.find(type) != func.cend());
  136. return (func[type].*Op)(ctx, registry);
  137. }
  138. public:
  139. duktape_registry(entt::registry<> &registry)
  140. : registry{registry}
  141. {
  142. reg<position, renderable, duktape_runtime>();
  143. }
  144. static duk_ret_t identifier(duk_context *ctx) {
  145. static auto next = udef;
  146. duk_push_uint(ctx, next++);
  147. return 1;
  148. }
  149. static duk_ret_t create(duk_context *ctx) {
  150. auto &dreg = instance(ctx);
  151. duk_push_uint(ctx, dreg.registry.create());
  152. return 1;
  153. }
  154. static duk_ret_t set(duk_context *ctx) {
  155. return invoke<&func_map::set>(ctx);
  156. }
  157. static duk_ret_t unset(duk_context *ctx) {
  158. return invoke<&func_map::unset>(ctx);
  159. }
  160. static duk_ret_t has(duk_context *ctx) {
  161. return invoke<&func_map::has>(ctx);
  162. }
  163. static duk_ret_t get(duk_context *ctx) {
  164. return invoke<&func_map::get>(ctx);
  165. }
  166. static duk_ret_t entities(duk_context *ctx) {
  167. const duk_idx_t nargs = duk_get_top(ctx);
  168. auto &dreg = instance(ctx);
  169. duk_uarridx_t pos = 0;
  170. duk_push_array(ctx);
  171. std::vector<typename entt::registry<>::component_type> components;
  172. std::vector<typename entt::registry<>::component_type> runtime;
  173. for(duk_idx_t arg = 0; arg < nargs; arg++) {
  174. auto type = duk_require_uint(ctx, arg);
  175. if(type < udef) {
  176. components.push_back(type);
  177. } else {
  178. if(runtime.empty()) {
  179. components.push_back(dreg.registry.type<duktape_runtime>());
  180. }
  181. runtime.push_back(type);
  182. }
  183. }
  184. auto view = dreg.registry.runtime_view(components.cbegin(), components.cend());
  185. for(const auto entity: view) {
  186. if(runtime.empty()) {
  187. duk_push_uint(ctx, entity);
  188. duk_put_prop_index(ctx, -2, pos++);
  189. } else {
  190. const auto &components = dreg.registry.get<duktape_runtime>(entity).components;
  191. const auto match = std::all_of(runtime.cbegin(), runtime.cend(), [&components](const auto type) {
  192. return components.find(type) != components.cend();
  193. });
  194. if(match) {
  195. duk_push_uint(ctx, entity);
  196. duk_put_prop_index(ctx, -2, pos++);
  197. }
  198. }
  199. }
  200. return 1;
  201. }
  202. private:
  203. std::map<duk_uint_t, func_map> func;
  204. entt::registry<> &registry;
  205. };
  206. const duk_function_list_entry js_duktape_registry_methods[] = {
  207. { "identifier", &duktape_registry::identifier, 0 },
  208. { "create", &duktape_registry::create, 0 },
  209. { "set", &duktape_registry::set, DUK_VARARGS },
  210. { "unset", &duktape_registry::unset, 2 },
  211. { "has", &duktape_registry::has, 2 },
  212. { "get", &duktape_registry::get, 2 },
  213. { "entities", &duktape_registry::entities, DUK_VARARGS },
  214. { nullptr, nullptr, 0 }
  215. };
  216. void export_types(duk_context *ctx, entt::registry<> &registry) {
  217. auto export_type = [](auto *ctx, auto &registry, auto idx, auto type, const auto *name) {
  218. duk_push_string(ctx, name);
  219. duk_push_uint(ctx, registry.template type<typename decltype(type)::type>());
  220. duk_def_prop(ctx, idx, DUK_DEFPROP_HAVE_VALUE | DUK_DEFPROP_CLEAR_WRITABLE);
  221. };
  222. auto idx = duk_push_object(ctx);
  223. export_type(ctx, registry, idx, tag<position>{}, "position");
  224. export_type(ctx, registry, idx, tag<renderable>{}, "renderable");
  225. duk_put_global_string(ctx, "Types");
  226. }
  227. void export_duktape_registry(duk_context *ctx, duktape_registry &dreg) {
  228. auto idx = duk_push_object(ctx);
  229. duk_push_string(ctx, DUK_HIDDEN_SYMBOL("dreg"));
  230. duk_push_pointer(ctx, &dreg);
  231. duk_put_prop(ctx, idx);
  232. duk_put_function_list(ctx, idx, js_duktape_registry_methods);
  233. duk_put_global_string(ctx, "Registry");
  234. }
  235. TEST(Mod, Duktape) {
  236. entt::registry<> registry;
  237. duktape_registry dreg{registry};
  238. duk_context *ctx = duk_create_heap_default();
  239. if(!ctx) {
  240. FAIL();
  241. }
  242. export_types(ctx, registry);
  243. export_duktape_registry(ctx, dreg);
  244. const char *s0 = ""
  245. "Types[\"PLAYING_CHARACTER\"] = Registry.identifier();"
  246. "Types[\"VELOCITY\"] = Registry.identifier();"
  247. "";
  248. if(duk_peval_string(ctx, s0)) {
  249. FAIL();
  250. }
  251. const auto e0 = registry.create();
  252. registry.assign<position>(e0, 0., 0.);
  253. registry.assign<renderable>(e0);
  254. const auto e1 = registry.create();
  255. registry.assign<position>(e1, 0., 0.);
  256. const char *s1 = ""
  257. "Registry.entities(Types.position, Types.renderable).forEach(function(entity) {"
  258. "Registry.set(entity, Types.position, 100., 100.);"
  259. "});"
  260. "var entity = Registry.create();"
  261. "Registry.set(entity, Types.position, 100., 100.);"
  262. "Registry.set(entity, Types.renderable);"
  263. "";
  264. if(duk_peval_string(ctx, s1)) {
  265. FAIL();
  266. }
  267. ASSERT_EQ(registry.view<duktape_runtime>().size(), 0u);
  268. ASSERT_EQ(registry.view<position>().size(), 3u);
  269. ASSERT_EQ(registry.view<renderable>().size(), 2u);
  270. registry.view<position>().each([&registry](auto entity, const auto &position) {
  271. ASSERT_FALSE(registry.has<duktape_runtime>(entity));
  272. if(registry.has<renderable>(entity)) {
  273. ASSERT_EQ(position.x, 100.);
  274. ASSERT_EQ(position.y, 100.);
  275. } else {
  276. ASSERT_EQ(position.x, 0.);
  277. ASSERT_EQ(position.y, 0.);
  278. }
  279. });
  280. const char *s2 = ""
  281. "Registry.entities(Types.position).forEach(function(entity) {"
  282. "if(!Registry.has(entity, Types.renderable)) {"
  283. "Registry.set(entity, Types.VELOCITY, { \"dx\": -100., \"dy\": -100. });"
  284. "Registry.set(entity, Types.PLAYING_CHARACTER, {});"
  285. "}"
  286. "});"
  287. "";
  288. if(duk_peval_string(ctx, s2)) {
  289. FAIL();
  290. }
  291. ASSERT_EQ(registry.view<duktape_runtime>().size(), 1u);
  292. ASSERT_EQ(registry.view<position>().size(), 3u);
  293. ASSERT_EQ(registry.view<renderable>().size(), 2u);
  294. registry.view<duktape_runtime>().each([](auto, const duktape_runtime &runtime) {
  295. ASSERT_EQ(runtime.components.size(), 2u);
  296. });
  297. const char *s3 = ""
  298. "Registry.entities(Types.position, Types.renderable, Types.VELOCITY, Types.PLAYING_CHARACTER).forEach(function(entity) {"
  299. "var velocity = Registry.get(entity, Types.VELOCITY);"
  300. "Registry.set(entity, Types.position, velocity.dx, velocity.dy)"
  301. "});"
  302. "";
  303. if(duk_peval_string(ctx, s3)) {
  304. FAIL();
  305. }
  306. ASSERT_EQ(registry.view<duktape_runtime>().size(), 1u);
  307. ASSERT_EQ(registry.view<position>().size(), 3u);
  308. ASSERT_EQ(registry.view<renderable>().size(), 2u);
  309. registry.view<position, renderable, duktape_runtime>().each([](auto, const position &position, const auto &...) {
  310. ASSERT_EQ(position.x, -100.);
  311. ASSERT_EQ(position.y, -100.);
  312. });
  313. const char *s4 = ""
  314. "Registry.entities(Types.VELOCITY, Types.PLAYING_CHARACTER).forEach(function(entity) {"
  315. "Registry.unset(entity, Types.VELOCITY);"
  316. "Registry.unset(entity, Types.PLAYING_CHARACTER);"
  317. "});"
  318. "Registry.entities(Types.position).forEach(function(entity) {"
  319. "Registry.unset(entity, Types.position);"
  320. "});"
  321. "";
  322. if(duk_peval_string(ctx, s4)) {
  323. FAIL();
  324. }
  325. ASSERT_EQ(registry.view<duktape_runtime>().size(), 0u);
  326. ASSERT_EQ(registry.view<position>().size(), 0u);
  327. ASSERT_EQ(registry.view<renderable>().size(), 2u);
  328. duk_destroy_heap(ctx);
  329. }