]> code.bitgloo.com Git - clyne/gamedev2.git/commitdiff
Made lua binding c++17 compatible
authorAndy <drumsetmonkey@gmail.com>
Mon, 26 Aug 2019 21:43:12 +0000 (17:43 -0400)
committerAndy <drumsetmonkey@gmail.com>
Mon, 26 Aug 2019 21:43:12 +0000 (17:43 -0400)
src/Script/entityx/EntityLua.hpp [new file with mode: 0644]
src/Script/entityx/entity_lua.cpp [new file with mode: 0644]
src/Script/entityx/entity_lua.hpp [new file with mode: 0644]
src/main.cpp

diff --git a/src/Script/entityx/EntityLua.hpp b/src/Script/entityx/EntityLua.hpp
new file mode 100644 (file)
index 0000000..c957252
--- /dev/null
@@ -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<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 = 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
diff --git a/src/Script/entityx/entity_lua.cpp b/src/Script/entityx/entity_lua.cpp
new file mode 100644 (file)
index 0000000..e62f040
--- /dev/null
@@ -0,0 +1,181 @@
+#include "entityx_lua.h"
+
+using namespace entityx::lua;
+ComponentList entityx::lua::components;
+
+namespace {
+       int ref_EntityManagerSharedPtr = LUA_REFNIL;
+       int ref_EntityId = LUA_REFNIL;
+       const char* TypeName_EntityManagerSharedPtr = "EntityManagerSharedPtr";
+       const char* TypeName_EntityId = "EntityId";
+       const char* setup_lua_create_entity_factory =
+               "function entityx.create_entity_factory(entity_manager_pointer)\n"
+               "       local c = {}\n"
+               "       c.__index = c\n"
+               "       function c:new()\n"
+               "               local ret = {}\n"
+               "               ret.__index = ret\n"
+               "               function ret:init()\n"
+               "                       -- Empty\n"
+               "               end\n"
+               "               function ret:instance(param)\n"
+               "                       -- Make id const\n"
+               "                       local r = {}\n"
+               "                       local id = entityx.create_new_entity(entity_manager_pointer, r)\n"
+               "                       r.id = id\n"
+               "                       function r:component(name, params)\n"
+               "                               return entityx.create_new_component(name, entity_manager_pointer, id, params)\n"
+               "                       end\n"
+               "                       setmetatable(r, self)\n"
+               "                       r:init(param)\n"
+               "                       return r\n"
+               "               end\n"
+               "               return ret\n"
+               "       end\n"
+               "       function c:get(id)\n"
+               "               return entityx.get_lua_object(entity_manager_pointer, id)\n"
+               "       end\n"
+               "       return c\n"
+               "end\n";
+}
+
+//TODO replace:entityx_lua:: settable->setfield/rawset
+void entityx::lua::setup_entityx_api(lua_State* L)
+{
+       // Create metatables for userdata
+       // metatable for shared_ptr of EntityManager
+       lua_newtable(L);
+       lua_pushstring(L, TypeName_EntityManagerSharedPtr);
+       lua_setfield(L, -2, "type");
+       lua_pushcfunction(L, [](lua_State* L){
+               EntityManagerSharedPtr* ptr = static_cast<EntityManagerSharedPtr*>(lua_touserdata(L, 1));
+               (*ptr).~EntityManagerSharedPtr();
+               return 0;
+       });
+       lua_setfield(L, -2, "__gc");
+       ref_EntityManagerSharedPtr = luaL_ref(L, LUA_REGISTRYINDEX);
+
+       // metatable for Entity::Id
+       lua_newtable(L);
+       lua_pushstring(L, TypeName_EntityId);
+       lua_setfield(L, -2, "type");
+       // We don't need __gc for Entity::Id (with static_assert it is_trivially_destructible)
+       ref_EntityId = luaL_ref(L, LUA_REGISTRYINDEX);
+
+
+       // Create global entityx table
+       lua_newtable(L);
+
+       // create_new_entity
+       lua_pushcfunction(L, [](lua_State* L){
+               // First check the type of the parameter
+               lua_getmetatable(L, 1);
+               lua_pushstring(L, "type");
+               lua_rawget(L, -2);
+               if (!lua_isuserdata(L, 1) || strcmp(lua_tostring(L, -1), TypeName_EntityManagerSharedPtr) != 0 || !lua_istable(L, 2))
+               {
+                       lua_pushstring(L, "create_new_entity should be called with pointer of EntityManager and a table.");
+                       return lua_error(L);
+               }
+               lua_pop(L, 2);
+
+               lua_pushvalue(L, 2);
+               int ref = luaL_ref(L, LUA_REGISTRYINDEX);
+
+               // Create an new entity
+               EntityManagerSharedPtr* ptr = static_cast<EntityManagerSharedPtr*>(lua_touserdata(L, 1));
+               entityx::Entity entity = (*ptr)->create();
+
+               // Save the lua object (ref) in the entity
+               entity.assign<LuaComponent>(ref);
+
+               // Return the Id of the entity
+               lua_newuserdata(L, sizeof(entityx::Entity::Id));
+               lua_rawgeti(L, LUA_REGISTRYINDEX, ref_EntityId);
+               lua_setmetatable(L, -2);
+               new (lua_touserdata(L, -1)) entityx::Entity::Id(entity.id());
+
+               return 1;
+       });
+       lua_setfield(L, -2, "create_new_entity");
+
+       // get_lua_object
+       lua_pushcfunction(L, [](lua_State* L){
+               // First check the type of the parameters
+               lua_getmetatable(L, 1);
+               lua_pushstring(L, "type");
+               lua_rawget(L, -2);
+               if (!lua_isuserdata(L, 1) || strcmp(lua_tostring(L, -1), TypeName_EntityManagerSharedPtr) != 0)
+               {
+                       lua_pushstring(L, "get_lua_object should be called with pointer of EntityManager and an EntityId.");
+                       return lua_error(L);
+               }
+               lua_pop(L, 2);
+
+               lua_getmetatable(L, 2);
+               lua_pushstring(L, "type");
+               lua_rawget(L, -2);
+               if (!lua_isuserdata(L, 2) || strcmp(lua_tostring(L, -1), TypeName_EntityId) != 0)
+               {
+                       lua_pushstring(L, "get_lua_object should be called with pointer of EntityManager and an EntityId.");
+                       return lua_error(L);
+               }
+               lua_pop(L, 2);
+
+               // Get the object
+               EntityManagerSharedPtr* ptr = static_cast<EntityManagerSharedPtr*>(lua_touserdata(L, 1));
+               entityx::Entity::Id* id = static_cast<entityx::Entity::Id*>(lua_touserdata(L, 2));
+
+               entityx::Entity entity = (*ptr)->get(*id);
+               int ref = entity.component<LuaComponent>()->ref;
+
+               lua_rawgeti(L, LUA_REGISTRYINDEX, ref);
+               return 1;
+       });
+       lua_setfield(L, -2, "get_lua_object");
+
+       // create_new_component
+       lua_pushcfunction(L, [](lua_State* L){
+               ComponentList::iterator i;
+               //return 0;
+               if ((i = components.find(std::string(lua_tostring(L, 1)))) != components.end())
+               {
+                       i->second(L);
+               }
+               else
+               {
+                       lua_pushstring(L, "Component not found.");
+                       return lua_error(L);
+               }
+               return 1;
+       });
+       lua_setfield(L, -2, "create_new_component");
+
+       lua_setglobal(L, "entityx");
+
+       luaL_loadstring(L, setup_lua_create_entity_factory);
+       lua_call(L, 0, 0);
+}
+
+void entityx::lua::push_entity_manager(lua_State* L, const EntityManagerSharedPtr& manager)
+{
+       lua_newuserdata(L, sizeof(EntityManagerSharedPtr));
+       lua_rawgeti(L, LUA_REGISTRYINDEX, ref_EntityManagerSharedPtr);
+       lua_setmetatable(L, -2);
+
+       new (lua_touserdata(L, -1)) EntityManagerSharedPtr(manager);
+}
+
+void entityx::lua::new_entity_manager(lua_State* L, const EntityManagerSharedPtr& manager, const char* name)
+{
+       lua_getglobal(L, "entityx");
+       lua_getfield(L, -1, "create_entity_factory");
+       lua_remove(L, -2); // _G["entityx"]
+       push_entity_manager(L, manager);
+       lua_pcall(L, 1, 1, 1);
+
+       lua_getglobal(L, "entityx");
+       lua_pushvalue(L, -2);
+       lua_setfield(L, -2, name);
+       lua_pop(L, 2); // original return value & entityx
+}
diff --git a/src/Script/entityx/entity_lua.hpp b/src/Script/entityx/entity_lua.hpp
new file mode 100644 (file)
index 0000000..034640a
--- /dev/null
@@ -0,0 +1,13 @@
+#ifndef ENTITY_LUA_HPP
+#define ENTITY_LUA_HPP
+
+#include <map>
+#include <type_traits>
+
+#include <lua.hpp>
+#include <entityx.h>
+
+#include <Script/entityx/LuaTypes.hpp>
+#include <Script/entityx/EntityLua.hpp>
+
+#endif//ENTITY_LUA_HPP
index 0fa903e0549b9bb9cf64d4a387b6d924808afff4..65d80a7c87c255df29044741bd2bea555c68f4c8 100644 (file)
@@ -19,7 +19,7 @@
  */
 
 #include <entityx.h>
-#include <Script/entityx/LuaTypes.hpp>
+#include <Script/entityx/entity_lua.hpp>
 
 #include <SDL2/SDL.h>