mod.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415
  1. #include <type_traits>
  2. #include <cassert>
  3. #include <map>
  4. #include <string>
  5. #include <duktape.h>
  6. #include <gtest/gtest.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 entt::entity 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.assign_or_replace<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.assign_or_replace<Comp>(entity);
  36. }
  37. return 0;
  38. }
  39. template<typename Comp>
  40. duk_ret_t unset(duk_context *ctx, entt::registry &registry) {
  41. const entt::entity 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 entt::entity 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 entt::entity 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_ID_TYPE udef = 100;
  99. struct func_map {
  100. using func_type = duk_ret_t(*)(duk_context *, entt::registry &);
  101. func_type set;
  102. func_type unset;
  103. func_type has;
  104. func_type get;
  105. };
  106. template<typename... Comp>
  107. void reg() {
  108. ((func[to_integer(registry.type<Comp>())] = {
  109. &::set<Comp>,
  110. &::unset<Comp>,
  111. &::has<Comp>,
  112. &::get<Comp>
  113. }), ...);
  114. }
  115. static duktape_registry & instance(duk_context *ctx) {
  116. duk_push_this(ctx);
  117. duk_push_string(ctx, DUK_HIDDEN_SYMBOL("dreg"));
  118. duk_get_prop(ctx, -2);
  119. auto &dreg = *static_cast<duktape_registry *>(duk_require_pointer(ctx, -1));
  120. duk_pop_2(ctx);
  121. return dreg;
  122. }
  123. template<func_map::func_type func_map::*Op>
  124. static duk_ret_t invoke(duk_context *ctx) {
  125. auto &dreg = instance(ctx);
  126. auto &func = dreg.func;
  127. auto &registry = dreg.registry;
  128. auto type = duk_require_uint(ctx, 1);
  129. if(type >= udef) {
  130. type = to_integer(registry.type<duktape_runtime>());
  131. }
  132. assert(func.find(type) != func.cend());
  133. return (func[type].*Op)(ctx, registry);
  134. }
  135. public:
  136. duktape_registry(entt::registry &ref)
  137. : registry{ref}
  138. {
  139. reg<position, renderable, duktape_runtime>();
  140. }
  141. static duk_ret_t identifier(duk_context *ctx) {
  142. static auto next = udef;
  143. duk_push_uint(ctx, next++);
  144. return 1;
  145. }
  146. static duk_ret_t create(duk_context *ctx) {
  147. auto &dreg = instance(ctx);
  148. const auto entity = dreg.registry.create();
  149. duk_push_uint(ctx, static_cast<std::underlying_type_t<entt::entity>>(entity));
  150. return 1;
  151. }
  152. static duk_ret_t set(duk_context *ctx) {
  153. return invoke<&func_map::set>(ctx);
  154. }
  155. static duk_ret_t unset(duk_context *ctx) {
  156. return invoke<&func_map::unset>(ctx);
  157. }
  158. static duk_ret_t has(duk_context *ctx) {
  159. return invoke<&func_map::has>(ctx);
  160. }
  161. static duk_ret_t get(duk_context *ctx) {
  162. return invoke<&func_map::get>(ctx);
  163. }
  164. static duk_ret_t entities(duk_context *ctx) {
  165. const duk_idx_t nargs = duk_get_top(ctx);
  166. auto &dreg = instance(ctx);
  167. duk_uarridx_t pos = 0;
  168. duk_push_array(ctx);
  169. std::vector<entt::component> components;
  170. std::vector<entt::component> runtime;
  171. for(duk_idx_t arg = 0; arg < nargs; arg++) {
  172. auto type = duk_require_uint(ctx, arg);
  173. if(type < udef) {
  174. components.push_back(entt::component{type});
  175. } else {
  176. if(runtime.empty()) {
  177. components.push_back(dreg.registry.type<duktape_runtime>());
  178. }
  179. runtime.push_back(entt::component{type});
  180. }
  181. }
  182. auto view = dreg.registry.runtime_view(components.cbegin(), components.cend());
  183. for(const auto entity: view) {
  184. if(runtime.empty()) {
  185. duk_push_uint(ctx, static_cast<std::underlying_type_t<entt::entity>>(entity));
  186. duk_put_prop_index(ctx, -2, pos++);
  187. } else {
  188. const auto &others = dreg.registry.get<duktape_runtime>(entity).components;
  189. const auto match = std::all_of(runtime.cbegin(), runtime.cend(), [&others](const auto type) {
  190. return others.find(to_integer(type)) != others.cend();
  191. });
  192. if(match) {
  193. duk_push_uint(ctx, static_cast<std::underlying_type_t<entt::entity>>(entity));
  194. duk_put_prop_index(ctx, -2, pos++);
  195. }
  196. }
  197. }
  198. return 1;
  199. }
  200. private:
  201. std::map<duk_uint_t, func_map> func;
  202. entt::registry &registry;
  203. };
  204. const duk_function_list_entry js_duktape_registry_methods[] = {
  205. { "identifier", &duktape_registry::identifier, 0 },
  206. { "create", &duktape_registry::create, 0 },
  207. { "set", &duktape_registry::set, DUK_VARARGS },
  208. { "unset", &duktape_registry::unset, 2 },
  209. { "has", &duktape_registry::has, 2 },
  210. { "get", &duktape_registry::get, 2 },
  211. { "entities", &duktape_registry::entities, DUK_VARARGS },
  212. { nullptr, nullptr, 0 }
  213. };
  214. void export_types(duk_context *context, entt::registry &registry) {
  215. auto export_type = [](auto *ctx, auto &reg, auto idx, auto type, const auto *name) {
  216. duk_push_string(ctx, name);
  217. duk_push_uint(ctx, to_integer(reg.template type<typename decltype(type)::type>()));
  218. duk_def_prop(ctx, idx, DUK_DEFPROP_HAVE_VALUE | DUK_DEFPROP_CLEAR_WRITABLE);
  219. };
  220. auto idx = duk_push_object(context);
  221. export_type(context, registry, idx, tag<position>{}, "position");
  222. export_type(context, registry, idx, tag<renderable>{}, "renderable");
  223. duk_put_global_string(context, "Types");
  224. }
  225. void export_duktape_registry(duk_context *ctx, duktape_registry &dreg) {
  226. auto idx = duk_push_object(ctx);
  227. duk_push_string(ctx, DUK_HIDDEN_SYMBOL("dreg"));
  228. duk_push_pointer(ctx, &dreg);
  229. duk_put_prop(ctx, idx);
  230. duk_put_function_list(ctx, idx, js_duktape_registry_methods);
  231. duk_put_global_string(ctx, "Registry");
  232. }
  233. TEST(Mod, Duktape) {
  234. entt::registry registry;
  235. duktape_registry dreg{registry};
  236. duk_context *ctx = duk_create_heap_default();
  237. if(!ctx) {
  238. FAIL();
  239. }
  240. export_types(ctx, registry);
  241. export_duktape_registry(ctx, dreg);
  242. const char *s0 = ""
  243. "Types[\"PLAYING_CHARACTER\"] = Registry.identifier();"
  244. "Types[\"VELOCITY\"] = Registry.identifier();"
  245. "";
  246. if(duk_peval_string(ctx, s0)) {
  247. FAIL();
  248. }
  249. const auto e0 = registry.create();
  250. registry.assign<position>(e0, 0., 0.);
  251. registry.assign<renderable>(e0);
  252. const auto e1 = registry.create();
  253. registry.assign<position>(e1, 0., 0.);
  254. const char *s1 = ""
  255. "Registry.entities(Types.position, Types.renderable).forEach(function(entity) {"
  256. "Registry.set(entity, Types.position, 100., 100.);"
  257. "});"
  258. "var entity = Registry.create();"
  259. "Registry.set(entity, Types.position, 100., 100.);"
  260. "Registry.set(entity, Types.renderable);"
  261. "";
  262. if(duk_peval_string(ctx, s1)) {
  263. FAIL();
  264. }
  265. ASSERT_EQ(registry.view<duktape_runtime>().size(), 0u);
  266. ASSERT_EQ(registry.view<position>().size(), 3u);
  267. ASSERT_EQ(registry.view<renderable>().size(), 2u);
  268. registry.view<position>().each([&registry](auto entity, const auto &position) {
  269. ASSERT_FALSE(registry.has<duktape_runtime>(entity));
  270. if(registry.has<renderable>(entity)) {
  271. ASSERT_EQ(position.x, 100.);
  272. ASSERT_EQ(position.y, 100.);
  273. } else {
  274. ASSERT_EQ(position.x, 0.);
  275. ASSERT_EQ(position.y, 0.);
  276. }
  277. });
  278. const char *s2 = ""
  279. "Registry.entities(Types.position).forEach(function(entity) {"
  280. "if(!Registry.has(entity, Types.renderable)) {"
  281. "Registry.set(entity, Types.VELOCITY, { \"dx\": -100., \"dy\": -100. });"
  282. "Registry.set(entity, Types.PLAYING_CHARACTER, {});"
  283. "}"
  284. "});"
  285. "";
  286. if(duk_peval_string(ctx, s2)) {
  287. FAIL();
  288. }
  289. ASSERT_EQ(registry.view<duktape_runtime>().size(), 1u);
  290. ASSERT_EQ(registry.view<position>().size(), 3u);
  291. ASSERT_EQ(registry.view<renderable>().size(), 2u);
  292. registry.view<duktape_runtime>().each([](const duktape_runtime &runtime) {
  293. ASSERT_EQ(runtime.components.size(), 2u);
  294. });
  295. const char *s3 = ""
  296. "Registry.entities(Types.position, Types.renderable, Types.VELOCITY, Types.PLAYING_CHARACTER).forEach(function(entity) {"
  297. "var velocity = Registry.get(entity, Types.VELOCITY);"
  298. "Registry.set(entity, Types.position, velocity.dx, velocity.dy)"
  299. "});"
  300. "";
  301. if(duk_peval_string(ctx, s3)) {
  302. FAIL();
  303. }
  304. ASSERT_EQ(registry.view<duktape_runtime>().size(), 1u);
  305. ASSERT_EQ(registry.view<position>().size(), 3u);
  306. ASSERT_EQ(registry.view<renderable>().size(), 2u);
  307. registry.view<position, renderable, duktape_runtime>().each([](const position &position, auto &&...) {
  308. ASSERT_EQ(position.x, -100.);
  309. ASSERT_EQ(position.y, -100.);
  310. });
  311. const char *s4 = ""
  312. "Registry.entities(Types.VELOCITY, Types.PLAYING_CHARACTER).forEach(function(entity) {"
  313. "Registry.unset(entity, Types.VELOCITY);"
  314. "Registry.unset(entity, Types.PLAYING_CHARACTER);"
  315. "});"
  316. "Registry.entities(Types.position).forEach(function(entity) {"
  317. "Registry.unset(entity, Types.position);"
  318. "});"
  319. "";
  320. if(duk_peval_string(ctx, s4)) {
  321. FAIL();
  322. }
  323. ASSERT_EQ(registry.view<duktape_runtime>().size(), 0u);
  324. ASSERT_EQ(registry.view<position>().size(), 0u);
  325. ASSERT_EQ(registry.view<renderable>().size(), 2u);
  326. duk_destroy_heap(ctx);
  327. }