Change ContentFeatures array to a vector

This commit is contained in:
Kahrl 2013-07-14 00:55:47 +02:00
parent 9733dd5b5e
commit 112dbba7c4
7 changed files with 162 additions and 115 deletions

@ -175,7 +175,7 @@ void ItemStack::deSerialize(std::istream &is, IItemDefManager *itemdef)
// Convert old materials // Convert old materials
if(material <= 0xff) if(material <= 0xff)
material = content_translate_from_19_to_internal(material); material = content_translate_from_19_to_internal(material);
if(material > MAX_CONTENT) if(material > 0xfff)
throw SerializationError("Too large material number"); throw SerializationError("Too large material number");
// Convert old id to name // Convert old id to name
NameIdMapping legacy_nimap; NameIdMapping legacy_nimap;
@ -194,7 +194,7 @@ void ItemStack::deSerialize(std::istream &is, IItemDefManager *itemdef)
is>>material; is>>material;
u16 materialcount; u16 materialcount;
is>>materialcount; is>>materialcount;
if(material > MAX_CONTENT) if(material > 0xfff)
throw SerializationError("Too large material number"); throw SerializationError("Too large material number");
// Convert old id to name // Convert old id to name
NameIdMapping legacy_nimap; NameIdMapping legacy_nimap;

@ -519,7 +519,8 @@ public:
// Add the four builtin items: // Add the four builtin items:
// "" is the hand // "" 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 // "air" is the air node
// "ignore" is the ignore node // "ignore" is the ignore node
@ -530,6 +531,7 @@ public:
m_item_definitions.insert(std::make_pair("", hand_def)); m_item_definitions.insert(std::make_pair("", hand_def));
ItemDefinition* unknown_def = new ItemDefinition; ItemDefinition* unknown_def = new ItemDefinition;
unknown_def->type = ITEM_NODE;
unknown_def->name = "unknown"; unknown_def->name = "unknown";
m_item_definitions.insert(std::make_pair("unknown", unknown_def)); m_item_definitions.insert(std::make_pair("unknown", unknown_def));

