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