X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=src%2Fmg_schematic.cpp;h=3cea3c321e66d26e08ee4f261922f14ebda6335c;hb=ff73c7a5da6ab8ac0bb678ebf25b83e805397029;hp=0ecbd70834d3b6a102052aa6a42fe268bbbce3dd;hpb=08d259cf41db805ada7c39a39ffe9dcec4f0c230;p=dragonfireclient.git diff --git a/src/mg_schematic.cpp b/src/mg_schematic.cpp index 0ecbd7083..3cea3c321 100644 --- a/src/mg_schematic.cpp +++ b/src/mg_schematic.cpp @@ -1,6 +1,7 @@ /* Minetest -Copyright (C) 2010-2014 kwolekr, Ryan Kwolek +Copyright (C) 2014-2016 kwolekr, Ryan Kwolek +Copyright (C) 2015-2017 paramat 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 @@ -18,8 +19,11 @@ with this program; if not, write to the Free Software Foundation, Inc., */ #include +#include #include "mg_schematic.h" +#include "server.h" #include "mapgen.h" +#include "emerge.h" #include "map.h" #include "mapblock.h" #include "log.h" @@ -27,15 +31,36 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "util/serialize.h" #include "serialization.h" #include "filesys.h" - -const char *SchematicManager::ELEMENT_TITLE = "schematic"; +#include "voxelalgorithms.h" /////////////////////////////////////////////////////////////////////////////// -SchematicManager::SchematicManager(IGameDef *gamedef) : - GenElementManager(gamedef) +SchematicManager::SchematicManager(Server *server) : + ObjDefManager(server, OBJDEF_SCHEMATIC) { + m_server = server; +} + + +void SchematicManager::clear() +{ + EmergeManager *emerge = m_server->getEmergeManager(); + + // Remove all dangling references in Decorations + DecorationManager *decomgr = emerge->decomgr; + for (size_t i = 0; i != decomgr->getNumObjects(); i++) { + Decoration *deco = (Decoration *)decomgr->getRaw(i); + + try { + DecoSchematic *dschem = dynamic_cast(deco); + if (dschem) + dschem->schematic = NULL; + } catch (std::bad_cast) { + } + } + + ObjDefManager::clear(); } @@ -58,34 +83,27 @@ Schematic::~Schematic() } -void Schematic::resolveNodeNames(NodeResolveInfo *nri) +void Schematic::resolveNodeNames() { - m_ndef->getIdsFromResolveInfo(nri, c_nodes); -} - - -void Schematic::updateContentIds() -{ - if (flags & SCHEM_CIDS_UPDATED) - return; - - flags |= SCHEM_CIDS_UPDATED; + getIdsFromNrBacklog(&c_nodes, true, CONTENT_AIR); size_t bufsize = size.X * size.Y * size.Z; - for (size_t i = 0; i != bufsize; i++) - schemdata[i].setContent(c_nodes[schemdata[i].getContent()]); + for (size_t i = 0; i != bufsize; i++) { + content_t c_original = schemdata[i].getContent(); + content_t c_new = c_nodes[c_original]; + schemdata[i].setContent(c_new); + } } -void Schematic::blitToVManip(v3s16 p, ManualMapVoxelManipulator *vm, - Rotation rot, bool force_placement, INodeDefManager *ndef) +void Schematic::blitToVManip(MMVManip *vm, v3s16 p, Rotation rot, bool force_place) { + sanity_check(m_ndef != NULL); + int xstride = 1; int ystride = size.X; int zstride = size.X * size.Y; - updateContentIds(); - s16 sx = size.X; s16 sy = size.Y; s16 sz = size.Z; @@ -117,8 +135,8 @@ void Schematic::blitToVManip(v3s16 p, ManualMapVoxelManipulator *vm, 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++) { @@ -131,24 +149,27 @@ void Schematic::blitToVManip(v3s16 p, ManualMapVoxelManipulator *vm, 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_placement) { + 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]; vm->m_data[vi].param1 = 0; if (rot) - vm->m_data[vi].rotateAlongYAxis(ndef, rot); + vm->m_data[vi].rotateAlongYAxis(m_ndef, rot); } } y_map++; @@ -156,18 +177,21 @@ void Schematic::blitToVManip(v3s16 p, ManualMapVoxelManipulator *vm, } -void Schematic::placeStructure(Map *map, v3s16 p, u32 flags, - Rotation rot, bool force_placement, INodeDefManager *ndef) +bool Schematic::placeOnVManip(MMVManip *vm, v3s16 p, u32 flags, + Rotation rot, bool force_place) { + assert(vm != NULL); assert(schemdata != NULL); - ManualMapVoxelManipulator *vm = new ManualMapVoxelManipulator(map); + sanity_check(m_ndef != NULL); + //// Determine effective rotation and effective schematic dimensions if (rot == ROTATE_RAND) rot = (Rotation)myrand_range(ROTATE_0, ROTATE_270); v3s16 s = (rot == ROTATE_90 || rot == ROTATE_270) ? - v3s16(size.Z, size.Y, size.X) : size; + v3s16(size.Z, size.Y, size.X) : size; + //// Adjust placement position if necessary if (flags & DECO_PLACE_CENTER_X) p.X -= (s.X + 1) / 2; if (flags & DECO_PLACE_CENTER_Y) @@ -175,136 +199,144 @@ void Schematic::placeStructure(Map *map, v3s16 p, u32 flags, if (flags & DECO_PLACE_CENTER_Z) p.Z -= (s.Z + 1) / 2; - v3s16 bp1 = getNodeBlockPos(p); - v3s16 bp2 = getNodeBlockPos(p + s - v3s16(1,1,1)); - vm->initialEmerge(bp1, bp2); + blitToVManip(vm, p, rot, force_place); - blitToVManip(p, vm, rot, force_placement, ndef); + return vm->m_area.contains(VoxelArea(p, p + s - v3s16(1,1,1))); +} +void Schematic::placeOnMap(ServerMap *map, v3s16 p, u32 flags, + Rotation rot, bool force_place) +{ std::map lighting_modified_blocks; std::map modified_blocks; - vm->blitBackAll(&modified_blocks); + std::map::iterator it; + + assert(map != NULL); + assert(schemdata != NULL); + sanity_check(m_ndef != NULL); + + //// Determine effective rotation and effective schematic dimensions + if (rot == ROTATE_RAND) + rot = (Rotation)myrand_range(ROTATE_0, ROTATE_270); - // TODO: Optimize this by using Mapgen::calcLighting() instead - lighting_modified_blocks.insert(modified_blocks.begin(), modified_blocks.end()); - map->updateLighting(lighting_modified_blocks, modified_blocks); + v3s16 s = (rot == ROTATE_90 || rot == ROTATE_270) ? + v3s16(size.Z, size.Y, size.X) : size; + //// Adjust placement position if necessary + if (flags & DECO_PLACE_CENTER_X) + p.X -= (s.X + 1) / 2; + if (flags & DECO_PLACE_CENTER_Y) + p.Y -= (s.Y + 1) / 2; + if (flags & DECO_PLACE_CENTER_Z) + p.Z -= (s.Z + 1) / 2; + + //// Create VManip for effected area, emerge our area, modify area + //// inside VManip, then blit back. + v3s16 bp1 = getNodeBlockPos(p); + v3s16 bp2 = getNodeBlockPos(p + s - v3s16(1,1,1)); + + MMVManip vm(map); + vm.initialEmerge(bp1, bp2); + + blitToVManip(&vm, p, rot, force_place); + + voxalgo::blit_back_with_light(map, &vm, &modified_blocks); + + //// Carry out post-map-modification actions + + //// Create & dispatch map modification events to observers MapEditEvent event; event.type = MEET_OTHER; - for (std::map::iterator - it = modified_blocks.begin(); - it != modified_blocks.end(); ++it) + for (it = modified_blocks.begin(); it != modified_blocks.end(); ++it) event.modified_blocks.insert(it->first); map->dispatchEvent(&event); } -bool Schematic::loadSchematicFromFile(const char *filename, - INodeDefManager *ndef, std::map &replace_names) +bool Schematic::deserializeFromMts(std::istream *is, + std::vector *names) { + std::istream &ss = *is; content_t cignore = CONTENT_IGNORE; bool have_cignore = false; - std::ifstream is(filename, std::ios_base::binary); - - u32 signature = readU32(is); + //// Read signature + u32 signature = readU32(ss); if (signature != MTSCHEM_FILE_SIGNATURE) { - errorstream << "loadSchematicFile: invalid schematic " + errorstream << __FUNCTION__ << ": invalid schematic " "file" << std::endl; return false; } - u16 version = readU16(is); + //// Read version + u16 version = readU16(ss); if (version > MTSCHEM_FILE_VER_HIGHEST_READ) { - errorstream << "loadSchematicFile: unsupported schematic " + errorstream << __FUNCTION__ << ": unsupported schematic " "file version" << std::endl; return false; } - size = readV3S16(is); + //// 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(is) : MTSCHEM_PROB_ALWAYS; + slice_probs[y] = (version >= 3) ? readU8(ss) : MTSCHEM_PROB_ALWAYS_OLD; - NodeResolveInfo *nri = new NodeResolveInfo(this); - - u16 nidmapcount = readU16(is); + //// Read node names + u16 nidmapcount = readU16(ss); for (int i = 0; i != nidmapcount; i++) { - std::string name = deSerializeString(is); + std::string name = deSerializeString(ss); + + // 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"; cignore = i; have_cignore = true; } - std::map::iterator it; - it = replace_names.find(name); - if (it != replace_names.end()) - name = it->second; - - nri->nodenames.push_back(name); + names->push_back(name); } - nri->nodelistinfo.push_back(NodeListInfo(nidmapcount, CONTENT_AIR)); - ndef->pendNodeResolve(nri); - + //// Read node data size_t nodecount = size.X * size.Y * size.Z; delete []schemdata; schemdata = new MapNode[nodecount]; - MapNode::deSerializeBulk(is, SER_FMT_VER_HIGHEST_READ, schemdata, + MapNode::deSerializeBulk(ss, SER_FMT_VER_HIGHEST_READ, schemdata, nodecount, 2, 2, true); - if (version == 1) { // fix up the probability values + // 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; + schemdata[i].param1 = MTSCHEM_PROB_ALWAYS_OLD; if (have_cignore && schemdata[i].getContent() == cignore) schemdata[i].param1 = MTSCHEM_PROB_NEVER; } } + // 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; } -/* - Minetest Schematic File Format - - All values are stored in big-endian byte order. - [u32] signature: 'MTSM' - [u16] version: 3 - [u16] size X - [u16] size Y - [u16] size Z - For each Y: - [u8] slice probability value - [Name-ID table] Name ID Mapping Table - [u16] name-id count - For each name-id mapping: - [u16] name length - [u8[]] name - ZLib deflated { - For each node in schematic: (for z, y, x) - [u16] content - For each node in schematic: - [u8] probability of occurance (param1) - For each node in schematic: - [u8] param2 - } - - Version changes: - 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 -*/ -void Schematic::saveSchematicToFile(const char *filename, INodeDefManager *ndef) +bool Schematic::serializeToMts(std::ostream *os, + const std::vector &names) { - std::ostringstream ss(std::ios_base::binary); + std::ostream &ss = *os; writeU32(ss, MTSCHEM_FILE_SIGNATURE); // signature writeU16(ss, MTSCHEM_FILE_VER_HIGHEST_WRITE); // version @@ -313,51 +345,167 @@ void Schematic::saveSchematicToFile(const char *filename, INodeDefManager *ndef) for (int y = 0; y != size.Y; y++) // Y slice probabilities writeU8(ss, slice_probs[y]); - std::vector usednodes; - int nodecount = size.X * size.Y * size.Z; - build_nnlist_and_update_ids(schemdata, nodecount, &usednodes); - - u16 numids = usednodes.size(); - writeU16(ss, numids); // name count - for (int i = 0; i != numids; i++) - ss << serializeString(ndef->get(usednodes[i]).name); // node names + writeU16(ss, names.size()); // name count + for (size_t i = 0; i != names.size(); i++) + ss << serializeString(names[i]); // node names // compressed bulk node data - MapNode::serializeBulk(ss, SER_FMT_VER_HIGHEST_WRITE, schemdata, - nodecount, 2, 2, true); + MapNode::serializeBulk(ss, SER_FMT_VER_HIGHEST_WRITE, + schemdata, size.X * size.Y * size.Z, 2, 2, true); - fs::safeWriteToFile(filename, ss.str()); + return true; } -void build_nnlist_and_update_ids(MapNode *nodes, u32 nodecount, - std::vector *usednodes) +bool Schematic::serializeToLua(std::ostream *os, + const std::vector &names, bool use_comments, u32 indent_spaces) { - std::map nodeidmap; - content_t numids = 0; + std::ostream &ss = *os; + + std::string indent("\t"); + if (indent_spaces > 0) + indent.assign(indent_spaces, ' '); + + //// Write header + { + ss << "schematic = {" << std::endl; + ss << indent << "size = " + << "{x=" << size.X + << ", y=" << size.Y + << ", z=" << size.Z + << "}," << std::endl; + } - for (u32 i = 0; i != nodecount; i++) { - content_t id; - content_t c = nodes[i].getContent(); + //// Write y-slice probabilities + { + ss << indent << "yslice_prob = {" << std::endl; - std::map::const_iterator it = nodeidmap.find(c); - if (it == nodeidmap.end()) { - id = numids; - numids++; + for (u16 y = 0; y != size.Y; y++) { + u8 probability = slice_probs[y] & MTSCHEM_PROB_MASK; - usednodes->push_back(c); - nodeidmap.insert(std::make_pair(c, id)); - } else { - id = it->second; + ss << indent << indent << "{" + << "ypos=" << y + << ", prob=" << (u16)probability * 2 + << "}," << std::endl; } - nodes[i].setContent(id); + + ss << indent << "}," << std::endl; } + + //// Write node data + { + ss << indent << "data = {" << std::endl; + + u32 i = 0; + for (u16 z = 0; z != size.Z; z++) + for (u16 y = 0; y != size.Y; y++) { + if (use_comments) { + ss << std::endl + << indent << indent + << "-- z=" << z + << ", y=" << y << std::endl; + } + + 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()] + << "\", prob=" << (u16)probability * 2 + << ", param2=" << (u16)schemdata[i].param2; + + if (force_place) + ss << ", force_place=true"; + + ss << "}," << std::endl; + } + } + + ss << indent << "}," << std::endl; + } + + ss << "}" << std::endl; + + return true; +} + + +bool Schematic::loadSchematicFromFile(const std::string &filename, + INodeDefManager *ndef, StringMap *replace_names) +{ + std::ifstream is(filename.c_str(), std::ios_base::binary); + if (!is.good()) { + errorstream << __FUNCTION__ << ": unable to open file '" + << filename << "'" << std::endl; + return false; + } + + size_t origsize = m_nodenames.size(); + if (!deserializeFromMts(&is, &m_nodenames)) + return false; + + m_nnlistsizes.push_back(m_nodenames.size() - origsize); + + name = filename; + + if (replace_names) { + for (size_t i = origsize; i < m_nodenames.size(); i++) { + std::string &node_name = m_nodenames[i]; + StringMap::iterator it = replace_names->find(node_name); + if (it != replace_names->end()) + node_name = it->second; + } + } + + if (ndef) + ndef->pendNodeResolve(this); + + return true; +} + + +bool Schematic::saveSchematicToFile(const std::string &filename, + INodeDefManager *ndef) +{ + MapNode *orig_schemdata = schemdata; + std::vector ndef_nodenames; + std::vector *names; + + if (m_resolve_done && ndef == NULL) + ndef = m_ndef; + + if (ndef) { + names = &ndef_nodenames; + + u32 volume = size.X * size.Y * size.Z; + schemdata = new MapNode[volume]; + for (u32 i = 0; i != volume; i++) + schemdata[i] = orig_schemdata[i]; + + generate_nodelist_and_update_ids(schemdata, volume, names, ndef); + } else { // otherwise, use the names we have on hand in the list + names = &m_nodenames; + } + + std::ostringstream os(std::ios_base::binary); + bool status = serializeToMts(&os, *names); + + if (ndef) { + delete []schemdata; + schemdata = orig_schemdata; + } + + if (!status) + return false; + + return fs::safeWriteToFile(filename, os.str()); } bool Schematic::getSchematicFromMap(Map *map, v3s16 p1, v3s16 p2) { - ManualMapVoxelManipulator *vm = new ManualMapVoxelManipulator(map); + MMVManip *vm = new MMVManip(map); v3s16 bp1 = getNodeBlockPos(p1); v3s16 bp2 = getNodeBlockPos(p2); @@ -408,3 +556,28 @@ void Schematic::applyProbabilities(v3s16 p0, slice_probs[y] = (*splist)[i].second; } } + + +void generate_nodelist_and_update_ids(MapNode *nodes, size_t nodecount, + std::vector *usednodes, INodeDefManager *ndef) +{ + std::unordered_map nodeidmap; + content_t numids = 0; + + for (size_t i = 0; i != nodecount; i++) { + content_t id; + content_t c = nodes[i].getContent(); + + std::unordered_map::const_iterator it = nodeidmap.find(c); + if (it == nodeidmap.end()) { + id = numids; + numids++; + + usednodes->push_back(ndef->get(c).name); + nodeidmap.insert(std::make_pair(c, id)); + } else { + id = it->second; + } + nodes[i].setContent(id); + } +}