Add ItemStack key-value meta storage
authorrubenwardy <rubenwardy@gmail.com>
Tue, 31 Jan 2017 19:49:01 +0000 (19:49 +0000)
committerrubenwardy <rubenwardy@gmail.com>
Sat, 4 Feb 2017 22:07:55 +0000 (22:07 +0000)
21 files changed:
build/android/jni/Android.mk
doc/lua_api.txt
src/CMakeLists.txt
src/content_cao.cpp
src/craftdef.cpp
src/inventory.cpp
src/inventory.h
src/itemstackmetadata.cpp [new file with mode: 0644]
src/itemstackmetadata.h [new file with mode: 0644]
src/metadata.cpp
src/metadata.h
src/script/common/c_content.cpp
src/script/lua_api/CMakeLists.txt
src/script/lua_api/l_env.cpp
src/script/lua_api/l_item.cpp
src/script/lua_api/l_item.h
src/script/lua_api/l_itemstackmeta.cpp [new file with mode: 0644]
src/script/lua_api/l_itemstackmeta.h [new file with mode: 0644]
src/script/scripting_game.cpp
src/util/serialize.cpp
src/util/serialize.h

index 70a3d29c082b62ab64dff93c97f4f4819e4e7aeb..47f37cfa5c1dafbe0975e88620fbef8498b8246c 100644 (file)
@@ -164,6 +164,7 @@ LOCAL_SRC_FILES := \
                jni/src/inventory.cpp                     \
                jni/src/inventorymanager.cpp              \
                jni/src/itemdef.cpp                       \
+               jni/src/itemstackmetadata.cpp             \
                jni/src/keycode.cpp                       \
                jni/src/light.cpp                         \
                jni/src/localplayer.cpp                   \
@@ -305,6 +306,7 @@ LOCAL_SRC_FILES += \
                jni/src/script/lua_api/l_env.cpp          \
                jni/src/script/lua_api/l_inventory.cpp    \
                jni/src/script/lua_api/l_item.cpp         \
+               jni/src/script/lua_api/l_itemstackmeta.cpp\
                jni/src/script/lua_api/l_mainmenu.cpp     \
                jni/src/script/lua_api/l_mapgen.cpp       \
                jni/src/script/lua_api/l_metadata.cpp     \
index 219882f465f7db2906ac457621b43b27eab99142..2f5e3706c56436eda13d1c564a699b2b81347325 100644 (file)
@@ -1411,7 +1411,7 @@ the entity itself.
 * `direction` is a unit vector, pointing from the source of the punch to
    the punched object.
 * `damage` damage that will be done to entity
-Return value of this function will determin if damage is done by this function 
+Return value of this function will determin if damage is done by this function
 (retval true) or shall be done by engine (retval false)
 
 To punch an entity/object in Lua, call:
@@ -1427,9 +1427,9 @@ Node Metadata
 -------------
 The instance of a node in the world normally only contains the three values
 mentioned in "Nodes". However, it is possible to insert extra data into a
-node. It is called "node metadata"; See "`NodeMetaRef`".
+node. It is called "node metadata"; See `NodeMetaRef`.
 
-Metadata contains two things:
+Node metadata contains two things:
 
 * A key-value store
 * An inventory
@@ -1467,6 +1467,18 @@ Example stuff:
         }
     })
 
+Item Metadata
+-------------
+Item stacks can store metadata too. See `ItemStackMetaRef`.
+
+Item metadata only contains a key-value store.
+
+Example stuff:
+
+    local meta = stack:get_meta()
+    meta:set_string("key", "value")
+    print(dump(meta:to_table()))
+
 Formspec
 --------
 Formspec defines a menu. Currently not much else than inventories are
@@ -2774,9 +2786,8 @@ These functions return the leftover itemstack.
 Class reference
 ---------------
 
-### `NodeMetaRef`
-Node metadata: reference extra data and functionality stored in a node.
-Can be gotten via `minetest.get_meta(pos)`.
+### `MetaDataRef`
+See `NodeMetaRef` and `ItemStackMetaRef`.
 
 #### Methods
 * `set_string(name, value)`
