From 2b99d904f6b8197931954772b6466d8ee56cafc9 Mon Sep 17 00:00:00 2001
From: kwolekr <kwolekr@minetest.net>
Date: Sat, 9 May 2015 01:38:20 -0400
Subject: [PATCH] Schematics: Add per-node force placement option

---
 doc/lua_api.txt                   | 41 +++++++------
 src/mg_schematic.cpp              | 54 +++++++++++++-----
 src/mg_schematic.h                | 24 +++++---
 src/script/common/c_converter.cpp | 13 +++++
 src/script/common/c_converter.h   |  2 +
 src/script/lua_api/l_mapgen.cpp   | 57 +++++++++----------
 src/unittest/test_schematic.cpp   | 95 ++++++++++++++++++-------------
 7 files changed, 172 insertions(+), 114 deletions(-)

diff --git a/doc/lua_api.txt b/doc/lua_api.txt
index cb8b8848f..93387ef0b 100644
--- a/doc/lua_api.txt
+++ b/doc/lua_api.txt
@@ -782,30 +782,27 @@ Schematic specifier
 --------------------
 A schematic specifier identifies a schematic by either a filename to a
 Minetest Schematic file (`.mts`) or through raw data supplied through Lua,
-in the form of a table.  This table must specify two fields:
-
-* The `size` field is a 3D vector containing the dimensions of the provided schematic.
-* The `data` field is a flat table of MapNodes making up the schematic,
-  in the order of `[z [y [x]]]`.
-
-**Important**: The default value for `param1` in MapNodes here is `255`,
-which represents "always place".
-
-In the bulk `MapNode` data, `param1`, instead of the typical light values,
-instead represents the probability of that node appearing in the structure.
-
-When passed to `minetest.create_schematic`, probability is an integer value
-ranging from `0` to `255`:
-
-* A probability value of `0` means that node will never appear (0% chance).
-* A probability value of `255` means the node will always appear (100% chance).
-* If the probability value `p` is greater than `0`, then there is a
-  `(p / 256 * 100)`% chance that node will appear when the schematic is
+in the form of a table.  This table specifies the following fields:
+
+* The `size` field is a 3D vector containing the dimensions of the provided schematic. (required)
+* The `yslice_prob` field is a table of {ypos, prob} which sets the `ypos`th vertical slice
+  of the schematic to have a `prob / 256 * 100` chance of occuring. (default: 255)
+* The `data` field is a flat table of MapNode tables making up the schematic,
+  in the order of `[z [y [x]]]`. (required)
+  Each MapNode table contains:
+  * `name`: the name of the map node to place (required)
+  * `prob` (alias `param1`): the probability of this node being placed (default: 255)
+  * `param2`: the raw param2 value of the node being placed onto the map (default: 0)
+  * `force_place`: boolean representing if the node should forcibly overwrite any
+     previous contents (default: false)
+
+About probability values:
+* A probability value of `0` or `1` means that node will never appear (0% chance).
+* A probability value of `254` or `255` means the node will always appear (100% chance).
+* If the probability value `p` is greater than `1`, then there is a
+  `(p / 256 * 100)` percent chance that node will appear when the schematic is
   placed on the map.
 
-**Important note**: Node aliases cannot be used for a raw schematic provided
-  when registering as a decoration.
-
 
 Schematic attributes
 --------------------
