mod.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436
  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.type<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.type<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. std::vector<typename entt::DefaultRegistry::component_type> components;
  188. std::vector<typename entt::DefaultRegistry::component_type> runtime;
  189. for(duk_idx_t arg = 0; arg < nargs; arg++) {
  190. auto type = duk_require_uint(ctx, arg);
  191. if(type < udef) {
  192. components.push_back(type);
  193. } else {
  194. if(runtime.empty()) {
  195. components.push_back(dreg.registry.type<DuktapeRuntime>());
  196. }
  197. runtime.push_back(type);
  198. }
  199. }
  200. auto view = dreg.registry.view(components.cbegin(), components.cend());
  201. for(const auto entity: view) {
  202. if(runtime.empty()) {
  203. duk_push_uint(ctx, entity);
  204. duk_put_prop_index(ctx, -2, pos++);
  205. } else {
  206. const auto &components = dreg.registry.get<DuktapeRuntime>(entity).components;
  207. const auto match = std::all_of(runtime.cbegin(), runtime.cend(), [&components](const auto type) {
  208. return components.find(type) != components.cend();
  209. });
  210. if(match) {
  211. duk_push_uint(ctx, entity);
  212. duk_put_prop_index(ctx, -2, pos++);
  213. }
  214. }
  215. }
  216. return 1;
  217. }
  218. private:
  219. std::map<duk_uint_t, Func> func;
  220. entt::DefaultRegistry &registry;
  221. };
  222. const duk_function_list_entry js_DuktapeRegistry_methods[] = {
  223. { "identifier", &DuktapeRegistry::identifier, 0 },
  224. { "create", &DuktapeRegistry::create, 0 },
  225. { "set", &DuktapeRegistry::set, DUK_VARARGS },
  226. { "unset", &DuktapeRegistry::unset, 2 },
  227. { "has", &DuktapeRegistry::has, 2 },
  228. { "get", &DuktapeRegistry::get, 2 },
  229. { "entities", &DuktapeRegistry::entities, DUK_VARARGS },
  230. { nullptr, nullptr, 0 }
  231. };
  232. void exportTypes(duk_context *ctx, entt::DefaultRegistry &registry) {
  233. auto exportType = [](auto *ctx, auto &registry, auto idx, auto type, const auto *name) {
  234. duk_push_string(ctx, name);
  235. duk_push_uint(ctx, registry.template type<typename decltype(type)::type>());
  236. duk_def_prop(ctx, idx, DUK_DEFPROP_HAVE_VALUE | DUK_DEFPROP_CLEAR_WRITABLE);
  237. };
  238. auto idx = duk_push_object(ctx);
  239. exportType(ctx, registry, idx, tag<Position>{}, "POSITION");
  240. exportType(ctx, registry, idx, tag<Renderable>{}, "RENDERABLE");
  241. duk_put_global_string(ctx, "Types");
  242. }
  243. void exportDuktapeRegistry(duk_context *ctx, DuktapeRegistry &dreg) {
  244. auto idx = duk_push_object(ctx);
  245. duk_push_string(ctx, DUK_HIDDEN_SYMBOL("dreg"));
  246. duk_push_pointer(ctx, &dreg);
  247. duk_put_prop(ctx, idx);
  248. duk_put_function_list(ctx, idx, js_DuktapeRegistry_methods);
  249. duk_put_global_string(ctx, "Registry");
  250. }
  251. TEST(Mod, Duktape) {
  252. entt::DefaultRegistry registry;
  253. DuktapeRegistry dreg{registry};
  254. duk_context *ctx = duk_create_heap_default();
  255. if(!ctx) {
  256. FAIL();
  257. }
  258. exportTypes(ctx, registry);
  259. exportDuktapeRegistry(ctx, dreg);
  260. const char *s0 = ""
  261. "Types[\"PLAYING_CHARACTER\"] = Registry.identifier();"
  262. "Types[\"VELOCITY\"] = Registry.identifier();"
  263. "";
  264. if(duk_peval_string(ctx, s0)) {
  265. FAIL();
  266. }
  267. const auto e0 = registry.create();
  268. registry.assign<Position>(e0, 0., 0.);
  269. registry.assign<Renderable>(e0);
  270. const auto e1 = registry.create();
  271. registry.assign<Position>(e1, 0., 0.);
  272. const char *s1 = ""
  273. "Registry.entities(Types.POSITION, Types.RENDERABLE).forEach(function(entity) {"
  274. "Registry.set(entity, Types.POSITION, 100., 100.);"
  275. "});"
  276. "var entity = Registry.create();"
  277. "Registry.set(entity, Types.POSITION, 100., 100.);"
  278. "Registry.set(entity, Types.RENDERABLE);"
  279. "";
  280. if(duk_peval_string(ctx, s1)) {
  281. FAIL();
  282. }
  283. ASSERT_EQ(registry.view<DuktapeRuntime>().size(), 0u);
  284. ASSERT_EQ(registry.view<Position>().size(), 3u);
  285. ASSERT_EQ(registry.view<Renderable>().size(), 2u);
  286. registry.view<Position>().each([&registry](auto entity, const auto &position) {
  287. ASSERT_FALSE(registry.has<DuktapeRuntime>(entity));
  288. if(registry.has<Renderable>(entity)) {
  289. ASSERT_EQ(position.x, 100.);
  290. ASSERT_EQ(position.y, 100.);
  291. } else {
  292. ASSERT_EQ(position.x, 0.);
  293. ASSERT_EQ(position.y, 0.);
  294. }
  295. });
  296. const char *s2 = ""
  297. "Registry.entities(Types.POSITION).forEach(function(entity) {"
  298. "if(!Registry.has(entity, Types.RENDERABLE)) {"
  299. "Registry.set(entity, Types.VELOCITY, { \"dx\": -100., \"dy\": -100. });"
  300. "Registry.set(entity, Types.PLAYING_CHARACTER, {});"
  301. "}"
  302. "});"
  303. "";
  304. if(duk_peval_string(ctx, s2)) {
  305. FAIL();
  306. }
  307. ASSERT_EQ(registry.view<DuktapeRuntime>().size(), 1u);
  308. ASSERT_EQ(registry.view<Position>().size(), 3u);
  309. ASSERT_EQ(registry.view<Renderable>().size(), 2u);
  310. registry.view<DuktapeRuntime>().each([](auto, const DuktapeRuntime &runtime) {
  311. ASSERT_EQ(runtime.components.size(), 2u);
  312. });
  313. const char *s3 = ""
  314. "Registry.entities(Types.POSITION, Types.RENDERABLE, Types.VELOCITY, Types.PLAYING_CHARACTER).forEach(function(entity) {"
  315. "var velocity = Registry.get(entity, Types.VELOCITY);"
  316. "Registry.set(entity, Types.POSITION, velocity.dx, velocity.dy)"
  317. "});"
  318. "";
  319. if(duk_peval_string(ctx, s3)) {
  320. FAIL();
  321. }
  322. ASSERT_EQ(registry.view<DuktapeRuntime>().size(), 1u);
  323. ASSERT_EQ(registry.view<Position>().size(), 3u);
  324. ASSERT_EQ(registry.view<Renderable>().size(), 2u);
  325. registry.view<Position, Renderable, DuktapeRuntime>().each([](auto, const Position &position, const auto &...) {
  326. ASSERT_EQ(position.x, -100.);
  327. ASSERT_EQ(position.y, -100.);
  328. });
  329. const char *s4 = ""
  330. "Registry.entities(Types.VELOCITY, Types.PLAYING_CHARACTER).forEach(function(entity) {"
  331. "Registry.unset(entity, Types.VELOCITY);"
  332. "Registry.unset(entity, Types.PLAYING_CHARACTER);"
  333. "});"
  334. "Registry.entities(Types.POSITION).forEach(function(entity) {"
  335. "Registry.unset(entity, Types.POSITION);"
  336. "});"
  337. "";
  338. if(duk_peval_string(ctx, s4)) {
  339. FAIL();
  340. }
  341. ASSERT_EQ(registry.view<DuktapeRuntime>().size(), 0u);
  342. ASSERT_EQ(registry.view<Position>().size(), 0u);
  343. ASSERT_EQ(registry.view<Renderable>().size(), 2u);
  344. duk_destroy_heap(ctx);
  345. }