aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndy <drumsetmonkey@gmail.com>2019-08-26 17:43:12 -0400
committerAndy <drumsetmonkey@gmail.com>2019-08-26 17:43:12 -0400
commit3cea77e3e6ef432d1bfc6026139b482154ccf604 (patch)
treeca4619fa59caa46b360f1e7730f875b799be36e1
parentc772d1b5ecffbeb9712b16a580080cb20b3bacfc (diff)
Made lua binding c++17 compatible
-rw-r--r--src/Script/entityx/EntityLua.hpp242
-rw-r--r--src/Script/entityx/entity_lua.cpp181
-rw-r--r--src/Script/entityx/entity_lua.hpp13
-rw-r--r--src/main.cpp2
4 files changed, 437 insertions, 1 deletions
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<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
index 0000000..e62f040
--- /dev/null
+++ b/src/Script/entityx/entity_lua.cpp
@@ -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
index 0000000..034640a
--- /dev/null
+++ b/src/Script/entityx/entity_lua.hpp
@@ -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
diff --git a/src/main.cpp b/src/main.cpp
index 0fa903e..65d80a7 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -19,7 +19,7 @@
*/
#include <entityx.h>
-#include <Script/entityx/LuaTypes.hpp>
+#include <Script/entityx/entity_lua.hpp>
#include <SDL2/SDL.h>