Modernize lua read (part 2 & 3): C++ templating assurance (#7410)
[oweals/minetest.git] / src / script / lua_api / l_areastore.cpp
1 /*
2 Minetest
3 Copyright (C) 2015 est31 <mtest31@outlook.com>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20
21 #include "lua_api/l_areastore.h"
22 #include "lua_api/l_internal.h"
23 #include "common/c_converter.h"
24 #include "cpp_api/s_security.h"
25 #include "irr_v3d.h"
26 #include "util/areastore.h"
27 #include "filesys.h"
28 #include <fstream>
29
30 static inline void get_data_and_border_flags(lua_State *L, u8 start_i,
31                 bool *borders, bool *data)
32 {
33         if (!lua_isboolean(L, start_i))
34                 return;
35         *borders = lua_toboolean(L, start_i);
36         if (!lua_isboolean(L, start_i + 1))
37                 return;
38         *data = lua_toboolean(L, start_i + 1);
39 }
40
41 static void push_area(lua_State *L, const Area *a,
42                 bool include_borders, bool include_data)
43 {
44         if (!include_borders && !include_data) {
45                 lua_pushboolean(L, true);
46                 return;
47         }
48         lua_newtable(L);
49         if (include_borders) {
50                 push_v3s16(L, a->minedge);
51                 lua_setfield(L, -2, "min");
52                 push_v3s16(L, a->maxedge);
53                 lua_setfield(L, -2, "max");
54         }
55         if (include_data) {
56                 lua_pushlstring(L, a->data.c_str(), a->data.size());
57                 lua_setfield(L, -2, "data");
58         }
59 }
60
61 static inline void push_areas(lua_State *L, const std::vector<Area *> &areas,
62                 bool borders, bool data)
63 {
64         lua_newtable(L);
65         size_t cnt = areas.size();
66         for (size_t i = 0; i < cnt; i++) {
67                 lua_pushnumber(L, areas[i]->id);
68                 push_area(L, areas[i], borders, data);
69                 lua_settable(L, -3);
70         }
71 }
72
73 // Deserializes value and handles errors
74 static int deserialization_helper(lua_State *L, AreaStore *as,
75                 std::istream &is)
76 {
77         try {
78                 as->deserialize(is);
79         } catch (const SerializationError &e) {
80                 lua_pushboolean(L, false);
81                 lua_pushstring(L, e.what());
82                 return 2;
83         }
84
85         lua_pushboolean(L, true);
86         return 1;
87 }
88
89 // garbage collector
90 int LuaAreaStore::gc_object(lua_State *L)
91 {
92         LuaAreaStore *o = *(LuaAreaStore **)(lua_touserdata(L, 1));
93         delete o;
94         return 0;
95 }
96
97 // get_area(id, include_borders, include_data)
98 int LuaAreaStore::l_get_area(lua_State *L)
99 {
100         NO_MAP_LOCK_REQUIRED;
101
102         LuaAreaStore *o = checkobject(L, 1);
103         AreaStore *ast = o->as;
104
105         u32 id = luaL_checknumber(L, 2);
106
107         bool include_borders = true;
108         bool include_data = false;
109         get_data_and_border_flags(L, 3, &include_borders, &include_data);
110
111         const Area *res;
112
113         res = ast->getArea(id);
114         if (!res)
115                 return 0;
116
117         push_area(L, res, include_borders, include_data);
118
119         return 1;
120 }
121
122 // get_areas_for_pos(pos, include_borders, include_data)
123 int LuaAreaStore::l_get_areas_for_pos(lua_State *L)
124 {
125         NO_MAP_LOCK_REQUIRED;
126
127         LuaAreaStore *o = checkobject(L, 1);
128         AreaStore *ast = o->as;
129
130         v3s16 pos = check_v3s16(L, 2);
131
132         bool include_borders = true;
133         bool include_data = false;
134         get_data_and_border_flags(L, 3, &include_borders, &include_data);
135
136         std::vector<Area *> res;
137
138         ast->getAreasForPos(&res, pos);
139         push_areas(L, res, include_borders, include_data);
140
141         return 1;
142 }
143
144 // get_areas_in_area(edge1, edge2, accept_overlap, include_borders, include_data)
145 int LuaAreaStore::l_get_areas_in_area(lua_State *L)
146 {
147         NO_MAP_LOCK_REQUIRED;
148
149         LuaAreaStore *o = checkobject(L, 1);
150         AreaStore *ast = o->as;
151
152         v3s16 minedge = check_v3s16(L, 2);
153         v3s16 maxedge = check_v3s16(L, 3);
154
155         bool include_borders = true;
156         bool include_data = false;
157         bool accept_overlap = false;
158         if (lua_isboolean(L, 4)) {
159                 accept_overlap = readParam<bool>(L, 4);
160                 get_data_and_border_flags(L, 5, &include_borders, &include_data);
161         }
162         std::vector<Area *> res;
163
164         ast->getAreasInArea(&res, minedge, maxedge, accept_overlap);
165         push_areas(L, res, include_borders, include_data);
166
167         return 1;
168 }
169
170 // insert_area(edge1, edge2, data, id)
171 int LuaAreaStore::l_insert_area(lua_State *L)
172 {
173         NO_MAP_LOCK_REQUIRED;
174
175         LuaAreaStore *o = checkobject(L, 1);
176         AreaStore *ast = o->as;
177
178         Area a(check_v3s16(L, 2), check_v3s16(L, 3));
179
180         size_t d_len;
181         const char *data = luaL_checklstring(L, 4, &d_len);
182
183         a.data = std::string(data, d_len);
184
185         if (lua_isnumber(L, 5))
186                 a.id = lua_tonumber(L, 5);
187
188         if (!ast->insertArea(&a))
189                 return 0;
190
191         lua_pushnumber(L, a.id);
192         return 1;
193 }
194
195 // reserve(count)
196 int LuaAreaStore::l_reserve(lua_State *L)
197 {
198         NO_MAP_LOCK_REQUIRED;
199
200         LuaAreaStore *o = checkobject(L, 1);
201         AreaStore *ast = o->as;
202
203         size_t count = luaL_checknumber(L, 2);
204         ast->reserve(count);
205         return 0;
206 }
207
208 // remove_area(id)
209 int LuaAreaStore::l_remove_area(lua_State *L)
210 {
211         NO_MAP_LOCK_REQUIRED;
212
213         LuaAreaStore *o = checkobject(L, 1);
214         AreaStore *ast = o->as;
215
216         u32 id = luaL_checknumber(L, 2);
217         bool success = ast->removeArea(id);
218
219         lua_pushboolean(L, success);
220         return 1;
221 }
222
223 // set_cache_params(params)
224 int LuaAreaStore::l_set_cache_params(lua_State *L)
225 {
226         NO_MAP_LOCK_REQUIRED;
227
228         LuaAreaStore *o = checkobject(L, 1);
229         AreaStore *ast = o->as;
230
231         luaL_checktype(L, 2, LUA_TTABLE);
232
233         bool enabled = getboolfield_default(L, 2, "enabled", true);
234         u8 block_radius = getintfield_default(L, 2, "block_radius", 64);
235         size_t limit = getintfield_default(L, 2, "block_radius", 1000);
236
237         ast->setCacheParams(enabled, block_radius, limit);
238
239         return 0;
240 }
241
242 // to_string()
243 int LuaAreaStore::l_to_string(lua_State *L)
244 {
245         NO_MAP_LOCK_REQUIRED;
246
247         LuaAreaStore *o = checkobject(L, 1);
248
249         std::ostringstream os(std::ios_base::binary);
250         o->as->serialize(os);
251         std::string str = os.str();
252
253         lua_pushlstring(L, str.c_str(), str.length());
254         return 1;
255 }
256
257 // to_file(filename)
258 int LuaAreaStore::l_to_file(lua_State *L)
259 {
260         NO_MAP_LOCK_REQUIRED;
261
262         LuaAreaStore *o = checkobject(L, 1);
263         AreaStore *ast = o->as;
264
265         const char *filename = luaL_checkstring(L, 2);
266         CHECK_SECURE_PATH(L, filename, true);
267
268         std::ostringstream os(std::ios_base::binary);
269         ast->serialize(os);
270
271         lua_pushboolean(L, fs::safeWriteToFile(filename, os.str()));
272         return 1;
273 }
274
275 // from_string(str)
276 int LuaAreaStore::l_from_string(lua_State *L)
277 {
278         NO_MAP_LOCK_REQUIRED;
279
280         LuaAreaStore *o = checkobject(L, 1);
281
282         size_t len;
283         const char *str = luaL_checklstring(L, 2, &len);
284
285         std::istringstream is(std::string(str, len), std::ios::binary);
286         return deserialization_helper(L, o->as, is);
287 }
288
289 // from_file(filename)
290 int LuaAreaStore::l_from_file(lua_State *L)
291 {
292         NO_MAP_LOCK_REQUIRED;
293
294         LuaAreaStore *o = checkobject(L, 1);
295
296         const char *filename = luaL_checkstring(L, 2);
297         CHECK_SECURE_PATH(L, filename, false);
298
299         std::ifstream is(filename, std::ios::binary);
300         return deserialization_helper(L, o->as, is);
301 }
302
303 LuaAreaStore::LuaAreaStore() : as(AreaStore::getOptimalImplementation())
304 {
305 }
306
307 LuaAreaStore::LuaAreaStore(const std::string &type)
308 {
309 #if USE_SPATIAL
310         if (type == "LibSpatial") {
311                 as = new SpatialAreaStore();
312         } else
313 #endif
314         {
315                 as = new VectorAreaStore();
316         }
317 }
318
319 LuaAreaStore::~LuaAreaStore()
320 {
321         delete as;
322 }
323
324 // LuaAreaStore()
325 // Creates an LuaAreaStore and leaves it on top of stack
326 int LuaAreaStore::create_object(lua_State *L)
327 {
328         NO_MAP_LOCK_REQUIRED;
329
330         LuaAreaStore *o = (lua_isstring(L, 1)) ?
331                 new LuaAreaStore(readParam<std::string>(L, 1)) :
332                 new LuaAreaStore();
333
334         *(void **)(lua_newuserdata(L, sizeof(void *))) = o;
335         luaL_getmetatable(L, className);
336         lua_setmetatable(L, -2);
337         return 1;
338 }
339
340 LuaAreaStore *LuaAreaStore::checkobject(lua_State *L, int narg)
341 {
342         NO_MAP_LOCK_REQUIRED;
343
344         luaL_checktype(L, narg, LUA_TUSERDATA);
345
346         void *ud = luaL_checkudata(L, narg, className);
347         if (!ud)
348                 luaL_typerror(L, narg, className);
349
350         return *(LuaAreaStore **)ud;  // unbox pointer
351 }
352
353 void LuaAreaStore::Register(lua_State *L)
354 {
355         lua_newtable(L);
356         int methodtable = lua_gettop(L);
357         luaL_newmetatable(L, className);
358         int metatable = lua_gettop(L);
359
360         lua_pushliteral(L, "__metatable");
361         lua_pushvalue(L, methodtable);
362         lua_settable(L, metatable);  // hide metatable from Lua getmetatable()
363
364         lua_pushliteral(L, "__index");
365         lua_pushvalue(L, methodtable);
366         lua_settable(L, metatable);
367
368         lua_pushliteral(L, "__gc");
369         lua_pushcfunction(L, gc_object);
370         lua_settable(L, metatable);
371
372         lua_pop(L, 1);  // drop metatable
373
374         luaL_openlib(L, 0, methods, 0);  // fill methodtable
375         lua_pop(L, 1);  // drop methodtable
376
377         // Can be created from Lua (AreaStore())
378         lua_register(L, className, create_object);
379 }
380
381 const char LuaAreaStore::className[] = "AreaStore";
382 const luaL_Reg LuaAreaStore::methods[] = {
383         luamethod(LuaAreaStore, get_area),
384         luamethod(LuaAreaStore, get_areas_for_pos),
385         luamethod(LuaAreaStore, get_areas_in_area),
386         luamethod(LuaAreaStore, insert_area),
387         luamethod(LuaAreaStore, reserve),
388         luamethod(LuaAreaStore, remove_area),
389         luamethod(LuaAreaStore, set_cache_params),
390         luamethod(LuaAreaStore, to_string),
391         luamethod(LuaAreaStore, to_file),
392         luamethod(LuaAreaStore, from_string),
393         luamethod(LuaAreaStore, from_file),
394         {0,0}
395 };