1 /**
2  * @file
3  * @brief Utility functions and macros for Lua bindings.
4 **/
5 
6 #pragma once
7 
8 #include "AppHdr.h"
9 
10 extern "C" {
11 #include <lua.h>
12 #include <lauxlib.h>
13 #include <lualib.h>
14 }
15 
16 #include <vector>
17 
18 using std::vector;
19 
20 lua_Integer luaL_safe_checkinteger(lua_State *L, int idx);
21 lua_Integer luaL_safe_tointeger(lua_State *L, int idx);
22 // override some #defines in the lua libs.
23 #define luaL_safe_checkint(L,n)    ((int)luaL_safe_checkinteger(L, (n)))
24 #define luaL_safe_checklong(L,n)   ((long)luaL_safe_checkinteger(L, (n)))
25 
26 /*
27  * Function definitions.
28  */
29 
30 #define LUAFN(name) static int name(lua_State *ls)
31 #define LUAWRAP(name, wrapexpr) \
32     static int name(lua_State *ls) \
33     {   \
34         UNUSED(ls); \
35         wrapexpr; \
36         return 0; \
37     }
38 #define PLUARET(type, val) \
39     do { \
40         lua_push##type(ls, val); \
41         return 1; \
42     } while (false)
43 #define LUARET1(name, type, val) \
44     static int name(lua_State *ls) \
45     { \
46         lua_push##type(ls, val); \
47         return 1; \
48     }
49 #define LUARET2(name, type, val1, val2)  \
50     static int name(lua_State *ls) \
51     { \
52         lua_push##type(ls, val1); \
53         lua_push##type(ls, val2); \
54         return 2; \
55     }
56 
57 #define ASSERT_DLUA \
58     do {                                                            \
59         if (CLua::get_vm(ls).managed_vm)                            \
60             luaL_error(ls, "Operation forbidden in end-user script");   \
61     } while (false)
62 
63 // FIXME: remove one of these.
64 void luaopen_setmeta(lua_State *ls,
65                      const char *global,
66                      const luaL_reg *lua_lib,
67                      const char *meta);
68 
69 void clua_register_metatable(lua_State *ls, const char *tn,
70                              const luaL_reg *lr,
71                              int (*gcfn)(lua_State *ls) = nullptr);
72 
73 int clua_stringtable(lua_State *ls, const vector<string> &s);
74 
75 /*
76  * User-data templates.
77  * TODO: Consolidate these.
78  */
79 
80 template <class T>
clua_get_lightuserdata(lua_State * ls,int ndx)81 static inline T *clua_get_lightuserdata(lua_State *ls, int ndx)
82 {
83     return (lua_islightuserdata(ls, ndx))?
84             static_cast<T *>(lua_touserdata(ls, ndx))
85           : nullptr;
86 }
87 
88 template <class T>
89 static inline T *clua_get_userdata(lua_State *ls, const char *mt, int ndx = 1)
90 {
91     return static_cast<T*>(luaL_checkudata(ls, ndx, mt));
92 }
93 
94 // Use for cases where the userdata is simply a pointer and is guaranteed
95 // to be created by `new`. For a more complex pattern, see _delete_wrapped_item
96 // in l-item.cc. The pattern instantiate here requires checking the pointer
97 // for validity...
98 template <class T>
lua_object_gc(lua_State * ls)99 static int lua_object_gc(lua_State *ls)
100 {
101     T **pptr = static_cast<T**>(lua_touserdata(ls, 1));
102     if (pptr && *pptr)
103     {
104         delete *pptr;
105         *pptr = nullptr;
106     }
107     return 0;
108 }
109 
110 // allocate space for an object of type T via lua. Any memory allocated this
111 // way will be garbage-collected by lua. There are basically two patterns:
112 // (i) T is a pointer (call it `*S`), so the type signature of this ends up as
113 //     **S. In that case, the memory management is probably handled on the c++
114 //     side, perhaps by creating a new object when instantiating this userdef.
115 // (ii) T is a wrapper struct, for example `item_wrapper` in l-item.cc. In this
116 //      case the memory management may be a bit more complicated, but the
117 //      wrapper object will still usually have a pointer to a c++ object, and
118 //      may still need a gc function.
119 //
120 // If a `delete` is needed on cleanup, bind an appropriate function to the __gc
121 // metamethod. For example, case (i) can usually use `lua_object_gc` above.
122 // Be aware that this method is callable directly from the lua side in addition
123 // to being automatically triggered -- so you need to be careful with your
124 // pointers!
clua_new_userdata(lua_State * ls,const char * mt)125 template <class T> T *clua_new_userdata(
126         lua_State *ls, const char *mt)
127 {
128     void *udata = lua_newuserdata(ls, sizeof(T));
129     luaL_getmetatable(ls, mt);
130     lua_setmetatable(ls, -2);
131     return static_cast<T*>(udata);
132 }
133 
134 template <typename T>
dlua_push_userdata(lua_State * ls,T udata,const char * meta)135 static inline void dlua_push_userdata(lua_State *ls, T udata, const char *meta)
136 {
137     T *de = clua_new_userdata<T>(ls, meta);
138     *de = udata;
139 }
140 
141 template <class T>
dlua_push_object_type(lua_State * ls,const char * meta,const T & data)142 static int dlua_push_object_type(lua_State *ls, const char *meta, const T &data)
143 {
144     T **ptr = clua_new_userdata<T*>(ls, meta);
145     if (ptr)
146         *ptr = new T(data);
147     else
148         lua_pushnil(ls);
149     return 1;
150 }
151 
152 /*
153  * Passing objects from and to Lua.
154  */
155 struct activity_interrupt_data;
156 int push_activity_interrupt(lua_State *ls, activity_interrupt_data *t);
157 
158 class map_def;
159 void clua_push_map(lua_State *ls, map_def *map);
160 
161 class dgn_event;
162 void clua_push_dgn_event(lua_State *ls, const dgn_event *devent);
163 
164 class monster;
165 struct MonsterWrap
166 {
167     monster* mons;
168     int      turn;
169 };
170 
171 // XXX: These are currently defined outside cluautil.cc.
172 void push_monster(lua_State *ls, monster* mons);
173 void clua_push_item(lua_State *ls, item_def *item);
174 item_def *clua_get_item(lua_State *ls, int ndx);
175 void lua_push_floor_items(lua_State *ls, int link);
176 dungeon_feature_type check_lua_feature(lua_State *ls, int idx,
177                                        bool optional = false);
178 tileidx_t get_tile_idx(lua_State *ls, int arg);
179 level_id dlua_level_id(lua_State *ls, int ndx);
180 
181 #define GETCOORD(c, p1, p2, boundfn)                      \
182     coord_def c;                                          \
183     c.x = luaL_safe_checkint(ls, p1);                          \
184     c.y = luaL_safe_checkint(ls, p2);                          \
185     if (!boundfn(c))                                        \
186         luaL_error(                                          \
187             ls,                                                 \
188             make_stringf("Point (%d,%d) is out of bounds",      \
189                          c.x, c.y).c_str());                    \
190     else {};
191 
192 #define COORDS(c, p1, p2)                                \
193     GETCOORD(c, p1, p2, in_bounds)
194 
195 #define COORDSHOW(c, p1, p2) \
196     GETCOORD(c, p1, p2, in_show_bounds)
197 
198 #define PLAYERCOORDS(p, p1, p2) \
199     const coord_def p = player2grid(coord_def(luaL_safe_checkint(ls,p1), \
200                                               luaL_safe_checkint(ls,p2)));
201 
202 #define FEAT(f, pos) \
203 dungeon_feature_type f = check_lua_feature(ls, pos)
204 
205 #define LEVEL(br, pos)                                              \
206     const char *branch_name = luaL_checkstring(ls, pos);            \
207     branch_type br = branch_by_abbrevname(branch_name);             \
208     if (br == NUM_BRANCHES)                                         \
209         luaL_error(ls, "Expected branch name");
210 
211 #define MAP(ls, n, var) \
212 map_def *var = *(map_def **) luaL_checkudata(ls, n, MAP_METATABLE); \
213 if (!var) \
214     return 0
215 
216 // TODO: ugh
217 #define LINES(ls, n, map_var, lines_var) \
218 MAP(ls, n, map_var); \
219 map_lines &lines_var = map_var->map
220 
221 #define DEVENT(ls, n, var) \
222 dgn_event *var = *(dgn_event **) luaL_checkudata(ls, n, DEVENT_METATABLE); \
223 if (!var) \
224     return 0
225 
226 #define MAPMARKER(ls, n, var) \
227 map_marker *var = *(map_marker **) luaL_checkudata(ls, n, MAPMARK_METATABLE); \
228 if (!var) \
229     return 0
230 
231 #define LUA_ITEM(ls, name, n) \
232 item_def *name = *(item_def **) luaL_checkudata(ls, n, ITEM_METATABLE); \
233 if (!name) \
234     return 0
235 
236 template <typename list, typename lpush>
clua_gentable(lua_State * ls,const list & strings,lpush push)237 static int clua_gentable(lua_State *ls, const list &strings, lpush push)
238 {
239     lua_newtable(ls);
240     for (int i = 0, size = strings.size(); i < size; ++i)
241     {
242         push(ls, strings[i]);
243         lua_rawseti(ls, -2, i + 1);
244     }
245     return 1;
246 }
247 
248 int clua_pushcxxstring(lua_State *ls, const string &s);
249 int clua_pushpoint(lua_State *ls, const coord_def &pos);
250