diff --git a/src/biome.cpp b/src/biome.cpp
index e1dfc47afbb021c31bf06e03a79d7f27c8d3d327..94a2435f299ad129d7a938f7b8cb6e40ffb28080 100644
--- a/src/biome.cpp
+++ b/src/biome.cpp
@@ -30,7 +30,7 @@ NoiseParams nparams_biome_def_heat(50, 50, v3f(500.0, 500.0, 500.0), 5349, 3, 0.
 NoiseParams nparams_biome_def_humidity(50, 50, v3f(500.0, 500.0, 500.0), 842, 3, 0.55);
 
 
-BiomeDefManager::BiomeDefManager() {
+BiomeDefManager::BiomeDefManager(NodeResolver *resolver) {
 	biome_registration_finished = false;
 	np_heat     = &nparams_biome_def_heat;
 	np_humidity = &nparams_biome_def_humidity;
@@ -38,30 +38,22 @@ BiomeDefManager::BiomeDefManager() {
 	// Create default biome to be used in case none exist
 	Biome *b = new Biome;
 	
-	b->id    = 0;
-	b->name  = "Default";
-	b->flags = 0;
-	
-	b->depth_top    = 0;
-	b->depth_filler = 0;
-
-	b->nname_top        = "air";
-	b->nname_filler     = "air";
-	b->nname_water      = "mapgen_water_source";
-	b->nname_dust       = "air";
-	b->nname_dust_water = "mapgen_water_source";
-
-	b->c_top        = CONTENT_IGNORE;
-	b->c_filler     = CONTENT_IGNORE;
-	b->c_water      = CONTENT_IGNORE;
-	b->c_dust       = CONTENT_IGNORE;
-	b->c_dust_water = CONTENT_IGNORE;
-
+	b->id             = 0;
+	b->name           = "Default";
+	b->flags          = 0;
+	b->depth_top      = 0;
+	b->depth_filler   = 0;
 	b->height_min     = -MAP_GENERATION_LIMIT;
 	b->height_max     = MAP_GENERATION_LIMIT;
 	b->heat_point     = 0.0;
 	b->humidity_point = 0.0;
 
+	resolver->addNode("air",                 "", CONTENT_AIR, &b->c_top);
+	resolver->addNode("air",                 "", CONTENT_AIR, &b->c_filler);
+	resolver->addNode("mapgen_water_source", "", CONTENT_AIR, &b->c_water);
+	resolver->addNode("air",                 "", CONTENT_AIR, &b->c_dust);
+	resolver->addNode("mapgen_water_source", "", CONTENT_AIR, &b->c_dust_water);
+
 	biomes.push_back(b);
 }
 
@@ -106,62 +98,18 @@ void BiomeDefManager::calcBiomes(BiomeNoiseInput *input, u8 *biomeid_map) {
 }
 
 
-void BiomeDefManager::resolveNodeNames(INodeDefManager *ndef) {
-	Biome *b;
-	
-	biome_registration_finished = true;
-	
-	for (size_t i = 0; i < biomes.size(); i++) {
-		b = biomes[i];
-
-		b->c_top = ndef->getId(b->nname_top);
-		if (b->c_top == CONTENT_IGNORE) {
-			errorstream << "BiomeDefManager::resolveNodeNames: node '"
-				<< b->nname_top << "' not defined" << std::endl;
-			b->c_top     = CONTENT_AIR;
-			b->depth_top = 0;
-		}
-	
-		b->c_filler = ndef->getId(b->nname_filler);
-		if (b->c_filler == CONTENT_IGNORE) {
-			errorstream << "BiomeDefManager::resolveNodeNames: node '"
-				<< b->nname_filler << "' not defined" << std::endl;
-			b->c_filler     = CONTENT_AIR;
-			b->depth_filler = 0;
-		}
-		
-		b->c_water = ndef->getId(b->nname_water);
-		if (b->c_water == CONTENT_IGNORE) {
-			errorstream << "BiomeDefManager::resolveNodeNames: node '"
-				<< b->nname_water << "' not defined" << std::endl;
-			b->c_water = CONTENT_AIR;
-		}
-		
-		b->c_dust = ndef->getId(b->nname_dust);
-		if (b->c_dust == CONTENT_IGNORE) {
-			errorstream << "BiomeDefManager::resolveNodeNames: node '"
-				<< b->nname_dust << "' not defined" << std::endl;
-		}
-		
-		b->c_dust_water = ndef->getId(b->nname_dust_water);
-		if (b->c_dust_water == CONTENT_IGNORE) {
-			errorstream << "BiomeDefManager::resolveNodeNames: node '"
-				<< b->nname_dust_water << "' not defined" << std::endl;
-		}
-	}
-}
-
-
 void BiomeDefManager::addBiome(Biome *b) {
 	if (biome_registration_finished) {
-		errorstream << "BIomeDefManager: biome registration already finished, dropping " << b->name <<std::endl;
+		errorstream << "BIomeDefManager: biome registration already "
+			"finished, dropping " << b->name << std::endl;
 		delete b;
 		return;
 	}
 	
 	size_t nbiomes = biomes.size();
 	if (nbiomes >= 0xFF) {
-		errorstream << "BiomeDefManager: too many biomes, dropping " << b->name << std::endl;
+		errorstream << "BiomeDefManager: too many biomes, dropping "
+			<< b->name << std::endl;
 		delete b;
 		return;
 	}
diff --git a/src/biome.h b/src/biome.h
index aa83c4e0b25ff9ec22b6ed375d8b8d3bd601d418..fdfefeaf9be0c3853cc64de4c2b41588f4713ce1 100644
--- a/src/biome.h
+++ b/src/biome.h
@@ -45,11 +45,13 @@ class Biome {
 	std::string name;
 	u32 flags;
 	
+/*
 	std::string nname_top;
 	std::string nname_filler;
 	std::string nname_water;
 	std::string nname_dust;
 	std::string nname_dust_water;
+*/
 
 	content_t c_top;
 	content_t c_filler;
@@ -81,7 +83,7 @@ class BiomeDefManager {
 	NoiseParams *np_heat;
 	NoiseParams *np_humidity;
 
-	BiomeDefManager();
+	BiomeDefManager(NodeResolver *resolver);
 	~BiomeDefManager();
 	
 	Biome *createBiome(BiomeTerrainType btt);
diff --git a/src/emerge.cpp b/src/emerge.cpp
index b6e2080a6efa97d2d43a080e96e8e4305738aa2c..a4b0752e5b7fe87b2513ee0b04d1dd104d26ed2b 100644
--- a/src/emerge.cpp
+++ b/src/emerge.cpp
@@ -85,7 +85,7 @@ EmergeManager::EmergeManager(IGameDef *gamedef) {
 	registerMapgen("singlenode", new MapgenFactorySinglenode());
 
 	this->ndef     = gamedef->getNodeDefManager();
-	this->biomedef = new BiomeDefManager();
+	this->biomedef = new BiomeDefManager(gamedef->getNodeDefManager()->getResolver());
 	this->gennotify = 0;
 
 	// Note that accesses to this variable are not synchronized.
@@ -145,9 +145,9 @@ EmergeManager::~EmergeManager() {
 		delete decorations[i];
 	decorations.clear();
 
-	for (std::map<std::string, MapgenFactory *>::iterator iter = mglist.begin();
-			iter != mglist.end(); iter ++) {
-		delete iter->second;
+	for (std::map<std::string, MapgenFactory *>::iterator it = mglist.begin();
+			it != mglist.end(); ++it) {
+		delete it->second;
 	}
 	mglist.clear();
 
@@ -176,16 +176,6 @@ void EmergeManager::initMapgens() {
 	if (mapgen.size())
 		return;
 
-	// Resolve names of nodes for things that were registered
-	// (at this point, the registration period is over)
-	biomedef->resolveNodeNames(ndef);
-
-	for (size_t i = 0; i != ores.size(); i++)
-		ores[i]->resolveNodeNames(ndef);
-
-	for (size_t i = 0; i != decorations.size(); i++)
-		decorations[i]->resolveNodeNames(ndef);
-
 	if (!params.sparams) {
 		params.sparams = createMapgenParams(params.mg_name);
 		if (!params.sparams) {
diff --git a/src/mapgen.cpp b/src/mapgen.cpp
index 176c8a8a36c46b4178baa70cf91f44b28dd752e1..b7c929be7d6cb4185c416f739a54f1849e6c9eec 100644
--- a/src/mapgen.cpp
+++ b/src/mapgen.cpp
@@ -96,28 +96,6 @@ Ore::~Ore() {
 }
 
 
-void Ore::resolveNodeNames(INodeDefManager *ndef) {
-	if (ore == CONTENT_IGNORE) {
-		ore = ndef->getId(ore_name);
-		if (ore == CONTENT_IGNORE) {
-			errorstream << "Ore::resolveNodeNames: ore node '"
-				<< ore_name << "' not defined";
-			ore = CONTENT_AIR;
-			wherein.push_back(CONTENT_AIR);
-			return;
-		}
-	}
-
-	for (size_t i=0; i != wherein_names.size(); i++) {
-		std::string name = wherein_names[i];
-		content_t c = ndef->getId(name);
-		if (c != CONTENT_IGNORE) {
-			wherein.push_back(c);
-		}
-	}
-}
-
-
 void Ore::placeOre(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax) {
 	int in_range = 0;
 
@@ -147,7 +125,7 @@ void Ore::placeOre(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax) {
 void OreScatter::generate(ManualMapVoxelManipulator *vm, int seed,
 						  u32 blockseed, v3s16 nmin, v3s16 nmax) {
 	PseudoRandom pr(blockseed);
-	MapNode n_ore(ore, 0, ore_param2);
+	MapNode n_ore(c_ore, 0, ore_param2);
 
 	int volume = (nmax.X - nmin.X + 1) *
 				 (nmax.Y - nmin.Y + 1) *
@@ -171,8 +149,8 @@ void OreScatter::generate(ManualMapVoxelManipulator *vm, int seed,
 				continue;
 
 			u32 i = vm->m_area.index(x0 + x1, y0 + y1, z0 + z1);
-			for (size_t ii = 0; ii < wherein.size(); ii++)
-				if (vm->m_data[i].getContent() == wherein[ii])
+			for (size_t ii = 0; ii < c_wherein.size(); ii++)
+				if (vm->m_data[i].getContent() == c_wherein[ii])
 					vm->m_data[i] = n_ore;
 		}
 	}
@@ -182,7 +160,7 @@ void OreScatter::generate(ManualMapVoxelManipulator *vm, int seed,
 void OreSheet::generate(ManualMapVoxelManipulator *vm, int seed,
 						u32 blockseed, v3s16 nmin, v3s16 nmax) {
 	PseudoRandom pr(blockseed + 4234);
-	MapNode n_ore(ore, 0, ore_param2);
+	MapNode n_ore(c_ore, 0, ore_param2);
 
 	int max_height = clust_size;
 	int y_start = pr.range(nmin.Y, nmax.Y - max_height);
@@ -210,9 +188,12 @@ void OreSheet::generate(ManualMapVoxelManipulator *vm, int seed,
 			if (!vm->m_area.contains(i))
 				continue;
 
-			for (size_t ii = 0; ii < wherein.size(); ii++)
-				if (vm->m_data[i].getContent() == wherein[ii])
+			for (size_t ii = 0; ii < c_wherein.size(); ii++) {
+				if (vm->m_data[i].getContent() == c_wherein[ii]) {
 					vm->m_data[i] = n_ore;
+					break;
+				}
+			}
 		}
 	}
 }
@@ -248,14 +229,6 @@ Decoration::~Decoration() {
 }
 
 
-void Decoration::resolveNodeNames(INodeDefManager *ndef) {
-	this->ndef = ndef;
-
-	if (c_place_on == CONTENT_IGNORE)
-		c_place_on = ndef->getId(place_on_name);
-}
-
-
 void Decoration::placeDeco(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax) {
 	PseudoRandom ps(blockseed + 53);
 	int carea_size = nmax.X - nmin.X + 1;
@@ -388,48 +361,17 @@ void Decoration::placeCutoffs(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax)
 ///////////////////////////////////////////////////////////////////////////////
 
 
-void DecoSimple::resolveNodeNames(INodeDefManager *ndef) {
-	Decoration::resolveNodeNames(ndef);
-
-	if (c_deco == CONTENT_IGNORE && !decolist_names.size()) {
-		c_deco = ndef->getId(deco_name);
-		if (c_deco == CONTENT_IGNORE) {
-			errorstream << "DecoSimple::resolveNodeNames: decoration node '"
-				<< deco_name << "' not defined" << std::endl;
-			c_deco = CONTENT_AIR;
-		}
-	}
-	if (c_spawnby == CONTENT_IGNORE) {
-		c_spawnby = ndef->getId(spawnby_name);
-		if (c_spawnby == CONTENT_IGNORE) {
-			errorstream << "DecoSimple::resolveNodeNames: spawnby node '"
-				<< spawnby_name << "' not defined" << std::endl;
-			nspawnby = -1;
-			c_spawnby = CONTENT_AIR;
-		}
-	}
-
-	if (c_decolist.size())
-		return;
-
-	for (size_t i = 0; i != decolist_names.size(); i++) {
-		content_t c = ndef->getId(decolist_names[i]);
-		if (c == CONTENT_IGNORE) {
-			errorstream << "DecoSimple::resolveNodeNames: decolist node '"
-				<< decolist_names[i] << "' not defined" << std::endl;
-			c = CONTENT_AIR;
-		}
-		c_decolist.push_back(c);
-	}
-}
-
-
 void DecoSimple::generate(Mapgen *mg, PseudoRandom *pr, s16 max_y, v3s16 p) {
 	ManualMapVoxelManipulator *vm = mg->vm;
 
 	u32 vi = vm->m_area.index(p);
-	if (vm->m_data[vi].getContent() != c_place_on &&
-		c_place_on != CONTENT_IGNORE)
+	content_t c = vm->m_data[vi].getContent();
+	size_t idx;
+	for (idx = 0; idx != c_place_on.size(); idx++) {
+		if (c == c_place_on[idx])
+			break;
+	}
+	if ((idx != 0) && (idx == c_place_on.size()))
 		return;
 
 	if (nspawnby != -1) {
@@ -447,17 +389,25 @@ void DecoSimple::generate(Mapgen *mg, PseudoRandom *pr, s16 max_y, v3s16 p) {
 
 		for (int i = 0; i != 8; i++) {
 			u32 index = vm->m_area.index(p + dirs[i]);
-			if (vm->m_area.contains(index) &&
-				vm->m_data[index].getContent() == c_spawnby)
-				nneighs++;
+			if (!vm->m_area.contains(index))
+				continue;
+
+			content_t c = vm->m_data[index].getContent();
+			for (size_t j = 0; j != c_spawnby.size(); j++) {
+				if (c == c_spawnby[j]) {
+					nneighs++;
+					break;
+				}
+			}
 		}
 
 		if (nneighs < nspawnby)
 			return;
 	}
 
-	size_t ndecos = c_decolist.size();
-	content_t c_place = ndecos ? c_decolist[pr->range(0, ndecos - 1)] : c_deco;
+	if (c_decos.size() == 0)
+		return;
+	content_t c_place = c_decos[pr->range(0, c_decos.size() - 1)];
 
 	s16 height = (deco_height_max > 0) ?
 		pr->range(deco_height, deco_height_max) : deco_height;
@@ -483,7 +433,7 @@ int DecoSimple::getHeight() {
 
 
 std::string DecoSimple::getName() {
-	return deco_name;
+	return "";
 }
 
 
@@ -491,7 +441,6 @@ std::string DecoSimple::getName() {
 
 
 DecoSchematic::DecoSchematic() {
-	node_names  = NULL;
 	schematic   = NULL;
 	slice_probs = NULL;
 	flags       = 0;
@@ -500,47 +449,19 @@ DecoSchematic::DecoSchematic() {
 
 
 DecoSchematic::~DecoSchematic() {
-	delete node_names;
 	delete []schematic;
 	delete []slice_probs;
 }
 
 
-void DecoSchematic::resolveNodeNames(INodeDefManager *ndef) {
-	Decoration::resolveNodeNames(ndef);
-
-	if (filename.empty())
-		return;
-
-	if (!node_names) {
-		errorstream << "DecoSchematic::resolveNodeNames: node name list was "
-			"not created" << std::endl;
+void DecoSchematic::updateContentIds() {
+	if (flags & DECO_SCHEM_CIDS_UPDATED)
 		return;
-	}
-
-	for (size_t i = 0; i != node_names->size(); i++) {
-		std::string name = node_names->at(i);
-
-		std::map<std::string, std::string>::iterator it;
-		it = replacements.find(name);
-		if (it != replacements.end())
-			name = it->second;
 
-		content_t c = ndef->getId(name);
-		if (c == CONTENT_IGNORE) {
-			errorstream << "DecoSchematic::resolveNodeNames: node '"
-				<< name << "' not defined" << std::endl;
-			c = CONTENT_AIR;
-		}
-
-		c_nodes.push_back(c);
-	}
+	flags |= DECO_SCHEM_CIDS_UPDATED;
 
 	for (int i = 0; i != size.X * size.Y * size.Z; i++)
 		schematic[i].setContent(c_nodes[schematic[i].getContent()]);
-
-	delete node_names;
-	node_names = NULL;
 }
 
 
@@ -555,8 +476,13 @@ void DecoSchematic::generate(Mapgen *mg, PseudoRandom *pr, s16 max_y, v3s16 p) {
 		p.Z -= (size.Z + 1) / 2;
 
 	u32 vi = vm->m_area.index(p);
-	if (vm->m_data[vi].getContent() != c_place_on &&
-		c_place_on != CONTENT_IGNORE)
+	content_t c = vm->m_data[vi].getContent();
+	size_t idx;
+	for (idx = 0; idx != c_place_on.size(); idx++) {
+		if (c == c_place_on[idx])
+			break;
+	}
+	if ((idx != 0) && (idx == c_place_on.size()))
 		return;
 
 	Rotation rot = (rotation == ROTATE_RAND) ?
@@ -582,6 +508,8 @@ void DecoSchematic::blitToVManip(v3s16 p, ManualMapVoxelManipulator *vm,
 	int ystride = size.X;
 	int zstride = size.X * size.Y;
 
+	updateContentIds();
+
 	s16 sx = size.X;
 	s16 sy = size.Y;
 	s16 sz = size.Z;
@@ -694,7 +622,9 @@ void DecoSchematic::placeStructure(Map *map, v3s16 p, bool force_placement) {
 }
 
 
-bool DecoSchematic::loadSchematicFile() {
+bool DecoSchematic::loadSchematicFile(NodeResolver *resolver,
+	std::map<std::string, std::string> &replace_names)
+{
 	content_t cignore = CONTENT_IGNORE;
 	bool have_cignore = false;
 
@@ -730,7 +660,6 @@ bool DecoSchematic::loadSchematicFile() {
 
 	u16 nidmapcount = readU16(is);
 
-	node_names = new std::vector<std::string>;
 	for (int i = 0; i != nidmapcount; i++) {
 		std::string name = deSerializeString(is);
 		if (name == "ignore") {
@@ -738,7 +667,14 @@ bool DecoSchematic::loadSchematicFile() {
 			cignore = i;
 			have_cignore = true;
 		}
-		node_names->push_back(name);
+
+		std::map<std::string, std::string>::iterator it;
+
+		it = replace_names.find(name);
+		if (it != replace_names.end())
+			name = it->second;
+
+		resolver->addNodeList(name.c_str(), &c_nodes);
 	}
 
 	delete []schematic;
diff --git a/src/mapgen.h b/src/mapgen.h
index b272b5cb215424a21d9a0646034bd60b3ff6b135..01ab22730613b2993a4a4dfb7e5f8eadf5eebce4 100644
--- a/src/mapgen.h
+++ b/src/mapgen.h
@@ -47,9 +47,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #define OREFLAG_NODEISNT  0x04 // not yet implemented
 
 /////////////////// Decoration flags
-#define DECO_PLACE_CENTER_X 1
-#define DECO_PLACE_CENTER_Y 2
-#define DECO_PLACE_CENTER_Z 4
+#define DECO_PLACE_CENTER_X     1
+#define DECO_PLACE_CENTER_Y     2
+#define DECO_PLACE_CENTER_Z     4
+#define DECO_SCHEM_CIDS_UPDATED 8
 
 #define ORE_RANGE_ACTUAL 1
 #define ORE_RANGE_MIRROR 2
@@ -164,10 +165,8 @@ struct MapgenFactory {
 
 class Ore {
 public:
-	std::string ore_name;
-	std::vector<std::string> wherein_names;
-	content_t ore;
-	std::vector<content_t> wherein;  // the node to be replaced
+	content_t c_ore;                  // the node to place
+	std::vector<content_t> c_wherein; // the nodes to be placed in
 	u32 clust_scarcity; // ore cluster has a 1-in-clust_scarcity chance of appearing at a node
 	s16 clust_num_ores; // how many ore nodes are in a chunk
 	s16 clust_size;     // how large (in nodes) a chunk of ore is
@@ -180,14 +179,13 @@ class Ore {
 	Noise *noise;
 
 	Ore() {
-		ore     = CONTENT_IGNORE;
+		c_ore   = CONTENT_IGNORE;
 		np      = NULL;
 		noise   = NULL;
 	}
 
 	virtual ~Ore();
 
-	void resolveNodeNames(INodeDefManager *ndef);
 	void placeOre(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax);
 	virtual void generate(ManualMapVoxelManipulator *vm, int seed,
 						u32 blockseed, v3s16 nmin, v3s16 nmax) = 0;
@@ -234,8 +232,7 @@ class Decoration {
 	INodeDefManager *ndef;
 
 	int mapseed;
-	std::string place_on_name;
-	content_t c_place_on;
+	std::vector<content_t> c_place_on;
 	s16 sidelen;
 	float fill_ratio;
 	NoiseParams *np;
@@ -247,7 +244,6 @@ class Decoration {
 	Decoration();
 	virtual ~Decoration();
 
-	virtual void resolveNodeNames(INodeDefManager *ndef);
 	void placeDeco(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax);
 	void placeCutoffs(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax);
 
@@ -258,20 +254,14 @@ class Decoration {
 
 class DecoSimple : public Decoration {
 public:
-	std::string deco_name;
-	std::string spawnby_name;
-	content_t c_deco;
-	content_t c_spawnby;
+	std::vector<content_t> c_decos;
+	std::vector<content_t> c_spawnby;
 	s16 deco_height;
 	s16 deco_height_max;
 	s16 nspawnby;
 
-	std::vector<std::string> decolist_names;
-	std::vector<content_t> c_decolist;
-
 	~DecoSimple() {}
 
-	void resolveNodeNames(INodeDefManager *ndef);
 	virtual void generate(Mapgen *mg, PseudoRandom *pr, s16 max_y, v3s16 p);
 	virtual int getHeight();
 	virtual std::string getName();
@@ -288,9 +278,7 @@ class DecoSchematic : public Decoration {
 public:
 	std::string filename;
 
-	std::vector<std::string> *node_names;
 	std::vector<content_t> c_nodes;
-	std::map<std::string, std::string> replacements;
 
 	u32 flags;
 	Rotation rotation;
@@ -301,7 +289,7 @@ class DecoSchematic : public Decoration {
 	DecoSchematic();
 	~DecoSchematic();
 
-	void resolveNodeNames(INodeDefManager *ndef);
+	void updateContentIds();
 	virtual void generate(Mapgen *mg, PseudoRandom *pr, s16 max_y, v3s16 p);
 	virtual int getHeight();
 	virtual std::string getName();
@@ -309,7 +297,8 @@ class DecoSchematic : public Decoration {
 	void blitToVManip(v3s16 p, ManualMapVoxelManipulator *vm,
 					Rotation rot, bool force_placement);
 
-	bool loadSchematicFile();
+	bool loadSchematicFile(NodeResolver *resolver,
+		std::map<std::string, std::string> &replace_names);
 	void saveSchematicFile(INodeDefManager *ndef);
 
 	bool getSchematicFromMap(Map *map, v3s16 p1, v3s16 p2);
diff --git a/src/nodedef.cpp b/src/nodedef.cpp
index cf30d76b3f77a3e7683ca45bdb52957851da932a..3fc9bbc11b6291561971c647f75cd5ace566f400 100644
--- a/src/nodedef.cpp
+++ b/src/nodedef.cpp
@@ -402,6 +402,7 @@ class CNodeDefManager: public IWritableNodeDefManager {
 	virtual void updateTextures(IGameDef *gamedef);
 	void serialize(std::ostream &os, u16 protocol_version);
 	void deSerialize(std::istream &is);
+	virtual NodeResolver *getResolver();
 
 private:
 	void addNameIdMapping(content_t i, std::string name);
@@ -430,10 +431,14 @@ class CNodeDefManager: public IWritableNodeDefManager {
 
 	// Next possibly free id
 	content_t m_next_id;
+
+	// NodeResolver to queue pending node resolutions
+	NodeResolver m_resolver;
 };
 
 
-CNodeDefManager::CNodeDefManager()
+CNodeDefManager::CNodeDefManager() :
+	m_resolver(this)
 {
 	clear();
 }
@@ -1017,6 +1022,12 @@ void CNodeDefManager::addNameIdMapping(content_t i, std::string name)
 }
 
 
+NodeResolver *CNodeDefManager::getResolver()
+{
+	return &m_resolver;
+}
+
+
 IWritableNodeDefManager *createNodeDefManager()
 {
 	return new CNodeDefManager();
@@ -1242,3 +1253,157 @@ void ContentFeatures::deSerializeOld(std::istream &is, int version)
 		throw SerializationError("unsupported ContentFeatures version");
 	}
 }
+
+/*
+	NodeResolver
+*/
+
+NodeResolver::NodeResolver(INodeDefManager *ndef)
+{
+	m_ndef = ndef;
+	m_is_node_registration_complete = false;
+}
+
+
+NodeResolver::~NodeResolver()
+{
+	while (!m_pending_contents.empty()) {
+		NodeResolveInfo *nri = m_pending_contents.front();
+		m_pending_contents.pop_front();
+		delete nri;
+	}
+}
+
+
+int NodeResolver::addNode(std::string n_wanted, std::string n_alt,
+		content_t c_fallback, content_t *content)
+{
+	if (m_is_node_registration_complete) {
+		if (m_ndef->getId(n_wanted, *content))
+			return NR_STATUS_SUCCESS;
+
+		if (n_alt == "")
+			return NR_STATUS_FAILURE;
+
+		return m_ndef->getId(n_alt, *content) ?
+			NR_STATUS_SUCCESS : NR_STATUS_FAILURE;
+	} else {
+		NodeResolveInfo *nfi = new NodeResolveInfo;
+		nfi->n_wanted   = n_wanted;
+		nfi->n_alt      = n_alt;
+		nfi->c_fallback = c_fallback;
+		nfi->output     = content;
+
+		m_pending_contents.push_back(nfi);
+
+		return NR_STATUS_PENDING;
+	}
+}
+
+
+int NodeResolver::addNodeList(const char *nodename,
+		std::vector<content_t> *content_vec)
+{
+	if (m_is_node_registration_complete) {
+		std::set<content_t> idset;
+		std::set<content_t>::iterator it;
+
+		m_ndef->getIds(nodename, idset);
+		for (it = idset.begin(); it != idset.end(); ++it)
+			content_vec->push_back(*it);
+
+		return idset.size() ? NR_STATUS_SUCCESS : NR_STATUS_FAILURE;
+	} else {
+		m_pending_content_vecs.push_back(
+			std::make_pair(std::string(nodename), content_vec));
+		return NR_STATUS_PENDING;
+	}
+}
+
+
+bool NodeResolver::cancelNode(content_t *content)
+{
+	bool found = false;
+
+	std::list<NodeResolveInfo *>::iterator it = m_pending_contents.begin();
+	while (it != m_pending_contents.end()) {
+		NodeResolveInfo *nfi = *it;
+		if (nfi->output == content) {
+			it = m_pending_contents.erase(it);
+			delete nfi;
+			found = true;
+		}
+	}
+
+	return found;
+}
+
+
+int NodeResolver::cancelNodeList(std::vector<content_t> *content_vec)
+{
+	int num_canceled = 0;
+
+	std::list<std::pair<std::string, std::vector<content_t> *> >::iterator it;
+	it = m_pending_content_vecs.begin();
+	while (it != m_pending_content_vecs.end()) {
+		if (it->second == content_vec) {
+			it = m_pending_content_vecs.erase(it);
+			num_canceled++;
+		}
+	}
+
+	return num_canceled;
+}
+
+
+int NodeResolver::resolveNodes()
+{
+	int num_failed = 0;
+
+	//// Resolve pending single node name -> content ID mappings
+	while (!m_pending_contents.empty()) {
+		NodeResolveInfo *nri = m_pending_contents.front();		
+		m_pending_contents.pop_front();
+
+		bool success = true;
+		if (!m_ndef->getId(nri->n_wanted, *nri->output)) {
+			success = (nri->n_alt != "") ?
+				m_ndef->getId(nri->n_alt, *nri->output) : false;
+		}
+
+		if (!success) {
+			*nri->output = nri->c_fallback;
+			num_failed++;
+			errorstream << "NodeResolver::resolveNodes():  Failed to "
+				"resolve '" << nri->n_wanted;
+			if (nri->n_alt != "")
+				errorstream << "' and '" << nri->n_alt;
+			errorstream << "' to a content ID" << std::endl;
+		}
+
+		delete nri;
+	}
+
+	//// Resolve pending node names and add to content_t vector
+	while (!m_pending_content_vecs.empty()) {
+		std::pair<std::string, std::vector<content_t> *> item =
+			m_pending_content_vecs.front();
+		m_pending_content_vecs.pop_front();
+
+		std::string &name = item.first;
+		std::vector<content_t> *output = item.second;
+		
+		std::set<content_t> idset;
+		std::set<content_t>::iterator it;
+
+		m_ndef->getIds(name, idset);
+		for (it = idset.begin(); it != idset.end(); ++it)
+			output->push_back(*it);
+	}
+
+	//// Mark node registration as complete so future resolve
+	//// requests are satisfied immediately
+	m_is_node_registration_complete = true;
+
+	return num_failed;
+}
diff --git a/src/nodedef.h b/src/nodedef.h
index 27d67b481198748086a9457e415a9910a773ccb9..bd29b92b640d3f093acacaa13812aa18759e960d 100644
--- a/src/nodedef.h
+++ b/src/nodedef.h
@@ -282,6 +282,40 @@ struct ContentFeatures
 	}
 };
 
+struct NodeResolveInfo {
+	std::string n_wanted;
+	std::string n_alt;
+	content_t c_fallback;
+	content_t *output;
+};
+
+#define NR_STATUS_FAILURE 0
+#define NR_STATUS_PENDING 1
+#define NR_STATUS_SUCCESS 2
+
+class NodeResolver {
+public:
+	NodeResolver(INodeDefManager *ndef);
+	~NodeResolver();
+
+	int addNode(std::string n_wanted, std::string n_alt,
+		content_t c_fallback, content_t *content);
+	int addNodeList(const char *nodename, std::vector<content_t> *content_vec);
+
+	bool cancelNode(content_t *content);
+	int cancelNodeList(std::vector<content_t> *content_vec);
+
+	int resolveNodes();
+
+	bool isNodeRegFinished() { return m_is_node_registration_complete; }
+
+private:
+	INodeDefManager *m_ndef;
+	bool m_is_node_registration_complete;
+	std::list<NodeResolveInfo *> m_pending_contents;
+	std::list<std::pair<std::string, std::vector<content_t> *> > m_pending_content_vecs;
+};
+
 class INodeDefManager
 {
 public:
@@ -298,6 +332,8 @@ class INodeDefManager
 	virtual const ContentFeatures& get(const std::string &name) const=0;
 	
 	virtual void serialize(std::ostream &os, u16 protocol_version)=0;
+
+	virtual NodeResolver *getResolver()=0;
 };
 
 class IWritableNodeDefManager : public INodeDefManager
@@ -338,9 +374,11 @@ class IWritableNodeDefManager : public INodeDefManager
 
 	virtual void serialize(std::ostream &os, u16 protocol_version)=0;
 	virtual void deSerialize(std::istream &is)=0;
+
+	virtual NodeResolver *getResolver()=0;
 };
 
-IWritableNodeDefManager* createNodeDefManager();
+IWritableNodeDefManager *createNodeDefManager();
 
 #endif
 
diff --git a/src/script/common/c_converter.cpp b/src/script/common/c_converter.cpp
index b2ef0573cf696c547509adfda8b2599e6c011522..a906171d3665fa6e1be92f27f3d32f7967a21e66 100644
--- a/src/script/common/c_converter.cpp
+++ b/src/script/common/c_converter.cpp
@@ -227,6 +227,25 @@ std::vector<aabb3f> read_aabb3f_vector(lua_State *L, int index, f32 scale)
 	return boxes;
 }
 
+bool read_stringlist(lua_State *L, int index, std::vector<const char *> &result)
+{
+	if (index < 0)
+		index = lua_gettop(L) + 1 + index;
+
+	if (lua_istable(L, index)) {
+		lua_pushnil(L);
+		while (lua_next(L, index)) {
+			result.push_back(lua_tostring(L, -1));
+			lua_pop(L, 1);
+		}
+	} else if (lua_isstring(L, index)) {
+		result.push_back(lua_tostring(L, index));
+	} else {
+		return false;
+	}
+	return true;
+}
+
 /*
 	Table field getters
 */
@@ -287,6 +306,17 @@ bool getboolfield(lua_State *L, int table,
 	return got;
 }
 
+bool getstringlistfield(lua_State *L, int table, const char *fieldname,
+		std::vector<const char *> &result)
+{
+	lua_getfield(L, table, fieldname);
+
+	bool got = read_stringlist(L, -1, result);
+
+	lua_pop(L, 1);
+	return got;
+}
+
 std::string checkstringfield(lua_State *L, int table,
 		const char *fieldname)
 {
diff --git a/src/script/common/c_converter.h b/src/script/common/c_converter.h
index 0c051a80328e82c2eb69ae782c18117bc811eada..3b7eb6f7d2f8a1db4bc22bd8aa69d56de48905a5 100644
--- a/src/script/common/c_converter.h
+++ b/src/script/common/c_converter.h
@@ -37,7 +37,7 @@ extern "C" {
 #include <lua.h>
 }
 
-std::string        getstringfield_default        (lua_State *L, int table,
+std::string        getstringfield_default(lua_State *L, int table,
                              const char *fieldname, const std::string &default_);
 bool               getboolfield_default(lua_State *L, int table,
                              const char *fieldname, bool default_);
@@ -48,9 +48,12 @@ int                getintfield_default           (lua_State *L, int table,
 
 bool               getstringfield(lua_State *L, int table,
                              const char *fieldname, std::string &result);
+bool               getstringlistfield(lua_State *L, int table,
+                             const char *fieldname,
+                             std::vector<const char *> &result);
 bool               getintfield(lua_State *L, int table,
                              const char *fieldname, int &result);
-void               read_groups                   (lua_State *L, int index,
+void               read_groups(lua_State *L, int index,
                              std::map<std::string, int> &result);
 bool               getboolfield(lua_State *L, int table,
                              const char *fieldname, bool &result);
@@ -68,28 +71,29 @@ void               setboolfield(lua_State *L, int table,
                              const char *fieldname, bool value);
 
 
-v3f           checkFloatPos             (lua_State *L, int index);
-v3f           check_v3f                 (lua_State *L, int index);
-v3s16         check_v3s16               (lua_State *L, int index);
+v3f                 checkFloatPos       (lua_State *L, int index);
+v3f                 check_v3f           (lua_State *L, int index);
+v3s16               check_v3s16         (lua_State *L, int index);
 
-v3f           read_v3f                  (lua_State *L, int index);
-v2f           read_v2f                  (lua_State *L, int index);
-v2s16         read_v2s16                (lua_State *L, int index);
-v2s32         read_v2s32                (lua_State *L, int index);
-video::SColor readARGB8                 (lua_State *L, int index);
-aabb3f        read_aabb3f               (lua_State *L, int index, f32 scale);
-v3s16         read_v3s16                (lua_State *L, int index);
-std::vector<aabb3f>
-              read_aabb3f_vector        (lua_State *L, int index, f32 scale);
+v3f                 read_v3f            (lua_State *L, int index);
+v2f                 read_v2f            (lua_State *L, int index);
+v2s16               read_v2s16          (lua_State *L, int index);
+v2s32               read_v2s32          (lua_State *L, int index);
+video::SColor       readARGB8           (lua_State *L, int index);
+aabb3f              read_aabb3f         (lua_State *L, int index, f32 scale);
+v3s16               read_v3s16          (lua_State *L, int index);
+std::vector<aabb3f> read_aabb3f_vector  (lua_State *L, int index, f32 scale);
+bool                read_stringlist     (lua_State *L, int index,
+                                         std::vector<const char *> &result);
 
-void          push_v3s16                (lua_State *L, v3s16 p);
-void          pushFloatPos              (lua_State *L, v3f p);
-void          push_v3f                  (lua_State *L, v3f p);
-void          push_v2f                  (lua_State *L, v2f p);
+void                push_v3s16          (lua_State *L, v3s16 p);
+void                pushFloatPos        (lua_State *L, v3f p);
+void                push_v3f            (lua_State *L, v3f p);
+void                push_v2f            (lua_State *L, v2f p);
 
 
 
-void               warn_if_field_exists      (lua_State *L,
+void                warn_if_field_exists      (lua_State *L,
                                               int table,
                                               const char *fieldname,
                                               const std::string &message);
diff --git a/src/script/lua_api/l_mapgen.cpp b/src/script/lua_api/l_mapgen.cpp
index d31e84b3d374f2e50eb413afa3c727eb5aba12ab..b7548881594b79f775e67d7b99e7ea2002fd4215 100644
--- a/src/script/lua_api/l_mapgen.cpp
+++ b/src/script/lua_api/l_mapgen.cpp
@@ -80,26 +80,28 @@ struct EnumString ModApiMapgen::es_Rotation[] =
 };
 
 
-static void read_schematic_replacements(lua_State *L, DecoSchematic *dschem, int index)
+static void read_schematic_replacements(lua_State *L,
+	std::map<std::string, std::string> replace_names, int index)
 {
 	lua_pushnil(L);
 	while (lua_next(L, index)) {
-		// key at index -2 and value at index -1
 		std::string replace_from;
 		std::string replace_to;
-		if (lua_istable(L, -1)) {  // Old {{"x", "y"}, ...} format
+
+		if (lua_istable(L, -1)) { // Old {{"x", "y"}, ...} format
 			lua_rawgeti(L, -1, 1);
 			replace_from = lua_tostring(L, -1);
 			lua_pop(L, 1);
+
 			lua_rawgeti(L, -1, 2);
 			replace_to = lua_tostring(L, -1);
 			lua_pop(L, 1);
-		} else {  // New {x = "y", ...} format
+		} else { // New {x = "y", ...} format
 			replace_from = lua_tostring(L, -2);
 			replace_to = lua_tostring(L, -1);
 		}
-		dschem->replacements[replace_from] = replace_to;
-		// removes value, keeps key for next iteration
+
+		replace_names[replace_from] = replace_to;
 		lua_pop(L, 1);
 	}
 }
@@ -298,7 +300,8 @@ int ModApiMapgen::l_register_biome(lua_State *L)
 	int index = 1;
 	luaL_checktype(L, index, LUA_TTABLE);
 
-	BiomeDefManager *bmgr = getServer(L)->getEmergeManager()->biomedef;
+	NodeResolver *resolver = getServer(L)->getNodeDefManager()->getResolver();
+	BiomeDefManager *bmgr  = getServer(L)->getEmergeManager()->biomedef;
 	if (!bmgr) {
 		verbosestream << "register_biome: BiomeDefManager not active" << std::endl;
 		return 0;
@@ -308,32 +311,25 @@ int ModApiMapgen::l_register_biome(lua_State *L)
 				"terrain_type", es_BiomeTerrainType, BIOME_TERRAIN_NORMAL);
 	Biome *b = bmgr->createBiome(terrain);
 
-	b->name         = getstringfield_default(L, index, "name",
-												"<no name>");
-	b->nname_top    = getstringfield_default(L, index, "node_top",
-												"mapgen_dirt_with_grass");
-	b->nname_filler = getstringfield_default(L, index, "node_filler",
-												"mapgen_dirt");
-	b->nname_water  = getstringfield_default(L, index, "node_water",
-												"mapgen_water_source");
-	b->nname_dust   = getstringfield_default(L, index, "node_dust",
-												"air");
-	b->nname_dust_water = getstringfield_default(L, index, "node_dust_water",
-												"mapgen_water_source");
-
+	resolver->addNode(getstringfield_default(L, index, "node_top", ""),
+		 "mapgen_dirt_with_grass", CONTENT_AIR, &b->c_top);
+	resolver->addNode(getstringfield_default(L, index, "node_filler", ""),
+		"mapgen_dirt", CONTENT_AIR, &b->c_filler);
+	resolver->addNode(getstringfield_default(L, index, "node_water", ""),
+		"mapgen_water_source", CONTENT_AIR, &b->c_water);
+	resolver->addNode(getstringfield_default(L, index, "node_dust", ""),
+		"air", CONTENT_IGNORE, &b->c_dust);
+	resolver->addNode(getstringfield_default(L, index, "node_dust_water", ""),
+		"mapgen_water_source", CONTENT_IGNORE, &b->c_dust_water);
+
+	b->name           = getstringfield_default(L, index, "name", "<no name>");
 	b->depth_top      = getintfield_default(L, index, "depth_top",    1);
 	b->depth_filler   = getintfield_default(L, index, "depth_filler", 3);
 	b->height_min     = getintfield_default(L, index, "height_min",   0);
 	b->height_max     = getintfield_default(L, index, "height_max",   0);
 	b->heat_point     = getfloatfield_default(L, index, "heat_point",     0.);
 	b->humidity_point = getfloatfield_default(L, index, "humidity_point", 0.);
-
-	b->flags        = 0; //reserved
-	b->c_top        = CONTENT_IGNORE;
-	b->c_filler     = CONTENT_IGNORE;
-	b->c_water      = CONTENT_IGNORE;
-	b->c_dust       = CONTENT_IGNORE;
-	b->c_dust_water = CONTENT_IGNORE;
+	b->flags          = 0; //reserved
 
 	verbosestream << "register_biome: " << b->name << std::endl;
 	bmgr->addBiome(b);
@@ -349,6 +345,7 @@ int ModApiMapgen::l_register_decoration(lua_State *L)
 
 	EmergeManager *emerge = getServer(L)->getEmergeManager();
 	BiomeDefManager *bdef = emerge->biomedef;
+	NodeResolver *resolver = getServer(L)->getNodeDefManager()->getResolver();
 
 	enum DecorationType decotype = (DecorationType)getenumfield(L, index,
 				"deco_type", es_DecorationType, 0);
@@ -364,11 +361,9 @@ int ModApiMapgen::l_register_decoration(lua_State *L)
 			<< decotype << " not implemented";
 		return 0;
 	}
-
-	deco->c_place_on    = CONTENT_IGNORE;
-	deco->place_on_name = getstringfield_default(L, index, "place_on", "ignore");
-	deco->fill_ratio    = getfloatfield_default(L, index, "fill_ratio", 0.02);
-	deco->sidelen       = getintfield_default(L, index, "sidelen", 8);
+	
+	deco->fill_ratio = getfloatfield_default(L, index, "fill_ratio", 0.02);
+	deco->sidelen    = getintfield_default(L, index, "sidelen", 8);
 	if (deco->sidelen <= 0) {
 		errorstream << "register_decoration: sidelen must be "
 			"greater than 0" << std::endl;
@@ -376,51 +371,35 @@ int ModApiMapgen::l_register_decoration(lua_State *L)
 		return 0;
 	}
 
+	//// Get node name(s) to place decoration on
+	std::vector<const char *> place_on_names;
+	getstringlistfield(L, index, "place_on", place_on_names);
+	for (size_t i = 0; i != place_on_names.size(); i++)
+		resolver->addNodeList(place_on_names[i], &deco->c_place_on);
+
+	//// Get NoiseParams to define how decoration is placed
 	lua_getfield(L, index, "noise_params");
 	deco->np = read_noiseparams(L, -1);
 	lua_pop(L, 1);
 
-	lua_getfield(L, index, "biomes");
-	if (lua_istable(L, -1)) {
-		lua_pushnil(L);
-		while (lua_next(L, -2)) {
-			const char *s = lua_tostring(L, -1);
-			u8 biomeid = bdef->getBiomeIdByName(s);
-			if (biomeid)
-				deco->biomes.insert(biomeid);
-
-			lua_pop(L, 1);
-		}
-		lua_pop(L, 1);
+	//// Get biomes associated with this decoration (if any)
+	std::vector<const char *> biome_list;
+	getstringlistfield(L, index, "biomes", biome_list);
+	for (size_t i = 0; i != biome_list.size(); i++) {
+		u8 biomeid = bdef->getBiomeIdByName(biome_list[i]);
+		if (biomeid)
+			deco->biomes.insert(biomeid);
 	}
 
+	//// Handle decoration type-specific parameters
 	switch (decotype) {
 		case DECO_SIMPLE: {
 			DecoSimple *dsimple = (DecoSimple *)deco;
-			dsimple->c_deco     = CONTENT_IGNORE;
-			dsimple->c_spawnby  = CONTENT_IGNORE;
-			dsimple->spawnby_name    = getstringfield_default(L, index, "spawn_by", "air");
+
 			dsimple->deco_height     = getintfield_default(L, index, "height", 1);
 			dsimple->deco_height_max = getintfield_default(L, index, "height_max", 0);
 			dsimple->nspawnby        = getintfield_default(L, index, "num_spawn_by", -1);
 
-			lua_getfield(L, index, "decoration");
-			if (lua_istable(L, -1)) {
-				lua_pushnil(L);
-				while (lua_next(L, -2)) {
-					const char *s = lua_tostring(L, -1);
-					std::string str(s);
-					dsimple->decolist_names.push_back(str);
-
-					lua_pop(L, 1);
-				}
-			} else if (lua_isstring(L, -1)) {
-				dsimple->deco_name = std::string(lua_tostring(L, -1));
-			} else {
-				dsimple->deco_name = std::string("air");
-			}
-			lua_pop(L, 1);
-
 			if (dsimple->deco_height <= 0) {
 				errorstream << "register_decoration: simple decoration height"
 					" must be greater than 0" << std::endl;
@@ -428,7 +407,31 @@ int ModApiMapgen::l_register_decoration(lua_State *L)
 				return 0;
 			}
 
-			break; }
+			std::vector<const char *> deco_names;
+			getstringlistfield(L, index, "decoration", deco_names);
+			if (deco_names.size() == 0) {
+				errorstream << "register_decoration: no decoration nodes "
+					"defined" << std::endl;
+				delete dsimple;
+				return 0;
+			}
+
+			std::vector<const char *> spawnby_names;
+			getstringlistfield(L, index, "spawn_by", spawnby_names);
+			if (dsimple->nspawnby != -1 && spawnby_names.size() == 0) {
+				errorstream << "register_decoration: no spawn_by nodes defined,"
+					" but num_spawn_by specified" << std::endl;
+				delete dsimple;
+				return 0;
+			}
+
+			for (size_t i = 0; i != deco_names.size(); i++)
+				resolver->addNodeList(deco_names[i], &dsimple->c_decos);
+			for (size_t i = 0; i != spawnby_names.size(); i++)
+				resolver->addNodeList(spawnby_names[i], &dsimple->c_spawnby);
+
+			break;
+		}
 		case DECO_SCHEMATIC: {
 			DecoSchematic *dschem = (DecoSchematic *)deco;
 
@@ -439,10 +442,10 @@ int ModApiMapgen::l_register_decoration(lua_State *L)
 			dschem->rotation = (Rotation)getenumfield(L, index,
 				"rotation", es_Rotation, ROTATE_0);
 
+			std::map<std::string, std::string> replace_names;
 			lua_getfield(L, index, "replacements");
-			if (lua_istable(L, -1)) {
-				read_schematic_replacements(L, dschem, lua_gettop(L));
-			}
+			if (lua_istable(L, -1))
+				read_schematic_replacements(L, replace_names, lua_gettop(L));
 			lua_pop(L, 1);
 
 			lua_getfield(L, index, "schematic");
@@ -452,17 +455,20 @@ int ModApiMapgen::l_register_decoration(lua_State *L)
 			}
 			lua_pop(L, -1);
 
-			if (!dschem->filename.empty() && !dschem->loadSchematicFile()) {
-				errorstream << "register_decoration: failed to load schematic file '"
-					<< dschem->filename << "'" << std::endl;
+			if (!dschem->filename.empty() &&
+				!dschem->loadSchematicFile(resolver, replace_names)) {
+				errorstream << "register_decoration: failed to load schematic"
+					" file '" << dschem->filename << "'" << std::endl;
 				delete dschem;
 				return 0;
 			}
-			break; }
+
+			break;
+		}
 		case DECO_LSYSTEM: {
 			//DecoLSystem *decolsystem = (DecoLSystem *)deco;
-
-			break; }
+			break;
+		}
 	}
 
 	emerge->decorations.push_back(deco);
@@ -478,7 +484,8 @@ int ModApiMapgen::l_register_ore(lua_State *L)
 	int index = 1;
 	luaL_checktype(L, index, LUA_TTABLE);
 
-	EmergeManager *emerge = getServer(L)->getEmergeManager();
+	EmergeManager *emerge  = getServer(L)->getEmergeManager();
+	NodeResolver *resolver = getServer(L)->getNodeDefManager()->getResolver();
 
 	enum OreType oretype = (OreType)getenumfield(L, index,
 				"ore_type", es_OreType, ORE_SCATTER);
@@ -489,7 +496,9 @@ int ModApiMapgen::l_register_ore(lua_State *L)
 		return 0;
 	}
 
-	ore->ore_name       = getstringfield_default(L, index, "ore", "");
+	resolver->addNode(getstringfield_default(L, index, "ore", ""),
+		"", CONTENT_AIR, &ore->c_ore);
+
 	ore->ore_param2     = (u8)getintfield_default(L, index, "ore_param2", 0);
 	ore->clust_scarcity = getintfield_default(L, index, "clust_scarcity", 1);
 	ore->clust_num_ores = getintfield_default(L, index, "clust_num_ores", 1);
@@ -500,20 +509,10 @@ int ModApiMapgen::l_register_ore(lua_State *L)
 	ore->flags          = 0;
 	getflagsfield(L, index, "flags", flagdesc_ore, &ore->flags, NULL);
 
-	lua_getfield(L, index, "wherein");
-	if (lua_istable(L, -1)) {
-		int  i = lua_gettop(L);
-		lua_pushnil(L);
-		while(lua_next(L, i) != 0) {
-			ore->wherein_names.push_back(lua_tostring(L, -1));
-			lua_pop(L, 1);
-		}
-	} else if (lua_isstring(L, -1)) {
-		ore->wherein_names.push_back(lua_tostring(L, -1));
-	} else {
-		ore->wherein_names.push_back("");
-	}
-	lua_pop(L, 1);
+	std::vector<const char *> wherein_names;
+	getstringlistfield(L, index, "wherein", wherein_names);
+	for (size_t i = 0; i != wherein_names.size(); i++)
+		resolver->addNodeList(wherein_names[i], &ore->c_wherein);
 
 	lua_getfield(L, index, "noise_params");
 	ore->np = read_noiseparams(L, -1);
@@ -530,8 +529,8 @@ int ModApiMapgen::l_register_ore(lua_State *L)
 
 	emerge->ores.push_back(ore);
 
-	verbosestream << "register_ore: ore '" << ore->ore_name
-		<< "' registered" << std::endl;
+	//verbosestream << "register_ore: ore '" << ore->ore_name
+	//	<< "' registered" << std::endl;
 	return 0;
 }
 
@@ -603,7 +602,7 @@ int ModApiMapgen::l_place_schematic(lua_State *L)
 	DecoSchematic dschem;
 
 	Map *map = &(getEnv(L)->getMap());
-	INodeDefManager *ndef = getServer(L)->getNodeDefManager();
+	NodeResolver *resolver = getServer(L)->getNodeDefManager()->getResolver();
 
 	v3s16 p = read_v3s16(L, 1);
 	if (!read_schematic(L, 2, &dschem, getServer(L)))
@@ -615,21 +614,20 @@ int ModApiMapgen::l_place_schematic(lua_State *L)
 
 	dschem.rotation = (Rotation)rot;
 
-	if (lua_istable(L, 4)) {
-		read_schematic_replacements(L, &dschem, 4);
-	}
+	std::map<std::string, std::string> replace_names;
+	if (lua_istable(L, 4))
+		read_schematic_replacements(L, replace_names, 4);
 
 	bool force_placement = true;
 	if (lua_isboolean(L, 5))
 		force_placement = lua_toboolean(L, 5);
 
 	if (!dschem.filename.empty()) {
-		if (!dschem.loadSchematicFile()) {
+		if (!dschem.loadSchematicFile(resolver, replace_names)) {
 			errorstream << "place_schematic: failed to load schematic file '"
 				<< dschem.filename << "'" << std::endl;
 			return 0;
 		}
-		dschem.resolveNodeNames(ndef);
 	}
 
 	dschem.placeStructure(map, p, force_placement);
diff --git a/src/server.cpp b/src/server.cpp
index d4d9816ddc125ca0be984463787d637991a3511b..812ab6410a82fa53d36caa6b1ba626f1c39726e4 100644
--- a/src/server.cpp
+++ b/src/server.cpp
@@ -338,6 +338,9 @@ Server::Server(
 	// Apply item aliases in the node definition manager
 	m_nodedef->updateAliases(m_itemdef);
 
+	// Perform pending node name resolutions
+	m_nodedef->getResolver()->resolveNodes();
+
 	// Load the mapgen params from global settings now after any
 	// initial overrides have been set by the mods
 	m_emerge->loadMapgenParams();