mod.cpp 13 KB

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