mod.cpp 13 KB

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