diff --git a/src/inventory.cpp b/src/inventory.cpp index 928021c..2ce50e0 100644 --- a/src/inventory.cpp +++ b/src/inventory.cpp @@ -175,7 +175,7 @@ void ItemStack::deSerialize(std::istream &is, IItemDefManager *itemdef) // Convert old materials if(material <= 0xff) material = content_translate_from_19_to_internal(material); - if(material > MAX_CONTENT) + if(material > 0xfff) throw SerializationError("Too large material number"); // Convert old id to name NameIdMapping legacy_nimap; @@ -194,7 +194,7 @@ void ItemStack::deSerialize(std::istream &is, IItemDefManager *itemdef) is>>material; u16 materialcount; is>>materialcount; - if(material > MAX_CONTENT) + if(material > 0xfff) throw SerializationError("Too large material number"); // Convert old id to name NameIdMapping legacy_nimap; diff --git a/src/itemdef.cpp b/src/itemdef.cpp index f692ccf..e7498ce 100644 --- a/src/itemdef.cpp +++ b/src/itemdef.cpp @@ -519,7 +519,8 @@ class CItemDefManager: public IWritableItemDefManager // Add the four builtin items: // "" is the hand - // "unknown" is returned whenever an undefined item is accessed + // "unknown" is returned whenever an undefined item + // is accessed (is also the unknown node) // "air" is the air node // "ignore" is the ignore node @@ -530,6 +531,7 @@ class CItemDefManager: public IWritableItemDefManager m_item_definitions.insert(std::make_pair("", hand_def)); ItemDefinition* unknown_def = new ItemDefinition; + unknown_def->type = ITEM_NODE; unknown_def->name = "unknown"; m_item_definitions.insert(std::make_pair("unknown", unknown_def)); diff --git a/src/mapnode.h b/src/mapnode.h index 60211b8..c0a0a40 100644 --- a/src/mapnode.h +++ b/src/mapnode.h @@ -35,19 +35,23 @@ - Tile = TileSpec at some side of a node of some content type */ typedef u16 content_t; -#define MAX_CONTENT 0xfff /* - Ignored node. + The maximum node ID that can be registered by mods. This must + be significantly lower than the maximum content_t value, so that + there is enough room for dummy node IDs, which are created when + a MapBlock containing unknown node names is loaded from disk. +*/ +#define MAX_REGISTERED_CONTENT 0xfffU - Anything that stores MapNodes doesn't have to preserve parameters - associated with this material. - - Doesn't create faces with anything and is considered being - out-of-map in the game map. +/* + A solid walkable node with the texture unknown_node.png. + + For example, used on the client to display unregistered node IDs + (instead of expanding the vector of node definitions each time + such a node is received). */ -#define CONTENT_IGNORE 127 -#define CONTENT_IGNORE_DEFAULT_PARAM 0 +#define CONTENT_UNKNOWN 125 /* The common material through which the player can walk and which @@ -55,6 +59,18 @@ */ #define CONTENT_AIR 126 +/* + Ignored node. + + Unloaded chunks are considered to consist of this. Several other + methods return this when an error occurs. Also, during + map generation this means the node has not been set yet. + + Doesn't create faces with anything and is considered being + out-of-map in the game map. +*/ +#define CONTENT_IGNORE 127 + enum LightBank { LIGHTBANK_DAY, diff --git a/src/nodedef.cpp b/src/nodedef.cpp index 13e7e99..2b1d085 100644 --- a/src/nodedef.cpp +++ b/src/nodedef.cpp @@ -27,6 +27,7 @@ #include "log.h" #include "settings.h" #include "nameidmapping.h" +#include "util/numeric.h" #include "util/serialize.h" //#include "profiler.h" // For TimeTaker @@ -358,13 +359,26 @@ class CNodeDefManager: public IWritableNodeDefManager public: void clear() { + m_content_features.clear(); m_name_id_mapping.clear(); m_name_id_mapping_with_aliases.clear(); + m_group_to_items.clear(); + m_next_id = 0; - for(u16 i=0; i<=MAX_CONTENT; i++) + u32 initial_length = 0; + initial_length = MYMAX(initial_length, CONTENT_UNKNOWN + 1); + initial_length = MYMAX(initial_length, CONTENT_AIR + 1); + initial_length = MYMAX(initial_length, CONTENT_IGNORE + 1); + m_content_features.resize(initial_length); + + // Set CONTENT_UNKNOWN { - ContentFeatures &f = m_content_features[i]; - f.reset(); // Reset to defaults + ContentFeatures f; + f.name = "unknown"; + // Insert directly into containers + content_t c = CONTENT_UNKNOWN; + m_content_features[c] = f; + addNameIdMapping(c, f.name); } // Set CONTENT_AIR @@ -384,6 +398,7 @@ class CNodeDefManager: public IWritableNodeDefManager m_content_features[c] = f; addNameIdMapping(c, f.name); } + // Set CONTENT_IGNORE { ContentFeatures f; @@ -403,16 +418,6 @@ class CNodeDefManager: public IWritableNodeDefManager addNameIdMapping(c, f.name); } } - // CONTENT_IGNORE = not found - content_t getFreeId() - { - for(u32 i=0; i<=0xffff; i++){ - const ContentFeatures &f = m_content_features[i]; - if(f.name == "") - return i; - } - return CONTENT_IGNORE; - } CNodeDefManager() { clear(); @@ -423,16 +428,15 @@ class CNodeDefManager: public IWritableNodeDefManager virtual IWritableNodeDefManager* clone() { CNodeDefManager *mgr = new CNodeDefManager(); - for(u16 i=0; i<=MAX_CONTENT; i++) - { - mgr->set(i, get(i)); - } + *mgr = *this; return mgr; } virtual const ContentFeatures& get(content_t c) const { - assert(c <= MAX_CONTENT); - return m_content_features[c]; + if(c < m_content_features.size()) + return m_content_features[c]; + else + return m_content_features[CONTENT_UNKNOWN]; } virtual const ContentFeatures& get(const MapNode &n) const { @@ -447,6 +451,7 @@ class CNodeDefManager: public IWritableNodeDefManager result = i->second; return true; } + // returns CONTENT_IGNORE if not found virtual content_t getId(const std::string &name) const { content_t id = CONTENT_IGNORE; @@ -465,7 +470,6 @@ class CNodeDefManager: public IWritableNodeDefManager } std::string group = name.substr(6); -#if 1 // Optimized version, takes less than 1 microsecond at -O1 std::map::const_iterator i = m_group_to_items.find(group); if (i == m_group_to_items.end()) @@ -477,50 +481,67 @@ class CNodeDefManager: public IWritableNodeDefManager if ((*j).second != 0) result.insert((*j).first); } -#else // Old version, takes about ~150-200us at -O1 - for(u16 id=0; id<=MAX_CONTENT; id++) - { - const ContentFeatures &f = m_content_features[id]; - if(f.name == "") // Quickly discard undefined nodes - continue; - if(itemgroup_get(f.groups, group) != 0) - result.insert(id); - } -#endif //printf("getIds: %dus\n", t.stop()); } virtual const ContentFeatures& get(const std::string &name) const { - content_t id = CONTENT_IGNORE; + content_t id = CONTENT_UNKNOWN; getId(name, id); return get(id); } + // returns CONTENT_IGNORE if no free ID found + content_t allocateId() + { + for(content_t id = m_next_id; + id >= m_next_id; // overflow? + ++id){ + while(id >= m_content_features.size()){ + m_content_features.push_back(ContentFeatures()); + } + const ContentFeatures &f = m_content_features[id]; + if(f.name == ""){ + m_next_id = id + 1; + return id; + } + } + // If we arrive here, an overflow occurred in id. + // That means no ID was found + return CONTENT_IGNORE; + } // IWritableNodeDefManager - virtual void set(content_t c, const ContentFeatures &def) + virtual content_t set(const std::string &name, + const ContentFeatures &def) { - verbosestream<<"registerNode: registering content id \""<first; @@ -528,28 +549,13 @@ class CNodeDefManager: public IWritableNodeDefManager std::map::iterator j = m_group_to_items.find(group_name); if (j == m_group_to_items.end()) { - m_group_to_items[group_name].push_back(std::make_pair(c, i->second)); + m_group_to_items[group_name].push_back( + std::make_pair(id, i->second)); } else { GroupItems &items = j->second; - items.push_back(std::make_pair(c, i->second)); + items.push_back(std::make_pair(id, i->second)); } } - } - virtual content_t set(const std::string &name, - const ContentFeatures &def) - { - assert(name == def.name); - u16 id = CONTENT_IGNORE; - bool found = m_name_id_mapping.getId(name, id); // ignore aliases - if(!found){ - // Get some id - id = getFreeId(); - if(id == CONTENT_IGNORE) - return CONTENT_IGNORE; - if(name != "") - addNameIdMapping(id, name); - } - set(id, def); return id; } virtual content_t allocateDummy(const std::string &name) @@ -586,7 +592,7 @@ class CNodeDefManager: public IWritableNodeDefManager bool new_style_leaves = g_settings->getBool("new_style_leaves"); bool opaque_water = g_settings->getBool("opaque_water"); - for(u16 i=0; i<=MAX_CONTENT; i++) + for(u32 i=0; iname == "") @@ -776,6 +783,8 @@ class CNodeDefManager: public IWritableNodeDefManager std::ostringstream wrapper_os(std::ios::binary); f->serialize(wrapper_os, protocol_version); os2< count); // must not overflow count++; } writeU16(os, count); @@ -789,24 +798,43 @@ class CNodeDefManager: public IWritableNodeDefManager throw SerializationError("unsupported NodeDefinitionManager version"); u16 count = readU16(is); std::istringstream is2(deSerializeLongString(is), std::ios::binary); + ContentFeatures f; for(u16 n=0; n MAX_CONTENT){ - errorstream<<"ContentFeatures::deSerialize(): " - <<"Too large content id: "<deSerialize(wrapper_is); - verbosestream<<"deserialized "<name<name != "") - addNameIdMapping(i, f->name); + f.deSerialize(wrapper_is); + + // Check error conditions + if(i == CONTENT_IGNORE || i == CONTENT_AIR + || i == CONTENT_UNKNOWN){ + infostream<<"NodeDefManager::deSerialize(): WARNING: " + <<"not changing builtin node "<= m_content_features.size()) + m_content_features.resize((u32)(i) + 1); + m_content_features[i] = f; + addNameIdMapping(i, f.name); + verbosestream<<"deserialized "< m_content_features; // A mapping for fast converting back and forth between names and ids NameIdMapping m_name_id_mapping; // Like m_name_id_mapping, but only from names to ids, and includes @@ -828,6 +856,8 @@ class CNodeDefManager: public IWritableNodeDefManager // that belong to it. Necessary for a direct lookup in getIds(). // Note: Not serialized. std::map m_group_to_items; + // Next possibly free id + content_t m_next_id; }; IWritableNodeDefManager* createNodeDefManager() diff --git a/src/nodedef.h b/src/nodedef.h index 665a5dd..5cf752b 100644 --- a/src/nodedef.h +++ b/src/nodedef.h @@ -295,8 +295,6 @@ class IWritableNodeDefManager : public INodeDefManager // If not found, returns the features of CONTENT_IGNORE virtual const ContentFeatures& get(const std::string &name) const=0; - // Register node definition - virtual void set(content_t c, const ContentFeatures &def)=0; // Register node definition by name (allocate an id) // If returns CONTENT_IGNORE, could not allocate id virtual content_t set(const std::string &name, diff --git a/src/script/lua_api/l_item.cpp b/src/script/lua_api/l_item.cpp index 4069c61..6182c03 100644 --- a/src/script/lua_api/l_item.cpp +++ b/src/script/lua_api/l_item.cpp @@ -432,10 +432,15 @@ int ModApiItemMod::l_register_item_raw(lua_State *L) idef->registerItem(def); // Read the node definition (content features) and register it - if(def.type == ITEM_NODE) - { + if(def.type == ITEM_NODE){ ContentFeatures f = read_content_features(L, table); - ndef->set(f.name, f); + content_t id = ndef->set(f.name, f); + + if(id > MAX_REGISTERED_CONTENT){ + throw LuaError(L, "Number of registerable nodes (" + + itos(MAX_REGISTERED_CONTENT+1) + + ") exceeded (" + name + ")"); + } } return 0; /* number of results */ diff --git a/src/test.cpp b/src/test.cpp index ddbcee7..7753e1d 100644 --- a/src/test.cpp +++ b/src/test.cpp @@ -70,20 +70,18 @@ A few item and node definitions for those tests that need them */ -#define CONTENT_STONE 0 -#define CONTENT_GRASS 0x800 -#define CONTENT_TORCH 100 +static content_t CONTENT_STONE; +static content_t CONTENT_GRASS; +static content_t CONTENT_TORCH; void define_some_nodes(IWritableItemDefManager *idef, IWritableNodeDefManager *ndef) { - content_t i; ItemDefinition itemdef; ContentFeatures f; /* Stone */ - i = CONTENT_STONE; itemdef = ItemDefinition(); itemdef.type = ITEM_NODE; itemdef.name = "default:stone"; @@ -99,12 +97,11 @@ void define_some_nodes(IWritableItemDefManager *idef, IWritableNodeDefManager *n f.tiledef[i].name = "default_stone.png"; f.is_ground_content = true; idef->registerItem(itemdef); - ndef->set(i, f); + CONTENT_STONE = ndef->set(f.name, f); /* Grass */ - i = CONTENT_GRASS; itemdef = ItemDefinition(); itemdef.type = ITEM_NODE; itemdef.name = "default:dirt_with_grass"; @@ -122,12 +119,11 @@ void define_some_nodes(IWritableItemDefManager *idef, IWritableNodeDefManager *n f.tiledef[i].name = "default_dirt.png^default_grass_side.png"; f.is_ground_content = true; idef->registerItem(itemdef); - ndef->set(i, f); + CONTENT_GRASS = ndef->set(f.name, f); /* Torch (minimal definition for lighting tests) */ - i = CONTENT_TORCH; itemdef = ItemDefinition(); itemdef.type = ITEM_NODE; itemdef.name = "default:torch"; @@ -138,7 +134,7 @@ void define_some_nodes(IWritableItemDefManager *idef, IWritableNodeDefManager *n f.sunlight_propagates = true; f.light_source = LIGHT_MAX-1; idef->registerItem(itemdef); - ndef->set(i, f); + CONTENT_TORCH = ndef->set(f.name, f); } struct TestBase