@@ -2785,13 +2796,29 @@ Can be gotten via `minetest.get_meta(pos)`.
 * `get_int(name)`
 * `set_float(name, value)`
 * `get_float(name)`
-* `get_inventory()`: returns `InvRef`
-* `to_table()`: returns `nil` or `{fields = {...}, inventory = {list1 = {}, ...}}`
+* `to_table()`: returns `nil` or a table with keys:
+    * `fields`: key-value storage
+    * `inventory`: `{list1 = {}, ...}}` (NodeMetaRef only)
 * `from_table(nil or {})`
     * Any non-table value will clear the metadata
-    * See "Node Metadata"
+    * See "Node Metadata" for an example
     * returns `true` on success
 
+### `NodeMetaRef`
+Node metadata: reference extra data and functionality stored in a node.
+Can be gotten via `minetest.get_meta(pos)`.
+
+#### Methods
+* All methods in MetaDataRef
+* `get_inventory()`: returns `InvRef`
+
+### `ItemStackMetaRef`
+ItemStack metadata: reference extra data and functionality stored in a stack.
+Can be gotten via `item:get_meta()`.
+
+#### Methods
+* All methods in MetaDataRef
+
 ### `NodeTimerRef`
 Node Timers: a high resolution persistent per-node timer.
 Can be gotten via `minetest.get_node_timer(pos)`.
index afb591a04345251b341dda01d8a72ebba7357ee4..30e6c85e4bb0dac6e37dede80f46cfc14f40d1ee 100644 (file)
@@ -415,6 +415,7 @@ set(common_SRCS
        inventory.cpp
        inventorymanager.cpp
        itemdef.cpp
+       itemstackmetadata.cpp
        light.cpp
        log.cpp
        map.cpp
index 5dc3866cfdd735499627f6f54bd0da38e5e7e8a1..e0b1c4cd2e841e87201b2b46230771af29c90dde 100644 (file)
@@ -938,7 +938,7 @@ void GenericCAO::addToScene(scene::ISceneManager *smgr,
                if(m_prop.textures.size() >= 1){
                        infostream<<"textures[0]: "<<m_prop.textures[0]<<std::endl;
                        IItemDefManager *idef = m_client->idef();
-                       ItemStack item(m_prop.textures[0], 1, 0, "", idef);
+                       ItemStack item(m_prop.textures[0], 1, 0, idef);
 
                        m_wield_meshnode = new WieldMeshSceneNode(
                                        smgr->getRootSceneNode(), smgr, -1);
index 45d3018a7b6bef1692bb1d5aad7b6334c9b64c08..286d1eadaf8dc699ec4c492f23b09b284d1ea5f2 100644 (file)
@@ -139,7 +139,7 @@ static std::vector<ItemStack> craftGetItems(
        for (std::vector<std::string>::size_type i = 0;
                        i < items.size(); i++) {
                result.push_back(ItemStack(std::string(items[i]), (u16)1,
-                       (u16)0, "", gamedef->getItemDefManager()));
+                       (u16)0, gamedef->getItemDefManager()));
        }
        return result;
 }
@@ -1126,4 +1126,3 @@ IWritableCraftDefManager* createCraftDefManager()
 {
        return new CCraftDefManager();
 }
-
index cb8faecbc4eba27fd360e848832a0b2682a93ac2..6d5b499168ac69c2f5d140659c3b37a1f8e7e1e3 100644 (file)
@@ -45,82 +45,16 @@ static content_t content_translate_from_19_to_internal(content_t c_from)
        return c_from;
 }
 
