}
#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>
+
+
+#define CHECK_TYPE(index, name, type) { \
+ 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) + ")."); \
+ } \
+ }
+#define CHECK_POS_COORD(name) CHECK_TYPE(-1, "position coordinate '" name "'", LUA_TNUMBER)
+#define CHECK_FLOAT_RANGE(value, name) \
+if (value < F1000_MIN || value > F1000_MAX) { \
+ std::ostringstream error_text; \
+ error_text << "Invalid float vector dimension range '" name "' " << \
+ "(expected " << F1000_MIN << " < " name " < " << F1000_MAX << \
+ " got " << value << ")." << std::endl; \
+ throw LuaError(error_text.str()); \
+}
+#define CHECK_POS_TAB(index) CHECK_TYPE(index, "position", LUA_TTABLE)
+
+
+/**
+ * A helper which sets (if available) the vector metatable from builtin as metatable
+ * for the table on top of the stack
+ */
+static void set_vector_metatable(lua_State *L)
+{
+ // get vector.metatable
+ lua_getglobal(L, "vector");
+ if (!lua_istable(L, -1)) {
+ // there is no global vector table
+ lua_pop(L, 1);
+ errorstream << "set_vector_metatable in c_converter.cpp: " <<
+ "missing global vector table" << std::endl;
+ return;
+ }
+ lua_getfield(L, -1, "metatable");
+ // set the metatable
+ lua_setmetatable(L, -3);
+ // pop vector global
+ lua_pop(L, 1);
+}
+
+
+void push_float_string(lua_State *L, float value)
+{
+ auto str = ftos(value);
+ lua_pushstring(L, str.c_str());
+}
void push_v3f(lua_State *L, v3f p)
{
- lua_newtable(L);
+ lua_createtable(L, 0, 3);
lua_pushnumber(L, p.X);
lua_setfield(L, -2, "x");
lua_pushnumber(L, p.Y);
lua_setfield(L, -2, "y");
lua_pushnumber(L, p.Z);
lua_setfield(L, -2, "z");
+ set_vector_metatable(L);
}
void push_v2f(lua_State *L, v2f p)
{
- lua_newtable(L);
+ 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");
}
+void push_v3_float_string(lua_State *L, v3f p)
+{
+ lua_createtable(L, 0, 3);
+ push_float_string(L, p.X);
+ lua_setfield(L, -2, "x");
+ push_float_string(L, p.Y);
+ lua_setfield(L, -2, "y");
+ push_float_string(L, p.Z);
+ lua_setfield(L, -2, "z");
+}
+
+void push_v2_float_string(lua_State *L, v2f p)
+{
+ lua_createtable(L, 0, 2);
+ push_float_string(L, p.X);
+ lua_setfield(L, -2, "x");
+ push_float_string(L, p.Y);
+ lua_setfield(L, -2, "y");
+}
+
v2s16 read_v2s16(lua_State *L, int index)
{
v2s16 p;
- luaL_checktype(L, index, LUA_TTABLE);
+ 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);
v2f read_v2f(lua_State *L, int index)
{
v2f p;
- luaL_checktype(L, index, LUA_TTABLE);
+ CHECK_POS_TAB(index);
lua_getfield(L, index, "x");
p.X = 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("x");
+ p.X = lua_tonumber(L, -1);
+ lua_pop(L, 1);
+ lua_getfield(L, index, "y");
+ CHECK_POS_COORD("y");
+ p.Y = lua_tonumber(L, -1);
+ lua_pop(L, 1);
+ return p;
+}
+
v3f read_v3f(lua_State *L, int index)
{
v3f pos;
- luaL_checktype(L, index, LUA_TTABLE);
+ CHECK_POS_TAB(index);
lua_getfield(L, index, "x");
pos.X = lua_tonumber(L, -1);
lua_pop(L, 1);
v3f check_v3f(lua_State *L, int index)
{
v3f pos;
- luaL_checktype(L, index, LUA_TTABLE);
+ CHECK_POS_TAB(index);
+ lua_getfield(L, index, "x");
+ CHECK_POS_COORD("x");
+ pos.X = lua_tonumber(L, -1);
+ CHECK_FLOAT_RANGE(pos.X, "x")
+ lua_pop(L, 1);
+ lua_getfield(L, index, "y");
+ CHECK_POS_COORD("y");
+ pos.Y = lua_tonumber(L, -1);
+ CHECK_FLOAT_RANGE(pos.Y, "y")
+ lua_pop(L, 1);
+ lua_getfield(L, index, "z");
+ CHECK_POS_COORD("z");
+ pos.Z = lua_tonumber(L, -1);
+ CHECK_FLOAT_RANGE(pos.Z, "z")
+ lua_pop(L, 1);
+ return pos;
+}
+
+v3d read_v3d(lua_State *L, int index)
+{
+ v3d pos;
+ CHECK_POS_TAB(index);
+ lua_getfield(L, index, "x");
+ pos.X = lua_tonumber(L, -1);
+ lua_pop(L, 1);
+ lua_getfield(L, index, "y");
+ pos.Y = lua_tonumber(L, -1);
+ lua_pop(L, 1);
+ lua_getfield(L, index, "z");
+ pos.Z = lua_tonumber(L, -1);
+ lua_pop(L, 1);
+ return pos;
+}
+
+v3d check_v3d(lua_State *L, int index)
+{
+ v3d pos;
+ CHECK_POS_TAB(index);
lua_getfield(L, index, "x");
- pos.X = luaL_checknumber(L, -1);
+ CHECK_POS_COORD("x");
+ pos.X = lua_tonumber(L, -1);
+ CHECK_FLOAT_RANGE(pos.X, "x")
lua_pop(L, 1);
lua_getfield(L, index, "y");
- pos.Y = luaL_checknumber(L, -1);
+ CHECK_POS_COORD("y");
+ pos.Y = lua_tonumber(L, -1);
+ CHECK_FLOAT_RANGE(pos.Y, "y")
lua_pop(L, 1);
lua_getfield(L, index, "z");
- pos.Z = luaL_checknumber(L, -1);
+ CHECK_POS_COORD("z");
+ pos.Z = lua_tonumber(L, -1);
+ CHECK_FLOAT_RANGE(pos.Z, "z")
lua_pop(L, 1);
return pos;
}
+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;
void push_v3s16(lua_State *L, v3s16 p)
{
- lua_newtable(L);
- lua_pushnumber(L, p.X);
+ lua_createtable(L, 0, 3);
+ lua_pushinteger(L, p.X);
lua_setfield(L, -2, "x");
- lua_pushnumber(L, p.Y);
+ lua_pushinteger(L, p.Y);
lua_setfield(L, -2, "y");
- lua_pushnumber(L, p.Z);
+ lua_pushinteger(L, p.Z);
lua_setfield(L, -2, "z");
+ set_vector_metatable(L);
}
v3s16 read_v3s16(lua_State *L, int index)
{
// Correct rounding at <0
- v3f pf = read_v3f(L, index);
- return floatToInt(pf, 1.0);
+ v3d pf = read_v3d(L, index);
+ return doubleToInt(pf, 1.0);
}
v3s16 check_v3s16(lua_State *L, int index)
{
// Correct rounding at <0
- v3f pf = check_v3f(L, index);
- return floatToInt(pf, 1.0);
+ v3d pf = check_v3d(L, index);
+ return doubleToInt(pf, 1.0);
}
-video::SColor readARGB8(lua_State *L, int index)
+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);
- luaL_checktype(L, index, LUA_TTABLE);
+ CHECK_TYPE(index, "ARGB color", LUA_TTABLE);
lua_getfield(L, index, "a");
- if(lua_isnumber(L, -1))
- color.setAlpha(lua_tonumber(L, -1));
+ 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));
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;
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;
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 getstringfield(lua_State *L, int table,
- const char *fieldname, std::string &result)
+#if defined(__MINGW32__) && !defined(__MINGW64__)
+/* MinGW 32-bit somehow crashes in the std::set destructor when this
+ * variable is thread-local, so just don't do that. */
+static std::set<u64> warned_msgs;
+#endif
+
+bool check_field_or_nil(lua_State *L, int index, int type, const char *fieldname)
{
- lua_getfield(L, table, fieldname);
- bool got = false;
- if(lua_isstring(L, -1)){
- size_t len = 0;
- const char *ptr = lua_tolstring(L, -1, &len);
- result.assign(ptr, len);
- got = true;
+#if !defined(__MINGW32__) || defined(__MINGW64__)
+ thread_local std::set<u64> warned_msgs;
+#endif
+
+ 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;
}
- lua_pop(L, 1);
- return got;
+
+ // 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 getintfield(lua_State *L, int table,
- const char *fieldname, int &result)
+bool getstringfield(lua_State *L, int table,
+ const char *fieldname, std::string &result)
{
lua_getfield(L, table, fieldname);
bool got = false;
- if(lua_isnumber(L, -1)){
- result = lua_tonumber(L, -1);
- got = true;
+
+ 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;
{
lua_getfield(L, table, fieldname);
bool got = false;
- if(lua_isnumber(L, -1)){
+
+ if (check_field_or_nil(L, -1, LUA_TNUMBER, fieldname)) {
result = lua_tonumber(L, -1);
got = true;
}
{
lua_getfield(L, table, fieldname);
bool got = false;
- if(lua_isboolean(L, -1)){
+
+ if (check_field_or_nil(L, -1, LUA_TBOOLEAN, fieldname)){
result = lua_toboolean(L, -1);
got = true;
}
return got;
}
-std::string checkstringfield(lua_State *L, int table,
- const char *fieldname)
+size_t getstringlistfield(lua_State *L, int table, const char *fieldname,
+ std::vector<std::string> *result)
{
lua_getfield(L, table, fieldname);
- std::string s = luaL_checkstring(L, -1);
+
+ size_t num_strings_read = read_stringlist(L, -1, result);
+
lua_pop(L, 1);
- return s;
+ return num_strings_read;
}
std::string getstringfield_default(lua_State *L, int table,
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)
{
}
+////
+//// 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;
+}