aboutsummaryrefslogtreecommitdiff
path: root/src/convert_json.cpp
blob: e548c45f54ea61a697844b883f53e595832d281f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
/*
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 <vector>
#include <iostream>
#include <sstream>

#include "convert_json.h"
#include "mods.h"
#include "config.h"
#include "log.h"
#include "settings.h"
#include "httpfetch.h"
#include "porting.h"

Json::Value fetchJsonValue(const std::string &url,
		std::vector<std::string> *extra_headers)
{
	HTTPFetchRequest fetch_request;
	HTTPFetchResult fetch_result;
	fetch_request.url = url;
	fetch_request.caller = HTTPFETCH_SYNC;

	if (extra_headers != NULL)
		fetch_request.extra_headers = *extra_headers;

	httpfetch_sync(fetch_request, fetch_result);

	if (!fetch_result.succeeded) {
		return Json::Value();
	}
	Json::Value root;
	Json::Reader reader;
	std::istringstream stream(fetch_result.data);

	if (!reader.parse(stream, root)) {
		errorstream << "URL: " << url << std::endl;
		errorstream << "Failed to parse json data " << reader.getFormattedErrorMessages();
		if (fetch_result.data.size() > 100) {
			errorstream << "Data (" << fetch_result.data.size()
				<< " bytes) printed to warningstream." << std::endl;
			warningstream << "data: \"" << fetch_result.data << "\"" << std::endl;
		} else {
			errorstream << "data: \"" << fetch_result.data << "\"" << std::endl;
		}
		return Json::Value();
	}

	return root;
}

std::vector<ModStoreMod>    readModStoreList(Json::Value& modlist) {
		std::vector<ModStoreMod> retval;

	if (modlist.isArray()) {
		for (unsigned int i = 0; i < modlist.size(); i++)
		{
			ModStoreMod toadd;
			toadd.valid = true;

			//id
			if (modlist[i]["id"].asString().size()) {
				std::string id_raw = modlist[i]["id"].asString();
				char* endptr = 0;
				int numbervalue = strtol(id_raw.c_str(),&endptr,10);

				if ((id_raw != "") && (*endptr == 0)) {
					toadd.id = numbervalue;
				}
				else {
					errorstream << "readModStoreList: missing id" << std::endl;
					toadd.valid = false;
				}
			}
			else {
				errorstream << "readModStoreList: missing id" << std::endl;
				toadd.valid = false;
			}

			//title
			if (modlist[i]["title"].asString().size()) {
				toadd.title = modlist[i]["title"].asString();
			}
			else {
				errorstream << "readModStoreList: missing title" << std::endl;
				toadd.valid = false;
			}

			//basename
			if (modlist[i]["basename"].asString().size()) {
				toadd.basename = modlist[i]["basename"].asString();
			}
			else {
				errorstream << "readModStoreList: missing basename" << std::endl;
				toadd.valid = false;
			}

			//author

			//rating

			//version

			if (toadd.valid) {
				retval.push_back(toadd);
			}
		}
	}
	return retval;
}

ModStoreModDetails          readModStoreModDetails(Json::Value& details) {

	ModStoreModDetails retval;

	retval.valid = true;

	//version set
	if (details["version_set"].isArray()) {
		for (unsigned int i = 0; i < details["version_set"].size(); i++)
		{
			ModStoreVersionEntry toadd;

			if (details["version_set"][i]["id"].asString().size()) {
				std::string id_raw = details["version_set"][i]["id"].asString();
				char* endptr = 0;
				int numbervalue = strtol(id_raw.c_str(),&endptr,10);

				if ((id_raw != "") && (*endptr == 0)) {
					toadd.id = numbervalue;
				}
			}
			else {
				errorstream << "readModStoreModDetails: missing version_set id" << std::endl;
				retval.valid = false;
			}

			//date
			if (details["version_set"][i]["date"].asString().size()) {
				toadd.date = details["version_set"][i]["date"].asString();
			}

			//file
			if (details["version_set"][i]["file"].asString().size()) {
				toadd.file = details["version_set"][i]["file"].asString();
			}
			else {
				errorstream << "readModStoreModDetails: missing version_set file" << std::endl;
				retval.valid = false;
			}

			//approved

			//mtversion

			if( retval.valid ) {
				retval.versions.push_back(toadd);
			}
			else {
				break;
			}
		}
	}

	if (retval.versions.size() < 1) {
		infostream << "readModStoreModDetails: not a single version specified!" << std::endl;
		retval.valid = false;
	}

	//categories
	if (details["categories"].isObject()) {
		for (unsigned int i = 0; i < details["categories"].size(); i++) {
			ModStoreCategoryInfo toadd;

			if (details["categories"][i]["id"].asString().size()) {

				std::string id_raw = details["categories"][i]["id"].asString();
				char* endptr = 0;
				int numbervalue = strtol(id_raw.c_str(),&endptr,10);

				if ((id_raw != "") && (*endptr == 0)) {
					toadd.id = numbervalue;
				}
			}
			else {
				errorstream << "readModStoreModDetails: missing categories id" << std::endl;
				retval.valid = false;
			}
			if (details["categories"][i]["title"].asString().size()) {
				toadd.name = details["categories"][i]["title"].asString();
			}
			else {
				errorstream << "readModStoreModDetails: missing categories title" << std::endl;
				retval.valid = false;
			}

			if( retval.valid ) {
				retval.categories.push_back(toadd);
			}
			else {
				break;
			}
		}
	}

	//author
	if (details["author"].isObject()) {
		if (details["author"]["id"].asString().size()) {

			std::string id_raw = details["author"]["id"].asString();
			char* endptr = 0;
			int numbervalue = strtol(id_raw.c_str(),&endptr,10);

			if ((id_raw != "") && (*endptr == 0)) {
				retval.author.id = numbervalue;
			}
			else {
				errorstream << "readModStoreModDetails: missing author id (convert)" << std::endl;
				retval.valid = false;
			}
		}
		else {
			errorstream << "readModStoreModDetails: missing author id" << std::endl;
			retval.valid = false;
		}

		if (details["author"]["username"].asString().size()) {
			retval.author.username = details["author"]["username"].asString();
		}
		else {
			errorstream << "readModStoreModDetails: missing author username" << std::endl;
			retval.valid = false;
		}
	}
	else {
		errorstream << "readModStoreModDetails: missing author" << std::endl;
		retval.valid = false;
	}

	//license
	if (details["license"].isObject()) {
		if (details["license"]["id"].asString().size()) {

			std::string id_raw = details["license"]["id"].asString();
			char* endptr = 0;
			int numbervalue = strtol(id_raw.c_str(),&endptr,10);

			if ((id_raw != "") && (*endptr == 0)) {
				retval.license.id = numbervalue;
			}
		}
		else {
			errorstream << "readModStoreModDetails: missing license id" << std::endl;
			retval.valid = false;
		}

		if (details["license"]["short"].asString().size()) {
			retval.license.shortinfo = details["license"]["short"].asString();
		}
		else {
			errorstream << "readModStoreModDetails: missing license short" << std::endl;
			retval.valid = false;
		}

		if (details["license"]["link"].asString().size()) {
			retval.license.url = details["license"]["link"].asString();
		}

	}

	//titlepic
	if (details["titlepic"].isObject()) {
		if (details["titlepic"]["id"].asString().size()) {

			std::string id_raw = details["titlepic"]["id"].asString();
			char* endptr = 0;
			int numbervalue = strtol(id_raw.c_str(),&endptr,10);

			if ((id_raw != "") && (*endptr == 0)) {
				retval.titlepic.id = numbervalue;
			}
		}

		if (details["titlepic"]["file"].asString().size()) {
			retval.titlepic.file = details["titlepic"]["file"].asString();
		}

		if (details["titlepic"]["desc"].asString().size()) {
			retval.titlepic.description = details["titlepic"]["desc"].asString();
		}

		if (details["titlepic"]["mod"].asString().size()) {

			std::string mod_raw = details["titlepic"]["mod"].asString();
			char* endptr = 0;
			int numbervalue = strtol(mod_raw.c_str(),&endptr,10);

			if ((mod_raw != "") && (*endptr == 0)) {
				retval.titlepic.mod = numbervalue;
			}
		}
	}

	//id
	if (details["id"].asString().size()) {

		std::string id_raw = details["id"].asString();
		char* endptr = 0;
		int numbervalue = strtol(id_raw.c_str(),&endptr,10);

		if ((id_raw != "") && (*endptr == 0)) {
			retval.id = numbervalue;
		}
	}
	else {
		errorstream << "readModStoreModDetails: missing id" << std::endl;
		retval.valid = false;
	}

	//title
	if (details["title"].asString().size()) {
		retval.title = details["title"].asString();
	}
	else {
		errorstream << "readModStoreModDetails: missing title" << std::endl;
		retval.valid = false;
	}

	//basename
	if (details["basename"].asString().size()) {
		retval.basename = details["basename"].asString();
	}
	else {
		errorstream << "readModStoreModDetails: missing basename" << std::endl;
		retval.valid = false;
	}

	//description
	if (details["desc"].asString().size()) {
		retval.description = details["desc"].asString();
	}

	//repository
	if (details["replink"].asString().size()) {
		retval.repository = details["replink"].asString();
	}

	//value
	if (details["value"].isInt()) {
		retval.rating = details["value"].asInt();
	} else {
		retval.rating = 0;
	}

	//depends
	if (details["depends"].isArray()) {
		//TODO
	}

	//softdepends
	if (details["softdep"].isArray()) {
		//TODO
	}

	//screenshot url
	if (details["screenshot_url"].asString().size()) {
		retval.screenshot_url = details["screenshot_url"].asString();
	}

	return retval;
}
1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808
/*
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_mapgen.h"
#include "lua_api/l_internal.h"
#include "lua_api/l_vmanip.h"
#include "common/c_converter.h"
#include "common/c_content.h"
#include "cpp_api/s_security.h"
#include "util/serialize.h"
#include "server.h"
#include "environment.h"
#include "emerge.h"
#include "mapgen/mg_biome.h"
#include "mapgen/mg_ore.h"
#include "mapgen/mg_decoration.h"
#include "mapgen/mg_schematic.h"
#include "mapgen/mapgen_v5.h"
#include "mapgen/mapgen_v7.h"
#include "filesys.h"
#include "settings.h"
#include "log.h"

struct EnumString ModApiMapgen::es_BiomeTerrainType[] =
{
	{BIOMETYPE_NORMAL, "normal"},
	{0, NULL},
};

struct EnumString ModApiMapgen::es_DecorationType[] =
{
	{DECO_SIMPLE,    "simple"},
	{DECO_SCHEMATIC, "schematic"},
	{DECO_LSYSTEM,   "lsystem"},
	{0, NULL},
};

struct EnumString ModApiMapgen::es_MapgenObject[] =
{
	{MGOBJ_VMANIP,    "voxelmanip"},
	{MGOBJ_HEIGHTMAP, "heightmap"},
	{MGOBJ_BIOMEMAP,  "biomemap"},
	{MGOBJ_HEATMAP,   "heatmap"},
	{MGOBJ_HUMIDMAP,  "humiditymap"},
	{MGOBJ_GENNOTIFY, "gennotify"},
	{0, NULL},
};

struct EnumString ModApiMapgen::es_OreType[] =
{
	{ORE_SCATTER, "scatter"},
	{ORE_SHEET,   "sheet"},
	{ORE_PUFF,    "puff"},
	{ORE_BLOB,    "blob"},
	{ORE_VEIN,    "vein"},
	{ORE_STRATUM, "stratum"},
	{0, NULL},
};

struct EnumString ModApiMapgen::es_Rotation[] =
{
	{ROTATE_0,    "0"},
	{ROTATE_90,   "90"},
	{ROTATE_180,  "180"},
	{ROTATE_270,  "270"},
	{ROTATE_RAND, "random"},
	{0, NULL},
};

struct EnumString ModApiMapgen::es_SchematicFormatType[] =
{
	{SCHEM_FMT_HANDLE, "handle"},
	{SCHEM_FMT_MTS,    "mts"},
	{SCHEM_FMT_LUA,    "lua"},
	{0, NULL},
};

ObjDef *get_objdef(lua_State *L, int index, const ObjDefManager *objmgr);

Biome *get_or_load_biome(lua_State *L, int index,
	BiomeManager *biomemgr);
Biome *read_biome_def(lua_State *L, int index, const NodeDefManager *ndef);
size_t get_biome_list(lua_State *L, int index,
	BiomeManager *biomemgr, std::unordered_set<biome_t> *biome_id_list);

Schematic *get_or_load_schematic(lua_State *L, int index,
	SchematicManager *schemmgr, StringMap *replace_names);
Schematic *load_schematic(lua_State *L, int index, const NodeDefManager *ndef,
	StringMap *replace_names);
Schematic *load_schematic_from_def(lua_State *L, int index,
	const NodeDefManager *ndef, StringMap *replace_names);
bool read_schematic_def(lua_State *L, int index,
	Schematic *schem, std::vector<std::string> *names);

bool read_deco_simple(lua_State *L, DecoSimple *deco);
bool read_deco_schematic(lua_State *L, SchematicManager *schemmgr, DecoSchematic *deco);


///////////////////////////////////////////////////////////////////////////////

ObjDef *get_objdef(lua_State *L, int index, const ObjDefManager *objmgr)
{
	if (index < 0)
		index = lua_gettop(L) + 1 + index;

	// If a number, assume this is a handle to an object def
	if (lua_isnumber(L, index))
		return objmgr->get(lua_tointeger(L, index));

	// If a string, assume a name is given instead
	if (lua_isstring(L, index))
		return objmgr->getByName(lua_tostring(L, index));

	return NULL;
}

///////////////////////////////////////////////////////////////////////////////

Schematic *get_or_load_schematic(lua_State *L, int index,
	SchematicManager *schemmgr, StringMap *replace_names)
{
	if (index < 0)
		index = lua_gettop(L) + 1 + index;

	Schematic *schem = (Schematic *)get_objdef(L, index, schemmgr);
	if (schem)
		return schem;

	schem = load_schematic(L, index, schemmgr->getNodeDef(),
		replace_names);
	if (!schem)
		return NULL;

	if (schemmgr->add(schem) == OBJDEF_INVALID_HANDLE) {
		delete schem;
		return NULL;
	}

	return schem;
}


Schematic *load_schematic(lua_State *L, int index, const NodeDefManager *ndef,
	StringMap *replace_names)
{
	if (index < 0)
		index = lua_gettop(L) + 1 + index;

	Schematic *schem = NULL;

	if (lua_istable(L, index)) {
		schem = load_schematic_from_def(L, index, ndef,
			replace_names);
		if (!schem) {
			delete schem;
			return NULL;
		}
	} else if (lua_isnumber(L, index)) {
		return NULL;
	} else if (lua_isstring(L, index)) {
		schem = SchematicManager::create(SCHEMATIC_NORMAL);

		std::string filepath = lua_tostring(L, index);
		if (!fs::IsPathAbsolute(filepath))
			filepath = ModApiBase::getCurrentModPath(L) + DIR_DELIM + filepath;

		if (!schem->loadSchematicFromFile(filepath, ndef,
				replace_names)) {
			delete schem;
			return NULL;
		}
	}

	return schem;
}


Schematic *load_schematic_from_def(lua_State *L, int index,
	const NodeDefManager *ndef, StringMap *replace_names)
{
	Schematic *schem = SchematicManager::create(SCHEMATIC_NORMAL);

	if (!read_schematic_def(L, index, schem, &schem->m_nodenames)) {
		delete schem;
		return NULL;
	}

	size_t num_nodes = schem->m_nodenames.size();

	schem->m_nnlistsizes.push_back(num_nodes);

	if (replace_names) {
		for (size_t i = 0; i != num_nodes; i++) {
			StringMap::iterator it = replace_names->find(schem->m_nodenames[i]);
			if (it != replace_names->end())
				schem->m_nodenames[i] = it->second;
		}
	}

	if (ndef)
		ndef->pendNodeResolve(schem);

	return schem;
}


bool read_schematic_def(lua_State *L, int index,
	Schematic *schem, std::vector<std::string> *names)
{
	if (!lua_istable(L, index))
		return false;

	//// Get schematic size
	lua_getfield(L, index, "size");
	v3s16 size = check_v3s16(L, -1);
	lua_pop(L, 1);

	schem->size = size;

	//// Get schematic data
	lua_getfield(L, index, "data");
	luaL_checktype(L, -1, LUA_TTABLE);

	u32 numnodes = size.X * size.Y * size.Z;
	schem->schemdata = new MapNode[numnodes];

	size_t names_base = names->size();
	std::unordered_map<std::string, content_t> name_id_map;

	u32 i = 0;
	for (lua_pushnil(L); lua_next(L, -2); i++, lua_pop(L, 1)) {
		if (i >= numnodes)
			continue;

		//// Read name
		std::string name;
		if (!getstringfield(L, -1, "name", name))
			throw LuaError("Schematic data definition with missing name field");

		//// Read param1/prob
		u8 param1;
		if (!getintfield(L, -1, "param1", param1) &&
			!getintfield(L, -1, "prob", param1))
			param1 = MTSCHEM_PROB_ALWAYS_OLD;

		//// Read param2
		u8 param2 = getintfield_default(L, -1, "param2", 0);

		//// Find or add new nodename-to-ID mapping
		std::unordered_map<std::string, content_t>::iterator it = name_id_map.find(name);
		content_t name_index;
		if (it != name_id_map.end()) {
			name_index = it->second;
		} else {
			name_index = names->size() - names_base;
			name_id_map[name] = name_index;
			names->push_back(name);
		}

		//// Perform probability/force_place fixup on param1
		param1 >>= 1;
		if (getboolfield_default(L, -1, "force_place", false))
			param1 |= MTSCHEM_FORCE_PLACE;

		//// Actually set the node in the schematic
		schem->schemdata[i] = MapNode(name_index, param1, param2);
	}

	if (i != numnodes) {
		errorstream << "read_schematic_def: incorrect number of "
			"nodes provided in raw schematic data (got " << i <<
			", expected " << numnodes << ")." << std::endl;
		return false;
	}

	//// Get Y-slice probability values (if present)
	schem->slice_probs = new u8[size.Y];
	for (i = 0; i != (u32) size.Y; i++)
		schem->slice_probs[i] = MTSCHEM_PROB_ALWAYS;

	lua_getfield(L, index, "yslice_prob");
	if (lua_istable(L, -1)) {
		for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1)) {
			u16 ypos;
			if (!getintfield(L, -1, "ypos", ypos) || (ypos >= size.Y) ||
				!getintfield(L, -1, "prob", schem->slice_probs[ypos]))
				continue;

			schem->slice_probs[ypos] >>= 1;
		}
	}

	return true;
}


void read_schematic_replacements(lua_State *L, int index, StringMap *replace_names)
{
	if (index < 0)
		index = lua_gettop(L) + 1 + index;

	lua_pushnil(L);
	while (lua_next(L, index)) {
		std::string replace_from;
		std::string replace_to;

		if (lua_istable(L, -1)) { // Old {{"x", "y"}, ...} format
			lua_rawgeti(L, -1, 1);
			if (!lua_isstring(L, -1))
				throw LuaError("schematics: replace_from field is not a string");
			replace_from = lua_tostring(L, -1);
			lua_pop(L, 1);

			lua_rawgeti(L, -1, 2);
			if (!lua_isstring(L, -1))
				throw LuaError("schematics: replace_to field is not a string");
			replace_to = lua_tostring(L, -1);
			lua_pop(L, 1);
		} else { // New {x = "y", ...} format
			if (!lua_isstring(L, -2))
				throw LuaError("schematics: replace_from field is not a string");
			replace_from = lua_tostring(L, -2);
			if (!lua_isstring(L, -1))
				throw LuaError("schematics: replace_to field is not a string");
			replace_to = lua_tostring(L, -1);
		}

		replace_names->insert(std::make_pair(replace_from, replace_to));
		lua_pop(L, 1);
	}
}

///////////////////////////////////////////////////////////////////////////////

Biome *get_or_load_biome(lua_State *L, int index, BiomeManager *biomemgr)
{
	if (index < 0)
		index = lua_gettop(L) + 1 + index;

	Biome *biome = (Biome *)get_objdef(L, index, biomemgr);
	if (biome)
		return biome;

	biome = read_biome_def(L, index, biomemgr->getNodeDef());
	if (!biome)
		return NULL;

	if (biomemgr->add(biome) == OBJDEF_INVALID_HANDLE) {
		delete biome;
		return NULL;
	}

	return biome;
}


Biome *read_biome_def(lua_State *L, int index, const NodeDefManager *ndef)
{
	if (!lua_istable(L, index))
		return NULL;

	BiomeType biometype = (BiomeType)getenumfield(L, index, "type",
		ModApiMapgen::es_BiomeTerrainType, BIOMETYPE_NORMAL);
	Biome *b = BiomeManager::create(biometype);

	b->name            = getstringfield_default(L, index, "name", "");
	b->depth_top       = getintfield_default(L,    index, "depth_top",       0);
	b->depth_filler    = getintfield_default(L,    index, "depth_filler",    -31000);
	b->depth_water_top = getintfield_default(L,    index, "depth_water_top", 0);
	b->depth_riverbed  = getintfield_default(L,    index, "depth_riverbed",  0);
	b->heat_point      = getfloatfield_default(L,  index, "heat_point",      0.f);
	b->humidity_point  = getfloatfield_default(L,  index, "humidity_point",  0.f);
	b->vertical_blend  = getintfield_default(L,    index, "vertical_blend",  0);
	b->flags           = 0; // reserved

	b->min_pos = getv3s16field_default(
		L, index, "min_pos", v3s16(-31000, -31000, -31000));
	getintfield(L, index, "y_min", b->min_pos.Y);
	b->max_pos = getv3s16field_default(
		L, index, "max_pos", v3s16(31000, 31000, 31000));
	getintfield(L, index, "y_max", b->max_pos.Y);

	std::vector<std::string> &nn = b->m_nodenames;
	nn.push_back(getstringfield_default(L, index, "node_top",           ""));
	nn.push_back(getstringfield_default(L, index, "node_filler",        ""));
	nn.push_back(getstringfield_default(L, index, "node_stone",         ""));
	nn.push_back(getstringfield_default(L, index, "node_water_top",     ""));
	nn.push_back(getstringfield_default(L, index, "node_water",         ""));
	nn.push_back(getstringfield_default(L, index, "node_river_water",   ""));
	nn.push_back(getstringfield_default(L, index, "node_riverbed",      ""));
	nn.push_back(getstringfield_default(L, index, "node_dust",          ""));

	size_t nnames = getstringlistfield(L, index, "node_cave_liquid", &nn);
	// If no cave liquids defined, set list to "ignore" to trigger old hardcoded
	// cave liquid behaviour.
	if (nnames == 0) {
		nn.emplace_back("ignore");
		nnames = 1;
	}
	b->m_nnlistsizes.push_back(nnames);

	nn.push_back(getstringfield_default(L, index, "node_dungeon",       ""));
	nn.push_back(getstringfield_default(L, index, "node_dungeon_alt",   ""));
	nn.push_back(getstringfield_default(L, index, "node_dungeon_stair", ""));
	ndef->pendNodeResolve(b);

	return b;
}


size_t get_biome_list(lua_State *L, int index,
	BiomeManager *biomemgr, std::unordered_set<biome_t> *biome_id_list)
{
	if (index < 0)
		index = lua_gettop(L) + 1 + index;

	if (lua_isnil(L, index))
		return 0;

	bool is_single = true;
	if (lua_istable(L, index)) {
		lua_getfield(L, index, "name");
		is_single = !lua_isnil(L, -1);
		lua_pop(L, 1);
	}

	if (is_single) {
		Biome *biome = get_or_load_biome(L, index, biomemgr);
		if (!biome) {
			infostream << "get_biome_list: failed to get biome '"
				<< (lua_isstring(L, index) ? lua_tostring(L, index) : "")
				<< "'." << std::endl;
			return 1;
		}

		biome_id_list->insert(biome->index);
		return 0;
	}

	// returns number of failed resolutions
	size_t fail_count = 0;
	size_t count = 0;

	for (lua_pushnil(L); lua_next(L, index); lua_pop(L, 1)) {
		count++;
		Biome *biome = get_or_load_biome(L, -1, biomemgr);
		if (!biome) {
			fail_count++;
			infostream << "get_biome_list: failed to get biome '"
				<< (lua_isstring(L, -1) ? lua_tostring(L, -1) : "")
				<< "'" << std::endl;
			continue;
		}

		biome_id_list->insert(biome->index);
	}

	return fail_count;
}

///////////////////////////////////////////////////////////////////////////////

// get_biome_id(biomename)
// returns the biome id as used in biomemap and returned by 'get_biome_data()'
int ModApiMapgen::l_get_biome_id(lua_State *L)
{
	NO_MAP_LOCK_REQUIRED;

	const char *biome_str = luaL_checkstring(L, 1);

	const BiomeManager *bmgr = getServer(L)->getEmergeManager()->getBiomeManager();
	if (!bmgr)
		return 0;

	const Biome *biome = (Biome *)bmgr->getByName(biome_str);
	if (!biome || biome->index == OBJDEF_INVALID_INDEX)
		return 0;

	lua_pushinteger(L, biome->index);

	return 1;
}


// get_biome_name(biome_id)
// returns the biome name string
int ModApiMapgen::l_get_biome_name(lua_State *L)
{
	NO_MAP_LOCK_REQUIRED;

	int biome_id = luaL_checkinteger(L, 1);

	const BiomeManager *bmgr = getServer(L)->getEmergeManager()->getBiomeManager();
	if (!bmgr)
		return 0;

	const Biome *b = (Biome *)bmgr->getRaw(biome_id);
	lua_pushstring(L, b->name.c_str());

	return 1;
}


// get_heat(pos)
// returns the heat at the position
int ModApiMapgen::l_get_heat(lua_State *L)
{
	NO_MAP_LOCK_REQUIRED;

	v3s16 pos = read_v3s16(L, 1);

	const BiomeGen *biomegen = getServer(L)->getEmergeManager()->getBiomeGen();

	if (!biomegen || biomegen->getType() != BIOMEGEN_ORIGINAL)
		return 0;

	float heat = ((BiomeGenOriginal*) biomegen)->calcHeatAtPoint(pos);

	lua_pushnumber(L, heat);

	return 1;
}


// get_humidity(pos)
// returns the humidity at the position
int ModApiMapgen::l_get_humidity(lua_State *L)
{
	NO_MAP_LOCK_REQUIRED;

	v3s16 pos = read_v3s16(L, 1);

	const BiomeGen *biomegen = getServer(L)->getEmergeManager()->getBiomeGen();

	if (!biomegen || biomegen->getType() != BIOMEGEN_ORIGINAL)
		return 0;

	float humidity = ((BiomeGenOriginal*) biomegen)->calcHumidityAtPoint(pos);

	lua_pushnumber(L, humidity);

	return 1;
}


// get_biome_data(pos)
// returns a table containing the biome id, heat and humidity at the position
int ModApiMapgen::l_get_biome_data(lua_State *L)
{
	NO_MAP_LOCK_REQUIRED;

	v3s16 pos = read_v3s16(L, 1);

	const BiomeGen *biomegen = getServer(L)->getEmergeManager()->getBiomeGen();
	if (!biomegen)
		return 0;

	const Biome *biome = biomegen->calcBiomeAtPoint(pos);
	if (!biome || biome->index == OBJDEF_INVALID_INDEX)
		return 0;

	lua_newtable(L);

	lua_pushinteger(L, biome->index);
	lua_setfield(L, -2, "biome");

	if (biomegen->getType() == BIOMEGEN_ORIGINAL) {
		float heat = ((BiomeGenOriginal*) biomegen)->calcHeatAtPoint(pos);
		float humidity = ((BiomeGenOriginal*) biomegen)->calcHumidityAtPoint(pos);

		lua_pushnumber(L, heat);
		lua_setfield(L, -2, "heat");

		lua_pushnumber(L, humidity);
		lua_setfield(L, -2, "humidity");
	}

	return 1;
}


// get_mapgen_object(objectname)
// returns the requested object used during map generation
int ModApiMapgen::l_get_mapgen_object(lua_State *L)
{
	NO_MAP_LOCK_REQUIRED;

	const char *mgobjstr = lua_tostring(L, 1);

	int mgobjint;
	if (!string_to_enum(es_MapgenObject, mgobjint, mgobjstr ? mgobjstr : ""))
		return 0;

	enum MapgenObject mgobj = (MapgenObject)mgobjint;

	EmergeManager *emerge = getServer(L)->getEmergeManager();
	Mapgen *mg = emerge->getCurrentMapgen();
	if (!mg)
		throw LuaError("Must only be called in a mapgen thread!");

	size_t maplen = mg->csize.X * mg->csize.Z;

	switch (mgobj) {
	case MGOBJ_VMANIP: {
		MMVManip *vm = mg->vm;

		// VoxelManip object
		LuaVoxelManip *o = new LuaVoxelManip(vm, true);
		*(void **)(lua_newuserdata(L, sizeof(void *))) = o;
		luaL_getmetatable(L, "VoxelManip");
		lua_setmetatable(L, -2);

		// emerged min pos
		push_v3s16(L, vm->m_area.MinEdge);

		// emerged max pos
		push_v3s16(L, vm->m_area.MaxEdge);

		return 3;
	}
	case MGOBJ_HEIGHTMAP: {
		if (!mg->heightmap)
			return 0;

		lua_createtable(L, maplen, 0);
		for (size_t i = 0; i != maplen; i++) {
			lua_pushinteger(L, mg->heightmap[i]);
			lua_rawseti(L, -2, i + 1);
		}

		return 1;
	}
	case MGOBJ_BIOMEMAP: {
		if (!mg->biomegen)
			return 0;

		lua_createtable(L, maplen, 0);
		for (size_t i = 0; i != maplen; i++) {
			lua_pushinteger(L, mg->biomegen->biomemap[i]);
			lua_rawseti(L, -2, i + 1);
		}

		return 1;
	}
	case MGOBJ_HEATMAP: {
		if (!mg->biomegen || mg->biomegen->getType() != BIOMEGEN_ORIGINAL)
			return 0;

		BiomeGenOriginal *bg = (BiomeGenOriginal *)mg->biomegen;

		lua_createtable(L, maplen, 0);
		for (size_t i = 0; i != maplen; i++) {
			lua_pushnumber(L, bg->heatmap[i]);
			lua_rawseti(L, -2, i + 1);
		}

		return 1;
	}

	case MGOBJ_HUMIDMAP: {
		if (!mg->biomegen || mg->biomegen->getType() != BIOMEGEN_ORIGINAL)
			return 0;

		BiomeGenOriginal *bg = (BiomeGenOriginal *)mg->biomegen;

		lua_createtable(L, maplen, 0);
		for (size_t i = 0; i != maplen; i++) {
			lua_pushnumber(L, bg->humidmap[i]);
			lua_rawseti(L, -2, i + 1);
		}

		return 1;
	}
	case MGOBJ_GENNOTIFY: {
		std::map<std::string, std::vector<v3s16> >event_map;

		mg->gennotify.getEvents(event_map);

		lua_createtable(L, 0, event_map.size());
		for (auto it = event_map.begin(); it != event_map.end(); ++it) {
			lua_createtable(L, it->second.size(), 0);

			for (size_t j = 0; j != it->second.size(); j++) {
				push_v3s16(L, it->second[j]);
				lua_rawseti(L, -2, j + 1);
			}

			lua_setfield(L, -2, it->first.c_str());
		}

		return 1;
	}
	}

	return 0;
}


// get_spawn_level(x = num, z = num)
int ModApiMapgen::l_get_spawn_level(lua_State *L)
{
	NO_MAP_LOCK_REQUIRED;

	s16 x = luaL_checkinteger(L, 1);
	s16 z = luaL_checkinteger(L, 2);

	EmergeManager *emerge = getServer(L)->getEmergeManager();
	int spawn_level = emerge->getSpawnLevelAtPoint(v2s16(x, z));
	// Unsuitable spawn point
	if (spawn_level == MAX_MAP_GENERATION_LIMIT)
		return 0;

	// 'findSpawnPos()' in server.cpp adds at least 1
	lua_pushinteger(L, spawn_level + 1);

	return 1;
}


int ModApiMapgen::l_get_mapgen_params(lua_State *L)
{
	NO_MAP_LOCK_REQUIRED;

	log_deprecated(L, "get_mapgen_params is deprecated; "
		"use get_mapgen_setting instead");

	std::string value;

	MapSettingsManager *settingsmgr =
		getServer(L)->getEmergeManager()->map_settings_mgr;

	lua_newtable(L);

	settingsmgr->getMapSetting("mg_name", &value);
	lua_pushstring(L, value.c_str());
	lua_setfield(L, -2, "mgname");

	settingsmgr->getMapSetting("seed", &value);
	u64 seed = from_string<u64>(value);
	lua_pushinteger(L, seed);
	lua_setfield(L, -2, "seed");

	settingsmgr->getMapSetting("water_level", &value);
	lua_pushinteger(L, stoi(value, -32768, 32767));
	lua_setfield(L, -2, "water_level");

	settingsmgr->getMapSetting("chunksize", &value);
	lua_pushinteger(L, stoi(value, -32768, 32767));
	lua_setfield(L, -2, "chunksize");

	settingsmgr->getMapSetting("mg_flags", &value);
	lua_pushstring(L, value.c_str());
	lua_setfield(L, -2, "flags");

	return 1;
}


// set_mapgen_params(params)
// set mapgen parameters
int ModApiMapgen::l_set_mapgen_params(lua_State *L)
{
	NO_MAP_LOCK_REQUIRED;

	log_deprecated(L, "set_mapgen_params is deprecated; "
		"use set_mapgen_setting instead");

	if (!lua_istable(L, 1))
		return 0;

	MapSettingsManager *settingsmgr =
		getServer(L)->getEmergeManager()->map_settings_mgr;

	lua_getfield(L, 1, "mgname");
	if (lua_isstring(L, -1))
		settingsmgr->setMapSetting("mg_name", readParam<std::string>(L, -1), true);

	lua_getfield(L, 1, "seed");
	if (lua_isnumber(L, -1))
		settingsmgr->setMapSetting("seed", readParam<std::string>(L, -1), true);

	lua_getfield(L, 1, "water_level");
	if (lua_isnumber(L, -1))
		settingsmgr->setMapSetting("water_level", readParam<std::string>(L, -1), true);

	lua_getfield(L, 1, "chunksize");
	if (lua_isnumber(L, -1))
		settingsmgr->setMapSetting("chunksize", readParam<std::string>(L, -1), true);

	lua_getfield(L, 1, "flags");
	if (lua_isstring(L, -1))
		settingsmgr->setMapSetting("mg_flags", readParam<std::string>(L, -1), true);

	return 0;
}

// get_mapgen_setting(name)
int ModApiMapgen::l_get_mapgen_setting(lua_State *L)
{
	NO_MAP_LOCK_REQUIRED;

	std::string value;
	MapSettingsManager *settingsmgr =
		getServer(L)->getEmergeManager()->map_settings_mgr;

	const char *name = luaL_checkstring(L, 1);
	if (!settingsmgr->getMapSetting(name, &value))
		return 0;

	lua_pushstring(L, value.c_str());
	return 1;
}

// get_mapgen_setting_noiseparams(name)
int ModApiMapgen::l_get_mapgen_setting_noiseparams(lua_State *L)
{
	NO_MAP_LOCK_REQUIRED;

	NoiseParams np;
	MapSettingsManager *settingsmgr =
		getServer(L)->getEmergeManager()->map_settings_mgr;

	const char *name = luaL_checkstring(L, 1);
	if (!settingsmgr->getMapSettingNoiseParams(name, &np))
		return 0;

	push_noiseparams(L, &np);
	return 1;
}

// set_mapgen_setting(name, value, override_meta)
// set mapgen config values
int ModApiMapgen::l_set_mapgen_setting(lua_State *L)
{
	NO_MAP_LOCK_REQUIRED;

	MapSettingsManager *settingsmgr =
		getServer(L)->getEmergeManager()->map_settings_mgr;

	const char *name   = luaL_checkstring(L, 1);
	const char *value  = luaL_checkstring(L, 2);
	bool override_meta = readParam<bool>(L, 3, false);

	if (!settingsmgr->setMapSetting(name, value, override_meta)) {
		errorstream << "set_mapgen_setting: cannot set '"
			<< name << "' after initialization" << std::endl;
	}

	return 0;
}


// set_mapgen_setting_noiseparams(name, noiseparams, set_default)
// set mapgen config values for noise parameters
int ModApiMapgen::l_set_mapgen_setting_noiseparams(lua_State *L)
{
	NO_MAP_LOCK_REQUIRED;

	MapSettingsManager *settingsmgr =
		getServer(L)->getEmergeManager()->map_settings_mgr;

	const char *name = luaL_checkstring(L, 1);

	NoiseParams np;
	if (!read_noiseparams(L, 2, &np)) {
		errorstream << "set_mapgen_setting_noiseparams: cannot set '" << name
			<< "'; invalid noiseparams table" << std::endl;
		return 0;
	}

	bool override_meta = readParam<bool>(L, 3, false);

	if (!settingsmgr->setMapSettingNoiseParams(name, &np, override_meta)) {
		errorstream << "set_mapgen_setting_noiseparams: cannot set '"
			<< name << "' after initialization" << std::endl;
	}

	return 0;
}


// set_noiseparams(name, noiseparams, set_default)
// set global config values for noise parameters
int ModApiMapgen::l_set_noiseparams(lua_State *L)
{
	NO_MAP_LOCK_REQUIRED;

	const char *name = luaL_checkstring(L, 1);

	NoiseParams np;
	if (!read_noiseparams(L, 2, &np)) {
		errorstream << "set_noiseparams: cannot set '" << name
			<< "'; invalid noiseparams table" << std::endl;
		return 0;
	}

	bool set_default = !lua_isboolean(L, 3) || readParam<bool>(L, 3);

	Settings::getLayer(set_default ? SL_DEFAULTS : SL_GLOBAL)->setNoiseParams(name, np);

	return 0;
}


// get_noiseparams(name)
int ModApiMapgen::l_get_noiseparams(lua_State *L)
{
	NO_MAP_LOCK_REQUIRED;

	std::string name = luaL_checkstring(L, 1);

	NoiseParams np;
	if (!g_settings->getNoiseParams(name, np))
		return 0;

	push_noiseparams(L, &np);
	return 1;
}


// set_gen_notify(flags, {deco_id_table})
int ModApiMapgen::l_set_gen_notify(lua_State *L)
{
	NO_MAP_LOCK_REQUIRED;

	u32 flags = 0, flagmask = 0;
	EmergeManager *emerge = getServer(L)->getEmergeManager();

	if (read_flags(L, 1, flagdesc_gennotify, &flags, &flagmask)) {
		emerge->gen_notify_on &= ~flagmask;
		emerge->gen_notify_on |= flags;
	}

	if (lua_istable(L, 2)) {
		lua_pushnil(L);
		while (lua_next(L, 2)) {
			if (lua_isnumber(L, -1))
				emerge->gen_notify_on_deco_ids.insert((u32)lua_tonumber(L, -1));
			lua_pop(L, 1);
		}
	}

	return 0;
}


// get_gen_notify()
int ModApiMapgen::l_get_gen_notify(lua_State *L)
{
	NO_MAP_LOCK_REQUIRED;

	EmergeManager *emerge = getServer(L)->getEmergeManager();
	push_flags_string(L, flagdesc_gennotify, emerge->gen_notify_on,
		emerge->gen_notify_on);

	lua_newtable(L);
	int i = 1;
	for (u32 gen_notify_on_deco_id : emerge->gen_notify_on_deco_ids) {
		lua_pushnumber(L, gen_notify_on_deco_id);
		lua_rawseti(L, -2, i++);
	}
	return 2;
}


// get_decoration_id(decoration_name)
// returns the decoration ID as used in gennotify
int ModApiMapgen::l_get_decoration_id(lua_State *L)
{
	NO_MAP_LOCK_REQUIRED;

	const char *deco_str = luaL_checkstring(L, 1);
	if (!deco_str)
		return 0;

	const DecorationManager *dmgr =
		getServer(L)->getEmergeManager()->getDecorationManager();

	if (!dmgr)
		return 0;

	Decoration *deco = (Decoration *)dmgr->getByName(deco_str);

	if (!deco)
		return 0;

	lua_pushinteger(L, deco->index);

	return 1;
}


// register_biome({lots of stuff})
int ModApiMapgen::l_register_biome(lua_State *L)
{
	NO_MAP_LOCK_REQUIRED;

	int index = 1;
	luaL_checktype(L, index, LUA_TTABLE);

	const NodeDefManager *ndef = getServer(L)->getNodeDefManager();
	BiomeManager *bmgr = getServer(L)->getEmergeManager()->getWritableBiomeManager();

	Biome *biome = read_biome_def(L, index, ndef);
	if (!biome)
		return 0;

	ObjDefHandle handle = bmgr->add(biome);
	if (handle == OBJDEF_INVALID_HANDLE) {
		delete biome;
		return 0;
	}

	lua_pushinteger(L, handle);
	return 1;
}


// register_decoration({lots of stuff})
int ModApiMapgen::l_register_decoration(lua_State *L)
{
	NO_MAP_LOCK_REQUIRED;

	int index = 1;
	luaL_checktype(L, index, LUA_TTABLE);

	const NodeDefManager *ndef      = getServer(L)->getNodeDefManager();
	EmergeManager *emerge = getServer(L)->getEmergeManager();
	DecorationManager *decomgr = emerge->getWritableDecorationManager();
	BiomeManager *biomemgr     = emerge->getWritableBiomeManager();
	SchematicManager *schemmgr = emerge->getWritableSchematicManager();

	enum DecorationType decotype = (DecorationType)getenumfield(L, index,
				"deco_type", es_DecorationType, -1);

	Decoration *deco = decomgr->create(decotype);
	if (!deco) {
		errorstream << "register_decoration: decoration placement type "
			<< decotype << " not implemented" << std::endl;
		return 0;
	}

	deco->name           = getstringfield_default(L, index, "name", "");
	deco->fill_ratio     = getfloatfield_default(L, index, "fill_ratio", 0.02);
	deco->y_min          = getintfield_default(L, index, "y_min", -31000);
	deco->y_max          = getintfield_default(L, index, "y_max", 31000);
	deco->nspawnby       = getintfield_default(L, index, "num_spawn_by", -1);
	deco->place_offset_y = getintfield_default(L, index, "place_offset_y", 0);
	deco->sidelen        = getintfield_default(L, index, "sidelen", 8);
	if (deco->sidelen <= 0) {
		errorstream << "register_decoration: sidelen must be "
			"greater than 0" << std::endl;
		delete deco;
		return 0;
	}

	//// Get node name(s) to place decoration on
	size_t nread = getstringlistfield(L, index, "place_on", &deco->m_nodenames);
	deco->m_nnlistsizes.push_back(nread);

	//// Get decoration flags
	getflagsfield(L, index, "flags", flagdesc_deco, &deco->flags, NULL);

	//// Get NoiseParams to define how decoration is placed
	lua_getfield(L, index, "noise_params");
	if (read_noiseparams(L, -1, &deco->np))
		deco->flags |= DECO_USE_NOISE;
	lua_pop(L, 1);

	//// Get biomes associated with this decoration (if any)
	lua_getfield(L, index, "biomes");
	if (get_biome_list(L, -1, biomemgr, &deco->biomes))
		infostream << "register_decoration: couldn't get all biomes " << std::endl;
	lua_pop(L, 1);

	//// Get node name(s) to 'spawn by'
	size_t nnames = getstringlistfield(L, index, "spawn_by", &deco->m_nodenames);
	deco->m_nnlistsizes.push_back(nnames);
	if (nnames == 0 && deco->nspawnby != -1) {
		errorstream << "register_decoration: no spawn_by nodes defined,"
			" but num_spawn_by specified" << std::endl;
	}

	//// Handle decoration type-specific parameters
	bool success = false;
	switch (decotype) {
	case DECO_SIMPLE:
		success = read_deco_simple(L, (DecoSimple *)deco);
		break;
	case DECO_SCHEMATIC:
		success = read_deco_schematic(L, schemmgr, (DecoSchematic *)deco);
		break;
	case DECO_LSYSTEM:
		break;
	}

	if (!success) {
		delete deco;
		return 0;
	}

	ndef->pendNodeResolve(deco);

	ObjDefHandle handle = decomgr->add(deco);
	if (handle == OBJDEF_INVALID_HANDLE) {
		delete deco;
		return 0;
	}

	lua_pushinteger(L, handle);
	return 1;
}


bool read_deco_simple(lua_State *L, DecoSimple *deco)
{
	int index = 1;
	int param2;
	int param2_max;

	deco->deco_height     = getintfield_default(L, index, "height", 1);
	deco->deco_height_max = getintfield_default(L, index, "height_max", 0);

	if (deco->deco_height <= 0) {
		errorstream << "register_decoration: simple decoration height"
			" must be greater than 0" << std::endl;
		return false;
	}

	size_t nnames = getstringlistfield(L, index, "decoration", &deco->m_nodenames);
	deco->m_nnlistsizes.push_back(nnames);

	if (nnames == 0) {
		errorstream << "register_decoration: no decoration nodes "
			"defined" << std::endl;
		return false;
	}

	param2 = getintfield_default(L, index, "param2", 0);
	param2_max = getintfield_default(L, index, "param2_max", 0);

	if (param2 < 0 || param2 > 255 || param2_max < 0 || param2_max > 255) {
		errorstream << "register_decoration: param2 or param2_max out of bounds (0-255)"
			<< std::endl;
		return false;
	}

	deco->deco_param2 = (u8)param2;
	deco->deco_param2_max = (u8)param2_max;

	return true;
}


bool read_deco_schematic(lua_State *L, SchematicManager *schemmgr, DecoSchematic *deco)
{
	int index = 1;

	deco->rotation = (Rotation)getenumfield(L, index, "rotation",
		ModApiMapgen::es_Rotation, ROTATE_0);

	StringMap replace_names;
	lua_getfield(L, index, "replacements");
	if (lua_istable(L, -1))
		read_schematic_replacements(L, -1, &replace_names);
	lua_pop(L, 1);

	lua_getfield(L, index, "schematic");
	Schematic *schem = get_or_load_schematic(L, -1, schemmgr, &replace_names);
	lua_pop(L, 1);

	deco->schematic = schem;
	return schem != NULL;
}


// register_ore({lots of stuff})
int ModApiMapgen::l_register_ore(lua_State *L)
{
	NO_MAP_LOCK_REQUIRED;

	int index = 1;
	luaL_checktype(L, index, LUA_TTABLE);

	const NodeDefManager *ndef = getServer(L)->getNodeDefManager();
	EmergeManager *emerge = getServer(L)->getEmergeManager();
	BiomeManager *bmgr    = emerge->getWritableBiomeManager();
	OreManager *oremgr    = emerge->getWritableOreManager();

	enum OreType oretype = (OreType)getenumfield(L, index,
				"ore_type", es_OreType, ORE_SCATTER);
	Ore *ore = oremgr->create(oretype);
	if (!ore) {
		errorstream << "register_ore: ore_type " << oretype << " not implemented\n";
		return 0;
	}

	ore->name           = getstringfield_default(L, index, "name", "");
	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);
	ore->clust_size     = getintfield_default(L, index, "clust_size", 0);
	ore->noise          = NULL;
	ore->flags          = 0;

	//// Get noise_threshold
	warn_if_field_exists(L, index, "noise_threshhold",
		"Deprecated: new name is \"noise_threshold\".");

	float nthresh;
	if (!getfloatfield(L, index, "noise_threshold", nthresh) &&
			!getfloatfield(L, index, "noise_threshhold", nthresh))
		nthresh = 0;
	ore->nthresh = nthresh;

	//// Get y_min/y_max
	warn_if_field_exists(L, index, "height_min",
		"Deprecated: new name is \"y_min\".");
	warn_if_field_exists(L, index, "height_max",
		"Deprecated: new name is \"y_max\".");

	int ymin, ymax;
	if (!getintfield(L, index, "y_min", ymin) &&
		!getintfield(L, index, "height_min", ymin))
		ymin = -31000;
	if (!getintfield(L, index, "y_max", ymax) &&
		!getintfield(L, index, "height_max", ymax))
		ymax = 31000;
	ore->y_min = ymin;
	ore->y_max = ymax;

	if (ore->clust_scarcity <= 0 || ore->clust_num_ores <= 0) {
		errorstream << "register_ore: clust_scarcity and clust_num_ores"
			"must be greater than 0" << std::endl;
		delete ore;
		return 0;
	}

	//// Get flags
	getflagsfield(L, index, "flags", flagdesc_ore, &ore->flags, NULL);

	//// Get biomes associated with this decoration (if any)
	lua_getfield(L, index, "biomes");
	if (get_biome_list(L, -1, bmgr, &ore->biomes))
		infostream << "register_ore: couldn't get all biomes " << std::endl;
	lua_pop(L, 1);

	//// Get noise parameters if needed
	lua_getfield(L, index, "noise_params");
	if (read_noiseparams(L, -1, &ore->np)) {
		ore->flags |= OREFLAG_USE_NOISE;
	} else if (ore->needs_noise) {
		log_deprecated(L,
			"register_ore: ore type requires 'noise_params' but it is not specified, falling back to defaults");
	}
	lua_pop(L, 1);

	//// Get type-specific parameters
	switch (oretype) {
		case ORE_SHEET: {
			OreSheet *oresheet = (OreSheet *)ore;

			oresheet->column_height_min = getintfield_default(L, index,
				"column_height_min", 1);
			oresheet->column_height_max = getintfield_default(L, index,
				"column_height_max", ore->clust_size);
			oresheet->column_midpoint_factor = getfloatfield_default(L, index,
				"column_midpoint_factor", 0.5f);

			break;
		}
		case ORE_PUFF: {
			OrePuff *orepuff = (OrePuff *)ore;

			lua_getfield(L, index, "np_puff_top");
			read_noiseparams(L, -1, &orepuff->np_puff_top);
			lua_pop(L, 1);

			lua_getfield(L, index, "np_puff_bottom");
			read_noiseparams(L, -1, &orepuff->np_puff_bottom);
			lua_pop(L, 1);

			break;
		}
		case ORE_VEIN: {
			OreVein *orevein = (OreVein *)ore;

			orevein->random_factor = getfloatfield_default(L, index,
				"random_factor", 1.f);

			break;
		}
		case ORE_STRATUM: {
			OreStratum *orestratum = (OreStratum *)ore;

			lua_getfield(L, index, "np_stratum_thickness");
			if (read_noiseparams(L, -1, &orestratum->np_stratum_thickness))
				ore->flags |= OREFLAG_USE_NOISE2;
			lua_pop(L, 1);

			orestratum->stratum_thickness = getintfield_default(L, index,
				"stratum_thickness", 8);

			break;
		}
		default:
			break;
	}

	ObjDefHandle handle = oremgr->add(ore);
	if (handle == OBJDEF_INVALID_HANDLE) {
		delete ore;
		return 0;
	}

	ore->m_nodenames.push_back(getstringfield_default(L, index, "ore", ""));

	size_t nnames = getstringlistfield(L, index, "wherein", &ore->m_nodenames);
	ore->m_nnlistsizes.push_back(nnames);

	ndef->pendNodeResolve(ore);

	lua_pushinteger(L, handle);
	return 1;
}


// register_schematic({schematic}, replacements={})
int ModApiMapgen::l_register_schematic(lua_State *L)
{
	NO_MAP_LOCK_REQUIRED;

	SchematicManager *schemmgr =
		getServer(L)->getEmergeManager()->getWritableSchematicManager();

	StringMap replace_names;
	if (lua_istable(L, 2))
		read_schematic_replacements(L, 2, &replace_names);

	Schematic *schem = load_schematic(L, 1, schemmgr->getNodeDef(),
		&replace_names);
	if (!schem)
		return 0;

	ObjDefHandle handle = schemmgr->add(schem);
	if (handle == OBJDEF_INVALID_HANDLE) {
		delete schem;
		return 0;
	}

	lua_pushinteger(L, handle);
	return 1;
}


// clear_registered_biomes()
int ModApiMapgen::l_clear_registered_biomes(lua_State *L)
{
	NO_MAP_LOCK_REQUIRED;

	BiomeManager *bmgr =
		getServer(L)->getEmergeManager()->getWritableBiomeManager();
	bmgr->clear();
	return 0;
}


// clear_registered_decorations()
int ModApiMapgen::l_clear_registered_decorations(lua_State *L)
{
	NO_MAP_LOCK_REQUIRED;

	DecorationManager *dmgr =
		getServer(L)->getEmergeManager()->getWritableDecorationManager();
	dmgr->clear();
	return 0;
}


// clear_registered_ores()
int ModApiMapgen::l_clear_registered_ores(lua_State *L)
{
	NO_MAP_LOCK_REQUIRED;

	OreManager *omgr =
		getServer(L)->getEmergeManager()->getWritableOreManager();
	omgr->clear();
	return 0;
}


// clear_registered_schematics()
int ModApiMapgen::l_clear_registered_schematics(lua_State *L)
{
	NO_MAP_LOCK_REQUIRED;

	SchematicManager *smgr =
		getServer(L)->getEmergeManager()->getWritableSchematicManager();
	smgr->clear();
	return 0;
}


// generate_ores(vm, p1, p2, [ore_id])
int ModApiMapgen::l_generate_ores(lua_State *L)
{
	NO_MAP_LOCK_REQUIRED;

	EmergeManager *emerge = getServer(L)->getEmergeManager();
	if (!emerge || !emerge->mgparams)
		return 0;

	Mapgen mg;
	// Intentionally truncates to s32, see Mapgen::Mapgen()
	mg.seed = (s32)emerge->mgparams->seed;
	mg.vm   = LuaVoxelManip::checkobject(L, 1)->vm;
	mg.ndef = getServer(L)->getNodeDefManager();

	v3s16 pmin = lua_istable(L, 2) ? check_v3s16(L, 2) :
			mg.vm->m_area.MinEdge + v3s16(1,1,1) * MAP_BLOCKSIZE;
	v3s16 pmax = lua_istable(L, 3) ? check_v3s16(L, 3) :
			mg.vm->m_area.MaxEdge - v3s16(1,1,1) * MAP_BLOCKSIZE;
	sortBoxVerticies(pmin, pmax);

	u32 blockseed = Mapgen::getBlockSeed(pmin, mg.seed);

	emerge->oremgr->placeAllOres(&mg, blockseed, pmin, pmax);

	return 0;
}


// generate_decorations(vm, p1, p2, [deco_id])
int ModApiMapgen::l_generate_decorations(lua_State *L)
{
	NO_MAP_LOCK_REQUIRED;

	EmergeManager *emerge = getServer(L)->getEmergeManager();
	if (!emerge || !emerge->mgparams)
		return 0;

	Mapgen mg;
	// Intentionally truncates to s32, see Mapgen::Mapgen()
	mg.seed = (s32)emerge->mgparams->seed;
	mg.vm   = LuaVoxelManip::checkobject(L, 1)->vm;
	mg.ndef = getServer(L)->getNodeDefManager();

	v3s16 pmin = lua_istable(L, 2) ? check_v3s16(L, 2) :
			mg.vm->m_area.MinEdge + v3s16(1,1,1) * MAP_BLOCKSIZE;
	v3s16 pmax = lua_istable(L, 3) ? check_v3s16(L, 3) :
			mg.vm->m_area.MaxEdge - v3s16(1,1,1) * MAP_BLOCKSIZE;
	sortBoxVerticies(pmin, pmax);

	u32 blockseed = Mapgen::getBlockSeed(pmin, mg.seed);

	emerge->decomgr->placeAllDecos(&mg, blockseed, pmin, pmax);

	return 0;
}


// create_schematic(p1, p2, probability_list, filename, y_slice_prob_list)
int ModApiMapgen::l_create_schematic(lua_State *L)
{
	MAP_LOCK_REQUIRED;

	const NodeDefManager *ndef = getServer(L)->getNodeDefManager();

	const char *filename = luaL_checkstring(L, 4);
	CHECK_SECURE_PATH(L, filename, true);

	Map *map = &(getEnv(L)->getMap());
	Schematic schem;

	v3s16 p1 = check_v3s16(L, 1);
	v3s16 p2 = check_v3s16(L, 2);
	sortBoxVerticies(p1, p2);

	std::vector<std::pair<v3s16, u8> > prob_list;
	if (lua_istable(L, 3)) {
		lua_pushnil(L);
		while (lua_next(L, 3)) {
			if (lua_istable(L, -1)) {
				lua_getfield(L, -1, "pos");
				v3s16 pos = check_v3s16(L, -1);
				lua_pop(L, 1);

				u8 prob = getintfield_default(L, -1, "prob", MTSCHEM_PROB_ALWAYS);
				prob_list.emplace_back(pos, prob);
			}

			lua_pop(L, 1);
		}
	}

	std::vector<std::pair<s16, u8> > slice_prob_list;
	if (lua_istable(L, 5)) {
		lua_pushnil(L);
		while (lua_next(L, 5)) {
			if (lua_istable(L, -1)) {
				s16 ypos = getintfield_default(L, -1, "ypos", 0);
				u8 prob  = getintfield_default(L, -1, "prob", MTSCHEM_PROB_ALWAYS);
				slice_prob_list.emplace_back(ypos, prob);
			}

			lua_pop(L, 1);
		}
	}

	if (!schem.getSchematicFromMap(map, p1, p2)) {
		errorstream << "create_schematic: failed to get schematic "
			"from map" << std::endl;
		return 0;
	}

	schem.applyProbabilities(p1, &prob_list, &slice_prob_list);

	schem.saveSchematicToFile(filename, ndef);
	actionstream << "create_schematic: saved schematic file '"
		<< filename << "'." << std::endl;

	lua_pushboolean(L, true);
	return 1;
}


// place_schematic(p, schematic, rotation,
//     replacements, force_placement, flagstring)
int ModApiMapgen::l_place_schematic(lua_State *L)
{
	MAP_LOCK_REQUIRED;

	GET_ENV_PTR;

	ServerMap *map = &(env->getServerMap());
	SchematicManager *schemmgr = getServer(L)->getEmergeManager()->schemmgr;

	//// Read position
	v3s16 p = check_v3s16(L, 1);

	//// Read rotation
	int rot = ROTATE_0;
	std::string enumstr = readParam<std::string>(L, 3, "");
	if (!enumstr.empty())
		string_to_enum(es_Rotation, rot, enumstr);

	//// Read force placement
	bool force_placement = true;
	if (lua_isboolean(L, 5))
		force_placement = readParam<bool>(L, 5);

	//// Read node replacements
	StringMap replace_names;
	if (lua_istable(L, 4))
		read_schematic_replacements(L, 4, &replace_names);

	//// Read schematic
	Schematic *schem = get_or_load_schematic(L, 2, schemmgr, &replace_names);
	if (!schem) {
		errorstream << "place_schematic: failed to get schematic" << std::endl;
		return 0;
	}

	//// Read flags
	u32 flags = 0;
	read_flags(L, 6, flagdesc_deco, &flags, NULL);

	schem->placeOnMap(map, p, flags, (Rotation)rot, force_placement);

	lua_pushboolean(L, true);
	return 1;
}


// place_schematic_on_vmanip(vm, p, schematic, rotation,
//     replacements, force_placement, flagstring)
int ModApiMapgen::l_place_schematic_on_vmanip(lua_State *L)
{
	NO_MAP_LOCK_REQUIRED;

	SchematicManager *schemmgr = getServer(L)->getEmergeManager()->schemmgr;

	//// Read VoxelManip object
	MMVManip *vm = LuaVoxelManip::checkobject(L, 1)->vm;

	//// Read position
	v3s16 p = check_v3s16(L, 2);

	//// Read rotation
	int rot = ROTATE_0;
	std::string enumstr = readParam<std::string>(L, 4, "");
	if (!enumstr.empty())
		string_to_enum(es_Rotation, rot, std::string(enumstr));

	//// Read force placement
	bool force_placement = true;
	if (lua_isboolean(L, 6))
		force_placement = readParam<bool>(L, 6);

	//// Read node replacements
	StringMap replace_names;
	if (lua_istable(L, 5))
		read_schematic_replacements(L, 5, &replace_names);

	//// Read schematic
	Schematic *schem = get_or_load_schematic(L, 3, schemmgr, &replace_names);
	if (!schem) {
		errorstream << "place_schematic: failed to get schematic" << std::endl;
		return 0;
	}

	//// Read flags
	u32 flags = 0;
	read_flags(L, 7, flagdesc_deco, &flags, NULL);

	bool schematic_did_fit = schem->placeOnVManip(
		vm, p, flags, (Rotation)rot, force_placement);

	lua_pushboolean(L, schematic_did_fit);
	return 1;
}


// serialize_schematic(schematic, format, options={...})
int ModApiMapgen::l_serialize_schematic(lua_State *L)
{
	NO_MAP_LOCK_REQUIRED;

	const SchematicManager *schemmgr = getServer(L)->getEmergeManager()->getSchematicManager();

	//// Read options
	bool use_comments = getboolfield_default(L, 3, "lua_use_comments", false);
	u32 indent_spaces = getintfield_default(L, 3, "lua_num_indent_spaces", 0);

	//// Get schematic
	bool was_loaded = false;
	const Schematic *schem = (Schematic *)get_objdef(L, 1, schemmgr);
	if (!schem) {
		schem = load_schematic(L, 1, NULL, NULL);
		was_loaded = true;
	}
	if (!schem) {
		errorstream << "serialize_schematic: failed to get schematic" << std::endl;
		return 0;
	}

	//// Read format of definition to save as
	int schem_format = SCHEM_FMT_MTS;
	std::string enumstr = readParam<std::string>(L, 2, "");
	if (!enumstr.empty())
		string_to_enum(es_SchematicFormatType, schem_format, enumstr);

	//// Serialize to binary string
	std::ostringstream os(std::ios_base::binary);
	switch (schem_format) {
	case SCHEM_FMT_MTS:
		schem->serializeToMts(&os);
		break;
	case SCHEM_FMT_LUA: