Skip to content

Commit b2a89c0

Browse files
committedApr 13, 2015
Schematics: Reorganize (de)serialization and add Lua serialization API
1 parent 39fd4da commit b2a89c0

File tree

5 files changed

+254
-89
lines changed

5 files changed

+254
-89
lines changed
 

Diff for: ‎doc/lua_api.txt

+9
Original file line numberDiff line numberDiff line change
@@ -2170,6 +2170,15 @@ These functions return the leftover itemstack.
21702170
* `force_placement` is a boolean indicating whether nodes other than `air` and
21712171
`ignore` are replaced by the schematic
21722172

2173+
* `minetest.serialize_schematic(schematic, format, use_comments)`
2174+
* Return the serialized schematic specified by schematic (see: Schematic specifier)
2175+
* in the `format` of either "mts" or "lua".
2176+
* "mts" - a string containing the binary MTS data used in the MTS file format
2177+
* "lua" - a string containing Lua code representing the schematic in table format
2178+
* If `use_comments` is true, the Lua code generated will have (X, Z) position comments
2179+
* for every X row generated in the schematic data for easier reading. This parameter
2180+
* is ignored if `format` is not "lua".
2181+
21732182
### Misc.
21742183
* `minetest.get_connected_players()`: returns list of `ObjectRefs`
21752184
* `minetest.hash_node_position({x=,y=,z=})`: returns an 48-bit integer

Diff for: ‎src/mg_schematic.cpp

+137-81
Original file line numberDiff line numberDiff line change
@@ -198,71 +198,59 @@ void Schematic::placeStructure(Map *map, v3s16 p, u32 flags, Rotation rot,
198198
}
199199

200200

201-
bool Schematic::loadSchematicFromFile(const char *filename, INodeDefManager *ndef,
202-
StringMap *replace_names)
201+
bool Schematic::deserializeFromMts(std::istream *is,
202+
INodeDefManager *ndef, std::vector<std::string> *names)
203203
{
204+
std::istream &ss = *is;
204205
content_t cignore = CONTENT_IGNORE;
205206
bool have_cignore = false;
206207

207-
std::ifstream is(filename, std::ios_base::binary);
208-
if (!is.good()) {
209-
errorstream << "loadSchematicFile: unable to open file '"
210-
<< filename << "'" << std::endl;
211-
return false;
212-
}
213-
214-
u32 signature = readU32(is);
208+
u32 signature = readU32(ss);
215209
if (signature != MTSCHEM_FILE_SIGNATURE) {
216-
errorstream << "loadSchematicFile: invalid schematic "
210+
errorstream << "Schematic::deserializeFromMts: invalid schematic "
217211
"file" << std::endl;
218212
return false;
219213
}
220214

221-
u16 version = readU16(is);
215+
u16 version = readU16(ss);
222216
if (version > MTSCHEM_FILE_VER_HIGHEST_READ) {
223-
errorstream << "loadSchematicFile: unsupported schematic "
217+
errorstream << "Schematic::deserializeFromMts: unsupported schematic "
224218
"file version" << std::endl;
225219
return false;
226220
}
227221

228-
size = readV3S16(is);
222+
size = readV3S16(ss);
229223

230224
delete []slice_probs;
231225
slice_probs = new u8[size.Y];
232226
for (int y = 0; y != size.Y; y++)
233-
slice_probs[y] = (version >= 3) ? readU8(is) : MTSCHEM_PROB_ALWAYS;
227+
slice_probs[y] = (version >= 3) ? readU8(ss) : MTSCHEM_PROB_ALWAYS;
234228

235-
NodeResolveInfo *nri = new NodeResolveInfo(this);
236-
237-
u16 nidmapcount = readU16(is);
229+
u16 nidmapcount = readU16(ss);
238230
for (int i = 0; i != nidmapcount; i++) {
239-
std::string name = deSerializeString(is);
231+
std::string name = deSerializeString(ss);
232+
233+
// Instances of "ignore" from ver 1 are converted to air (and instances
234+
// are fixed to have MTSCHEM_PROB_NEVER later on).
240235
if (name == "ignore") {
241236
name = "air";
242237
cignore = i;
243238
have_cignore = true;
244239
}
245240

246-
std::map<std::string, std::string>::iterator it;
247-
it = replace_names->find(name);
248-
if (it != replace_names->end())
249-
name = it->second;
250-
251-
nri->nodenames.push_back(name);
241+
names->push_back(name);
252242
}
253243

254-
nri->nodelistinfo.push_back(NodeListInfo(nidmapcount, CONTENT_AIR));
255-
ndef->pendNodeResolve(nri);
256-
257244
size_t nodecount = size.X * size.Y * size.Z;
258245

259246
delete []schemdata;
260247
schemdata = new MapNode[nodecount];
261248

262-
MapNode::deSerializeBulk(is, SER_FMT_VER_HIGHEST_READ, schemdata,
249+
MapNode::deSerializeBulk(ss, SER_FMT_VER_HIGHEST_READ, schemdata,
263250
nodecount, 2, 2, true);
264251

265-
if (version == 1) { // fix up the probability values
252+
// fix any probability values for nodes that were ignore
253+
if (version == 1) {
266254
for (size_t i = 0; i != nodecount; i++) {
267255
if (schemdata[i].param1 == 0)
268256
schemdata[i].param1 = MTSCHEM_PROB_ALWAYS;
@@ -275,39 +263,9 @@ bool Schematic::loadSchematicFromFile(const char *filename, INodeDefManager *nde
275263
}
276264

277265

278-
/*
279-
Minetest Schematic File Format
280-
281-
All values are stored in big-endian byte order.
282-
[u32] signature: 'MTSM'
283-
[u16] version: 3
284-
[u16] size X
285-
[u16] size Y
286-
[u16] size Z
287-
For each Y:
288-
[u8] slice probability value
289-
[Name-ID table] Name ID Mapping Table
290-
[u16] name-id count
291-
For each name-id mapping:
292-
[u16] name length
293-
[u8[]] name
294-
ZLib deflated {
295-
For each node in schematic: (for z, y, x)
296-
[u16] content
297-
For each node in schematic:
298-
[u8] probability of occurance (param1)
299-
For each node in schematic:
300-
[u8] param2
301-
}
302-
303-
Version changes:
304-
1 - Initial version
305-
2 - Fixed messy never/always place; 0 probability is now never, 0xFF is always
306-
3 - Added y-slice probabilities; this allows for variable height structures
307-
*/
308-
void Schematic::saveSchematicToFile(const char *filename, INodeDefManager *ndef)
266+
bool Schematic::serializeToMts(std::ostream *os, INodeDefManager *ndef)
309267
{
310-
std::ostringstream ss(std::ios_base::binary);
268+
std::ostream &ss = *os;
311269

312270
writeU32(ss, MTSCHEM_FILE_SIGNATURE); // signature
313271
writeU16(ss, MTSCHEM_FILE_VER_HIGHEST_WRITE); // version
@@ -326,35 +284,108 @@ void Schematic::saveSchematicToFile(const char *filename, INodeDefManager *ndef)
326284
ss << serializeString(ndef->get(usednodes[i]).name); // node names
327285

328286
// compressed bulk node data
329-
MapNode::serializeBulk(ss, SER_FMT_VER_HIGHEST_WRITE, schemdata,
330-
nodecount, 2, 2, true);
287+
MapNode::serializeBulk(ss, SER_FMT_VER_HIGHEST_WRITE,
288+
schemdata, nodecount, 2, 2, true);
331289

332-
fs::safeWriteToFile(filename, ss.str());
290+
return true;
333291
}
334292

335293

336-
void build_nnlist_and_update_ids(MapNode *nodes, u32 nodecount,
337-
std::vector<content_t> *usednodes)
294+
bool Schematic::serializeToLua(std::ostream *os,
295+
INodeDefManager *ndef, bool use_comments)
338296
{
339-
std::map<content_t, content_t> nodeidmap;
340-
content_t numids = 0;
297+
std::ostream &ss = *os;
298+
299+
//// Write header
300+
{
301+
ss << "schematic = {" << std::endl;
302+
ss << "\tsize = "
303+
<< "{x=" << size.X
304+
<< ", y=" << size.Y
305+
<< ", z=" << size.Z
306+
<< "}," << std::endl;
307+
}
341308

342-
for (u32 i = 0; i != nodecount; i++) {
343-
content_t id;
344-
content_t c = nodes[i].getContent();
309+
//// Write y-slice probabilities
310+
{
311+
ss << "\tyslice_prob = {" << std::endl;
345312

346-
std::map<content_t, content_t>::const_iterator it = nodeidmap.find(c);
347-
if (it == nodeidmap.end()) {
348-
id = numids;
349-
numids++;
313+
for (u16 y = 0; y != size.Y; y++) {
314+
ss << "\t\t{"
315+
<< "ypos=" << y
316+
<< ", prob=" << (u16)slice_probs[y]
317+
<< "}," << std::endl;
318+
}
350319

351-
usednodes->push_back(c);
352-
nodeidmap.insert(std::make_pair(c, id));
353-
} else {
354-
id = it->second;
320+
ss << "\t}," << std::endl;
321+
}
322+
323+
//// Write node data
324+
{
325+
ss << "\tdata = {" << std::endl;
326+
327+
u32 i = 0;
328+
for (u16 z = 0; z != size.Z; z++)
329+
for (u16 y = 0; y != size.Y; y++) {
330+
if (use_comments) {
331+
ss << std::endl
332+
<< "\t\t-- z=" << z
333+
<< ", y=" << y << std::endl;
334+
}
335+
336+
for (u16 x = 0; x != size.X; x++, i++) {
337+
ss << "\t\t{"
338+
<< "name=\"" << ndef->get(schemdata[i]).name
339+
<< "\", param1=" << (u16)schemdata[i].param1
340+
<< ", param2=" << (u16)schemdata[i].param2
341+
<< "}," << std::endl;
342+
}
355343
}
356-
nodes[i].setContent(id);
344+
345+
ss << "\t}," << std::endl;
357346
}
347+
348+
ss << "}" << std::endl;
349+
350+
return true;
351+
}
352+
353+
354+
bool Schematic::loadSchematicFromFile(const char *filename,
355+
INodeDefManager *ndef, StringMap *replace_names)
356+
{
357+
std::ifstream is(filename, std::ios_base::binary);
358+
if (!is.good()) {
359+
errorstream << "Schematic::loadSchematicFile: unable to open file '"
360+
<< filename << "'" << std::endl;
361+
return false;
362+
}
363+
364+
std::vector<std::string> names;
365+
if (!deserializeFromMts(&is, ndef, &names))
366+
return false;
367+
368+
NodeResolveInfo *nri = new NodeResolveInfo(this);
369+
for (size_t i = 0; i != names.size(); i++) {
370+
if (replace_names) {
371+
StringMap::iterator it = replace_names->find(names[i]);
372+
if (it != replace_names->end())
373+
names[i] = it->second;
374+
}
375+
nri->nodenames.push_back(names[i]);
376+
}
377+
nri->nodelistinfo.push_back(NodeListInfo(names.size(), CONTENT_AIR));
378+
ndef->pendNodeResolve(nri);
379+
380+
return true;
381+
}
382+
383+
384+
bool Schematic::saveSchematicToFile(const char *filename, INodeDefManager *ndef)
385+
{
386+
std::ostringstream os(std::ios_base::binary);
387+
serializeToMts(&os, ndef);
388+
return fs::safeWriteToFile(filename, os.str());
358389
}
359390

360391

@@ -411,3 +442,28 @@ void Schematic::applyProbabilities(v3s16 p0,
411442
slice_probs[y] = (*splist)[i].second;
412443
}
413444
}
445+
446+
447+
void build_nnlist_and_update_ids(MapNode *nodes, u32 nodecount,
448+
std::vector<content_t> *usednodes)
449+
{
450+
std::map<content_t, content_t> nodeidmap;
451+
content_t numids = 0;
452+
453+
for (u32 i = 0; i != nodecount; i++) {
454+
content_t id;
455+
content_t c = nodes[i].getContent();
456+
457+
std::map<content_t, content_t>::const_iterator it = nodeidmap.find(c);
458+
if (it == nodeidmap.end()) {
459+
id = numids;
460+
numids++;
461+
462+
usednodes->push_back(c);
463+
nodeidmap.insert(std::make_pair(c, id));
464+
} else {
465+
id = it->second;
466+
}
467+
nodes[i].setContent(id);
468+
}
469+
}

Diff for: ‎src/mg_schematic.h

+46-2
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,40 @@ class MMVManip;
3030
class PseudoRandom;
3131
class NodeResolver;
3232

33+
/*
34+
Minetest Schematic File Format
35+
36+
All values are stored in big-endian byte order.
37+
[u32] signature: 'MTSM'
38+
[u16] version: 3
39+
[u16] size X
40+
[u16] size Y
41+
[u16] size Z
42+
For each Y:
43+
[u8] slice probability value
44+
[Name-ID table] Name ID Mapping Table
45+
[u16] name-id count
46+
For each name-id mapping:
47+
[u16] name length
48+
[u8[]] name
49+
ZLib deflated {
50+
For each node in schematic: (for z, y, x)
51+
[u16] content
52+
For each node in schematic:
53+
[u8] probability of occurance (param1)
54+
For each node in schematic:
55+
[u8] param2
56+
}
57+
58+
Version changes:
59+
1 - Initial version
60+
2 - Fixed messy never/always place; 0 probability is now never, 0xFF is always
61+
3 - Added y-slice probabilities; this allows for variable height structures
62+
*/
63+
3364
/////////////////// Schematic flags
3465
#define SCHEM_CIDS_UPDATED 0x08
3566

36-
3767
#define MTSCHEM_FILE_SIGNATURE 0x4d54534d // 'MTSM'
3868
#define MTSCHEM_FILE_VER_HIGHEST_READ 3
3969
#define MTSCHEM_FILE_VER_HIGHEST_WRITE 3
@@ -46,6 +76,11 @@ enum SchematicType
4676
SCHEMATIC_NORMAL,
4777
};
4878

79+
enum SchematicFormatType {
80+
SCHEM_FMT_HANDLE,
81+
SCHEM_FMT_MTS,
82+
SCHEM_FMT_LUA,
83+
};
4984

5085
class Schematic : public ObjDef, public NodeResolver {
5186
public:
@@ -68,14 +103,23 @@ class Schematic : public ObjDef, public NodeResolver {
68103

69104
bool loadSchematicFromFile(const char *filename, INodeDefManager *ndef,
70105
StringMap *replace_names);
71-
void saveSchematicToFile(const char *filename, INodeDefManager *ndef);
106+
bool saveSchematicToFile(const char *filename, INodeDefManager *ndef);
72107
bool getSchematicFromMap(Map *map, v3s16 p1, v3s16 p2);
73108

109+
bool deserializeFromMts(std::istream *is, INodeDefManager *ndef,
110+
std::vector<std::string> *names);
111+
bool serializeToMts(std::ostream *os, INodeDefManager *ndef);
112+
bool serializeToLua(std::ostream *os,
113+
INodeDefManager *ndef, bool use_comments);
114+
115+
74116
void placeStructure(Map *map, v3s16 p, u32 flags,
75117
Rotation rot, bool force_placement, INodeDefManager *nef);
76118
void applyProbabilities(v3s16 p0,
77119
std::vector<std::pair<v3s16, u8> > *plist,
78120
std::vector<std::pair<s16, u8> > *splist);
121+
122+
std::string getAsLuaTable(INodeDefManager *ndef, bool use_comments);
79123
};
80124

81125
class SchematicManager : public ObjDefManager {

Diff for: ‎src/script/lua_api/l_mapgen.cpp

+58-6
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
3636
#include "settings.h"
3737
#include "log.h"
3838

39-
4039
struct EnumString ModApiMapgen::es_BiomeTerrainType[] =
4140
{
4241
{BIOME_NORMAL, "normal"},
@@ -85,6 +84,13 @@ struct EnumString ModApiMapgen::es_Rotation[] =
8584
{0, NULL},
8685
};
8786

87+
struct EnumString ModApiMapgen::es_SchematicFormatType[] =
88+
{
89+
{SCHEM_FMT_HANDLE, "handle"},
90+
{SCHEM_FMT_MTS, "mts"},
91+
{SCHEM_FMT_LUA, "lua"},
92+
{0, NULL},
93+
};
8894

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

@@ -329,9 +335,11 @@ Schematic *read_schematic_def(lua_State *L, int index,
329335
param2 = !lua_isnil(L, -1) ? lua_tonumber(L, -1) : 0;
330336
lua_pop(L, 1);
331337

332-
StringMap::iterator it = replace_names->find(name);
333-
if (it != replace_names->end())
334-
name = it->second;
338+
if (replace_names) {
339+
StringMap::iterator it = replace_names->find(name);
340+
if (it != replace_names->end())
341+
name = it->second;
342+
}
335343

336344
schemdata[i] = MapNode(ndef, name, param1, param2);
337345

@@ -1067,8 +1075,9 @@ int ModApiMapgen::l_place_schematic(lua_State *L)
10671075

10681076
//// Read rotation
10691077
int rot = ROTATE_0;
1070-
if (lua_isstring(L, 3))
1071-
string_to_enum(es_Rotation, rot, std::string(lua_tostring(L, 3)));
1078+
const char *enumstr = lua_tostring(L, 3);
1079+
if (enumstr)
1080+
string_to_enum(es_Rotation, rot, std::string(enumstr));
10721081

10731082
//// Read force placement
10741083
bool force_placement = true;
@@ -1094,6 +1103,48 @@ int ModApiMapgen::l_place_schematic(lua_State *L)
10941103
return 1;
10951104
}
10961105

1106+
// serialize_schematic(schematic, format, use_comments)
1107+
int ModApiMapgen::l_serialize_schematic(lua_State *L)
1108+
{
1109+
SchematicManager *schemmgr = getServer(L)->getEmergeManager()->schemmgr;
1110+
INodeDefManager *ndef = getServer(L)->getNodeDefManager();
1111+
1112+
//// Read schematic
1113+
Schematic *schem = get_or_load_schematic(L, 1, schemmgr, NULL);
1114+
if (!schem) {
1115+
errorstream << "serialize_schematic: failed to get schematic" << std::endl;
1116+
return 0;
1117+
}
1118+
1119+
//// Read format of definition to save as
1120+
int schem_format = SCHEM_FMT_MTS;
1121+
const char *enumstr = lua_tostring(L, 2);
1122+
if (enumstr)
1123+
string_to_enum(es_SchematicFormatType, schem_format, std::string(enumstr));
1124+
1125+
//// Read use_comments
1126+
bool use_comments = false;
1127+
if (lua_isboolean(L, 3))
1128+
use_comments = lua_toboolean(L, 3);
1129+
1130+
//// Serialize to binary string
1131+
std::ostringstream os(std::ios_base::binary);
1132+
switch (schem_format) {
1133+
case SCHEM_FMT_MTS:
1134+
schem->serializeToMts(&os, ndef);
1135+
break;
1136+
case SCHEM_FMT_LUA:
1137+
schem->serializeToLua(&os, ndef, use_comments);
1138+
break;
1139+
default:
1140+
return 0;
1141+
}
1142+
1143+
std::string ser = os.str();
1144+
lua_pushlstring(L, ser.c_str(), ser.length());
1145+
return 1;
1146+
}
1147+
10971148

10981149
void ModApiMapgen::Initialize(lua_State *L, int top)
10991150
{
@@ -1118,4 +1169,5 @@ void ModApiMapgen::Initialize(lua_State *L, int top)
11181169
API_FCT(generate_decorations);
11191170
API_FCT(create_schematic);
11201171
API_FCT(place_schematic);
1172+
API_FCT(serialize_schematic);
11211173
}

Diff for: ‎src/script/lua_api/l_mapgen.h

+4
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,9 @@ class ModApiMapgen : public ModApiBase {
8484
// place_schematic(p, schematic, rotation, replacement)
8585
static int l_place_schematic(lua_State *L);
8686

87+
// serialize_schematic(schematic, format, use_comments)
88+
static int l_serialize_schematic(lua_State *L);
89+
8790
public:
8891
static void Initialize(lua_State *L, int top);
8992

@@ -92,6 +95,7 @@ class ModApiMapgen : public ModApiBase {
9295
static struct EnumString es_MapgenObject[];
9396
static struct EnumString es_OreType[];
9497
static struct EnumString es_Rotation[];
98+
static struct EnumString es_SchematicFormatType[];
9599
};
96100

97101
#endif /* L_MAPGEN_H_ */

0 commit comments

Comments
 (0)
Please sign in to comment.