-// If the string contains spaces, quotes or control characters, encodes as JSON.
-// Else returns the string unmodified.
-static std::string serializeJsonStringIfNeeded(const std::string &s)
-{
-       for(size_t i = 0; i < s.size(); ++i)
-       {
-               if(s[i] <= 0x1f || s[i] >= 0x7f || s[i] == ' ' || s[i] == '\"')
-                       return serializeJsonString(s);
-       }
-       return s;
-}
-
-// Parses a string serialized by serializeJsonStringIfNeeded.
-static std::string deSerializeJsonStringIfNeeded(std::istream &is)
-{
-       std::ostringstream tmp_os;
-       bool expect_initial_quote = true;
-       bool is_json = false;
-       bool was_backslash = false;
-       for(;;)
-       {
-               char c = is.get();
-               if(is.eof())
-                       break;
-               if(expect_initial_quote && c == '"')
-               {
-                       tmp_os << c;
-                       is_json = true;
-               }
-               else if(is_json)
-               {
-                       tmp_os << c;
-                       if(was_backslash)
-                               was_backslash = false;
-                       else if(c == '\\')
-                               was_backslash = true;
-                       else if(c == '"')
-                               break; // Found end of string
-               }
-               else
-               {
-                       if(c == ' ')
-                       {
-                               // Found end of word
-                               is.unget();
-                               break;
-                       }
-                       else
-                       {
-                               tmp_os << c;
-                       }
-               }
-               expect_initial_quote = false;
-       }
-       if(is_json)
-       {
-               std::istringstream tmp_is(tmp_os.str(), std::ios::binary);
-               return deSerializeJsonString(tmp_is);
-       }
-       else
-               return tmp_os.str();
-}
-
-
-ItemStack::ItemStack(std::string name_, u16 count_,
-               u16 wear_, std::string metadata_,
-               IItemDefManager *itemdef)
+ItemStack::ItemStack(const std::string &name_, u16 count_,
+               u16 wear_, IItemDefManager *itemdef)
 {
        name = itemdef->getAlias(name_);
        count = count_;
        wear = wear_;
-       metadata = metadata_;
 
-       if(name.empty() || count == 0)
+       if (name.empty() || count == 0)
                clear();
-       else if(itemdef->get(name).type == ITEM_TOOL)
+       else if (itemdef->get(name).type == ITEM_TOOL)
                count = 1;
 }
 
@@ -137,7 +71,7 @@ void ItemStack::serialize(std::ostream &os) const
                parts = 2;
        if(wear != 0)
                parts = 3;
-       if(metadata != "")
+       if (!metadata.empty())
                parts = 4;
 
        os<<serializeJsonStringIfNeeded(name);
@@ -145,8 +79,10 @@ void ItemStack::serialize(std::ostream &os) const
                os<<" "<<count;
        if(parts >= 3)
                os<<" "<<wear;
-       if(parts >= 4)
-               os<<" "<<serializeJsonStringIfNeeded(metadata);
+       if (parts >= 4) {
+               os << " ";
+               metadata.serialize(os);
+       }
 }
 
 void ItemStack::deSerialize(std::istream &is, IItemDefManager *itemdef)
@@ -289,7 +225,7 @@ void ItemStack::deSerialize(std::istream &is, IItemDefManager *itemdef)
                                wear = stoi(wear_str);
 
                        // Read metadata
-                       metadata = deSerializeJsonStringIfNeeded(is);
+                       metadata.deSerialize(is);
 
                        // In case fields are added after metadata, skip space here:
                        //std::getline(is, tmp, ' ');
@@ -335,7 +271,7 @@ ItemStack ItemStack::addItem(const ItemStack &newitem_,
                *this = newitem;
                newitem.clear();
        }
