1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
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 = 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
|