/* 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 "itemdef.h" #ifndef SERVER #include "mesh.h" #include "shader.h" #include "client.h" #include "client/renderingengine.h" #include "client/tile.h" #include #endif #include "log.h" #include "settings.h" #include "nameidmapping.h" #include "util/numeric.h" #include "util/serialize.h" #include "exceptions.h" #include "debug.h" #include "gamedef.h" #include "mapnode.h" #include // Used in applyTextureOverrides() /* 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); // no default for other parts connect_top.clear(); connect_bottom.clear(); connect_front.clear(); connect_left.clear(); connect_back.clear(); connect_right.clear(); } void NodeBox::serialize(std::ostream &os, u16 protocol_version) const { // Protocol >= 21 int version = 2; if (protocol_version >= 27) version = 3; writeU8(os, version); switch (type) { case NODEBOX_LEVELED: case NODEBOX_FIXED: if (version == 1) writeU8(os, NODEBOX_FIXED); else writeU8(os, type); writeU16(os, fixed.size()); for (std::vector::const_iterator i = fixed.begin(); i != fixed.end(); ++i) { writeV3F1000(os, i->MinEdge); writeV3F1000(os, i->MaxEdge); } break; case NODEBOX_WALLMOUNTED: writeU8(os, type); 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); break; case NODEBOX_CONNECTED: if (version <= 2) { // send old clients nodes that can't be walked through // to prevent abuse writeU8(os, NODEBOX_FIXED); writeU16(os, 1); writeV3F1000(os, v3f(-BS/2, -BS/2, -BS/2)); writeV3F1000(os, v3f(BS/2, BS/2, BS/2)); } else { writeU8(os, type); #define WRITEBOX(box) do { \ writeU16(os, (box).size()); \ for (std::vector::const_iterator \ i = (box).begin(); \ i != (box).end(); ++i) { \ writeV3F1000(os, i->MinEdge); \ writeV3F1000(os, i->MaxEdge); \ }; } while (0) WRITEBOX(fixed); WRITEBOX(connect_top); WRITEBOX(connect_bottom); WRITEBOX(connect_front); WRITEBOX(connect_left); WRITEBOX(connect_back); WRITEBOX(connect_right); } break; default: writeU8(os, type); break; } } void NodeBox::deSerialize(std::istream &is) { int version = readU8(is); if (version < 1 || version > 3) 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); } else if (type == NODEBOX_CONNECTED) { #define READBOXES(box) do { \ count = readU16(is); \ (box).reserve(count); \ while (count--) { \ v3f min = readV3F1000(is); \ v3f max = readV3F1000(is); \ (box).push_back(aabb3f(min, max)); }; } while (0) u16 count; READBOXES(fixed); READBOXES(connect_top); READBOXES(connect_bottom); READBOXES(connect_front); READBOXES(connect_left); READBOXES(connect_back); READBOXES(connect_right); } } /* TileDef */ void TileDef::serialize(std::ostream &os, u16 protocol_version) const { if (protocol_version >= 30) writeU8(os, 4); else if (protocol_version >= 29) writeU8(os, 3); else if (protocol_version >= 26) writeU8(os, 2); else writeU8(os, 1); os << serializeString(name); animation.serialize(os, protocol_version); writeU8(os, backface_culling); if (protocol_version >= 26) { writeU8(os, tileable_horizontal); writeU8(os, tileable_vertical); } if (protocol_version >= 30) { writeU8(os, has_color); if (has_color) { writeU8(os, color.getRed()); writeU8(os, color.getGreen()); writeU8(os, color.getBlue()); } } } void TileDef::deSerialize(std::istream &is, const u8 contenfeatures_version, const NodeDrawType drawtype) { int version = readU8(is); name = deSerializeString(is); animation.deSerialize(is, version >= 3 ? 29 : 26); if (version >= 1) backface_culling = readU8(is); if (version >= 2) { tileable_horizontal = readU8(is); tileable_vertical = readU8(is); } if (version >= 4) { has_color = readU8(is); if (has_color) { color.setRed(readU8(is)); color.setGreen(readU8(is)); color.setBlue(readU8(is)); } } if ((contenfeatures_version < 8) && ((drawtype == NDT_MESH) || (drawtype == NDT_FIRELIKE) || (drawtype == NDT_LIQUID) || (drawtype == NDT_PLANTLIKE))) backface_culling = false; } /* SimpleSoundSpec serialization */ static void serializeSimpleSoundSpec(const SimpleSoundSpec &ss, std::ostream &os, u8 version) { os<= 11) writeF1000(os, ss.pitch); } static void deSerializeSimpleSoundSpec(SimpleSoundSpec &ss, std::istream &is, u8 version) { ss.name = deSerializeString(is); ss.gain = readF1000(is); if (version >= 11) ss.pitch = readF1000(is); } void TextureSettings::readSettings() { connected_glass = g_settings->getBool("connected_glass"); opaque_water = g_settings->getBool("opaque_water"); bool enable_shaders = g_settings->getBool("enable_shaders"); bool enable_bumpmapping = g_settings->getBool("enable_bumpmapping"); bool enable_parallax_occlusion = g_settings->getBool("enable_parallax_occlusion"); bool smooth_lighting = g_settings->getBool("smooth_lighting"); enable_mesh_cache = g_settings->getBool("enable_mesh_cache"); enable_minimap = g_settings->getBool("enable_minimap"); std::string leaves_style_str = g_settings->get("leaves_style"); // Mesh cache is not supported in combination with smooth lighting if (smooth_lighting) enable_mesh_cache = false; use_normal_texture = enable_shaders && (enable_bumpmapping || enable_parallax_occlusion); if (leaves_style_str == "fancy") { leaves_style = LEAVES_FANCY; } else if (leaves_style_str == "simple") { leaves_style = LEAVES_SIMPLE; } else { leaves_style = LEAVES_OPAQUE; } } /* ContentFeatures */ ContentFeatures::ContentFeatures() { reset(); } ContentFeatures::~ContentFeatures() { } void ContentFeatures::reset() { /* Cached stuff */ #ifndef SERVER solidness = 2; visual_solidness = 0; backface_culling = true; #endif has_on_construct = false; has_on_destruct = false; has_after_destruct = false; /* Actual data NOTE: Most of this is always overridden by the default values given in builtin.lua */ name = ""; groups.clear(); // Unknown nodes can be dug groups["dig_immediate"] = 2; drawtype = NDT_NORMAL; mesh = ""; #ifndef SERVER for(u32 i = 0; i < 24; i++) mesh_ptr[i] = NULL; minimap_color = video::SColor(0, 0, 0, 0); #endif visual_scale = 1.0; for(u32 i = 0; i < 6; i++) tiledef[i] = TileDef(); for(u16 j = 0; j < CF_SPECIAL_COUNT; j++) tiledef_special[j] = TileDef(); alpha = 255; post_effect_color = video::SColor(0, 0, 0, 0); param_type = CPT_NONE; param_type_2 = CPT2_NONE; is_ground_content = false; light_propagates = false; sunlight_propagates = false; walkable = true; pointable = true; diggable = true; climbable = false; buildable_to = false; floodable = false; rightclickable = true; leveled = 0; liquid_type = LIQUID_NONE; liquid_alternative_flowing = ""; liquid_alternative_source = ""; liquid_viscosity = 0; liquid_renewable = true; liquid_range = LIQUID_LEVEL_MAX+1; drowning = 0; light_source = 0; damage_per_second = 0; node_box = NodeBox(); selection_box = NodeBox(); collision_box = NodeBox(); waving = 0; legacy_facedir_simple = false; legacy_wallmounted = false; sound_footstep = SimpleSoundSpec(); sound_dig = SimpleSoundSpec("__group"); sound_dug = SimpleSoundSpec(); connects_to.clear(); connects_to_ids.clear(); connect_sides = 0; color = video::SColor(0xFFFFFFFF); palette_name = ""; palette = NULL; } void ContentFeatures::serialize(std::ostream &os, u16 protocol_version) const { if (protocol_version < 31) { serializeOld(os, protocol_version); return; } // version u8 version = (protocol_version >= 34) ? 11 : 10; writeU8(os, version); // general os << serializeString(name); writeU16(os, groups.size()); for (ItemGroupList::const_iterator i = groups.begin(); i != groups.end(); ++i) { os << serializeString(i->first); writeS16(os, i->second); } writeU8(os, param_type); writeU8(os, param_type_2); // visual writeU8(os, drawtype); os << serializeString(mesh); writeF1000(os, visual_scale); writeU8(os, 6); for (u32 i = 0; i < 6; i++) tiledef[i].serialize(os, protocol_version); for (u32 i = 0; i < 6; i++) tiledef_overlay[i].serialize(os, protocol_version); writeU8(os, CF_SPECIAL_COUNT); for (u32 i = 0; i < CF_SPECIAL_COUNT; i++) { tiledef_special[i].serialize(os, protocol_version); } writeU8(os, alpha); writeU8(os, color.getRed()); writeU8(os, color.getGreen()); writeU8(os, color.getBlue()); os << serializeString(palette_name); writeU8(os, waving); writeU8(os, connect_sides); writeU16(os, connects_to_ids.size()); for (std::set::const_iterator i = connects_to_ids.begin(); i != connects_to_ids.end(); ++i) writeU16(os, *i); writeU8(os, post_effect_color.getAlpha()); writeU8(os, post_effect_color.getRed()); writeU8(os, post_effect_color.getGreen()); writeU8(os, post_effect_color.getBlue()); writeU8(os, leveled); // lighting writeU8(os, light_propagates); writeU8(os, sunlight_propagates); writeU8(os, light_source); // map generation writeU8(os, is_ground_content); // interaction writeU8(os, walkable); writeU8(os, pointable); writeU8(os, diggable); writeU8(os, climbable); writeU8(os, buildable_to); writeU8(os, rightclickable); writeU32(os, damage_per_second); // liquid writeU8(os, liquid_type); os << serializeString(liquid_alternative_flowing); os << serializeString(liquid_alternative_source); writeU8(os, liquid_viscosity); writeU8(os, liquid_renewable); writeU8(os, liquid_range); writeU8(os, drowning); writeU8(os, floodable); // node boxes node_box.serialize(os, protocol_version); selection_box.serialize(os, protocol_version); collision_box.serialize(os, protocol_version); // sound serializeSimpleSoundSpec(sound_footstep, os, version); serializeSimpleSoundSpec(sound_dig, os, version); serializeSimpleSoundSpec(sound_dug, os, version); // legacy writeU8(os, legacy_facedir_simple); writeU8(os, legacy_wallmounted); } void ContentFeatures::correctAlpha(TileDef *tiles, int length) { // alpha == 0 means that the node is using texture alpha if (alpha == 0 || alpha == 255) return; for (int i = 0; i < length; i++) { if (tiles[i].name == "") continue; std::stringstream s; s << tiles[i].name << "^[noalpha^[opacity:" << ((int)alpha); tiles[i].name = s.str(); } } void ContentFeatures::deSerialize(std::istream &is) { // version detection int version = readU8(is); if (version < 9) { deSerializeOld(is, version); return; } else if (version > 11) { throw SerializationError("unsupported ContentFeatures version"); } // general name = deSerializeString(is); groups.clear(); u32 groups_size = readU16(is); for (u32 i = 0; i < groups_size; i++) { std::string name = deSerializeString(is); int value = readS16(is); groups[name] = value; } param_type = (enum ContentParamType) readU8(is); param_type_2 = (enum ContentParamType2) readU8(is); // visual drawtype = (enum NodeDrawType) readU8(is); mesh = deSerializeString(is); visual_scale = readF1000(is); if (readU8(is) != 6) throw SerializationError("unsupported tile count"); for (u32 i = 0; i < 6; i++) tiledef[i].deSerialize(is, version, drawtype); if (version >= 10) for (u32 i = 0; i < 6; i++) tiledef_overlay[i].deSerialize(is, version, drawtype); if (readU8(is) != CF_SPECIAL_COUNT) throw SerializationError("unsupported CF_SPECIAL_COUNT"); for (u32 i = 0; i < lua_pushinteger(L, en); return 3; } } static int os_execute (lua_State *L) { lua_pushinteger(L, system(luaL_optstring(L, 1, NULL))); return 1; } static int os_remove (lua_State *L) { const char *filename = luaL_checkstring(L, 1); return os_pushresult(L, remove(filename) == 0, filename); } static int os_rename (lua_State *L) { const char *fromname = luaL_checkstring(L, 1); const char *toname = luaL_checkstring(L, 2); return os_pushresult(L, rename(fromname, toname) == 0, fromname); } static int os_tmpname (lua_State *L) { char buff[LUA_TMPNAMBUFSIZE]; int err; lua_tmpnam(buff, err); if (err) return luaL_error(L, "unable to generate a unique filename"); lua_pushstring(L, buff); return 1; } static int os_getenv (lua_State *L) { lua_pushstring(L, getenv(luaL_checkstring(L, 1))); /* if NULL push nil */ return 1; } static int os_clock (lua_State *L) { lua_pushnumber(L, ((lua_Number)clock())/(lua_Number)CLOCKS_PER_SEC); return 1; } /* ** {====================================================== ** Time/Date operations ** { year=%Y, month=%m, day=%d, hour=%H, min=%M, sec=%S, ** wday=%w+1, yday=%j, isdst=? } ** ======================================================= */ static void setfield (lua_State *L, const char *key, int value) { lua_pushinteger(L, value); lua_setfield(L, -2, key); } static void setboolfield (lua_State *L, const char *key, int value) { if (value < 0) /* undefined? */ return; /* does not set field */ lua_pushboolean(L, value); lua_setfield(L, -2, key); } static int getboolfield (lua_State *L, const char *key) { int res; lua_getfield(L, -1, key); res = lua_isnil(L, -1) ? -1 : lua_toboolean(L, -1); lua_pop(L, 1); return res; } static int getfield (lua_State *L, const char *key, int d) { int res; lua_getfield(L, -1, key); if (lua_isnumber(L, -1)) res = (int)lua_tointeger(L, -1); else { if (d < 0) return luaL_error(L, "field " LUA_QS " missing in date table", key); res = d; } lua_pop(L, 1); return res; } static int os_date (lua_State *L) { const char *s = luaL_optstring(L, 1, "%c"); time_t t = luaL_opt(L, (time_t)luaL_checknumber, 2, time(NULL)); struct tm *stm; if (*s == '!') { /* UTC? */ stm = gmtime(&t); s++; /* skip `!' */ } else stm = localtime(&t); if (stm == NULL) /* invalid date? */ lua_pushnil(L); else if (strcmp(s, "*t") == 0) { lua_createtable(L, 0, 9); /* 9 = number of fields */ setfield(L, "sec", stm->tm_sec); setfield(L, "min", stm->tm_min); setfield(L, "hour", stm->tm_hour); setfield(L, "day", stm->tm_mday); setfield(L, "month", stm->tm_mon+1); setfield(L, "year", stm->tm_year+1900); setfield(L, "wday", stm->tm_wday+1); setfield(L, "yday", stm->tm_yday+1); setboolfield(L, "isdst", stm->tm_isdst); } else { char cc[3]; luaL_Buffer b; cc[0] = '%'; cc[2] = '\0'; luaL_buffinit(L, &b); for (; *s; s++) { if (*s != '%' || *(s + 1) == '\0') /* no conversion specifier? */ luaL_addchar(&b, *s); else { size_t reslen; char buff[200]; /* should be big enough for any conversion result */ cc[1] = *(++s); reslen = strftime(buff, sizeof(buff), cc, stm); luaL_addlstring(&b, buff, reslen); } } luaL_pushresult(&b); } return 1; } static int os_time (lua_State *L) { time_t t; if (lua_isnoneornil(L, 1)) /* called without args? */ t = time(NULL); /* get current time */ else { struct tm ts; luaL_checktype(L, 1, LUA_TTABLE); lua_settop(L, 1); /* make sure table is at the top */ ts.tm_sec = getfield(L, "sec", 0); ts.tm_min = getfield(L, "min", 0); ts.tm_hour = getfield(L, "hour", 12); ts.tm_mday = getfield(L, "day", -1); ts.tm_mon = getfield(L, "month", -1) - 1; ts.tm_year = getfield(L, "year", -1) - 1900; ts.tm_isdst = getboolfield(L, "isdst"); t = mktime(&ts); } if (t == (time_t)(-1)) lua_pushnil(L); else lua_pushnumber(L, (lua_Number)t); return 1; } static int os_difftime (lua_State *L) { lua_pushnumber(L, difftime((time_t)(luaL_checknumber(L, 1)), (time_t)(luaL_optnumber(L, 2, 0)))); return 1; } /* }====================================================== */ static int os_setlocale (lua_State *L) { static const int cat[] = {LC_ALL, LC_COLLATE, LC_CTYPE, LC_MONETARY, LC_NUMERIC, LC_TIME}; static const char *const catnames[] = {"all", "collate", "ctype", "monetary", "numeric", "time", NULL}; const char *l = luaL_optstring(L, 1, NULL); int op = luaL_checkoption(L, 2, "all", catnames); lua_pushstring(L, setlocale(cat[op], l)); return 1; } static int os_exit (lua_State *L) { exit(luaL_optint(L, 1, EXIT_SUCCESS)); } static const luaL_Reg syslib[] = { {"clock", os_clock}, {"date", os_date}, {"difftime", os_difftime}, {"execute", os_execute}, {"exit", os_exit}, {"getenv", os_getenv}, {"remove", os_remove}, {"rename", os_rename}, {"setlocale", os_setlocale}, {"time", os_time}, {"tmpname", os_tmpname}, {NULL, NULL} }; /* }====================================================== */ LUALIB_API int luaopen_os (lua_State *L) { luaL_register(L, LUA_OSLIBNAME, syslib); return 1; } m_content_features.resize((u32)(i) + 1); m_content_features[i] = f; addNameIdMapping(i, f.name); verbosestream << "deserialized " << f.name << std::endl; getNodeBoxUnion(f.selection_box, f, &m_selection_box_union); fixSelectionBoxIntUnion(); } } void CNodeDefManager::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)); } IWritableNodeDefManager *createNodeDefManager() { return new CNodeDefManager(); } //// Serialization of old ContentFeatures formats void ContentFeatures::serializeOld(std::ostream &os, u16 protocol_version) const { u8 compatible_param_type_2 = param_type_2; if ((protocol_version < 28) && (compatible_param_type_2 == CPT2_MESHOPTIONS)) compatible_param_type_2 = CPT2_NONE; else if (protocol_version < 30) { if (compatible_param_type_2 == CPT2_COLOR) compatible_param_type_2 = CPT2_NONE; else if (compatible_param_type_2 == CPT2_COLORED_FACEDIR) compatible_param_type_2 = CPT2_FACEDIR; else if (compatible_param_type_2 == CPT2_COLORED_WALLMOUNTED) compatible_param_type_2 = CPT2_WALLMOUNTED; } float compatible_visual_scale = visual_scale; if (protocol_version < 30 && drawtype == NDT_PLANTLIKE) compatible_visual_scale = sqrt(visual_scale); TileDef compatible_tiles[6]; for (u8 i = 0; i < 6; i++) { compatible_tiles[i] = tiledef[i]; if (tiledef_overlay[i].name != "") { std::stringstream s; s << "(" << tiledef[i].name << ")^(" << tiledef_overlay[i].name << ")"; compatible_tiles[i].name = s.str(); } } // Protocol >= 24 if (protocol_version < 31) { writeU8(os, protocol_version < 27 ? 7 : 8); os << serializeString(name); writeU16(os, groups.size()); for (ItemGroupList::const_iterator i = groups.begin(); i != groups.end(); ++i) { os << serializeString(i->first); writeS16(os, i->second); } writeU8(os, drawtype); writeF1000(os, compatible_visual_scale); writeU8(os, 6); for (u32 i = 0; i < 6; i++) compatible_tiles[i].serialize(os, protocol_version); writeU8(os, CF_SPECIAL_COUNT); for (u32 i = 0; i < CF_SPECIAL_COUNT; i++) tiledef_special[i].serialize(os, protocol_version); writeU8(os, alpha); writeU8(os, post_effect_color.getAlpha()); writeU8(os, post_effect_color.getRed()); writeU8(os, post_effect_color.getGreen()); writeU8(os, post_effect_color.getBlue()); writeU8(os, param_type); writeU8(os, compatible_param_type_2); writeU8(os, is_ground_content); writeU8(os, light_propagates); writeU8(os, sunlight_propagates); writeU8(os, walkable); writeU8(os, pointable); writeU8(os, diggable); writeU8(os, climbable); writeU8(os, buildable_to); os << serializeString(""); // legacy: used to be metadata_name writeU8(os, liquid_type); os << serializeString(liquid_alternative_flowing); os << serializeString(liquid_alternative_source); writeU8(os, liquid_viscosity); writeU8(os, liquid_renewable); writeU8(os, light_source); writeU32(os, damage_per_second); node_box.serialize(os, protocol_version); selection_box.serialize(os, protocol_version); writeU8(os, legacy_facedir_simple); writeU8(os, legacy_wallmounted); serializeSimpleSoundSpec(sound_footstep, os, 10); serializeSimpleSoundSpec(sound_dig, os, 10); serializeSimpleSoundSpec(sound_dug, os, 10); writeU8(os, rightclickable); writeU8(os, drowning); writeU8(os, leveled); writeU8(os, liquid_range); writeU8(os, waving); os << serializeString(mesh); collision_box.serialize(os, protocol_version); writeU8(os, floodable); writeU16(os, connects_to_ids.size()); for (std::set::const_iterator i = connects_to_ids.begin(); i != connects_to_ids.end(); ++i) writeU16(os, *i); writeU8(os, connect_sides); } else { throw SerializationError("ContentFeatures::serialize(): " "Unsupported version requested"); } } void ContentFeatures::deSerializeOld(std::istream &is, int version) { if (version == 5) // In PROTOCOL_VERSION 13 { name = deSerializeString(is); groups.clear(); u32 groups_size = readU16(is); for(u32 i=0; im_ndef = this; if (m_node_registration_complete) nr->nodeResolveInternal(); else m_pending_resolve_callbacks.push_back(nr); } bool CNodeDefManager::cancelNodeResolveCallback(NodeResolver *nr) { size_t len = m_pending_resolve_callbacks.size(); for (size_t i = 0; i != len; i++) { if (nr != m_pending_resolve_callbacks[i]) continue; len--; m_pending_resolve_callbacks[i] = m_pending_resolve_callbacks[len]; m_pending_resolve_callbacks.resize(len); return true; } return false; } void CNodeDefManager::runNodeResolveCallbacks() { for (size_t i = 0; i != m_pending_resolve_callbacks.size(); i++) { NodeResolver *nr = m_pending_resolve_callbacks[i]; nr->nodeResolveInternal(); } m_pending_resolve_callbacks.clear(); } void CNodeDefManager::resetNodeResolveState() { m_node_registration_complete = false; m_pending_resolve_callbacks.clear(); } void CNodeDefManager::mapNodeboxConnections() { for (u32 i = 0; i < m_content_features.size(); i++) { ContentFeatures *f = &m_content_features[i]; if ((f->drawtype != NDT_NODEBOX) || (f->node_box.type != NODEBOX_CONNECTED)) continue; for (std::vector::iterator it = f->connects_to.begin(); it != f->connects_to.end(); ++it) { getIds(*it, f->connects_to_ids); } } } bool CNodeDefManager::nodeboxConnects(MapNode from, MapNode to, u8 connect_face) { const ContentFeatures &f1 = get(from); if ((f1.drawtype != NDT_NODEBOX) || (f1.node_box.type != NODEBOX_CONNECTED)) return false; // lookup target in connected set if (f1.connects_to_ids.find(to.param0) == f1.connects_to_ids.end()) return false; const ContentFeatures &f2 = get(to); if ((f2.drawtype == NDT_NODEBOX) && (f2.node_box.type == NODEBOX_CONNECTED)) // ignores actually looking if back connection exists return (f2.connects_to_ids.find(from.param0) != f2.connects_to_ids.end()); // does to node declare usable faces? if (f2.connect_sides > 0) { if ((f2.param_type_2 == CPT2_FACEDIR || f2.param_type_2 == CPT2_COLORED_FACEDIR) && (connect_face >= 4)) { static const u8 rot[33 * 4] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 32, 16, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 4 - back 8, 4, 32, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8 - right 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 8, 4, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16 - front 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 16, 8, 4 // 32 - left }; return (f2.connect_sides & rot[(connect_face * 4) + (to.param2 & 0x1F)]); } return (f2.connect_sides & connect_face); } // the target is just a regular node, so connect no matter back connection return true; } //// //// NodeResolver //// NodeResolver::NodeResolver() { m_nodenames.reserve(16); m_nnlistsizes.reserve(4); } NodeResolver::~NodeResolver() { if (!m_resolve_done && m_ndef) m_ndef->cancelNodeResolveCallback(this); } void NodeResolver::nodeResolveInternal() { m_nodenames_idx = 0; m_nnlistsizes_idx = 0; resolveNodeNames(); m_resolve_done = true; m_nodenames.clear(); m_nnlistsizes.clear(); } bool NodeResolver::getIdFromNrBacklog(content_t *result_out, const std::string &node_alt, content_t c_fallback) { if (m_nodenames_idx == m_nodenames.size()) { *result_out = c_fallback; errorstream << "NodeResolver: no more nodes in list" << std::endl; return false; } content_t c; std::string name = m_nodenames[m_nodenames_idx++]; bool success = m_ndef->getId(name, c); if (!success && node_alt != "") { name = node_alt; success = m_ndef->getId(name, c); } if (!success) { errorstream << "NodeResolver: failed to resolve node name '" << name << "'." << std::endl; c = c_fallback; } *result_out = c; return success; } bool NodeResolver::getIdsFromNrBacklog(std::vector *result_out, bool all_required, content_t c_fallback) { bool success = true; if (m_nnlistsizes_idx == m_nnlistsizes.size()) { errorstream << "NodeResolver: no more node lists" << std::endl; return false; } size_t length = m_nnlistsizes[m_nnlistsizes_idx++]; while (length--) { if (m_nodenames_idx == m_nodenames.size()) { errorstream << "NodeResolver: no more nodes in list" << std::endl; return false; } content_t c; std::string &name = m_nodenames[m_nodenames_idx++]; if (name.substr(0,6) != "group:") { if (m_ndef->getId(name, c)) { result_out->push_back(c); } else if (all_required) { errorstream << "NodeResolver: failed to resolve node name '" << name << "'." << std::endl; result_out->push_back(c_fallback); success = false; } } else { std::set cids; std::set::iterator it; m_ndef->getIds(name, cids); for (it = cids.begin(); it != cids.end(); ++it) result_out->push_back(*it); } } return success; }