-       // If item name or metadata differs, bail out 
+       // If item name or metadata differs, bail out
        else if (name != newitem.name
                || metadata != newitem.metadata)
        {
@@ -375,7 +311,7 @@ bool ItemStack::itemFits(const ItemStack &newitem_,
        {
                newitem.clear();
        }
-       // If item name or metadata differs, bail out 
+       // If item name or metadata differs, bail out
        else if (name != newitem.name
                || metadata != newitem.metadata)
        {
index 7d7e58d610d9f5ae48ce7d65dd208748f07022d1..fe16397280cc8b86d653b2894bfa99d0219ae954 100644 (file)
@@ -23,6 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "debug.h"
 #include "itemdef.h"
 #include "irrlichttypes.h"
+#include "itemstackmetadata.h"
 #include <istream>
 #include <ostream>
 #include <string>
@@ -32,10 +33,10 @@ struct ToolCapabilities;
 
 struct ItemStack
 {
-       ItemStack(): name(""), count(0), wear(0), metadata("") {}
-       ItemStack(std::string name_, u16 count_,
-                       u16 wear, std::string metadata_,
-                       IItemDefManager *itemdef);
+       ItemStack(): name(""), count(0), wear(0) {}
+       ItemStack(const std::string &name_, u16 count_,
+                       u16 wear, IItemDefManager *itemdef);
+
        ~ItemStack() {}
 
        // Serialization
@@ -61,7 +62,7 @@ struct ItemStack
                name = "";
                count = 0;
                wear = 0;
-               metadata = "";
+               metadata.clear();
        }
 
        void add(u16 n)
@@ -166,7 +167,7 @@ struct ItemStack
        std::string name;
        u16 count;
        u16 wear;
-       std::string metadata;
+       ItemStackMetadata metadata;
 };
 
 class InventoryList
@@ -313,4 +314,3 @@ private:
 };
 
 #endif
-
diff --git a/src/itemstackmetadata.cpp b/src/itemstackmetadata.cpp
new file mode 100644 (file)
index 0000000..c3d6022
--- /dev/null
@@ -0,0 +1,43 @@
+#include "itemstackmetadata.h"
+#include "util/serialize.h"
+#include "util/strfnd.h"
+
+#define DESERIALIZE_START '\x01'
+#define DESERIALIZE_KV_DELIM '\x02'
+#define DESERIALIZE_PAIR_DELIM '\x03'
+#define DESERIALIZE_START_STR "\x01"
+#define DESERIALIZE_KV_DELIM_STR "\x02"
+#define DESERIALIZE_PAIR_DELIM_STR "\x03"
+
+void ItemStackMetadata::serialize(std::ostream &os) const
+{
+       std::ostringstream os2;
+       os2 << DESERIALIZE_START;
+       for (StringMap::const_iterator
+                       it = m_stringvars.begin();
+                       it != m_stringvars.end(); ++it) {
+               os2 << it->first << DESERIALIZE_KV_DELIM
+                   << it->second << DESERIALIZE_PAIR_DELIM;
+       }
+       os << serializeJsonStringIfNeeded(os2.str());
+}
+
+void ItemStackMetadata::deSerialize(std::istream &is)
+{
+       std::string in = deSerializeJsonStringIfNeeded(is);
+
+       m_stringvars.clear();
+
+       if (!in.empty() && in[0] == DESERIALIZE_START) {
+               Strfnd fnd(in);
+               fnd.to(1);
+               while (!fnd.at_end()) {
+                       std::string name = fnd.next(DESERIALIZE_KV_DELIM_STR);
+                       std::string var  = fnd.next(DESERIALIZE_PAIR_DELIM_STR);
+                       m_stringvars[name] = var;
+               }
+       } else {
+               // BACKWARDS COMPATIBILITY
+               m_stringvars[""] = in;
+       }
+}
diff --git a/src/itemstackmetadata.h b/src/itemstackmetadata.h
new file mode 100644 (file)
index 0000000..c56c58f
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+Minetest
+Copyright (C) 2010-2013 rubenwardy <rubenwardy@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.
+*/
+
+#ifndef ITEMSTACKMETADATA_HEADER
+#define ITEMSTACKMETADATA_HEADER
+
+#include "metadata.h"
+
+class Inventory;
+class IItemDefManager;
+
+class ItemStackMetadata : public Metadata
+{
+public:
+       void serialize(std::ostream &os) const;
+       void deSerialize(std::istream &is);
+};
+
+#endif
index 96453d710849e0578fa8f03bc20b8048f2b6143c..3cc45f9196043fe59a2c6daa04b485ea0ed36d51 100644 (file)
@@ -22,6 +22,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "gamedef.h"
 #include "log.h"
 #include <sstream>
+#include "constants.h" // MAP_BLOCKSIZE
+#include <sstream>
 
 /*
        Metadata
index a629c0615d251253ca1edceeebb06613f0bb4d4c..4bb3c2ee7f78d9a33eb1be0b5f888eaccaf15cd4 100644 (file)
@@ -25,35 +25,37 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include <vector>
 #include "util/string.h"
 
-/*
-       NodeMetadata stores arbitary amounts of data for special blocks.
-       Used for furnaces, chests and signs.
-
-       There are two interaction methods: inventory menu and text input.
-       Only one can be used for a single metadata, thus only inventory OR
-       text input should exist in a metadata.
-*/
-
-class Inventory;
-class IItemDefManager;
-
 class Metadata
 {
 public:
-       void clear();
-       bool empty() const;
+       virtual ~Metadata() {}
+
+       virtual void clear();
+       virtual bool empty() const;
 
-       // Generic key/value store
+       bool operator==(const Metadata &other) const;
+       inline bool operator!=(const Metadata &other) const
+       {
+               return !(*this == other);
+       }
+
+       //
+       // Key-value related
+       //
+
+       size_t size() const;
+       bool contains(const std::string &name) const;
        const std::string &getString(const std::string &name, u16 recursion = 0) const;
        void setString(const std::string &name, const std::string &var);
-       // Support variable names in values
-       const std::string &resolveString(const std::string &str, u16 recursion = 0) const;
        const StringMap &getStrings() const
        {
                return m_stringvars;
        }
-private:
+       // Add support for variable names in values
+       const std::string &resolveString(const std::string &str, u16 recursion = 0) const;
+protected:
        StringMap m_stringvars;
+
 };
 
 #endif
index ebc951295a6bfbd29cb0edaadba30edfd8782e6f..8925b51f428e1268a4de6d7e3a53d2346537dfa2 100644 (file)
@@ -824,11 +824,32 @@ ItemStack read_item(lua_State* L, int index,Server* srv)
                std::string name = getstringfield_default(L, index, "name", "");
                int count = getintfield_default(L, index, "count", 1);
                int wear = getintfield_default(L, index, "wear", 0);
-               std::string metadata = getstringfield_default(L, index, "metadata", "");
-               return ItemStack(name, count, wear, metadata, idef);
-       }
-       else
-       {
+
+               ItemStack istack(name, count, wear, idef);
+
+               lua_getfield(L, index, "metadata");
+
+               // Support old metadata format by checking type
+               int fieldstable = lua_gettop(L);
+               if (lua_istable(L, fieldstable)) {
+                       lua_pushnil(L);
+                       while (lua_next(L, fieldstable) != 0) {
+                               // key at index -2 and value at index -1
+                               std::string key = lua_tostring(L, -2);
+                               size_t value_len;
+                               const char *value_cs = lua_tolstring(L, -1, &value_len);
+                               std::string value(value_cs, value_len);
+                               istack.metadata.setString(name, value);
+                               lua_pop(L, 1); // removes value, keeps key for next iteration
+                       }
+               } else {
+                       // BACKWARDS COMPATIBLITY
+                       std::string value = getstringfield_default(L, index, "metadata", "");
+                       istack.metadata.setString("", value);
+               }
+
+               return istack;
+       } else {
                throw LuaError("Expecting itemstack, itemstring, table or nil");
        }
 }
