]> git.lizzy.rs Git - minetest.git/blob - src/script/common/c_converter.cpp
90b78a0810f0ad6e87037962fb583dd8ef247ecb
[minetest.git] / src / script / common / c_converter.cpp
1 /*
2 Minetest
3 Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.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 extern "C" {
21 #include <lua.h>
22 #include <lauxlib.h>
23 }
24
25 #include "util/numeric.h"
26 #include "util/serialize.h"
27 #include "util/string.h"
28 #include "common/c_converter.h"
29 #include "common/c_internal.h"
30 #include "constants.h"
31 #include <set>
32 #include <cmath>
33
34
35 #define CHECK_TYPE(index, name, type) do { \
36                 int t = lua_type(L, (index)); \
37                 if (t != (type)) { \
38                         throw LuaError(std::string("Invalid ") + (name) + \
39                                 " (expected " + lua_typename(L, (type)) + \
40                                 " got " + lua_typename(L, t) + ")."); \
41                 } \
42         } while(0)
43
44 #define CHECK_FLOAT(value, name) do {\
45                 if (std::isnan(value) || std::isinf(value)) { \
46                         throw LuaError("Invalid float value for '" name \
47                                 "' (NaN or infinity)"); \
48                 } \
49         } while (0)
50
51 #define CHECK_POS_COORD(index, name) CHECK_TYPE(index, "vector coordinate " name, LUA_TNUMBER)
52 #define CHECK_POS_TAB(index) CHECK_TYPE(index, "vector", LUA_TTABLE)
53
54
55 /**
56  * A helper which calls CUSTOM_RIDX_READ_VECTOR with the argument at the given index
57  */
58 static void read_v3_aux(lua_State *L, int index)
59 {
60         lua_pushvalue(L, index);
61         lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_READ_VECTOR);
62         lua_insert(L, -2);
63         lua_call(L, 1, 3);
64 }
65
66 // Retrieve an integer vector where all components are optional
67 template<class T>
68 static bool getv3intfield(lua_State *L, int index,
69                 const char *fieldname, T &result)
70 {
71         lua_getfield(L, index, fieldname);
72         bool got = false;
73         if (lua_istable(L, -1)) {
74                 got |= getintfield(L, -1, "x", result.X);
75                 got |= getintfield(L, -1, "y", result.Y);
76                 got |= getintfield(L, -1, "z", result.Z);
77         }
78         lua_pop(L, 1);
79         return got;
80 }
81
82 void push_v3f(lua_State *L, v3f p)
83 {
84         lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_PUSH_VECTOR);
85         lua_pushnumber(L, p.X);
86         lua_pushnumber(L, p.Y);
87         lua_pushnumber(L, p.Z);
88         lua_call(L, 3, 1);
89 }
90
91 void push_v2f(lua_State *L, v2f p)
92 {
93         lua_createtable(L, 0, 2);
94         lua_pushnumber(L, p.X);
95         lua_setfield(L, -2, "x");
96         lua_pushnumber(L, p.Y);
97         lua_setfield(L, -2, "y");
98 }
99
100 v2s16 read_v2s16(lua_State *L, int index)
101 {
102         v2s16 p;
103         CHECK_POS_TAB(index);
104         lua_getfield(L, index, "x");
105         p.X = lua_tonumber(L, -1);
106         lua_pop(L, 1);
107         lua_getfield(L, index, "y");
108         p.Y = lua_tonumber(L, -1);
109         lua_pop(L, 1);
110         return p;
111 }
112
113 void push_v2s16(lua_State *L, v2s16 p)
114 {
115         lua_createtable(L, 0, 2);
116         lua_pushinteger(L, p.X);
117         lua_setfield(L, -2, "x");
118         lua_pushinteger(L, p.Y);
119         lua_setfield(L, -2, "y");
120 }
121
122 void push_v2s32(lua_State *L, v2s32 p)
123 {
124         lua_createtable(L, 0, 2);
125         lua_pushinteger(L, p.X);
126         lua_setfield(L, -2, "x");
127         lua_pushinteger(L, p.Y);
128         lua_setfield(L, -2, "y");
129 }
130
131 v2s32 read_v2s32(lua_State *L, int index)
132 {
133         v2s32 p;
134         CHECK_POS_TAB(index);
135         lua_getfield(L, index, "x");
136         p.X = lua_tonumber(L, -1);
137         lua_pop(L, 1);
138         lua_getfield(L, index, "y");
139         p.Y = lua_tonumber(L, -1);
140         lua_pop(L, 1);
141         return p;
142 }
143
144 v2f read_v2f(lua_State *L, int index)
145 {
146         v2f p;
147         CHECK_POS_TAB(index);
148         lua_getfield(L, index, "x");
149         p.X = lua_tonumber(L, -1);
150         lua_pop(L, 1);
151         lua_getfield(L, index, "y");
152         p.Y = lua_tonumber(L, -1);
153         lua_pop(L, 1);
154         return p;
155 }
156
157 v2f check_v2f(lua_State *L, int index)
158 {
159         v2f p;
160         CHECK_POS_TAB(index);
161         lua_getfield(L, index, "x");
162         CHECK_POS_COORD(-1, "x");
163         p.X = lua_tonumber(L, -1);
164         CHECK_FLOAT(p.X, "x");
165         lua_pop(L, 1);
166         lua_getfield(L, index, "y");
167         CHECK_POS_COORD(-1, "y");
168         p.Y = lua_tonumber(L, -1);
169         CHECK_FLOAT(p.Y, "y");
170         lua_pop(L, 1);
171         return p;
172 }
173
174 v3f read_v3f(lua_State *L, int index)
175 {
176         read_v3_aux(L, index);
177         float x = lua_tonumber(L, -3);
178         float y = lua_tonumber(L, -2);
179         float z = lua_tonumber(L, -1);
180         lua_pop(L, 3);
181         return v3f(x, y, z);
182 }
183
184 v3f check_v3f(lua_State *L, int index)
185 {
186         read_v3_aux(L, index);
187         CHECK_POS_COORD(-3, "x");
188         CHECK_POS_COORD(-2, "y");
189         CHECK_POS_COORD(-1, "z");
190         float x = lua_tonumber(L, -3);
191         float y = lua_tonumber(L, -2);
192         float z = lua_tonumber(L, -1);
193         lua_pop(L, 3);
194         return v3f(x, y, z);
195 }
196
197 v3d read_v3d(lua_State *L, int index)
198 {
199         read_v3_aux(L, index);
200         double x = lua_tonumber(L, -3);
201         double y = lua_tonumber(L, -2);
202         double z = lua_tonumber(L, -1);
203         lua_pop(L, 3);
204         return v3d(x, y, z);
205 }
206
207 v3d check_v3d(lua_State *L, int index)
208 {
209         read_v3_aux(L, index);
210         CHECK_POS_COORD(-3, "x");
211         CHECK_POS_COORD(-2, "y");
212         CHECK_POS_COORD(-1, "z");
213         double x = lua_tonumber(L, -3);
214         double y = lua_tonumber(L, -2);
215         double z = lua_tonumber(L, -1);
216         lua_pop(L, 3);
217         return v3d(x, y, z);
218 }
219
220 void push_ARGB8(lua_State *L, video::SColor color)
221 {
222         lua_createtable(L, 0, 4);
223         lua_pushinteger(L, color.getAlpha());
224         lua_setfield(L, -2, "a");
225         lua_pushinteger(L, color.getRed());
226         lua_setfield(L, -2, "r");
227         lua_pushinteger(L, color.getGreen());
228         lua_setfield(L, -2, "g");
229         lua_pushinteger(L, color.getBlue());
230         lua_setfield(L, -2, "b");
231 }
232
233 void pushFloatPos(lua_State *L, v3f p)
234 {
235         p /= BS;
236         push_v3f(L, p);
237 }
238
239 v3f checkFloatPos(lua_State *L, int index)
240 {
241         return check_v3f(L, index) * BS;
242 }
243
244 void push_v3s16(lua_State *L, v3s16 p)
245 {
246         lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_PUSH_VECTOR);
247         lua_pushinteger(L, p.X);
248         lua_pushinteger(L, p.Y);
249         lua_pushinteger(L, p.Z);
250         lua_call(L, 3, 1);
251 }
252
253 v3s16 read_v3s16(lua_State *L, int index)
254 {
255         // Correct rounding at <0
256         v3d pf = read_v3d(L, index);
257         return doubleToInt(pf, 1.0);
258 }
259
260 v3s16 check_v3s16(lua_State *L, int index)
261 {
262         // Correct rounding at <0
263         v3d pf = check_v3d(L, index);
264         return doubleToInt(pf, 1.0);
265 }
266
267 bool read_color(lua_State *L, int index, video::SColor *color)
268 {
269         if (lua_istable(L, index)) {
270                 *color = read_ARGB8(L, index);
271         } else if (lua_isnumber(L, index)) {
272                 color->set(lua_tonumber(L, index));
273         } else if (lua_isstring(L, index)) {
274                 video::SColor parsed_color;
275                 if (!parseColorString(lua_tostring(L, index), parsed_color, true))
276                         return false;
277
278                 *color = parsed_color;
279         } else {
280                 return false;
281         }
282
283         return true;
284 }
285
286 video::SColor read_ARGB8(lua_State *L, int index)
287 {
288         video::SColor color(0);
289         CHECK_TYPE(index, "ARGB color", LUA_TTABLE);
290         lua_getfield(L, index, "a");
291         color.setAlpha(lua_isnumber(L, -1) ? lua_tonumber(L, -1) : 0xFF);
292         lua_pop(L, 1);
293         lua_getfield(L, index, "r");
294         color.setRed(lua_tonumber(L, -1));
295         lua_pop(L, 1);
296         lua_getfield(L, index, "g");
297         color.setGreen(lua_tonumber(L, -1));
298         lua_pop(L, 1);
299         lua_getfield(L, index, "b");
300         color.setBlue(lua_tonumber(L, -1));
301         lua_pop(L, 1);
302         return color;
303 }
304
305 bool is_color_table(lua_State *L, int index)
306 {
307         // Check whole table in case of missing ColorSpec keys:
308         // This check does not remove the checked value from the stack.
309         // Only update the value if we know we have a valid ColorSpec key pair.
310         if (!lua_istable(L, index))
311                 return false;
312
313         bool is_color_table = false;
314         lua_getfield(L, index, "r");
315         if (!is_color_table)
316                 is_color_table = lua_isnumber(L, -1);
317         lua_getfield(L, index, "g");
318         if (!is_color_table)
319                 is_color_table = lua_isnumber(L, -1);
320         lua_getfield(L, index, "b");
321         if (!is_color_table)
322                 is_color_table = lua_isnumber(L, -1);
323         lua_pop(L, 3); // b, g, r values
324         return is_color_table;
325 }
326
327 aabb3f read_aabb3f(lua_State *L, int index, f32 scale)
328 {
329         aabb3f box;
330         if(lua_istable(L, index)){
331                 lua_rawgeti(L, index, 1);
332                 box.MinEdge.X = lua_tonumber(L, -1) * scale;
333                 lua_pop(L, 1);
334                 lua_rawgeti(L, index, 2);
335                 box.MinEdge.Y = lua_tonumber(L, -1) * scale;
336                 lua_pop(L, 1);
337                 lua_rawgeti(L, index, 3);
338                 box.MinEdge.Z = lua_tonumber(L, -1) * scale;
339                 lua_pop(L, 1);
340                 lua_rawgeti(L, index, 4);
341                 box.MaxEdge.X = lua_tonumber(L, -1) * scale;
342                 lua_pop(L, 1);
343                 lua_rawgeti(L, index, 5);
344                 box.MaxEdge.Y = lua_tonumber(L, -1) * scale;
345                 lua_pop(L, 1);
346                 lua_rawgeti(L, index, 6);
347                 box.MaxEdge.Z = lua_tonumber(L, -1) * scale;
348                 lua_pop(L, 1);
349         }
350         box.repair();
351         return box;
352 }
353
354 void push_aabb3f(lua_State *L, aabb3f box)
355 {
356         lua_createtable(L, 6, 0);
357         lua_pushnumber(L, box.MinEdge.X);
358         lua_rawseti(L, -2, 1);
359         lua_pushnumber(L, box.MinEdge.Y);
360         lua_rawseti(L, -2, 2);
361         lua_pushnumber(L, box.MinEdge.Z);
362         lua_rawseti(L, -2, 3);
363         lua_pushnumber(L, box.MaxEdge.X);
364         lua_rawseti(L, -2, 4);
365         lua_pushnumber(L, box.MaxEdge.Y);
366         lua_rawseti(L, -2, 5);
367         lua_pushnumber(L, box.MaxEdge.Z);
368         lua_rawseti(L, -2, 6);
369 }
370
371 std::vector<aabb3f> read_aabb3f_vector(lua_State *L, int index, f32 scale)
372 {
373         std::vector<aabb3f> boxes;
374         if(lua_istable(L, index)){
375                 int n = lua_objlen(L, index);
376                 // Check if it's a single box or a list of boxes
377                 bool possibly_single_box = (n == 6);
378                 for(int i = 1; i <= n && possibly_single_box; i++){
379                         lua_rawgeti(L, index, i);
380                         if(!lua_isnumber(L, -1))
381                                 possibly_single_box = false;
382                         lua_pop(L, 1);
383                 }
384                 if(possibly_single_box){
385                         // Read a single box
386                         boxes.push_back(read_aabb3f(L, index, scale));
387                 } else {
388                         // Read a list of boxes
389                         for(int i = 1; i <= n; i++){
390                                 lua_rawgeti(L, index, i);
391                                 boxes.push_back(read_aabb3f(L, -1, scale));
392                                 lua_pop(L, 1);
393                         }
394                 }
395         }
396         return boxes;
397 }
398
399 size_t read_stringlist(lua_State *L, int index, std::vector<std::string> *result)
400 {
401         if (index < 0)
402                 index = lua_gettop(L) + 1 + index;
403
404         size_t num_strings = 0;
405
406         if (lua_istable(L, index)) {
407                 lua_pushnil(L);
408                 while (lua_next(L, index)) {
409                         if (lua_isstring(L, -1)) {
410                                 result->push_back(lua_tostring(L, -1));
411                                 num_strings++;
412                         }
413                         lua_pop(L, 1);
414                 }
415         } else if (lua_isstring(L, index)) {
416                 result->push_back(lua_tostring(L, index));
417                 num_strings++;
418         }
419
420         return num_strings;
421 }
422
423 /*
424         Table field getters
425 */
426
427 bool check_field_or_nil(lua_State *L, int index, int type, const char *fieldname)
428 {
429         thread_local std::set<u64> warned_msgs;
430
431         int t = lua_type(L, index);
432         if (t == LUA_TNIL)
433                 return false;
434
435         if (t == type)
436                 return true;
437
438         // Check coercion types
439         if (type == LUA_TNUMBER) {
440                 if (lua_isnumber(L, index))
441                         return true;
442         } else if (type == LUA_TSTRING) {
443                 if (lua_isstring(L, index))
444                         return true;
445         }
446
447         // Types mismatch. Log unique line.
448         std::string backtrace = std::string("Invalid field ") + fieldname +
449                 " (expected " + lua_typename(L, type) +
450                 " got " + lua_typename(L, t) + ").\n" + script_get_backtrace(L);
451
452         u64 hash = murmur_hash_64_ua(backtrace.data(), backtrace.length(), 0xBADBABE);
453         if (warned_msgs.find(hash) == warned_msgs.end()) {
454                 errorstream << backtrace << std::endl;
455                 warned_msgs.insert(hash);
456         }
457
458         return false;
459 }
460
461 bool getstringfield(lua_State *L, int table,
462                 const char *fieldname, std::string &result)
463 {
464         lua_getfield(L, table, fieldname);
465         bool got = false;
466
467         if (check_field_or_nil(L, -1, LUA_TSTRING, fieldname)) {
468                 size_t len = 0;
469                 const char *ptr = lua_tolstring(L, -1, &len);
470                 if (ptr) {
471                         result.assign(ptr, len);
472                         got = true;
473                 }
474         }
475         lua_pop(L, 1);
476         return got;
477 }
478
479 bool getfloatfield(lua_State *L, int table,
480                 const char *fieldname, float &result)
481 {
482         lua_getfield(L, table, fieldname);
483         bool got = false;
484
485         if (check_field_or_nil(L, -1, LUA_TNUMBER, fieldname)) {
486                 result = lua_tonumber(L, -1);
487                 got = true;
488         }
489         lua_pop(L, 1);
490         return got;
491 }
492
493 bool getboolfield(lua_State *L, int table,
494                 const char *fieldname, bool &result)
495 {
496         lua_getfield(L, table, fieldname);
497         bool got = false;
498
499         if (check_field_or_nil(L, -1, LUA_TBOOLEAN, fieldname)){
500                 result = lua_toboolean(L, -1);
501                 got = true;
502         }
503         lua_pop(L, 1);
504         return got;
505 }
506
507 size_t getstringlistfield(lua_State *L, int table, const char *fieldname,
508                 std::vector<std::string> *result)
509 {
510         lua_getfield(L, table, fieldname);
511
512         size_t num_strings_read = read_stringlist(L, -1, result);
513
514         lua_pop(L, 1);
515         return num_strings_read;
516 }
517
518 std::string getstringfield_default(lua_State *L, int table,
519                 const char *fieldname, const std::string &default_)
520 {
521         std::string result = default_;
522         getstringfield(L, table, fieldname, result);
523         return result;
524 }
525
526 int getintfield_default(lua_State *L, int table,
527                 const char *fieldname, int default_)
528 {
529         int result = default_;
530         getintfield(L, table, fieldname, result);
531         return result;
532 }
533
534 float getfloatfield_default(lua_State *L, int table,
535                 const char *fieldname, float default_)
536 {
537         float result = default_;
538         getfloatfield(L, table, fieldname, result);
539         return result;
540 }
541
542 bool getboolfield_default(lua_State *L, int table,
543                 const char *fieldname, bool default_)
544 {
545         bool result = default_;
546         getboolfield(L, table, fieldname, result);
547         return result;
548 }
549
550 v3s16 getv3s16field_default(lua_State *L, int table,
551                 const char *fieldname, v3s16 default_)
552 {
553         getv3intfield(L, table, fieldname, default_);
554         return default_;
555 }
556
557 void setstringfield(lua_State *L, int table,
558                 const char *fieldname, const std::string &value)
559 {
560         lua_pushlstring(L, value.c_str(), value.length());
561         if(table < 0)
562                 table -= 1;
563         lua_setfield(L, table, fieldname);
564 }
565
566 void setintfield(lua_State *L, int table,
567                 const char *fieldname, int value)
568 {
569         lua_pushinteger(L, value);
570         if(table < 0)
571                 table -= 1;
572         lua_setfield(L, table, fieldname);
573 }
574
575 void setfloatfield(lua_State *L, int table,
576                 const char *fieldname, float value)
577 {
578         lua_pushnumber(L, value);
579         if(table < 0)
580                 table -= 1;
581         lua_setfield(L, table, fieldname);
582 }
583
584 void setboolfield(lua_State *L, int table,
585                 const char *fieldname, bool value)
586 {
587         lua_pushboolean(L, value);
588         if(table < 0)
589                 table -= 1;
590         lua_setfield(L, table, fieldname);
591 }
592
593
594 ////
595 //// Array table slices
596 ////
597
598 size_t write_array_slice_float(
599         lua_State *L,
600         int table_index,
601         float *data,
602         v3u16 data_size,
603         v3u16 slice_offset,
604         v3u16 slice_size)
605 {
606         v3u16 pmin, pmax(data_size);
607
608         if (slice_offset.X > 0) {
609                 slice_offset.X--;
610                 pmin.X = slice_offset.X;
611                 pmax.X = MYMIN(slice_offset.X + slice_size.X, data_size.X);
612         }
613
614         if (slice_offset.Y > 0) {
615                 slice_offset.Y--;
616                 pmin.Y = slice_offset.Y;
617                 pmax.Y = MYMIN(slice_offset.Y + slice_size.Y, data_size.Y);
618         }
619
620         if (slice_offset.Z > 0) {
621                 slice_offset.Z--;
622                 pmin.Z = slice_offset.Z;
623                 pmax.Z = MYMIN(slice_offset.Z + slice_size.Z, data_size.Z);
624         }
625
626         const u32 ystride = data_size.X;
627         const u32 zstride = data_size.X * data_size.Y;
628
629         u32 elem_index = 1;
630         for (u32 z = pmin.Z; z != pmax.Z; z++)
631         for (u32 y = pmin.Y; y != pmax.Y; y++)
632         for (u32 x = pmin.X; x != pmax.X; x++) {
633                 u32 i = z * zstride + y * ystride + x;
634                 lua_pushnumber(L, data[i]);
635                 lua_rawseti(L, table_index, elem_index);
636                 elem_index++;
637         }
638
639         return elem_index - 1;
640 }