@ -35,19 +35,23 @@ class INodeDefManager;
- Tile = TileSpec at some side of a node of some content type - Tile = TileSpec at some side of a node of some content type
*/ */
typedef u16 content_t; 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
Anything that stores MapNodes doesn't have to preserve parameters there is enough room for dummy node IDs, which are created when
associated with this material. a MapBlock containing unknown node names is loaded from disk.
Doesn't create faces with anything and is considered being
out-of-map in the game map.
*/ */
#define CONTENT_IGNORE 127 #define MAX_REGISTERED_CONTENT 0xfffU
#define CONTENT_IGNORE_DEFAULT_PARAM 0
/*
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_UNKNOWN 125
/* /*
The common material through which the player can walk and which The common material through which the player can walk and which
@ -55,6 +59,18 @@ typedef u16 content_t;
*/ */
#define CONTENT_AIR 126 #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 enum LightBank
{ {
LIGHTBANK_DAY, LIGHTBANK_DAY,

@ -27,6 +27,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "log.h" #include "log.h"
#include "settings.h" #include "settings.h"
#include "nameidmapping.h" #include "nameidmapping.h"
#include "util/numeric.h"
#include "util/serialize.h" #include "util/serialize.h"
//#include "profiler.h" // For TimeTaker //#include "profiler.h" // For TimeTaker
@ -361,13 +362,26 @@ class CNodeDefManager: public IWritableNodeDefManager
public: public:
void clear() void clear()
{ {
m_content_features.clear();
m_name_id_mapping.clear(); m_name_id_mapping.clear();
m_name_id_mapping_with_aliases.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]; ContentFeatures f;
f.reset(); // Reset to defaults f.name = "unknown";
// Insert directly into containers
content_t c = CONTENT_UNKNOWN;
m_content_features[c] = f;
addNameIdMapping(c, f.name);
} }
// Set CONTENT_AIR // Set CONTENT_AIR
@ -387,6 +401,7 @@ public:
m_content_features[c] = f; m_content_features[c] = f;
addNameIdMapping(c, f.name); addNameIdMapping(c, f.name);
} }
// Set CONTENT_IGNORE // Set CONTENT_IGNORE
{ {
ContentFeatures f; ContentFeatures f;
@ -406,16 +421,6 @@ public:
addNameIdMapping(c, f.name); 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() CNodeDefManager()
{ {
clear(); clear();
@ -426,16 +431,15 @@ public:
virtual IWritableNodeDefManager* clone() virtual IWritableNodeDefManager* clone()
{ {
CNodeDefManager *mgr = new CNodeDefManager(); CNodeDefManager *mgr = new CNodeDefManager();
for(u16 i=0; i<=MAX_CONTENT; i++) *mgr = *this;
{
mgr->set(i, get(i));
}
return mgr; return mgr;
} }
virtual const ContentFeatures& get(content_t c) const virtual const ContentFeatures& get(content_t c) const
{ {
assert(c <= MAX_CONTENT); if(c < m_content_features.size())
return m_content_features[c]; return m_content_features[c];
else
return m_content_features[CONTENT_UNKNOWN];
} }
virtual const ContentFeatures& get(const MapNode &n) const virtual const ContentFeatures& get(const MapNode &n) const
{ {
@ -468,7 +472,6 @@ public:
} }
std::string group = name.substr(6); std::string group = name.substr(6);
#if 1 // Optimized version, takes less than 1 microsecond at -O1
std::map<std::string, GroupItems>::const_iterator std::map<std::string, GroupItems>::const_iterator
i = m_group_to_items.find(group); i = m_group_to_items.find(group);
if (i == m_group_to_items.end()) if (i == m_group_to_items.end())
@ -480,50 +483,67 @@ public:
if ((*j).second != 0) if ((*j).second != 0)
result.insert((*j).first); 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()); //printf("getIds: %dus\n", t.stop());
} }
virtual const ContentFeatures& get(const std::string &name) const virtual const ContentFeatures& get(const std::string &name) const
{ {
content_t id = CONTENT_IGNORE; content_t id = CONTENT_UNKNOWN;
getId(name, id); getId(name, id);
return get(id); return get(id);
} }
// IWritableNodeDefManager // returns CONTENT_IGNORE if no free ID found
virtual void set(content_t c, const ContentFeatures &def) content_t allocateId()
{ {
verbosestream<<"registerNode: registering content id \""<<c for(content_t id = m_next_id;
<<"\": name=\""<<def.name<<"\""<<std::endl; id >= m_next_id; // overflow?
assert(c <= MAX_CONTENT); ++id){
// Don't allow redefining CONTENT_IGNORE (but allow air) while(id >= m_content_features.size()){
if(def.name == "ignore" || c == CONTENT_IGNORE){ m_content_features.push_back(ContentFeatures());
infostream<<"registerNode: WARNING: Ignoring " }
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 content_t set(const std::string &name,
const ContentFeatures &def)
{
assert(name != "");
assert(name == def.name);
// Don't allow redefining ignore (but allow air and unknown)
if(name == "ignore"){
infostream<<"NodeDefManager: WARNING: Ignoring "
<<"CONTENT_IGNORE redefinition"<<std::endl; <<"CONTENT_IGNORE redefinition"<<std::endl;
return; return CONTENT_IGNORE;
} }
// Check that the special contents are not redefined as different id
// because it would mess up everything content_t id = CONTENT_IGNORE;
if((def.name == "ignore" && c != CONTENT_IGNORE) || bool found = m_name_id_mapping.getId(name, id); // ignore aliases
(def.name == "air" && c != CONTENT_AIR)){ if(!found){
errorstream<<"registerNode: IGNORING ERROR: " // Get new id
<<"trying to register built-in type \"" id = allocateId();
<<def.name<<"\" as different id"<<std::endl; if(id == CONTENT_IGNORE){
return; infostream<<"NodeDefManager: WARNING: Absolute "
<<"limit reached"<<std::endl;
return CONTENT_IGNORE;
}
assert(id != CONTENT_IGNORE);
addNameIdMapping(id, name);
} }
m_content_features[c] = def; m_content_features[id] = def;
if(def.name != "") verbosestream<<"NodeDefManager: registering content id \""<<id
addNameIdMapping(c, def.name); <<"\": name=\""<<def.name<<"\""<<std::endl;
// Add this content to the list of all groups it belongs to // Add this content to the list of all groups it belongs to
// FIXME: This should remove a node from groups it no longer
// belongs to when a node is re-registered
for (ItemGroupList::const_iterator i = def.groups.begin(); for (ItemGroupList::const_iterator i = def.groups.begin();
i != def.groups.end(); ++i) { i != def.groups.end(); ++i) {
std::string group_name = i->first; std::string group_name = i->first;
@ -531,28 +551,13 @@ public:
std::map<std::string, GroupItems>::iterator std::map<std::string, GroupItems>::iterator
j = m_group_to_items.find(group_name); j = m_group_to_items.find(group_name);
if (j == m_group_to_items.end()) { 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 { } else {
GroupItems &items = j->second; 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; return id;
} }
virtual content_t allocateDummy(const std::string &name) virtual content_t allocateDummy(const std::string &name)
@ -589,7 +594,7 @@ public:
bool new_style_leaves = g_settings->getBool("new_style_leaves"); bool new_style_leaves = g_settings->getBool("new_style_leaves");
bool opaque_water = g_settings->getBool("opaque_water"); bool opaque_water = g_settings->getBool("opaque_water");
for(u16 i=0; i<=MAX_CONTENT; i++) for(u32 i=0; i<m_content_features.size(); i++)
{ {
ContentFeatures *f = &m_content_features[i]; ContentFeatures *f = &m_content_features[i];
@ -766,9 +771,10 @@ public:
writeU8(os, 1); // version writeU8(os, 1); // version
u16 count = 0; u16 count = 0;
std::ostringstream os2(std::ios::binary); std::ostringstream os2(std::ios::binary);
for(u16 i=0; i<=MAX_CONTENT; i++) for(u32 i=0; i<m_content_features.size(); i++)
{ {
if(i == CONTENT_IGNORE || i == CONTENT_AIR) if(i == CONTENT_IGNORE || i == CONTENT_AIR
|| i == CONTENT_UNKNOWN)
continue; continue;
ContentFeatures *f = &m_content_features[i]; ContentFeatures *f = &m_content_features[i];
if(f->name == "") if(f->name == "")
@ -779,6 +785,8 @@ public:
std::ostringstream wrapper_os(std::ios::binary); std::ostringstream wrapper_os(std::ios::binary);
f->serialize(wrapper_os, protocol_version); f->serialize(wrapper_os, protocol_version);
os2<<serializeString(wrapper_os.str()); os2<<serializeString(wrapper_os.str());
assert(count + 1 > count); // must not overflow
count++; count++;
} }
writeU16(os, count); writeU16(os, count);
@ -792,24 +800,43 @@ public:
throw SerializationError("unsupported NodeDefinitionManager version"); throw SerializationError("unsupported NodeDefinitionManager version");
u16 count = readU16(is); u16 count = readU16(is);
std::istringstream is2(deSerializeLongString(is), std::ios::binary); std::istringstream is2(deSerializeLongString(is), std::ios::binary);
ContentFeatures f;
for(u16 n=0; n<count; n++){ for(u16 n=0; n<count; n++){
u16 i = readU16(is2); u16 i = readU16(is2);
if(i > MAX_CONTENT){
errorstream<<"ContentFeatures::deSerialize(): "
<<"Too large content id: "<<i<<std::endl;
continue;
}
/*// Do not deserialize special types
if(i == CONTENT_IGNORE || i == CONTENT_AIR)
continue;*/
ContentFeatures *f = &m_content_features[i];
// Read it from the string wrapper // Read it from the string wrapper
std::string wrapper = deSerializeString(is2); std::string wrapper = deSerializeString(is2);
std::istringstream wrapper_is(wrapper, std::ios::binary); std::istringstream wrapper_is(wrapper, std::ios::binary);
f->deSerialize(wrapper_is); f.deSerialize(wrapper_is);
verbosestream<<"deserialized "<<f->name<<std::endl;
if(f->name != "") // Check error conditions
addNameIdMapping(i, f->name); if(i == CONTENT_IGNORE || i == CONTENT_AIR
|| i == CONTENT_UNKNOWN){
infostream<<"NodeDefManager::deSerialize(): WARNING: "
<<"not changing builtin node "<<i
<<std::endl;
continue;
}
if(f.name == ""){
infostream<<"NodeDefManager::deSerialize(): WARNING: "
<<"received empty name"<<std::endl;
continue;
}
u16 existing_id;
bool found = m_name_id_mapping.getId(f.name, existing_id); // ignore aliases
if(found && i != existing_id){
infostream<<"NodeDefManager::deSerialize(): WARNING: "
<<"already defined with different ID: "
<<f.name<<std::endl;
continue;
}
// All is ok, add node definition with the requested ID
if(i >= m_content_features.size())
m_content_features.resize((u32)(i) + 1);
m_content_features[i] = f;
addNameIdMapping(i, f.name);
verbosestream<<"deserialized "<<f.name<<std::endl;
} }
} }
private: private:
@ -820,7 +847,7 @@ private:
} }
private: private:
// Features indexed by id // Features indexed by id
ContentFeatures m_content_features[MAX_CONTENT+1]; std::vector<ContentFeatures> m_content_features;
// A mapping for fast converting back and forth between names and ids // A mapping for fast converting back and forth between names and ids
NameIdMapping m_name_id_mapping; NameIdMapping m_name_id_mapping;
// Like m_name_id_mapping, but only from names to ids, and includes // Like m_name_id_mapping, but only from names to ids, and includes
@ -831,6 +858,8 @@ private:
// that belong to it. Necessary for a direct lookup in getIds(). // that belong to it. Necessary for a direct lookup in getIds().
// Note: Not serialized. // Note: Not serialized.
std::map<std::string, GroupItems> m_group_to_items; std::map<std::string, GroupItems> m_group_to_items;
// Next possibly free id
content_t m_next_id;
}; };
IWritableNodeDefManager* createNodeDefManager() IWritableNodeDefManager* createNodeDefManager()

@ -293,15 +293,14 @@ public:
virtual const ContentFeatures& get(content_t c) const=0; virtual const ContentFeatures& get(content_t c) const=0;
virtual const ContentFeatures& get(const MapNode &n) const=0; virtual const ContentFeatures& get(const MapNode &n) const=0;
virtual bool getId(const std::string &name, content_t &result) const=0; virtual bool getId(const std::string &name, content_t &result) const=0;
// If not found, returns CONTENT_IGNORE
virtual content_t getId(const std::string &name) const=0; virtual content_t getId(const std::string &name) const=0;
// Allows "group:name" in addition to regular node names // Allows "group:name" in addition to regular node names
virtual void getIds(const std::string &name, std::set<content_t> &result) virtual void getIds(const std::string &name, std::set<content_t> &result)
const=0; const=0;
// If not found, returns the features of CONTENT_IGNORE // If not found, returns the features of CONTENT_UNKNOWN
virtual const ContentFeatures& get(const std::string &name) const=0; 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) // Register node definition by name (allocate an id)
// If returns CONTENT_IGNORE, could not allocate id // If returns CONTENT_IGNORE, could not allocate id
virtual content_t set(const std::string &name, virtual content_t set(const std::string &name,

@ -432,10 +432,15 @@ int ModApiItemMod::l_register_item_raw(lua_State *L)
idef->registerItem(def); idef->registerItem(def);
// Read the node definition (content features) and register it // 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); 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 */ return 0; /* number of results */

@ -70,20 +70,18 @@ with this program; if not, write to the Free Software Foundation, Inc.,
A few item and node definitions for those tests that need them A few item and node definitions for those tests that need them
*/ */
#define CONTENT_STONE 0 static content_t CONTENT_STONE;
#define CONTENT_GRASS 0x800 static content_t CONTENT_GRASS;
#define CONTENT_TORCH 100 static content_t CONTENT_TORCH;
void define_some_nodes(IWritableItemDefManager *idef, IWritableNodeDefManager *ndef) void define_some_nodes(IWritableItemDefManager *idef, IWritableNodeDefManager *ndef)
{ {
content_t i;
ItemDefinition itemdef; ItemDefinition itemdef;
ContentFeatures f; ContentFeatures f;
/* /*
Stone Stone
*/ */
i = CONTENT_STONE;
itemdef = ItemDefinition(); itemdef = ItemDefinition();
itemdef.type = ITEM_NODE; itemdef.type = ITEM_NODE;
itemdef.name = "default:stone"; itemdef.name = "default:stone";
@ -99,12 +97,11 @@ void define_some_nodes(IWritableItemDefManager *idef, IWritableNodeDefManager *n
f.tiledef[i].name = "default_stone.png"; f.tiledef[i].name = "default_stone.png";
f.is_ground_content = true; f.is_ground_content = true;
idef->registerItem(itemdef); idef->registerItem(itemdef);
ndef->set(i, f); CONTENT_STONE = ndef->set(f.name, f);
/* /*
Grass Grass
*/ */
i = CONTENT_GRASS;
itemdef = ItemDefinition(); itemdef = ItemDefinition();
itemdef.type = ITEM_NODE; itemdef.type = ITEM_NODE;
itemdef.name = "default:dirt_with_grass"; 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.tiledef[i].name = "default_dirt.png^default_grass_side.png";
f.is_ground_content = true; f.is_ground_content = true;
idef->registerItem(itemdef); idef->registerItem(itemdef);
ndef->set(i, f); CONTENT_GRASS = ndef->set(f.name, f);
/* /*
Torch (minimal definition for lighting tests) Torch (minimal definition for lighting tests)
*/ */
i = CONTENT_TORCH;
itemdef = ItemDefinition(); itemdef = ItemDefinition();
itemdef.type = ITEM_NODE; itemdef.type = ITEM_NODE;
itemdef.name = "default:torch"; itemdef.name = "default:torch";
@ -138,7 +134,7 @@ void define_some_nodes(IWritableItemDefManager *idef, IWritableNodeDefManager *n
f.sunlight_propagates = true; f.sunlight_propagates = true;
f.light_source = LIGHT_MAX-1; f.light_source = LIGHT_MAX-1;
idef->registerItem(itemdef); idef->registerItem(itemdef);
ndef->set(i, f); CONTENT_TORCH = ndef->set(f.name, f);
} }
struct TestBase struct TestBase