/* Minetest Copyright (C) 2013 celeron55, Perttu Ahola 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 "nodedef.h" #include "main.h" // For g_settings #include "itemdef.h" #ifndef SERVER #include "tile.h" #endif #include "log.h" #include "settings.h" #include "nameidmapping.h" #include "util/serialize.h" //#include "profiler.h" // For TimeTaker /* NodeBox */ void NodeBox::reset() { type = NODEBOX_REGULAR; // default is empty fixed.clear(); // default is sign/ladder-like wall_top = aabb3f(-BS/2, BS/2-BS/16., -BS/2, BS/2, BS/2, BS/2); wall_bottom = aabb3f(-BS/2, -BS/2, -BS/2, BS/2, -BS/2+BS/16., BS/2); wall_side = aabb3f(-BS/2, -BS/2, -BS/2, -BS/2+BS/16., BS/2, BS/2); } void NodeBox::serialize(std::ostream &os) const { writeU8(os, 1); // version writeU8(os, type); if(type == NODEBOX_FIXED || type == NODEBOX_LEVELED) { writeU16(os, fixed.size()); for(std::vector::const_iterator i = fixed.begin(); i != fixed.end(); i++) { writeV3F1000(os, i->MinEdge); writeV3F1000(os, i->MaxEdge); } } else if(type == NODEBOX_WALLMOUNTED) { writeV3F1000(os, wall_top.MinEdge); writeV3F1000(os, wall_top.MaxEdge); writeV3F1000(os, wall_bottom.MinEdge); writeV3F1000(os, wall_bottom.MaxEdge); writeV3F1000(os, wall_side.MinEdge); writeV3F1000(os, wall_side.MaxEdge); } } void NodeBox::deSerialize(std::istream &is) { int version = readU8(is); if(version != 1) throw SerializationError("unsupported NodeBox version"); reset(); type = (enum NodeBoxType)readU8(is); if(type == NODEBOX_FIXED || type == NODEBOX_LEVELED) { u16 fixed_count = readU16(is); while(fixed_count--) { aabb3f box; box.MinEdge = readV3F1000(is); box.MaxEdge = readV3F1000(is); fixed.push_back(box); } } else if(type == NODEBOX_WALLMOUNTED) { wall_top.MinEdge = readV3F1000(is); wall_top.MaxEdge = readV3F1000(is); wall_bottom.MinEdge = readV3F1000(is); wall_bottom.MaxEdge = readV3F1000(is); wall_side.MinEdge = readV3F1000(is); wall_side.MaxEdge = readV3F1000(is); } } /* TileDef */ void TileDef::serialize(std::ostream &os, u16 protocol_version) const { if(protocol_version >= 17) writeU8(os, 1); else writeU8(os, 0); os<= 17) writeU8(os, backface_culling); } void TileDef::deSerialize(std::istream &is) { int version = readU8(is); name = deSerializeString(is); animation.type = (TileAnimationType)readU8(is); animation.aspect_w = readU16(is); animation.aspect_h = readU16(is); animation.length = readF1000(is); if(version >= 1) backface_culling = readU8(is); } /* SimpleSoundSpec serialization */ static void serializeSimpleSoundSpec(const SimpleSoundSpec &ss, std::ostream &os) { os<first); writeS16(os, i->second); } writeU8(os, drawtype); writeF1000(os, visual_scale); writeU8(os, 6); for(u32 i=0; i<6; i++) tiledef[i].serialize(os, protocol_version); writeU8(os, CF_SPECIAL_COUNT); for(u32 i=0; iset(i, get(i)); } return mgr; } virtual const ContentFeatures& get(content_t c) const { assert(c <= MAX_CONTENT); return m_content_features[c]; } virtual const ContentFeatures& get(const MapNode &n) const { return get(n.getContent()); } virtual bool getId(const std::string &name, content_t &result) const { std::map::const_iterator i = m_name_id_mapping_with_aliases.find(name); if(i == m_name_id_mapping_with_aliases.end()) return false; result = i->second; return true; } virtual content_t getId(const std::string &name) const { content_t id = CONTENT_IGNORE; getId(name, id); return id; } virtual void getIds(const std::string &name, std::set &result) const { //TimeTaker t("getIds", NULL, PRECISION_MICRO); if(name.substr(0,6) != "group:"){ content_t id = CONTENT_IGNORE; if(getId(name, id)) result.insert(id); return; } std::string group = name.substr(6); #if 1 // Optimized version, takes less than 1 microsecond at -O1 std::map::const_iterator i = m_group_to_items.find(group); if (i == m_group_to_items.end()) return; const GroupItems &items = i->second; for (GroupItems::const_iterator j = items.begin(); j != items.end(); ++j) { if ((*j).second != 0) result.insert((*j).first); } #else // Old version, takes about ~150-200us at -O1 for(u16 id=0; id<=MAX_CONTENT; id++) { const ContentFeatures &f = m_content_features[id]; if(f.name == "") // Quickly discard undefined nodes continue; if(itemgroup_get(f.groups, group) != 0) result.insert(id); } #endif //printf("getIds: %dus\n", t.stop()); } virtual const ContentFeatures& get(const std::string &name) const { content_t id = CONTENT_IGNORE; getId(name, id); return get(id); } // IWritableNodeDefManager virtual void set(content_t c, const ContentFeatures &def) { verbosestream<<"registerNode: registering content id \""<first; std::map::iterator j = m_group_to_items.find(group_name); if (j == m_group_to_items.end()) { m_group_to_items[group_name].push_back(std::make_pair(c, i->second)); } else { GroupItems &items = j->second; items.push_back(std::make_pair(c, i->second)); } } } virtual content_t set(const std::string &name, const ContentFeatures &def) { assert(name == def.name); u16 id = CONTENT_IGNORE; bool found = m_name_id_mapping.getId(name, id); // ignore aliases if(!found){ // Get some id id = getFreeId(); if(id == CONTENT_IGNORE) return CONTENT_IGNORE; if(name != "") addNameIdMapping(id, name); } set(id, def); return id; } virtual content_t allocateDummy(const std::string &name) { assert(name != ""); ContentFeatures f; f.name = name; return set(name, f); } virtual void updateAliases(IItemDefManager *idef) { std::set all = idef->getAll(); m_name_id_mapping_with_aliases.clear(); for(std::set::iterator i = all.begin(); i != all.end(); i++) { std::string name = *i; std::string convert_to = idef->getAlias(name); content_t id; if(m_name_id_mapping.getId(convert_to, id)) { m_name_id_mapping_with_aliases.insert( std::make_pair(name, id)); } } } virtual void updateTextures(ITextureSource *tsrc) { #ifndef SERVER infostream<<"CNodeDefManager::updateTextures(): Updating " <<"textures in node definitions"<getBool("new_style_water"); bool new_style_leaves = g_settings->getBool("new_style_leaves"); bool opaque_water = g_settings->getBool("opaque_water"); for(u16 i=0; i<=MAX_CONTENT; i++) { ContentFeatures *f = &m_content_features[i]; // Figure out the actual tiles to use TileDef tiledef[6]; for(u32 j=0; j<6; j++) { tiledef[j] = f->tiledef[j]; if(tiledef[j].name == "") tiledef[j].name = "unknown_node.png"; } bool is_liquid = false; switch(f->drawtype){ default: case NDT_NORMAL: f->solidness = 2; break; case NDT_AIRLIKE: f->solidness = 0; break; case NDT_LIQUID: assert(f->liquid_type == LIQUID_SOURCE); if(opaque_water) f->alpha = 255; if(new_style_water){ f->solidness = 0; } else { f->solidness = 1; f->backface_culling = false; } is_liquid = true; break; case NDT_FLOWINGLIQUID: assert(f->liquid_type == LIQUID_FLOWING); f->solidness = 0; if(opaque_water) f->alpha = 255; is_liquid = true; break; case NDT_GLASSLIKE: f->solidness = 0; f->visual_solidness = 1; break; case NDT_GLASSLIKE_FRAMED: f->solidness = 0; f->visual_solidness = 1; break; case NDT_ALLFACES: f->solidness = 0; f->visual_solidness = 1; break; case NDT_ALLFACES_OPTIONAL: if(new_style_leaves){ f->drawtype = NDT_ALLFACES; f->solidness = 0; f->visual_solidness = 1; } else { f->drawtype = NDT_NORMAL; f->solidness = 2; for(u32 i=0; i<6; i++){ tiledef[i].name += std::string("^[noalpha"); } } break; case NDT_PLANTLIKE: f->solidness = 0; f->backface_culling = false; break; case NDT_TORCHLIKE: case NDT_SIGNLIKE: case NDT_FENCELIKE: case NDT_RAILLIKE: case NDT_NODEBOX: f->solidness = 0; break; } u8 material_type; if (is_liquid) material_type = (f->alpha == 255) ? TILE_MATERIAL_LIQUID_OPAQUE : TILE_MATERIAL_LIQUID_TRANSPARENT; else material_type = (f->alpha == 255) ? TILE_MATERIAL_BASIC : TILE_MATERIAL_ALPHA; // Tiles (fill in f->tiles[]) for(u16 j=0; j<6; j++){ // Texture f->tiles[j].texture = tsrc->getTexture( tiledef[j].name, &f->tiles[j].texture_id); // Alpha f->tiles[j].alpha = f->alpha; // Material type f->tiles[j].material_type = material_type; // Material flags f->tiles[j].material_flags = 0; if(f->backface_culling) f->tiles[j].material_flags |= MATERIAL_FLAG_BACKFACE_CULLING; if(tiledef[j].animation.type == TAT_VERTICAL_FRAMES) f->tiles[j].material_flags |= MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES; // Animation parameters if(f->tiles[j].material_flags & MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES) { // Get texture size to determine frame count by // aspect ratio v2u32 size = f->tiles[j].texture->getOriginalSize(); int frame_height = (float)size.X / (float)tiledef[j].animation.aspect_w * (float)tiledef[j].animation.aspect_h; int frame_count = size.Y / frame_height; int frame_length_ms = 1000.0 * tiledef[j].animation.length / frame_count; f->tiles[j].animation_frame_count = frame_count; f->tiles[j].animation_frame_length_ms = frame_length_ms; // If there are no frames for an animation, switch // animation off (so that having specified an animation // for something but not using it in the texture pack // gives no overhead) if(frame_count == 1){ f->tiles[j].material_flags &= ~MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES; } } } // Special tiles (fill in f->special_tiles[]) for(u16 j=0; jspecial_tiles[j].texture = tsrc->getTexture( f->tiledef_special[j].name, &f->special_tiles[j].texture_id); // Alpha f->special_tiles[j].alpha = f->alpha; // Material type f->special_tiles[j].material_type = material_type; // Material flags f->special_tiles[j].material_flags = 0; if(f->tiledef_special[j].backface_culling) f->special_tiles[j].material_flags |= MATERIAL_FLAG_BACKFACE_CULLING; if(f->tiledef_special[j].animation.type == TAT_VERTICAL_FRAMES) f->special_tiles[j].material_flags |= MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES; // Animation parameters if(f->special_tiles[j].material_flags & MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES) { // Get texture size to determine frame count by // aspect ratio v2u32 size = f->special_tiles[j].texture->getOriginalSize(); int frame_height = (float)size.X / (float)f->tiledef_special[j].animation.aspect_w * (float)f->tiledef_special[j].animation.aspect_h; int frame_count = size.Y / frame_height; int frame_length_ms = 1000.0 * f->tiledef_special[j].animation.length / frame_count; f->special_tiles[j].animation_frame_count = frame_count; f->special_tiles[j].animation_frame_length_ms = frame_length_ms; // If there are no frames for an animation, switch // animation off (so that having specified an animation // for something but not using it in the texture pack // gives no overhead) if(frame_count == 1){ f->special_tiles[j].material_flags &= ~MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES; } } } } #endif } void serialize(std::ostream &os, u16 protocol_version) { writeU8(os, 1); // version u16 count = 0; std::ostringstream os2(std::ios::binary); for(u16 i=0; i<=MAX_CONTENT; i++) { if(i == CONTENT_IGNORE || i == CONTENT_AIR) continue; ContentFeatures *f = &m_content_features[i]; if(f->name == "") continue; writeU16(os2, i); // Wrap it in a string to allow different lengths without // strict version incompatibilities std::ostringstream wrapper_os(std::ios::binary); f->serialize(wrapper_os, protocol_version); os2< MAX_CONTENT){ errorstream<<"ContentFeatures::deSerialize(): " <<"Too large content id: "<deSerialize(wrapper_is); verbosestream<<"deserialized "<name<name != "") addNameIdMapping(i, f->name); } } private: void addNameIdMapping(content_t i, std::string name) { m_name_id_mapping.set(i, name); m_name_id_mapping_with_aliases.insert(std::make_pair(name, i)); } private: // Features indexed by id ContentFeatures m_content_features[MAX_CONTENT+1]; // A mapping for fast converting back and forth between names and ids NameIdMapping m_name_id_mapping; // Like m_name_id_mapping, but only from names to ids, and includes // item aliases too. Updated by updateAliases() // Note: Not serialized. std::map m_name_id_mapping_with_aliases; // A mapping from groups to a list of content_ts (and their levels) // that belong to it. Necessary for a direct lookup in getIds(). // Note: Not serialized. std::map m_group_to_items; }; IWritableNodeDefManager* createNodeDefManager() { return new CNodeDefManager(); } /* Serialization of old ContentFeatures formats */ void ContentFeatures::serializeOld(std::ostream &os, u16 protocol_version) { if(protocol_version == 13) { writeU8(os, 5); // version os<first); writeS16(os, i->second); } writeU8(os, drawtype); writeF1000(os, visual_scale); writeU8(os, 6); for(u32 i=0; i<6; i++) tiledef[i].serialize(os, protocol_version); writeU8(os, CF_SPECIAL_COUNT); for(u32 i=0; i