index efccce515952909000de2b22648054112fa8301c..070234ebac9f78232868178108eebb338102d12b 100644 (file)
@@ -5,6 +5,7 @@ set(common_SCRIPT_LUA_API_SRCS
        ${CMAKE_CURRENT_SOURCE_DIR}/l_env.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/l_inventory.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/l_item.cpp
+       ${CMAKE_CURRENT_SOURCE_DIR}/l_itemstackmeta.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/l_mapgen.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/l_metadata.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/l_nodemeta.cpp
index 3d9db79172926844beb49d73b6a70eb10b74d3a4..2722e35a4d8357bc5d5b1df4472286b7ee0ed44f 100644 (file)
@@ -284,7 +284,7 @@ int ModApiEnvMod::l_place_node(lua_State *L)
                return 1;
        }
        // Create item to place
-       ItemStack item(ndef->get(n).name, 1, 0, "", idef);
+       ItemStack item(ndef->get(n).name, 1, 0, idef);
        // Make pointed position
        PointedThing pointed;
        pointed.type = POINTEDTHING_NODE;
index ff0baea14ee6bab42ba1c18bffee1e154c312cbe..f0293bed89e79c43b051006ad99b6af83ecb12bb 100644 (file)
@@ -18,6 +18,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 */
 
 #include "lua_api/l_item.h"
