From 3cea77e3e6ef432d1bfc6026139b482154ccf604 Mon Sep 17 00:00:00 2001 From: Andy Date: Mon, 26 Aug 2019 17:43:12 -0400 Subject: Made lua binding c++17 compatible --- src/Script/entityx/EntityLua.hpp | 242 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 242 insertions(+) create mode 100644 src/Script/entityx/EntityLua.hpp (limited to 'src/Script/entityx/EntityLua.hpp') diff --git a/src/Script/entityx/EntityLua.hpp b/src/Script/entityx/EntityLua.hpp new file mode 100644 index 0000000..c957252 --- /dev/null +++ b/src/Script/entityx/EntityLua.hpp @@ -0,0 +1,242 @@ +#ifndef ENTITYLUA_HPP +#define ENTITYLUA_HPP + +// Check is_trivially_destructible so we don't need __gc for userdata. +static_assert(std::is_trivially_destructible::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::value, "Entity::Id is not copy constructible"); + +namespace entityx +{ + namespace lua + { + typedef std::shared_ptr 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 + { + // 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> ComponentList; + extern ComponentList components; + + template + class MemberRegister + { + lua_State* L; + public: + MemberRegister(lua_State* L) : L(L) {} + + template + 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 = ComponentHelper::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::ptr* pptr = static_cast::ptr*>(lua_touserdata(L, lua_upvalueindex(1))); + int offset = lua_tointeger(L, lua_upvalueindex(2)); + + T& ref = typename ComponentHelper::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 + 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()) ptr; + + template + static ptrdiff_t offset(Member ptr) + { + C c; + return static_cast(static_cast(&(c.*ptr))) - static_cast(static_cast(&c)); + } + + template + static T& get(C& c, ptrdiff_t offset) + { + return *static_cast(static_cast( + static_cast(static_cast(&c)) + offset + )); + } + + typedef std::function&)> memreg_func; + }; + + namespace lua_params + { + // gens::type = seq<0, 1, ..., n-1> + template + struct seq {}; + + template + struct gens : gens { }; + + template + struct gens<0, S...> { + typedef seq type; + }; + + template + T get_param_from_lua(lua_State* L, int n) + { + T ret; + lua_pushvalue(L, n); + LuaToC(L, ret); + return ret; + } + + template + Ret call_func(lua_State* L, Func func, seq) + { + // 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(get_param_from_lua(L, N + start_from))...); + } + + // std::function version + template + Ret call_func(lua_State* L, const std::function& func) + { + return call_func, Ret, Args...>(L, func, gens::type()); + } + } + + template + std::function wrap_ctor(const std::function& func) + { + return [func](lua_State* L) { + return lua_params::call_func(L, func); + }; + } + + template + std::function wrap_ctor_no_args(C(*func)()) + { + return [func](lua_State* L) { + return lua_params::call_func(L, func); + }; + } + + template + std::function use_default_ctor() + { + static_assert(std::is_constructible::value, "Not able to use default ctor here"); + return [](lua_State* L) { + return C(); + }; + } + + template + void export_component(Str name, Ctor ctor, const std::function&)>& memreg) + { + static_assert(std::is_constructible::value, "Bad type of name"); + static_assert(std::is_same>::value, "Bad type of ctor"); + + // Create memreg as a std::function + //ComponentHelper::memreg_func memreg_func_save = memreg; + + std::function create_component = [ctor, memreg](lua_State* L) { + // This function is called with (name, entity_manager_weak_pointer, id, params) + EntityManagerSharedPtr* m = static_cast(lua_touserdata(L, 2)); + entityx::Entity::Id* id = static_cast(lua_touserdata(L, 3)); + entityx::Entity entity = (*m)->get(*id); + + typedef typename ComponentHelper::ptr CompPtr; + // Create the c++ component + CompPtr component = entity.assign(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::value) + { + lua_newtable(L); + lua_pushcfunction(L, [](lua_State* L){ + CompPtr* component = static_cast(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 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 -- cgit v1.2.3