Add optional buffer param for bulk data array writes in Lua
[oweals/minetest.git] / src / script / lua_api / l_vmanip.cpp
1 /*
2 Minetest
3 Copyright (C) 2013 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
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_vmanip.h"
22 #include "lua_api/l_internal.h"
23 #include "common/c_content.h"
24 #include "common/c_converter.h"
25 #include "emerge.h"
26 #include "environment.h"
27 #include "map.h"
28 #include "server.h"
29 #include "mapgen.h"
30
31 #define GET_ENV_PTR ServerEnvironment* env =                                   \
32                                 dynamic_cast<ServerEnvironment*>(getEnv(L));                   \
33                                 if (env == NULL) return 0
34
35 // garbage collector
36 int LuaVoxelManip::gc_object(lua_State *L)
37 {
38         LuaVoxelManip *o = *(LuaVoxelManip **)(lua_touserdata(L, 1));
39         delete o;
40
41         return 0;
42 }
43
44 int LuaVoxelManip::l_read_from_map(lua_State *L)
45 {
46         LuaVoxelManip *o = checkobject(L, 1);
47         MMVManip *vm = o->vm;
48
49         v3s16 bp1 = getNodeBlockPos(check_v3s16(L, 2));
50         v3s16 bp2 = getNodeBlockPos(check_v3s16(L, 3));
51         sortBoxVerticies(bp1, bp2);
52
53         vm->initialEmerge(bp1, bp2);
54
55         push_v3s16(L, vm->m_area.MinEdge);
56         push_v3s16(L, vm->m_area.MaxEdge);
57
58         return 2;
59 }
60
61 int LuaVoxelManip::l_get_data(lua_State *L)
62 {
63         NO_MAP_LOCK_REQUIRED;
64
65         LuaVoxelManip *o = checkobject(L, 1);
66         bool use_buffer  = lua_istable(L, 2);
67
68         MMVManip *vm = o->vm;
69
70         u32 volume = vm->m_area.getVolume();
71
72         if (use_buffer)
73                 lua_pushvalue(L, 2);
74         else
75                 lua_newtable(L);
76
77         for (u32 i = 0; i != volume; i++) {
78                 lua_Integer cid = vm->m_data[i].getContent();
79                 lua_pushinteger(L, cid);
80                 lua_rawseti(L, -2, i + 1);
81         }
82
83         return 1;
84 }
85
86 int LuaVoxelManip::l_set_data(lua_State *L)
87 {
88         NO_MAP_LOCK_REQUIRED;
89
90         LuaVoxelManip *o = checkobject(L, 1);
91         MMVManip *vm = o->vm;
92
93         if (!lua_istable(L, 2))
94                 return 0;
95
96         u32 volume = vm->m_area.getVolume();
97         for (u32 i = 0; i != volume; i++) {
98                 lua_rawgeti(L, 2, i + 1);
99                 content_t c = lua_tointeger(L, -1);
100
101                 vm->m_data[i].setContent(c);
102
103                 lua_pop(L, 1);
104         }
105
106         return 0;
107 }
108
109 int LuaVoxelManip::l_write_to_map(lua_State *L)
110 {
111         LuaVoxelManip *o = checkobject(L, 1);
112         MMVManip *vm = o->vm;
113
114         vm->blitBackAll(&o->modified_blocks);
115
116         return 0;
117 }
118
119 int LuaVoxelManip::l_get_node_at(lua_State *L)
120 {
121         NO_MAP_LOCK_REQUIRED;
122         GET_ENV_PTR;
123
124         LuaVoxelManip *o = checkobject(L, 1);
125         v3s16 pos        = check_v3s16(L, 2);
126
127         pushnode(L, o->vm->getNodeNoExNoEmerge(pos), env->getGameDef()->ndef());
128         return 1;
129 }
130
131 int LuaVoxelManip::l_set_node_at(lua_State *L)
132 {
133         NO_MAP_LOCK_REQUIRED;
134         GET_ENV_PTR;
135
136         LuaVoxelManip *o = checkobject(L, 1);
137         v3s16 pos        = check_v3s16(L, 2);
138         MapNode n        = readnode(L, 3, env->getGameDef()->ndef());
139
140         o->vm->setNodeNoEmerge(pos, n);
141
142         return 0;
143 }
144
145 int LuaVoxelManip::l_update_liquids(lua_State *L)
146 {
147         GET_ENV_PTR;
148
149         LuaVoxelManip *o = checkobject(L, 1);
150
151         Map *map = &(env->getMap());
152         INodeDefManager *ndef = getServer(L)->getNodeDefManager();
153         MMVManip *vm = o->vm;
154
155         Mapgen mg;
156         mg.vm   = vm;
157         mg.ndef = ndef;
158
159         mg.updateLiquid(&map->m_transforming_liquid,
160                         vm->m_area.MinEdge, vm->m_area.MaxEdge);
161
162         return 0;
163 }
164
165 int LuaVoxelManip::l_calc_lighting(lua_State *L)
166 {
167         NO_MAP_LOCK_REQUIRED;
168
169         LuaVoxelManip *o = checkobject(L, 1);
170         if (!o->is_mapgen_vm)
171                 return 0;
172
173         INodeDefManager *ndef = getServer(L)->getNodeDefManager();
174         EmergeManager *emerge = getServer(L)->getEmergeManager();
175         MMVManip *vm = o->vm;
176
177         v3s16 yblock = v3s16(0, 1, 0) * MAP_BLOCKSIZE;
178         v3s16 fpmin  = vm->m_area.MinEdge;
179         v3s16 fpmax  = vm->m_area.MaxEdge;
180         v3s16 pmin   = lua_istable(L, 2) ? check_v3s16(L, 2) : fpmin + yblock;
181         v3s16 pmax   = lua_istable(L, 3) ? check_v3s16(L, 3) : fpmax - yblock;
182
183         sortBoxVerticies(pmin, pmax);
184         if (!vm->m_area.contains(VoxelArea(pmin, pmax)))
185                 throw LuaError("Specified voxel area out of VoxelManipulator bounds");
186
187         Mapgen mg;
188         mg.vm          = vm;
189         mg.ndef        = ndef;
190         mg.water_level = emerge->params.water_level;
191
192         mg.calcLighting(pmin, pmax, fpmin, fpmax);
193
194         return 0;
195 }
196
197 int LuaVoxelManip::l_set_lighting(lua_State *L)
198 {
199         NO_MAP_LOCK_REQUIRED;
200
201         LuaVoxelManip *o = checkobject(L, 1);
202         if (!o->is_mapgen_vm)
203                 return 0;
204
205         if (!lua_istable(L, 2))
206                 return 0;
207
208         u8 light;
209         light  = (getintfield_default(L, 2, "day",   0) & 0x0F);
210         light |= (getintfield_default(L, 2, "night", 0) & 0x0F) << 4;
211
212         MMVManip *vm = o->vm;
213
214         v3s16 yblock = v3s16(0, 1, 0) * MAP_BLOCKSIZE;
215         v3s16 pmin = lua_istable(L, 3) ? check_v3s16(L, 3) : vm->m_area.MinEdge + yblock;
216         v3s16 pmax = lua_istable(L, 4) ? check_v3s16(L, 4) : vm->m_area.MaxEdge - yblock;
217
218         sortBoxVerticies(pmin, pmax);
219         if (!vm->m_area.contains(VoxelArea(pmin, pmax)))
220                 throw LuaError("Specified voxel area out of VoxelManipulator bounds");
221
222         Mapgen mg;
223         mg.vm = vm;
224
225         mg.setLighting(light, pmin, pmax);
226
227         return 0;
228 }
229
230 int LuaVoxelManip::l_get_light_data(lua_State *L)
231 {
232         NO_MAP_LOCK_REQUIRED;
233
234         LuaVoxelManip *o = checkobject(L, 1);
235         MMVManip *vm = o->vm;
236
237         u32 volume = vm->m_area.getVolume();
238
239         lua_newtable(L);
240         for (u32 i = 0; i != volume; i++) {
241                 lua_Integer light = vm->m_data[i].param1;
242                 lua_pushinteger(L, light);
243                 lua_rawseti(L, -2, i + 1);
244         }
245
246         return 1;
247 }
248
249 int LuaVoxelManip::l_set_light_data(lua_State *L)
250 {
251         NO_MAP_LOCK_REQUIRED;
252
253         LuaVoxelManip *o = checkobject(L, 1);
254         MMVManip *vm = o->vm;
255
256         if (!lua_istable(L, 2))
257                 return 0;
258
259         u32 volume = vm->m_area.getVolume();
260         for (u32 i = 0; i != volume; i++) {
261                 lua_rawgeti(L, 2, i + 1);
262                 u8 light = lua_tointeger(L, -1);
263
264                 vm->m_data[i].param1 = light;
265
266                 lua_pop(L, 1);
267         }
268
269         return 0;
270 }
271
272 int LuaVoxelManip::l_get_param2_data(lua_State *L)
273 {
274         NO_MAP_LOCK_REQUIRED;
275
276         LuaVoxelManip *o = checkobject(L, 1);
277         MMVManip *vm = o->vm;
278
279         u32 volume = vm->m_area.getVolume();
280
281         lua_newtable(L);
282         for (u32 i = 0; i != volume; i++) {
283                 lua_Integer param2 = vm->m_data[i].param2;
284                 lua_pushinteger(L, param2);
285                 lua_rawseti(L, -2, i + 1);
286         }
287
288         return 1;
289 }
290
291 int LuaVoxelManip::l_set_param2_data(lua_State *L)
292 {
293         NO_MAP_LOCK_REQUIRED;
294
295         LuaVoxelManip *o = checkobject(L, 1);
296         MMVManip *vm = o->vm;
297
298         if (!lua_istable(L, 2))
299                 return 0;
300
301         u32 volume = vm->m_area.getVolume();
302         for (u32 i = 0; i != volume; i++) {
303                 lua_rawgeti(L, 2, i + 1);
304                 u8 param2 = lua_tointeger(L, -1);
305
306                 vm->m_data[i].param2 = param2;
307
308                 lua_pop(L, 1);
309         }
310
311         return 0;
312 }
313
314 int LuaVoxelManip::l_update_map(lua_State *L)
315 {
316         LuaVoxelManip *o = checkobject(L, 1);
317         if (o->is_mapgen_vm)
318                 return 0;
319
320         Environment *env = getEnv(L);
321         if (!env)
322                 return 0;
323
324         Map *map = &(env->getMap());
325
326         // TODO: Optimize this by using Mapgen::calcLighting() instead
327         std::map<v3s16, MapBlock *> lighting_mblocks;
328         std::map<v3s16, MapBlock *> *mblocks = &o->modified_blocks;
329
330         lighting_mblocks.insert(mblocks->begin(), mblocks->end());
331
332         map->updateLighting(lighting_mblocks, *mblocks);
333
334         MapEditEvent event;
335         event.type = MEET_OTHER;
336         for (std::map<v3s16, MapBlock *>::iterator
337                 it = mblocks->begin();
338                 it != mblocks->end(); ++it)
339                 event.modified_blocks.insert(it->first);
340
341         map->dispatchEvent(&event);
342
343         mblocks->clear();
344
345         return 0;
346 }
347
348 int LuaVoxelManip::l_was_modified(lua_State *L)
349 {
350         NO_MAP_LOCK_REQUIRED;
351
352         LuaVoxelManip *o = checkobject(L, 1);
353         MMVManip *vm = o->vm;
354
355         lua_pushboolean(L, vm->m_is_dirty);
356
357         return 1;
358 }
359
360 int LuaVoxelManip::l_get_emerged_area(lua_State *L)
361 {
362         LuaVoxelManip *o = checkobject(L, 1);
363
364         push_v3s16(L, o->vm->m_area.MinEdge);
365         push_v3s16(L, o->vm->m_area.MaxEdge);
366
367         return 2;
368 }
369
370 LuaVoxelManip::LuaVoxelManip(MMVManip *mmvm, bool is_mg_vm)
371 {
372         this->vm           = mmvm;
373         this->is_mapgen_vm = is_mg_vm;
374 }
375
376 LuaVoxelManip::LuaVoxelManip(Map *map)
377 {
378         this->vm = new MMVManip(map);
379         this->is_mapgen_vm = false;
380 }
381
382 LuaVoxelManip::LuaVoxelManip(Map *map, v3s16 p1, v3s16 p2)
383 {
384         this->vm = new MMVManip(map);
385         this->is_mapgen_vm = false;
386
387         v3s16 bp1 = getNodeBlockPos(p1);
388         v3s16 bp2 = getNodeBlockPos(p2);
389         sortBoxVerticies(bp1, bp2);
390         vm->initialEmerge(bp1, bp2);
391 }
392
393 LuaVoxelManip::~LuaVoxelManip()
394 {
395         if (!is_mapgen_vm)
396                 delete vm;
397 }
398
399 // LuaVoxelManip()
400 // Creates an LuaVoxelManip and leaves it on top of stack
401 int LuaVoxelManip::create_object(lua_State *L)
402 {
403         NO_MAP_LOCK_REQUIRED;
404
405         Environment *env = getEnv(L);
406         if (!env)
407                 return 0;
408
409         Map *map = &(env->getMap());
410         LuaVoxelManip *o = (lua_istable(L, 1) && lua_istable(L, 2)) ?
411                 new LuaVoxelManip(map, check_v3s16(L, 1), check_v3s16(L, 2)) :
412                 new LuaVoxelManip(map);
413
414         *(void **)(lua_newuserdata(L, sizeof(void *))) = o;
415         luaL_getmetatable(L, className);
416         lua_setmetatable(L, -2);
417         return 1;
418 }
419
420 LuaVoxelManip *LuaVoxelManip::checkobject(lua_State *L, int narg)
421 {
422         NO_MAP_LOCK_REQUIRED;
423
424         luaL_checktype(L, narg, LUA_TUSERDATA);
425
426         void *ud = luaL_checkudata(L, narg, className);
427         if (!ud)
428                 luaL_typerror(L, narg, className);
429
430         return *(LuaVoxelManip **)ud;  // unbox pointer
431 }
432
433 void LuaVoxelManip::Register(lua_State *L)
434 {
435         lua_newtable(L);
436         int methodtable = lua_gettop(L);
437         luaL_newmetatable(L, className);
438         int metatable = lua_gettop(L);
439
440         lua_pushliteral(L, "__metatable");
441         lua_pushvalue(L, methodtable);
442         lua_settable(L, metatable);  // hide metatable from Lua getmetatable()
443
444         lua_pushliteral(L, "__index");
445         lua_pushvalue(L, methodtable);
446         lua_settable(L, metatable);
447
448         lua_pushliteral(L, "__gc");
449         lua_pushcfunction(L, gc_object);
450         lua_settable(L, metatable);
451
452         lua_pop(L, 1);  // drop metatable
453
454         luaL_openlib(L, 0, methods, 0);  // fill methodtable
455         lua_pop(L, 1);  // drop methodtable
456
457         // Can be created from Lua (VoxelManip())
458         lua_register(L, className, create_object);
459 }
460
461 const char LuaVoxelManip::className[] = "VoxelManip";
462 const luaL_reg LuaVoxelManip::methods[] = {
463         luamethod(LuaVoxelManip, read_from_map),
464         luamethod(LuaVoxelManip, get_data),
465         luamethod(LuaVoxelManip, set_data),
466         luamethod(LuaVoxelManip, get_node_at),
467         luamethod(LuaVoxelManip, set_node_at),
468         luamethod(LuaVoxelManip, write_to_map),
469         luamethod(LuaVoxelManip, update_map),
470         luamethod(LuaVoxelManip, update_liquids),
471         luamethod(LuaVoxelManip, calc_lighting),
472         luamethod(LuaVoxelManip, set_lighting),
473         luamethod(LuaVoxelManip, get_light_data),
474         luamethod(LuaVoxelManip, set_light_data),
475         luamethod(LuaVoxelManip, get_param2_data),
476         luamethod(LuaVoxelManip, set_param2_data),
477         luamethod(LuaVoxelManip, was_modified),
478         luamethod(LuaVoxelManip, get_emerged_area),
479         {0,0}
480 };