diff --git a/src/mg_schematic.cpp b/src/mg_schematic.cpp
index 33f82a74c..81d849a66 100644
--- a/src/mg_schematic.cpp
+++ b/src/mg_schematic.cpp
@@ -133,8 +133,8 @@ void Schematic::blitToVManip(v3s16 p, MMVManip *vm, Rotation rot, bool force_pla
 
 	s16 y_map = p.Y;
 	for (s16 y = 0; y != sy; y++) {
-		if (slice_probs[y] != MTSCHEM_PROB_ALWAYS &&
-			myrand_range(1, 255) > slice_probs[y])
+		if ((slice_probs[y] != MTSCHEM_PROB_ALWAYS) &&
+			(slice_probs[y] <= myrand_range(1, MTSCHEM_PROB_ALWAYS)))
 			continue;
 
 		for (s16 z = 0; z != sz; z++) {
@@ -147,17 +147,20 @@ void Schematic::blitToVManip(v3s16 p, MMVManip *vm, Rotation rot, bool force_pla
 				if (schemdata[i].getContent() == CONTENT_IGNORE)
 					continue;
 
-				if (schemdata[i].param1 == MTSCHEM_PROB_NEVER)
+				u8 placement_prob     = schemdata[i].param1 & MTSCHEM_PROB_MASK;
+				bool force_place_node = schemdata[i].param1 & MTSCHEM_FORCE_PLACE;
+
+				if (placement_prob == MTSCHEM_PROB_NEVER)
 					continue;
 
-				if (!force_place) {
+				if (!force_place && !force_place_node) {
 					content_t c = vm->m_data[vi].getContent();
 					if (c != CONTENT_AIR && c != CONTENT_IGNORE)
 						continue;
 				}
 
-				if (schemdata[i].param1 != MTSCHEM_PROB_ALWAYS &&
-					myrand_range(1, 255) > schemdata[i].param1)
+				if ((placement_prob != MTSCHEM_PROB_ALWAYS) &&
+					(placement_prob <= myrand_range(1, MTSCHEM_PROB_ALWAYS)))
 					continue;
 
 				vm->m_data[vi] = schemdata[i];
@@ -225,6 +228,7 @@ bool Schematic::deserializeFromMts(std::istream *is,
 	content_t cignore = CONTENT_IGNORE;
 	bool have_cignore = false;
 
+	//// Read signature
 	u32 signature = readU32(ss);
 	if (signature != MTSCHEM_FILE_SIGNATURE) {
 		errorstream << "Schematic::deserializeFromMts: invalid schematic "
@@ -232,6 +236,7 @@ bool Schematic::deserializeFromMts(std::istream *is,
 		return false;
 	}
 
+	//// Read version
 	u16 version = readU16(ss);
 	if (version > MTSCHEM_FILE_VER_HIGHEST_READ) {
 		errorstream << "Schematic::deserializeFromMts: unsupported schematic "
@@ -239,18 +244,21 @@ bool Schematic::deserializeFromMts(std::istream *is,
 		return false;
 	}
 
+	//// Read size
 	size = readV3S16(ss);
 
+	//// Read Y-slice probability values
 	delete []slice_probs;
 	slice_probs = new u8[size.Y];
 	for (int y = 0; y != size.Y; y++)
-		slice_probs[y] = (version >= 3) ? readU8(ss) : MTSCHEM_PROB_ALWAYS;
+		slice_probs[y] = (version >= 3) ? readU8(ss) : MTSCHEM_PROB_ALWAYS_OLD;
 
+	//// Read node names
 	u16 nidmapcount = readU16(ss);
 	for (int i = 0; i != nidmapcount; i++) {
 		std::string name = deSerializeString(ss);
 
-		// Instances of "ignore" from ver 1 are converted to air (and instances
+		// Instances of "ignore" from v1 are converted to air (and instances
 		// are fixed to have MTSCHEM_PROB_NEVER later on).
 		if (name == "ignore") {
 			name = "air";
@@ -261,6 +269,7 @@ bool Schematic::deserializeFromMts(std::istream *is,
 		names->push_back(name);
 	}
 
+	//// Read node data
 	size_t nodecount = size.X * size.Y * size.Z;
 
 	delete []schemdata;
@@ -269,8 +278,8 @@ bool Schematic::deserializeFromMts(std::istream *is,
 	MapNode::deSerializeBulk(ss, SER_FMT_VER_HIGHEST_READ, schemdata,
 		nodecount, 2, 2, true);
 
-	// fix any probability values for nodes that were ignore
-	if (version == 1) {
+	// Fix probability values for nodes that were ignore; removed in v2
+	if (version < 2) {
 		for (size_t i = 0; i != nodecount; i++) {
 			if (schemdata[i].param1 == 0)
 				schemdata[i].param1 = MTSCHEM_PROB_ALWAYS;
@@ -279,6 +288,14 @@ bool Schematic::deserializeFromMts(std::istream *is,
 		}
 	}
 
+	// Fix probability values for probability range truncation introduced in v4
+	if (version < 4) {
+		for (s16 y = 0; y != size.Y; y++)
+			slice_probs[y] >>= 1;
+		for (size_t i = 0; i != nodecount; i++)
+			schemdata[i].param1 >>= 1;
+	}
+
 	return true;
 }
 
@@ -331,9 +348,11 @@ bool Schematic::serializeToLua(std::ostream *os,
 		ss << indent << "yslice_prob = {" << std::endl;
 
 		for (u16 y = 0; y != size.Y; y++) {
+			u8 probability = slice_probs[y] & MTSCHEM_PROB_MASK;
+
 			ss << indent << indent << "{"
 				<< "ypos=" << y
-				<< ", prob=" << (u16)slice_probs[y]
+				<< ", prob=" << (u16)probability * 2
 				<< "}," << std::endl;
 		}
 
@@ -355,11 +374,18 @@ bool Schematic::serializeToLua(std::ostream *os,
 			}
 
 			for (u16 x = 0; x != size.X; x++, i++) {
+				u8 probability   = schemdata[i].param1 & MTSCHEM_PROB_MASK;
+				bool force_place = schemdata[i].param1 & MTSCHEM_FORCE_PLACE;
+
 				ss << indent << indent << "{"
 					<< "name=\"" << names[schemdata[i].getContent()]
-					<< "\", param1=" << (u16)schemdata[i].param1
-					<< ", param2=" << (u16)schemdata[i].param2
-					<< "}," << std::endl;
+					<< "\", prob=" << (u16)probability * 2
+					<< ", param2=" << (u16)schemdata[i].param2;
+
+				if (force_place)
+					ss << ", force_place=true";
+
+				ss << "}," << std::endl;
 			}
 		}
 
diff --git a/src/mg_schematic.h b/src/mg_schematic.h
index 3d3e328d3..5c732648e 100644
--- a/src/mg_schematic.h
+++ b/src/mg_schematic.h
@@ -36,7 +36,7 @@ class IGameDef;
 
 	All values are stored in big-endian byte order.
 	[u32] signature: 'MTSM'
-	[u16] version: 3
+	[u16] version: 4
 	[u16] size X
 	[u16] size Y
 	[u16] size Z
@@ -51,7 +51,9 @@ class IGameDef;
 	For each node in schematic:  (for z, y, x)
 		[u16] content
 	For each node in schematic:
-		[u8] probability of occurance (param1)
+		[u8] param1
+		  bit 0-6: probability
+		  bit 7:   specific node force placement
 	For each node in schematic:
 		[u8] param2
 	}
@@ -60,17 +62,21 @@ class IGameDef;
 	1 - Initial version
 	2 - Fixed messy never/always place; 0 probability is now never, 0xFF is always
 	3 - Added y-slice probabilities; this allows for variable height structures
+	4 - Compressed range of node occurence prob., added per-node force placement bit
 */
 
-/////////////////// Schematic flags
-#define SCHEM_CIDS_UPDATED 0x08
-
+//// Schematic constants
 #define MTSCHEM_FILE_SIGNATURE 0x4d54534d // 'MTSM'
-#define MTSCHEM_FILE_VER_HIGHEST_READ  3
-#define MTSCHEM_FILE_VER_HIGHEST_WRITE 3
+#define MTSCHEM_FILE_VER_HIGHEST_READ  4
+#define MTSCHEM_FILE_VER_HIGHEST_WRITE 4
+
+#define MTSCHEM_PROB_MASK       0x7F
+
+#define MTSCHEM_PROB_NEVER      0x00
+#define MTSCHEM_PROB_ALWAYS     0x7F
+#define MTSCHEM_PROB_ALWAYS_OLD 0xFF
 
-#define MTSCHEM_PROB_NEVER  0x00
-#define MTSCHEM_PROB_ALWAYS 0xFF
+#define MTSCHEM_FORCE_PLACE     0x80
 
 enum SchematicType
 {
diff --git a/src/script/common/c_converter.cpp b/src/script/common/c_converter.cpp
index 157db3b7d..fc489ea95 100644
--- a/src/script/common/c_converter.cpp
+++ b/src/script/common/c_converter.cpp
@@ -331,6 +331,19 @@ bool getintfield(lua_State *L, int table,
 	return got;
 }
 
+bool getintfield(lua_State *L, int table,
+		const char *fieldname, u8 &result)
+{
+	lua_getfield(L, table, fieldname);
+	bool got = false;
+	if(lua_isnumber(L, -1)){
+		result = lua_tonumber(L, -1);
+		got = true;
+	}
+	lua_pop(L, 1);
+	return got;
+}
+
 bool getintfield(lua_State *L, int table,
 		const char *fieldname, u16 &result)
 {
diff --git a/src/script/common/c_converter.h b/src/script/common/c_converter.h
index 1d5be6971..0847f47c9 100644
--- a/src/script/common/c_converter.h
+++ b/src/script/common/c_converter.h
@@ -53,6 +53,8 @@ size_t             getstringlistfield(lua_State *L, int table,
                              std::vector<std::string> *result);
 bool               getintfield(lua_State *L, int table,
                              const char *fieldname, int &result);
+bool               getintfield(lua_State *L, int table,
+                             const char *fieldname, u8 &result);
 bool               getintfield(lua_State *L, int table,
                              const char *fieldname, u16 &result);
 bool               getintfield(lua_State *L, int table,
diff --git a/src/script/lua_api/l_mapgen.cpp b/src/script/lua_api/l_mapgen.cpp
index 5422447ed..3cb52eab4 100644
--- a/src/script/lua_api/l_mapgen.cpp
+++ b/src/script/lua_api/l_mapgen.cpp
@@ -237,36 +237,32 @@ bool read_schematic_def(lua_State *L, int index,
 	lua_getfield(L, index, "data");
 	luaL_checktype(L, -1, LUA_TTABLE);
 
-	int numnodes = size.X * size.Y * size.Z;
+	u32 numnodes = size.X * size.Y * size.Z;
 	schem->schemdata = new MapNode[numnodes];
-	int i = 0;
 
 	size_t names_base = names->size();
 	std::map<std::string, content_t> name_id_map;
 
-	lua_pushnil(L);
-	while (lua_next(L, -2)) {
-		if (i >= numnodes) {
-			i++;
-			lua_pop(L, 1);
+	u32 i = 0;
+	for (lua_pushnil(L); lua_next(L, -2); i++, lua_pop(L, 1)) {
+		if (i >= numnodes)
 			continue;
-		}
 
-		// same as readnode, except param1 default is MTSCHEM_PROB_CONST
-		lua_getfield(L, -1, "name");
-		std::string name = luaL_checkstring(L, -1);
-		lua_pop(L, 1);
+		//// 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;
-		lua_getfield(L, -1, "param1");
-		param1 = !lua_isnil(L, -1) ? lua_tonumber(L, -1) : MTSCHEM_PROB_ALWAYS;
-		lua_pop(L, 1);
+		if (!getintfield(L, -1, "param1", param1) &&
+			!getintfield(L, -1, "prob", param1))
+			param1 = MTSCHEM_PROB_ALWAYS_OLD;
 
-		u8 param2;
-		lua_getfield(L, -1, "param2");
-		param2 = !lua_isnil(L, -1) ? lua_tonumber(L, -1) : 0;
-		lua_pop(L, 1);
+		//// Read param2
+		u8 param2 = getintfield_default(L, -1, "param2", 0);
 
+		//// Find or add new nodename-to-ID mapping
 		std::map<std::string, content_t>::iterator it = name_id_map.find(name);
 		content_t name_index;
 		if (it != name_id_map.end()) {
@@ -277,10 +273,13 @@ bool read_schematic_def(lua_State *L, int index,
 			names->push_back(name);
 		}
 
-		schem->schemdata[i] = MapNode(name_index, param1, param2);
+		//// Perform probability/force_place fixup on param1
+		param1 >>= 1;
+		if (getboolfield_default(L, -1, "force_place", false))
+			param1 |= MTSCHEM_FORCE_PLACE;
 
-		i++;
-		lua_pop(L, 1);
+		//// Actually set the node in the schematic
+		schem->schemdata[i] = MapNode(name_index, param1, param2);
 	}
 
 	if (i != numnodes) {
@@ -297,13 +296,13 @@ bool read_schematic_def(lua_State *L, int index,
 
 	lua_getfield(L, index, "yslice_prob");
 	if (lua_istable(L, -1)) {
-		lua_pushnil(L);
-		while (lua_next(L, -2)) {
-			if (getintfield(L, -1, "ypos", i) && i >= 0 && i < size.Y) {
-				schem->slice_probs[i] = getintfield_default(L, -1,
-					"prob", MTSCHEM_PROB_ALWAYS);
-			}
-			lua_pop(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;
 		}
 	}
 
diff --git a/src/unittest/test_schematic.cpp b/src/unittest/test_schematic.cpp
index 24bacf6c5..df47d2bc0 100644
--- a/src/unittest/test_schematic.cpp
+++ b/src/unittest/test_schematic.cpp
@@ -34,8 +34,9 @@ class TestSchematic : public TestBase {
 	void testLuaTableSerialize(INodeDefManager *ndef);
 	void testFileSerializeDeserialize(INodeDefManager *ndef);
 
-	static const content_t test_schem_data[7 * 6 * 4];
-	static const content_t test_schem_data2[3 * 3 * 3];
+	static const content_t test_schem1_data[7 * 6 * 4];
+	static const content_t test_schem2_data[3 * 3 * 3];
+	static const u8 test_schem2_prob[3 * 3 * 3];
 	static const char *expected_lua_output;
 };
 
@@ -78,7 +79,7 @@ void TestSchematic::testMtsSerializeDeserialize(INodeDefManager *ndef)
 	schem.schemdata   = new MapNode[volume];
 	schem.slice_probs = new u8[size.Y];
 	for (size_t i = 0; i != volume; i++)
-		schem.schemdata[i] = MapNode(test_schem_data[i], MTSCHEM_PROB_ALWAYS, 0);
+		schem.schemdata[i] = MapNode(test_schem1_data[i], MTSCHEM_PROB_ALWAYS, 0);
 	for (s16 y = 0; y != size.Y; y++)
 		schem.slice_probs[y] = MTSCHEM_PROB_ALWAYS;
 
@@ -115,7 +116,7 @@ void TestSchematic::testLuaTableSerialize(INodeDefManager *ndef)
 	schem.schemdata   = new MapNode[volume];
 	schem.slice_probs = new u8[size.Y];
 	for (size_t i = 0; i != volume; i++)
-		schem.schemdata[i] = MapNode(test_schem_data2[i], MTSCHEM_PROB_ALWAYS, 0);
+		schem.schemdata[i] = MapNode(test_schem2_data[i], test_schem2_prob[i], 0);
 	for (s16 y = 0; y != size.Y; y++)
 		schem.slice_probs[y] = MTSCHEM_PROB_ALWAYS;
 
@@ -160,8 +161,8 @@ void TestSchematic::testFileSerializeDeserialize(INodeDefManager *ndef)
 	schem1.slice_probs[2] = 240;
 
 	for (size_t i = 0; i != volume; i++) {
-		content_t c = content_map[test_schem_data2[i]];
-		schem1.schemdata[i] = MapNode(c, MTSCHEM_PROB_ALWAYS, 0);
+		content_t c = content_map[test_schem2_data[i]];
+		schem1.schemdata[i] = MapNode(c, test_schem2_prob[i], 0);
 	}
 
 	std::string temp_file = getTestTempFile();
@@ -174,14 +175,14 @@ void TestSchematic::testFileSerializeDeserialize(INodeDefManager *ndef)
 	UASSERT(schem2.slice_probs[2] == 240);
 
 	for (size_t i = 0; i != volume; i++) {
-		content_t c = content_map2[test_schem_data2[i]];
-		UASSERT(schem2.schemdata[i] == MapNode(c, MTSCHEM_PROB_ALWAYS, 0));
+		content_t c = content_map2[test_schem2_data[i]];
+		UASSERT(schem2.schemdata[i] == MapNode(c, test_schem2_prob[i], 0));
 	}
 }
 
 
 // Should form a cross-shaped-thing...?
-const content_t TestSchematic::test_schem_data[7 * 6 * 4] = {
+const content_t TestSchematic::test_schem1_data[7 * 6 * 4] = {
 	3, 3, 1, 1, 1, 3, 3, // Y=0, Z=0
 	3, 0, 1, 2, 1, 0, 3, // Y=1, Z=0
 	3, 0, 1, 2, 1, 0, 3, // Y=2, Z=0
@@ -211,7 +212,7 @@ const content_t TestSchematic::test_schem_data[7 * 6 * 4] = {
 	3, 1, 1, 2, 1, 1, 3, // Y=5, Z=3
 };
 
-const content_t TestSchematic::test_schem_data2[3 * 3 * 3] = {
+const content_t TestSchematic::test_schem2_data[3 * 3 * 3] = {
 	0, 0, 0,
 	0, 2, 0,
 	0, 0, 0,
@@ -225,41 +226,55 @@ const content_t TestSchematic::test_schem_data2[3 * 3 * 3] = {
 	0, 0, 0,
 };
 
+const u8 TestSchematic::test_schem2_prob[3 * 3 * 3] = {
+	0x00, 0x00, 0x00,
+	0x00, 0xFF, 0x00,
+	0x00, 0x00, 0x00,
+
+	0x00, 0xFF, 0x00,
+	0xFF, 0xFF, 0xFF,
+	0x00, 0xFF, 0x00,
+
+	0x00, 0x00, 0x00,
+	0x00, 0xFF, 0x00,
+	0x00, 0x00, 0x00,
+};
+
 const char *TestSchematic::expected_lua_output =
 	"schematic = {\n"
 	"\tsize = {x=3, y=3, z=3},\n"
 	"\tyslice_prob = {\n"
-	"\t\t{ypos=0, prob=255},\n"
-	"\t\t{ypos=1, prob=255},\n"
-	"\t\t{ypos=2, prob=255},\n"
+	"\t\t{ypos=0, prob=254},\n"
+	"\t\t{ypos=1, prob=254},\n"
+	"\t\t{ypos=2, prob=254},\n"
 	"\t},\n"
 	"\tdata = {\n"
-	"\t\t{name=\"air\", param1=255, param2=0},\n"
-	"\t\t{name=\"air\", param1=255, param2=0},\n"
-	"\t\t{name=\"air\", param1=255, param2=0},\n"
-	"\t\t{name=\"air\", param1=255, param2=0},\n"
-	"\t\t{name=\"default:glass\", param1=255, param2=0},\n"
-	"\t\t{name=\"air\", param1=255, param2=0},\n"
-	"\t\t{name=\"air\", param1=255, param2=0},\n"
-	"\t\t{name=\"air\", param1=255, param2=0},\n"
-	"\t\t{name=\"air\", param1=255, param2=0},\n"
-	"\t\t{name=\"air\", param1=255, param2=0},\n"
-	"\t\t{name=\"default:glass\", param1=255, param2=0},\n"
-	"\t\t{name=\"air\", param1=255, param2=0},\n"
-	"\t\t{name=\"default:glass\", param1=255, param2=0},\n"
-	"\t\t{name=\"default:lava_source\", param1=255, param2=0},\n"
-	"\t\t{name=\"default:glass\", param1=255, param2=0},\n"
-	"\t\t{name=\"air\", param1=255, param2=0},\n"
-	"\t\t{name=\"default:glass\", param1=255, param2=0},\n"
-	"\t\t{name=\"air\", param1=255, param2=0},\n"
-	"\t\t{name=\"air\", param1=255, param2=0},\n"
-	"\t\t{name=\"air\", param1=255, param2=0},\n"
-	"\t\t{name=\"air\", param1=255, param2=0},\n"
-	"\t\t{name=\"air\", param1=255, param2=0},\n"
-	"\t\t{name=\"default:glass\", param1=255, param2=0},\n"
-	"\t\t{name=\"air\", param1=255, param2=0},\n"
-	"\t\t{name=\"air\", param1=255, param2=0},\n"
-	"\t\t{name=\"air\", param1=255, param2=0},\n"
-	"\t\t{name=\"air\", param1=255, param2=0},\n"
+	"\t\t{name=\"air\", prob=0, param2=0},\n"
+	"\t\t{name=\"air\", prob=0, param2=0},\n"
+	"\t\t{name=\"air\", prob=0, param2=0},\n"
+	"\t\t{name=\"air\", prob=0, param2=0},\n"
+	"\t\t{name=\"default:glass\", prob=254, param2=0, force_place=true},\n"
+	"\t\t{name=\"air\", prob=0, param2=0},\n"
+	"\t\t{name=\"air\", prob=0, param2=0},\n"
+	"\t\t{name=\"air\", prob=0, param2=0},\n"
+	"\t\t{name=\"air\", prob=0, param2=0},\n"
+	"\t\t{name=\"air\", prob=0, param2=0},\n"
+	"\t\t{name=\"default:glass\", prob=254, param2=0, force_place=true},\n"
+	"\t\t{name=\"air\", prob=0, param2=0},\n"
+	"\t\t{name=\"default:glass\", prob=254, param2=0, force_place=true},\n"
+	"\t\t{name=\"default:lava_source\", prob=254, param2=0, force_place=true},\n"
+	"\t\t{name=\"default:glass\", prob=254, param2=0, force_place=true},\n"
+	"\t\t{name=\"air\", prob=0, param2=0},\n"
+	"\t\t{name=\"default:glass\", prob=254, param2=0, force_place=true},\n"
+	"\t\t{name=\"air\", prob=0, param2=0},\n"
+	"\t\t{name=\"air\", prob=0, param2=0},\n"
+	"\t\t{name=\"air\", prob=0, param2=0},\n"
+	"\t\t{name=\"air\", prob=0, param2=0},\n"
+	"\t\t{name=\"air\", prob=0, param2=0},\n"
+	"\t\t{name=\"default:glass\", prob=254, param2=0, force_place=true},\n"
+	"\t\t{name=\"air\", prob=0, param2=0},\n"
+	"\t\t{name=\"air\", prob=0, param2=0},\n"
+	"\t\t{name=\"air\", prob=0, param2=0},\n"
+	"\t\t{name=\"air\", prob=0, param2=0},\n"
 	"\t},\n"
 	"}\n";
-- 
GitLab