mod.cpp 12 KB

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