+#include "lua_api/l_itemstackmeta.h"
 #include "lua_api/l_internal.h"
 #include "common/c_converter.h"
 #include "common/c_content.h"
@@ -137,16 +138,28 @@ int LuaItemStack::l_set_wear(lua_State *L)
        return 1;
 }
 
+// get_meta(self) -> string
+int LuaItemStack::l_get_meta(lua_State *L)
+{
+       NO_MAP_LOCK_REQUIRED;
+       LuaItemStack *o = checkobject(L, 1);
+       ItemStackMetaRef::create(L, &o->m_stack);
+       return 1;
+}
+
+// DEPRECATED
 // get_metadata(self) -> string
 int LuaItemStack::l_get_metadata(lua_State *L)
 {
        NO_MAP_LOCK_REQUIRED;
        LuaItemStack *o = checkobject(L, 1);
        ItemStack &item = o->m_stack;
-       lua_pushlstring(L, item.metadata.c_str(), item.metadata.size());
+       const std::string &value = item.metadata.getString("");
+       lua_pushlstring(L, value.c_str(), value.size());
        return 1;
 }
 
+// DEPRECATED
 // set_metadata(self, string)
 int LuaItemStack::l_set_metadata(lua_State *L)
 {
@@ -156,7 +169,7 @@ int LuaItemStack::l_set_metadata(lua_State *L)
 
        size_t len = 0;
        const char *ptr = luaL_checklstring(L, 2, &len);
-       item.metadata.assign(ptr, len);
+       item.metadata.setString("", std::string(ptr, len));
 
        lua_pushboolean(L, true);
        return 1;
@@ -211,8 +224,24 @@ int LuaItemStack::l_to_table(lua_State *L)
                lua_setfield(L, -2, "count");
                lua_pushinteger(L, item.wear);
                lua_setfield(L, -2, "wear");
-               lua_pushlstring(L, item.metadata.c_str(), item.metadata.size());
-               lua_setfield(L, -2, "metadata");
+
+               if (item.metadata.size() == 1 && item.metadata.contains("")) {
+                       const std::string &value = item.metadata.getString("");
+                       lua_pushlstring(L, value.c_str(), value.size());
+                       lua_setfield(L, -2, "metadata");
+               } else {
+                       lua_newtable(L);
+                       const StringMap &fields = item.metadata.getStrings();
+                       for (StringMap::const_iterator it = fields.begin();
+                                       it != fields.end(); ++it) {
+                               const std::string &name = it->first;
+                               const std::string &value = it->second;
+                               lua_pushlstring(L, name.c_str(), name.size());
+                               lua_pushlstring(L, value.c_str(), value.size());
+                               lua_settable(L, -3);
+                       }
+                       lua_setfield(L, -2, "metadata");
+               }
        }
        return 1;
 }
