summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--build/android/jni/Android.mk2
-rw-r--r--doc/lua_api.txt45
-rw-r--r--src/CMakeLists.txt1
-rw-r--r--src/content_cao.cpp2
-rw-r--r--src/craftdef.cpp3
-rw-r--r--src/inventory.cpp88
-rw-r--r--src/inventory.h14
-rw-r--r--src/itemstackmetadata.cpp43
-rw-r--r--src/itemstackmetadata.h35
-rw-r--r--src/metadata.cpp2
-rw-r--r--src/metadata.h38
-rw-r--r--src/script/common/c_content.cpp31
-rw-r--r--src/script/lua_api/CMakeLists.txt1
-rw-r--r--src/script/lua_api/l_env.cpp2
-rw-r--r--src/script/lua_api/l_item.cpp38
-rw-r--r--src/script/lua_api/l_item.h5
-rw-r--r--src/script/lua_api/l_itemstackmeta.cpp115
-rw-r--r--src/script/lua_api/l_itemstackmeta.h59
-rw-r--r--src/script/scripting_game.cpp2
-rw-r--r--src/util/serialize.cpp49
-rw-r--r--src/util/serialize.h7
21 files changed, 459 insertions, 123 deletions
diff --git a/build/android/jni/Android.mk b/build/android/jni/Android.mk
index 70a3d29c0..47f37cfa5 100644
--- a/build/android/jni/Android.mk
+++ b/build/android/jni/Android.mk
@@ -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 \
diff --git a/doc/lua_api.txt b/doc/lua_api.txt
index 219882f46..2f5e3706c 100644
--- a/doc/lua_api.txt
+++ b/doc/lua_api.txt
@@ -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)`.
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index afb591a04..30e6c85e4 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -415,6 +415,7 @@ set(common_SRCS
inventory.cpp
inventorymanager.cpp
itemdef.cpp
+ itemstackmetadata.cpp
light.cpp
log.cpp
map.cpp
diff --git a/src/content_cao.cpp b/src/content_cao.cpp
index 5dc3866cf..e0b1c4cd2 100644
--- a/src/content_cao.cpp
+++ b/src/content_cao.cpp
@@ -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);
diff --git a/src/craftdef.cpp b/src/craftdef.cpp
index 45d3018a7..286d1eada 100644
--- a/src/craftdef.cpp
+++ b/src/craftdef.cpp
@@ -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();
}
-
diff --git a/src/inventory.cpp b/src/inventory.cpp
index cb8faecbc..6d5b49916 100644
--- a/src/inventory.cpp
+++ b/src/inventory.cpp
@@ -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)
{
diff --git a/src/inventory.h b/src/inventory.h
index 7d7e58d61..fe1639728 100644
--- a/src/inventory.h
+++ b/src/inventory.h
@@ -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
index 000000000..c3d602245
--- /dev/null
+++ b/src/itemstackmetadata.cpp
@@ -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
index 000000000..c56c58fd2
--- /dev/null
+++ b/src/itemstackmetadata.h
@@ -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
diff --git a/src/metadata.cpp b/src/metadata.cpp
index 96453d710..3cc45f919 100644
--- a/src/metadata.cpp
+++ b/src/metadata.cpp
@@ -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
diff --git a/src/metadata.h b/src/metadata.h
index a629c0615..4bb3c2ee7 100644
--- a/src/metadata.h
+++ b/src/metadata.h
@@ -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
diff --git a/src/script/common/c_content.cpp b/src/script/common/c_content.cpp
index ebc951295..8925b51f4 100644
--- a/src/script/common/c_content.cpp
+++ b/src/script/common/c_content.cpp
@@ -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");
}
}
diff --git a/src/script/lua_api/CMakeLists.txt b/src/script/lua_api/CMakeLists.txt
index efccce515..070234eba 100644
--- a/src/script/lua_api/CMakeLists.txt
+++ b/src/script/lua_api/CMakeLists.txt
@@ -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
diff --git a/src/script/lua_api/l_env.cpp b/src/script/lua_api/l_env.cpp
index 3d9db7917..2722e35a4 100644
--- a/src/script/lua_api/l_env.cpp
+++ b/src/script/lua_api/l_env.cpp
@@ -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;
diff --git a/src/script/lua_api/l_item.cpp b/src/script/lua_api/l_item.cpp
index ff0baea14..f0293bed8 100644
--- a/src/script/lua_api/l_item.cpp
+++ b/src/script/lua_api/l_item.cpp
@@ -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),
diff --git a/src/script/lua_api/l_item.h b/src/script/lua_api/l_item.h
index be919b701..1ba5d79e0 100644
--- a/src/script/lua_api/l_item.h
+++ b/src/script/lua_api/l_item.h
@@ -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
index 000000000..304a7cdf3
--- /dev/null
+++ b/src/script/lua_api/l_itemstackmeta.cpp
@@ -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
index 000000000..6f9b2016c
--- /dev/null
+++ b/src/script/lua_api/l_itemstackmeta.h
@@ -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
diff --git a/src/script/scripting_game.cpp b/src/script/scripting_game.cpp
index e313d55f8..7becef6dc 100644
--- a/src/script/scripting_game.cpp
+++ b/src/script/scripting_game.cpp
@@ -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);
diff --git a/src/util/serialize.cpp b/src/util/serialize.cpp
index 99cb990f1..61d369bc4 100644
--- a/src/util/serialize.cpp
+++ b/src/util/serialize.cpp
@@ -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
////
diff --git a/src/util/serialize.h b/src/util/serialize.h
index 36324a675..e22434191 100644
--- a/src/util/serialize.h
+++ b/src/util/serialize.h
@@ -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);