Schematics: Add per-node force placement option
authorkwolekr <kwolekr@minetest.net>
Sat, 9 May 2015 05:38:20 +0000 (01:38 -0400)
committerkwolekr <kwolekr@minetest.net>
Sat, 9 May 2015 22:44:00 +0000 (18:44 -0400)
doc/lua_api.txt
src/mg_schematic.cpp
src/mg_schematic.h
src/script/common/c_converter.cpp
src/script/common/c_converter.h
src/script/lua_api/l_mapgen.cpp
src/unittest/test_schematic.cpp

index cb8b8848fd95fd3b6f614950b4cf1d8e62f1fbd1..93387ef0bd1c4662b4b7bf71a5fb2fc301ed9b9b 100644 (file)
@@ -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
 --------------------
index 33f82a74cbaa8cbd99eaead66c97263239247013..81d849a66b7d12e26f977ddd1ce7b08b83637aae 100644 (file)
@@ -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;
                        }
                }
 
index 3d3e328d3185636a10d92d4ab74cedcb11a43198..5c732648edaadd561bf3451af186e08c243ac21f 100644 (file)
@@ -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
 {
index 157db3b7d67b238e47e4da5aa03868f8daef7475..fc489ea9574d00726009eee8ab8a849fbfcfbcc5 100644 (file)
@@ -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)
 {
index 1d5be697121527d9ffe36aa58ba97a0b839e00a8..0847f47c9ed26d241b943fff58fe89cf19a80df4 100644 (file)
@@ -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,
index 5422447edeb6365f6d384278a383a4cc89c55dc3..3cb52eab492e292460268f885d77404dd547c37d 100644 (file)
@@ -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;
                }
        }
 
index 24bacf6c5a4e36476497226d2d47083bc9c91e85..df47d2bc0709eefbac92dc80b11892a21f1dc5ed 100644 (file)
@@ -34,8 +34,9 @@ public:
        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";