@@ -443,6 +472,7 @@ const luaL_reg LuaItemStack::methods[] = {
        luamethod(LuaItemStack, set_count),
        luamethod(LuaItemStack, get_wear),
        luamethod(LuaItemStack, set_wear),
+       luamethod(LuaItemStack, get_meta),
        luamethod(LuaItemStack, get_metadata),
        luamethod(LuaItemStack, set_metadata),
        luamethod(LuaItemStack, clear),
index be919b70193e60f56ae5b4722d5fdafb555216a9..1ba5d79e0f0ccfff529d6a341f09b86e59685403 100644 (file)
@@ -56,9 +56,14 @@ private:
        // set_wear(self, number)
        static int l_set_wear(lua_State *L);
 
+       // get_meta(self) -> string
+       static int l_get_meta(lua_State *L);
+
+       // DEPRECATED
        // get_metadata(self) -> string
        static int l_get_metadata(lua_State *L);
 
+       // DEPRECATED
        // set_metadata(self, string)
        static int l_set_metadata(lua_State *L);
 
diff --git a/src/script/lua_api/l_itemstackmeta.cpp b/src/script/lua_api/l_itemstackmeta.cpp
new file mode 100644 (file)
index 0000000..304a7cd
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+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.
+*/
+
+#include "lua_api/l_itemstackmeta.h"
+#include "lua_api/l_internal.h"
+#include "common/c_content.h"
+
+/*
+       NodeMetaRef
+*/
+ItemStackMetaRef* ItemStackMetaRef::checkobject(lua_State *L, int narg)
+{
+       luaL_checktype(L, narg, LUA_TUSERDATA);
+       void *ud = luaL_checkudata(L, narg, className);
+       if (!ud)
+               luaL_typerror(L, narg, className);
+
+       return *(ItemStackMetaRef**)ud;  // unbox pointer
+}
+
+Metadata* ItemStackMetaRef::getmeta(bool auto_create)
+{
+       return &istack->metadata;
+}
+
+void ItemStackMetaRef::clearMeta()
+{
+       istack->metadata.clear();
+}
+
+void ItemStackMetaRef::reportMetadataChange()
+{
+       // TODO
+}
+
+// Exported functions
+
+// garbage collector
+int ItemStackMetaRef::gc_object(lua_State *L) {
+       ItemStackMetaRef *o = *(ItemStackMetaRef **)(lua_touserdata(L, 1));
+       delete o;
+       return 0;
+}
+
+// Creates an NodeMetaRef and leaves it on top of stack
+// Not callable from Lua; all references are created on the C side.
+void ItemStackMetaRef::create(lua_State *L, ItemStack *istack)
+{
+       ItemStackMetaRef *o = new ItemStackMetaRef(istack);
+       //infostream<<"NodeMetaRef::create: o="<<o<<std::endl;
+       *(void **)(lua_newuserdata(L, sizeof(void *))) = o;
+       luaL_getmetatable(L, className);
+       lua_setmetatable(L, -2);
+}
+
+void ItemStackMetaRef::Register(lua_State *L)
+{
+       lua_newtable(L);
+       int methodtable = lua_gettop(L);
+       luaL_newmetatable(L, className);
+       int metatable = lua_gettop(L);
+
+       lua_pushliteral(L, "__metatable");
+       lua_pushvalue(L, methodtable);
+       lua_settable(L, metatable);  // hide metatable from Lua getmetatable()
+
+       lua_pushliteral(L, "metadata_class");
+       lua_pushlstring(L, className, strlen(className));
+       lua_settable(L, metatable);
+
+       lua_pushliteral(L, "__index");
+       lua_pushvalue(L, methodtable);
+       lua_settable(L, metatable);
+
+       lua_pushliteral(L, "__gc");
+       lua_pushcfunction(L, gc_object);
+       lua_settable(L, metatable);
+
+       lua_pop(L, 1);  // drop metatable
+
+       luaL_openlib(L, 0, methods, 0);  // fill methodtable
+       lua_pop(L, 1);  // drop methodtable
+
+       // Cannot be created from Lua
+       //lua_register(L, className, create_object);
+}
+
+const char ItemStackMetaRef::className[] = "ItemStackMetaRef";
+const luaL_reg ItemStackMetaRef::methods[] = {
+       luamethod(MetaDataRef, get_string),
+       luamethod(MetaDataRef, set_string),
+       luamethod(MetaDataRef, get_int),
+       luamethod(MetaDataRef, set_int),
+       luamethod(MetaDataRef, get_float),
+       luamethod(MetaDataRef, set_float),
+       luamethod(MetaDataRef, to_table),
+       luamethod(MetaDataRef, from_table),
+       {0,0}
+};
diff --git a/src/script/lua_api/l_itemstackmeta.h b/src/script/lua_api/l_itemstackmeta.h
new file mode 100644 (file)
index 0000000..6f9b201
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+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.
+*/
+
+#ifndef L_ITEMSTACKMETA_H_
+#define L_ITEMSTACKMETA_H_
+
+#include "lua_api/l_base.h"
+#include "lua_api/l_metadata.h"
+#include "irrlichttypes_bloated.h"
+#include "inventory.h"
+
+class ItemStackMetaRef : public MetaDataRef
+{
+private:
+       ItemStack *istack;
+
+       static const char className[];
+       static const luaL_reg methods[];
+
+       static ItemStackMetaRef *checkobject(lua_State *L, int narg);
+
+       virtual Metadata* getmeta(bool auto_create);
+
+       virtual void clearMeta();
+
+       virtual void reportMetadataChange();
+
+       // Exported functions
+
+       // garbage collector
+       static int gc_object(lua_State *L);
+public:
+       ItemStackMetaRef(ItemStack *istack): istack(istack) {}
+       ~ItemStackMetaRef() {}
+
+       // Creates an ItemStackMetaRef and leaves it on top of stack
+       // Not callable from Lua; all references are created on the C side.
+       static void create(lua_State *L, ItemStack *istack);
+
+       static void Register(lua_State *L);
+};
+
+#endif
index e313d55f8b5d6fd27374874391debb1d9d448997..7becef6dca26acce61d1fe5ed40b630a64c8e873 100644 (file)
@@ -28,6 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "lua_api/l_env.h"
 #include "lua_api/l_inventory.h"
 #include "lua_api/l_item.h"
+#include "lua_api/l_itemstackmeta.h"
 #include "lua_api/l_mapgen.h"
 #include "lua_api/l_nodemeta.h"
 #include "lua_api/l_nodetimer.h"
@@ -94,6 +95,7 @@ void GameScripting::InitializeModApi(lua_State *L, int top)
 
        // Register reference classes (userdata)
        InvRef::Register(L);
+       ItemStackMetaRef::Register(L);
        LuaAreaStore::Register(L);
        LuaItemStack::Register(L);
        LuaPerlinNoise::Register(L);
index 99cb990f1dada7ee78f0dafa9f68349aab7e1a0f..61d369bc487f3a5ae8099c228cb09568237bc47d 100644 (file)
@@ -354,6 +354,55 @@ std::string deSerializeJsonString(std::istream &is)
        return os.str();
 }
 
