]> git.lizzy.rs Git - dragonfireclient.git/blobdiff - src/script/common/c_converter.cpp
Make sure relevant std::stringstreams are set to binary
[dragonfireclient.git] / src / script / common / c_converter.cpp
index 65c3654ae29154a20a209ecde84ec06691022452..19734b913bf8349d40afa28090b445b05f77399b 100644 (file)
@@ -23,33 +23,139 @@ extern "C" {
 }
 
 #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);
@@ -62,7 +168,7 @@ v2s16 read_v2s16(lua_State *L, int index)
 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);
@@ -72,10 +178,25 @@ v2f read_v2f(lua_State *L, int index)
        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);
@@ -91,19 +212,76 @@ v3f read_v3f(lua_State *L, int index)
 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;
@@ -117,36 +295,55 @@ v3f checkFloatPos(lua_State *L, int index)
 
 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));
@@ -160,6 +357,28 @@ video::SColor readARGB8(lua_State *L, int index)
        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;
@@ -183,9 +402,27 @@ aabb3f read_aabb3f(lua_State *L, int index, f32 scale)
                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;
@@ -214,33 +451,89 @@ std::vector<aabb3f> read_aabb3f_vector(lua_State *L, int index, f32 scale)
        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;
@@ -251,7 +544,8 @@ bool getfloatfield(lua_State *L, int table,
 {
        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;
        }
@@ -264,7 +558,8 @@ bool getboolfield(lua_State *L, int table,
 {
        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;
        }
@@ -272,13 +567,15 @@ bool getboolfield(lua_State *L, int table,
        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,
@@ -313,6 +610,22 @@ bool getboolfield_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)
 {
@@ -341,3 +654,50 @@ void setboolfield(lua_State *L, int table,
 }
 
 
+////
+//// 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;
+}