123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640 |
- /*
- Minetest
- Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Lesser General Public License for more details.
- You should have received a copy of the GNU Lesser General Public License along
- with this program; if not, write to the Free Software Foundation, Inc.,
- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
- extern "C" {
- #include <lua.h>
- #include <lauxlib.h>
- }
- #include "util/numeric.h"
- #include "util/serialize.h"
- #include "util/string.h"
- #include "common/c_converter.h"
- #include "common/c_internal.h"
- #include "constants.h"
- #include <set>
- #include <cmath>
- #define CHECK_TYPE(index, name, type) do { \
- int t = lua_type(L, (index)); \
- if (t != (type)) { \
- throw LuaError(std::string("Invalid ") + (name) + \
- " (expected " + lua_typename(L, (type)) + \
- " got " + lua_typename(L, t) + ")."); \
- } \
- } while(0)
- #define CHECK_FLOAT(value, name) do {\
- if (std::isnan(value) || std::isinf(value)) { \
- throw LuaError("Invalid float value for '" name \
- "' (NaN or infinity)"); \
- } \
- } while (0)
- #define CHECK_POS_COORD(index, name) CHECK_TYPE(index, "vector coordinate " name, LUA_TNUMBER)
- #define CHECK_POS_TAB(index) CHECK_TYPE(index, "vector", LUA_TTABLE)
- /**
- * A helper which calls CUSTOM_RIDX_READ_VECTOR with the argument at the given index
- */
- static void read_v3_aux(lua_State *L, int index)
- {
- lua_pushvalue(L, index);
- lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_READ_VECTOR);
- lua_insert(L, -2);
- lua_call(L, 1, 3);
- }
- // Retrieve an integer vector where all components are optional
- template<class T>
- static bool getv3intfield(lua_State *L, int index,
- const char *fieldname, T &result)
- {
- lua_getfield(L, index, fieldname);
- bool got = false;
- if (lua_istable(L, -1)) {
- got |= getintfield(L, -1, "x", result.X);
- got |= getintfield(L, -1, "y", result.Y);
- got |= getintfield(L, -1, "z", result.Z);
- }
- lua_pop(L, 1);
- return got;
- }
- void push_v3f(lua_State *L, v3f p)
- {
- lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_PUSH_VECTOR);
- lua_pushnumber(L, p.X);
- lua_pushnumber(L, p.Y);
- lua_pushnumber(L, p.Z);
- lua_call(L, 3, 1);
- }
- void push_v2f(lua_State *L, v2f p)
- {
- lua_createtable(L, 0, 2);
- lua_pushnumber(L, p.X);
- lua_setfield(L, -2, "x");
- lua_pushnumber(L, p.Y);
- lua_setfield(L, -2, "y");
- }
- v2s16 read_v2s16(lua_State *L, int index)
- {
- v2s16 p;
- CHECK_POS_TAB(index);
- lua_getfield(L, index, "x");
- p.X = lua_tonumber(L, -1);
- lua_pop(L, 1);
- lua_getfield(L, index, "y");
- p.Y = lua_tonumber(L, -1);
- lua_pop(L, 1);
- return p;
- }
- void push_v2s16(lua_State *L, v2s16 p)
- {
- lua_createtable(L, 0, 2);
- lua_pushinteger(L, p.X);
- lua_setfield(L, -2, "x");
- lua_pushinteger(L, p.Y);
- lua_setfield(L, -2, "y");
- }
- void push_v2s32(lua_State *L, v2s32 p)
- {
- lua_createtable(L, 0, 2);
- lua_pushinteger(L, p.X);
- lua_setfield(L, -2, "x");
- lua_pushinteger(L, p.Y);
- lua_setfield(L, -2, "y");
- }
- v2s32 read_v2s32(lua_State *L, int index)
- {
- v2s32 p;
- CHECK_POS_TAB(index);
- lua_getfield(L, index, "x");
- p.X = lua_tonumber(L, -1);
- lua_pop(L, 1);
- lua_getfield(L, index, "y");
- p.Y = lua_tonumber(L, -1);
- lua_pop(L, 1);
- return p;
- }
- v2f read_v2f(lua_State *L, int index)
- {
- v2f p;
- CHECK_POS_TAB(index);
- lua_getfield(L, index, "x");
- p.X = lua_tonumber(L, -1);
- lua_pop(L, 1);
- lua_getfield(L, index, "y");
- p.Y = lua_tonumber(L, -1);
- lua_pop(L, 1);
- return p;
- }
- v2f check_v2f(lua_State *L, int index)
- {
- v2f p;
- CHECK_POS_TAB(index);
- lua_getfield(L, index, "x");
- CHECK_POS_COORD(-1, "x");
- p.X = lua_tonumber(L, -1);
- CHECK_FLOAT(p.X, "x");
- lua_pop(L, 1);
- lua_getfield(L, index, "y");
- CHECK_POS_COORD(-1, "y");
- p.Y = lua_tonumber(L, -1);
- CHECK_FLOAT(p.Y, "y");
- lua_pop(L, 1);
- return p;
- }
- v3f read_v3f(lua_State *L, int index)
- {
- read_v3_aux(L, index);
- float x = lua_tonumber(L, -3);
- float y = lua_tonumber(L, -2);
- float z = lua_tonumber(L, -1);
- lua_pop(L, 3);
- return v3f(x, y, z);
- }
- v3f check_v3f(lua_State *L, int index)
- {
- read_v3_aux(L, index);
- CHECK_POS_COORD(-3, "x");
- CHECK_POS_COORD(-2, "y");
- CHECK_POS_COORD(-1, "z");
- float x = lua_tonumber(L, -3);
- float y = lua_tonumber(L, -2);
- float z = lua_tonumber(L, -1);
- lua_pop(L, 3);
- return v3f(x, y, z);
- }
- v3d read_v3d(lua_State *L, int index)
- {
- read_v3_aux(L, index);
- double x = lua_tonumber(L, -3);
- double y = lua_tonumber(L, -2);
- double z = lua_tonumber(L, -1);
- lua_pop(L, 3);
- return v3d(x, y, z);
- }
- v3d check_v3d(lua_State *L, int index)
- {
- read_v3_aux(L, index);
- CHECK_POS_COORD(-3, "x");
- CHECK_POS_COORD(-2, "y");
- CHECK_POS_COORD(-1, "z");
- double x = lua_tonumber(L, -3);
- double y = lua_tonumber(L, -2);
- double z = lua_tonumber(L, -1);
- lua_pop(L, 3);
- return v3d(x, y, z);
- }
- void push_ARGB8(lua_State *L, video::SColor color)
- {
- lua_createtable(L, 0, 4);
- lua_pushinteger(L, color.getAlpha());
- lua_setfield(L, -2, "a");
- lua_pushinteger(L, color.getRed());
- lua_setfield(L, -2, "r");
- lua_pushinteger(L, color.getGreen());
- lua_setfield(L, -2, "g");
- lua_pushinteger(L, color.getBlue());
- lua_setfield(L, -2, "b");
- }
- void pushFloatPos(lua_State *L, v3f p)
- {
- p /= BS;
- push_v3f(L, p);
- }
- v3f checkFloatPos(lua_State *L, int index)
- {
- return check_v3f(L, index) * BS;
- }
- void push_v3s16(lua_State *L, v3s16 p)
- {
- lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_PUSH_VECTOR);
- lua_pushinteger(L, p.X);
- lua_pushinteger(L, p.Y);
- lua_pushinteger(L, p.Z);
- lua_call(L, 3, 1);
- }
- v3s16 read_v3s16(lua_State *L, int index)
- {
- // Correct rounding at <0
- v3d pf = read_v3d(L, index);
- return doubleToInt(pf, 1.0);
- }
- v3s16 check_v3s16(lua_State *L, int index)
- {
- // Correct rounding at <0
- v3d pf = check_v3d(L, index);
- return doubleToInt(pf, 1.0);
- }
- bool read_color(lua_State *L, int index, video::SColor *color)
- {
- if (lua_istable(L, index)) {
- *color = read_ARGB8(L, index);
- } else if (lua_isnumber(L, index)) {
- color->set(lua_tonumber(L, index));
- } else if (lua_isstring(L, index)) {
- video::SColor parsed_color;
- if (!parseColorString(lua_tostring(L, index), parsed_color, true))
- return false;
- *color = parsed_color;
- } else {
- return false;
- }
- return true;
- }
- video::SColor read_ARGB8(lua_State *L, int index)
- {
- video::SColor color(0);
- CHECK_TYPE(index, "ARGB color", LUA_TTABLE);
- lua_getfield(L, index, "a");
- color.setAlpha(lua_isnumber(L, -1) ? lua_tonumber(L, -1) : 0xFF);
- lua_pop(L, 1);
- lua_getfield(L, index, "r");
- color.setRed(lua_tonumber(L, -1));
- lua_pop(L, 1);
- lua_getfield(L, index, "g");
- color.setGreen(lua_tonumber(L, -1));
- lua_pop(L, 1);
- lua_getfield(L, index, "b");
- color.setBlue(lua_tonumber(L, -1));
- lua_pop(L, 1);
- return color;
- }
- bool is_color_table(lua_State *L, int index)
- {
- // Check whole table in case of missing ColorSpec keys:
- // This check does not remove the checked value from the stack.
- // Only update the value if we know we have a valid ColorSpec key pair.
- if (!lua_istable(L, index))
- return false;
- bool is_color_table = false;
- lua_getfield(L, index, "r");
- if (!is_color_table)
- is_color_table = lua_isnumber(L, -1);
- lua_getfield(L, index, "g");
- if (!is_color_table)
- is_color_table = lua_isnumber(L, -1);
- lua_getfield(L, index, "b");
- if (!is_color_table)
- is_color_table = lua_isnumber(L, -1);
- lua_pop(L, 3); // b, g, r values
- return is_color_table;
- }
- aabb3f read_aabb3f(lua_State *L, int index, f32 scale)
- {
- aabb3f box;
- if(lua_istable(L, index)){
- lua_rawgeti(L, index, 1);
- box.MinEdge.X = lua_tonumber(L, -1) * scale;
- lua_pop(L, 1);
- lua_rawgeti(L, index, 2);
- box.MinEdge.Y = lua_tonumber(L, -1) * scale;
- lua_pop(L, 1);
- lua_rawgeti(L, index, 3);
- box.MinEdge.Z = lua_tonumber(L, -1) * scale;
- lua_pop(L, 1);
- lua_rawgeti(L, index, 4);
- box.MaxEdge.X = lua_tonumber(L, -1) * scale;
- lua_pop(L, 1);
- lua_rawgeti(L, index, 5);
- box.MaxEdge.Y = lua_tonumber(L, -1) * scale;
- lua_pop(L, 1);
- lua_rawgeti(L, index, 6);
- box.MaxEdge.Z = lua_tonumber(L, -1) * scale;
- lua_pop(L, 1);
- }
- box.repair();
- return box;
- }
- void push_aabb3f(lua_State *L, aabb3f box)
- {
- lua_createtable(L, 6, 0);
- lua_pushnumber(L, box.MinEdge.X);
- lua_rawseti(L, -2, 1);
- lua_pushnumber(L, box.MinEdge.Y);
- lua_rawseti(L, -2, 2);
- lua_pushnumber(L, box.MinEdge.Z);
- lua_rawseti(L, -2, 3);
- lua_pushnumber(L, box.MaxEdge.X);
- lua_rawseti(L, -2, 4);
- lua_pushnumber(L, box.MaxEdge.Y);
- lua_rawseti(L, -2, 5);
- lua_pushnumber(L, box.MaxEdge.Z);
- lua_rawseti(L, -2, 6);
- }
- std::vector<aabb3f> read_aabb3f_vector(lua_State *L, int index, f32 scale)
- {
- std::vector<aabb3f> boxes;
- if(lua_istable(L, index)){
- int n = lua_objlen(L, index);
- // Check if it's a single box or a list of boxes
- bool possibly_single_box = (n == 6);
- for(int i = 1; i <= n && possibly_single_box; i++){
- lua_rawgeti(L, index, i);
- if(!lua_isnumber(L, -1))
- possibly_single_box = false;
- lua_pop(L, 1);
- }
- if(possibly_single_box){
- // Read a single box
- boxes.push_back(read_aabb3f(L, index, scale));
- } else {
- // Read a list of boxes
- for(int i = 1; i <= n; i++){
- lua_rawgeti(L, index, i);
- boxes.push_back(read_aabb3f(L, -1, scale));
- lua_pop(L, 1);
- }
- }
- }
- return boxes;
- }
- size_t read_stringlist(lua_State *L, int index, std::vector<std::string> *result)
- {
- if (index < 0)
- index = lua_gettop(L) + 1 + index;
- size_t num_strings = 0;
- if (lua_istable(L, index)) {
- lua_pushnil(L);
- while (lua_next(L, index)) {
- if (lua_isstring(L, -1)) {
- result->push_back(lua_tostring(L, -1));
- num_strings++;
- }
- lua_pop(L, 1);
- }
- } else if (lua_isstring(L, index)) {
- result->push_back(lua_tostring(L, index));
- num_strings++;
- }
- return num_strings;
- }
- /*
- Table field getters
- */
- bool check_field_or_nil(lua_State *L, int index, int type, const char *fieldname)
- {
- thread_local std::set<u64> warned_msgs;
- int t = lua_type(L, index);
- if (t == LUA_TNIL)
- return false;
- if (t == type)
- return true;
- // Check coercion types
- if (type == LUA_TNUMBER) {
- if (lua_isnumber(L, index))
- return true;
- } else if (type == LUA_TSTRING) {
- if (lua_isstring(L, index))
- return true;
- }
- // Types mismatch. Log unique line.
- std::string backtrace = std::string("Invalid field ") + fieldname +
- " (expected " + lua_typename(L, type) +
- " got " + lua_typename(L, t) + ").\n" + script_get_backtrace(L);
- u64 hash = murmur_hash_64_ua(backtrace.data(), backtrace.length(), 0xBADBABE);
- if (warned_msgs.find(hash) == warned_msgs.end()) {
- errorstream << backtrace << std::endl;
- warned_msgs.insert(hash);
- }
- return false;
- }
- bool getstringfield(lua_State *L, int table,
- const char *fieldname, std::string &result)
- {
- lua_getfield(L, table, fieldname);
- bool got = false;
- if (check_field_or_nil(L, -1, LUA_TSTRING, fieldname)) {
- size_t len = 0;
- const char *ptr = lua_tolstring(L, -1, &len);
- if (ptr) {
- result.assign(ptr, len);
- got = true;
- }
- }
- lua_pop(L, 1);
- return got;
- }
- bool getfloatfield(lua_State *L, int table,
- const char *fieldname, float &result)
- {
- lua_getfield(L, table, fieldname);
- bool got = false;
- if (check_field_or_nil(L, -1, LUA_TNUMBER, fieldname)) {
- result = lua_tonumber(L, -1);
- got = true;
- }
- lua_pop(L, 1);
- return got;
- }
- bool getboolfield(lua_State *L, int table,
- const char *fieldname, bool &result)
- {
- lua_getfield(L, table, fieldname);
- bool got = false;
- if (check_field_or_nil(L, -1, LUA_TBOOLEAN, fieldname)){
- result = lua_toboolean(L, -1);
- got = true;
- }
- lua_pop(L, 1);
- return got;
- }
- size_t getstringlistfield(lua_State *L, int table, const char *fieldname,
- std::vector<std::string> *result)
- {
- lua_getfield(L, table, fieldname);
- size_t num_strings_read = read_stringlist(L, -1, result);
- lua_pop(L, 1);
- return num_strings_read;
- }
- std::string getstringfield_default(lua_State *L, int table,
- const char *fieldname, const std::string &default_)
- {
- std::string result = default_;
- getstringfield(L, table, fieldname, result);
- return result;
- }
- int getintfield_default(lua_State *L, int table,
- const char *fieldname, int default_)
- {
- int result = default_;
- getintfield(L, table, fieldname, result);
- return result;
- }
- float getfloatfield_default(lua_State *L, int table,
- const char *fieldname, float default_)
- {
- float result = default_;
- getfloatfield(L, table, fieldname, result);
- return result;
- }
- bool getboolfield_default(lua_State *L, int table,
- const char *fieldname, bool default_)
- {
- bool result = default_;
- getboolfield(L, table, fieldname, result);
- return result;
- }
- v3s16 getv3s16field_default(lua_State *L, int table,
- const char *fieldname, v3s16 default_)
- {
- getv3intfield(L, table, fieldname, default_);
- return default_;
- }
- void setstringfield(lua_State *L, int table,
- const char *fieldname, const std::string &value)
- {
- lua_pushlstring(L, value.c_str(), value.length());
- if(table < 0)
- table -= 1;
- lua_setfield(L, table, fieldname);
- }
- void setintfield(lua_State *L, int table,
- const char *fieldname, int value)
- {
- lua_pushinteger(L, value);
- if(table < 0)
- table -= 1;
- lua_setfield(L, table, fieldname);
- }
- void setfloatfield(lua_State *L, int table,
- const char *fieldname, float value)
- {
- lua_pushnumber(L, value);
- if(table < 0)
- table -= 1;
- lua_setfield(L, table, fieldname);
- }
- void setboolfield(lua_State *L, int table,
- const char *fieldname, bool value)
- {
- lua_pushboolean(L, value);
- if(table < 0)
- table -= 1;
- lua_setfield(L, table, fieldname);
- }
- ////
- //// Array table slices
- ////
- size_t write_array_slice_float(
- lua_State *L,
- int table_index,
- float *data,
- v3u16 data_size,
- v3u16 slice_offset,
- v3u16 slice_size)
- {
- v3u16 pmin, pmax(data_size);
- if (slice_offset.X > 0) {
- slice_offset.X--;
- pmin.X = slice_offset.X;
- pmax.X = MYMIN(slice_offset.X + slice_size.X, data_size.X);
- }
- if (slice_offset.Y > 0) {
- slice_offset.Y--;
- pmin.Y = slice_offset.Y;
- pmax.Y = MYMIN(slice_offset.Y + slice_size.Y, data_size.Y);
- }
- if (slice_offset.Z > 0) {
- slice_offset.Z--;
- pmin.Z = slice_offset.Z;
- pmax.Z = MYMIN(slice_offset.Z + slice_size.Z, data_size.Z);
- }
- const u32 ystride = data_size.X;
- const u32 zstride = data_size.X * data_size.Y;
- u32 elem_index = 1;
- for (u32 z = pmin.Z; z != pmax.Z; z++)
- for (u32 y = pmin.Y; y != pmax.Y; y++)
- for (u32 x = pmin.X; x != pmax.X; x++) {
- u32 i = z * zstride + y * ystride + x;
- lua_pushnumber(L, data[i]);
- lua_rawseti(L, table_index, elem_index);
- elem_index++;
- }
- return elem_index - 1;
- }
|