+std::string serializeJsonStringIfNeeded(const std::string &s)
+{
+       for (size_t i = 0; i < s.size(); ++i) {
+               if (s[i] <= 0x1f || s[i] >= 0x7f || s[i] == ' ' || s[i] == '\"')
+                       return serializeJsonString(s);
+       }
+       return s;
+}
+
+std::string deSerializeJsonStringIfNeeded(std::istream &is)
+{
+       std::ostringstream tmp_os;
+       bool expect_initial_quote = true;
+       bool is_json = false;
+       bool was_backslash = false;
+       for (;;) {
+               char c = is.get();
+               if (is.eof())
+                       break;
+
+               if (expect_initial_quote && c == '"') {
+                       tmp_os << c;
+                       is_json = true;
+               } else if(is_json) {
+                       tmp_os << c;
+                       if (was_backslash)
+                               was_backslash = false;
+                       else if (c == '\\')
+                               was_backslash = true;
+                       else if (c == '"')
+                               break; // Found end of string
+               } else {
+                       if (c == ' ') {
+                               // Found end of word
+                               is.unget();
+                               break;
+                       } else {
+                               tmp_os << c;
+                       }
+               }
+               expect_initial_quote = false;
+       }
+       if (is_json) {
+               std::istringstream tmp_is(tmp_os.str(), std::ios::binary);
+               return deSerializeJsonString(tmp_is);
+       } else
+               return tmp_os.str();
+}
+
 ////
 //// String/Struct conversions
 ////
index 36324a6751e7dae9c4af3dae6eb09ac14a2b95a4..e224341918480ed1531ab1acaa31068fcea5ea36 100644 (file)
@@ -405,6 +405,13 @@ std::string serializeJsonString(const std::string &plain);
 // Reads a string encoded in JSON format
 std::string deSerializeJsonString(std::istream &is);
 
+// If the string contains spaces, quotes or control characters, encodes as JSON.
+// Else returns the string unmodified.
+std::string serializeJsonStringIfNeeded(const std::string &s);
+
+// Parses a string serialized by serializeJsonStringIfNeeded.
+std::string deSerializeJsonStringIfNeeded(std::istream &is);
+
 // Creates a string consisting of the hexadecimal representation of `data`
 std::string serializeHexString(const std::string &data, bool insert_spaces=false);