#ifndef ENTITYLUA_HPP #define ENTITYLUA_HPP // Check is_trivially_destructible so we don't need __gc for userdata. static_assert(std::is_trivially_destructible<entityx::Entity::Id>::value, "Entity::Id is not trivially destructible"); // Check is_copy_constructible so that we can copy data onto memory for userdata created by lua. static_assert(std::is_copy_constructible<entityx::Entity::Id>::value, "Entity::Id is not copy constructible"); namespace entityx { namespace lua { typedef std::shared_ptr<entityx::EntityManager> EntityManagerSharedPtr; // This component is internally assigned to all entities created from lua. // It is used to store the ref of the lua table struct LuaComponent : entityx::Component<LuaComponent> { // ref of the lua object in LUA_REGISTRYINDEX int ref; LuaComponent(int ref = LUA_REFNIL) : ref(ref) {} }; // This function should be called right after luaL_openlibs, to create global entityx table void setup_entityx_api(lua_State* L); // This function add a new entity manager to lua global "entityx.xxx" where xxx is the name provided void new_entity_manager(lua_State* L, const EntityManagerSharedPtr& manager, const char* name); // This function is used to push an entity manager to lua. // It only pushes the userdata onto the stack, so use lua api such as lua_setglobal, // lua_setfield, or lua_settable to really save it to lua. void push_entity_manager(lua_State* L, const EntityManagerSharedPtr& manager); typedef std::map<std::string, std::function<void(lua_State*)>> ComponentList; extern ComponentList components; template <typename C> class MemberRegister { lua_State* L; public: MemberRegister(lua_State* L) : L(L) {} template <typename T> void add(const char* name, T C::*ptr) { // Now the ComponentHandler should be at #-2 of stack (-1 is the metatable), this will be used as up-values lua_pushvalue(L, -2); // Also remember the offset int offset = typename ComponentHelper<C>::offset(ptr); lua_pushinteger(L, offset); lua_pushcclosure(L, [](lua_State* L){ // This function is used as both getter and setter: // value = entity.comp.x() // entity.comp.x(value) // Note that self is not needed (that is, use comp.x() instead of comp:x()) typename ComponentHelper<C>::ptr* pptr = static_cast<typename ComponentHelper<C>::ptr*>(lua_touserdata(L, lua_upvalueindex(1))); int offset = lua_tointeger(L, lua_upvalueindex(2)); T& ref = typename ComponentHelper<C>::get(**pptr, offset); if (lua_gettop(L) == 0 || lua_isnil(L, 1)) { // Getter mode, push value of at pptr CToLua(L, ref); } else { // Setter mode, set value to pptr LuaToC(L, ref); } return 1; }, 2); lua_setfield(L, -2, name); } }; template <typename C> struct ComponentHelper { public: // Use this ptr to get the ComponentHandle type, to support both 0.x and 1.x of entityx. typedef decltype(entityx::Entity().assign<C>()) ptr; template <typename Member> static ptrdiff_t offset(Member ptr) { C c; return static_cast<char*>(static_cast<void*>(&(c.*ptr))) - static_cast<char*>(static_cast<void*>(&c)); } template <typename T> static T& get(C& c, ptrdiff_t offset) { return *static_cast<T*>(static_cast<void*>( static_cast<char*>(static_cast<void*>(&c)) + offset )); } typedef std::function<void(MemberRegister<C>&)> memreg_func; }; namespace lua_params { // gens<n>::type = seq<0, 1, ..., n-1> template<int ...> struct seq {}; template<int N, int ...S> struct gens : gens<N - 1, N - 1, S...> { }; template<int ...S> struct gens<0, S...> { typedef seq<S...> type; }; template <typename T> T get_param_from_lua(lua_State* L, int n) { T ret; lua_pushvalue(L, n); LuaToC<T>(L, ret); return ret; } template <typename Func, typename Ret, typename... Args, int... N> Ret call_func(lua_State* L, Func func, seq<N...>) { // This function is called with a table on top of the stack, // first we call table.unpack to get the elements in the table lua_getglobal(L, "table"); lua_getfield(L, -1, "unpack"); lua_remove(L, -2); // now function is pushed lua_pushvalue(L, -2); // push the table lua_call(L, 1, sizeof...(N)); // we only receive sizeof...(N) results // Then we call func with get_param_from_lua int start_from = lua_gettop(L) - sizeof...(N)+1; // Note that N... itself starts from 0 return func(std::forward<Args>(get_param_from_lua<Args>(L, N + start_from))...); } // std::function version template <typename Ret, typename... Args> Ret call_func(lua_State* L, const std::function<Ret(Args...)>& func) { return call_func<std::function<Ret(Args...)>, Ret, Args...>(L, func, gens<sizeof...(Args)>::type()); } } template <typename C, typename... Args> std::function<C(lua_State*)> wrap_ctor(const std::function<C(Args...)>& func) { return [func](lua_State* L) { return lua_params::call_func<C, Args...>(L, func); }; } template <typename C> std::function<C(lua_State*)> wrap_ctor_no_args(C(*func)()) { return [func](lua_State* L) { return lua_params::call_func<C>(L, func); }; } template <typename C> std::function<C(lua_State*)> use_default_ctor() { static_assert(std::is_constructible<C>::value, "Not able to use default ctor here"); return [](lua_State* L) { return C(); }; } template <typename C, typename Str, typename Ctor> void export_component(Str name, Ctor ctor, const std::function<void(MemberRegister<C>&)>& memreg) { static_assert(std::is_constructible<std::string, Str>::value, "Bad type of name"); static_assert(std::is_same<Ctor, std::function<C(lua_State*)>>::value, "Bad type of ctor"); // Create memreg as a std::function //ComponentHelper<C>::memreg_func memreg_func_save = memreg; std::function<void(lua_State*)> create_component = [ctor, memreg](lua_State* L) { // This function is called with (name, entity_manager_weak_pointer, id, params) EntityManagerSharedPtr* m = static_cast<EntityManagerSharedPtr*>(lua_touserdata(L, 2)); entityx::Entity::Id* id = static_cast<entityx::Entity::Id*>(lua_touserdata(L, 3)); entityx::Entity entity = (*m)->get(*id); typedef typename ComponentHelper<C>::ptr CompPtr; // Create the c++ component CompPtr component = entity.assign<C>(std::move(ctor(L))); // Create the component as a lightuserdata (although no data is stored in it) lua_pushlightuserdata(L, 0); // Push CompPtr which will be used in MemberRegister (will be removed after pushing members) lua_newuserdata(L, sizeof(CompPtr)); new (lua_touserdata(L, -1)) CompPtr(component); // If needed, create metatable for it (we only need __gc) if (!std::is_trivially_destructible<CompPtr>::value) { lua_newtable(L); lua_pushcfunction(L, [](lua_State* L){ CompPtr* component = static_cast<CompPtr*>(lua_touserdata(L, -1)); component->~CompPtr(); return 0; }); lua_setfield(L, -2, "__gc"); lua_setmetatable(L, -2); } // Stack: lightuserdata(0) | CompPtr // Create metatable for the lightuserdata lua_newtable(L); lua_pushvalue(L, -1); lua_setfield(L, -2, "__index"); // Stack: lightuserdata(0) | CompPtr | metatable // Now we're ready to push members MemberRegister<C> reg(L); memreg(reg); // Stack: lightuserdata(0) | CompPtr | metatable // Remove the CompPtr lua_remove(L, -2); // Set metatable for the lightuserdata lua_setmetatable(L, -2); // Leave the result on top of the stack }; components[name] = std::move(create_component); } } } #endif//ENTITYLUA_HPP