aboutsummaryrefslogtreecommitdiff
path: root/src/nodedef.cpp
Commit message (Collapse)AuthorAge
* Proselytize the network. Use IEEE F32 (#8030)SmallJoker2019-01-03
| | | | | * Proselytize the network. Use IEEE F32 * Remove unused V2F1000 functions
* Move client-specific files to 'src/client' (#7902)Quentin Bazin2018-11-28
| | | | | Update Android.mk Remove 'src/client' from include_directories
* Fix missing ignore textures (#7326)you2018-05-20
|
* Node resolver: Make error on fallback optional, disable for mapgen aliasesParamat2018-04-20
|
* Node definition manager refactor (#7016)Dániel Juhász2018-02-10
| | | | | | | | | * Rename IWritableNodeDefManager to NodeDefManager * Make INodeDefManager functions const * Use "const *NodeDefManager" instead of "*INodeDefManager" * Remove unused INodeDefManager class * Merge NodeDefManager and CNodeDefManager * Document NodeDefManager
* Connected Nodeboxes: Add `disconnected` boxesThomas--S2018-01-03
| | | | | | | | | | | | | | | The `disconnected_*` boxes are the opposites of the `connect_*` ones, i.e. when a node has no suitable neighbours on the respective side, the according disconnected box is drawn. * disconnected_top * disconnected_bottom * disconnected_front * disconnected_left * disconnected_back * disconnected_right * disconnected (when there is *no* neighbour) * disconnected_sides (when there are *no* neighbours to the sides)
* Add missing? includeEsteban I. Ruiz Moreno2017-10-26
|
* Real global textures (#6105)Vitaliy2017-10-15
| | | | | | | | * Real global textures * Add world-aligned textures * Update minimal to support world-aligned tiles * Update minimal
* Make INodeDefManager::getIds return a vector, not a setKahrl2017-09-12
|
* Implement client node dig predictionAuke Kok2017-09-11
| | | | | | | | Dig prediction allows clients to remove dug nodes without waiting for server acknowledgement. This patch allows mods to override dig prediction, it can either be turned off or a different "prediction node" can be selected.
* Pushing typo fix introduced in b7ee608e70f8e031e3e01c9672bedb16efa648b8Loic Blot2017-08-29
|
* Bump minimal protocol version to 36 (#6319)SmallJoker2017-08-29
| | | | | | | * Bump minimal protocol version to 36 Item/Node/TileDef, NodeBox, TileAnimation: Remove old compat code * Accept future serialisation versions
* Modernize code: very last fixes (#6290)Loïc Blot2017-08-20
| | | Last modernization fixes
* Code modernization: src/n*, src/o* (#6280)Loïc Blot2017-08-19
| | | | | | | | | | | * Code modernization: src/n*, src/o* * empty function * default constructor/destructor * for range-based loops * use emplace_back instead of push_back * remove unused IWritableNodeDefManager::clone() * C++ STL header style * Pointer constness in some functions
* Optimize headers (part 2) (#6272)Loïc Blot2017-08-18
| | | | | | | | | | | | | | * Optimize headers (part 2) * less debug.h in headers * less remoteplayer.h for everybody * Cleanup (part 2) * camera.h: mesh.h * mapgen.h: mapnode.h * serverenvironment.h: mapblock.h * nodedef.h: shader.h
* TileLayer: use shared_ptr for FrameSpec vector (#6171)Loïc Blot2017-07-26
| | | | | | | | | | | | * TileLayer: use shared_ptr for vector framespec This reduce memory copy of TileLayer from (4 to 16) * FrameSpec where FrameSpec = (sizeof(int) + 3 * sizeof(ptr)) to int + sizeof(ptr) Callgrind difference Before: https://lut.im/RGkiJqQb8T/LeQIEXpAuRzfl7gd.png After: https://lut.im/bcqmwee1xu/cTwtptY5tRuS9lp0.png * Fix one push_back to use vector::emplace_back & optimize inclusions
* Add 'plantlike_rooted' drawtypenumber Zero2017-07-11
| | | | | | | Useful for underwater plants. Node consists of a base cube plus a plantlike extension that can pass through liquid nodes above without creating air bubbles or interfering with liquid flow. Uses paramtype2 'leveled', param2 defines height of plantlike extension.
* Tile material: Add 'TILE_MATERIAL_OPAQUE', use for drawtype 'NDT_NORMAL'stujones112017-07-01
| | | | | Prevents normal drawtype nodes having transparency. Avoids clients cheating by using 'x-ray' texture packs with transparent textures.
* Isolate irrlicht references and use a singleton (#6041)Loïc Blot2017-06-26
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | * Add Device3D class which will contain IrrlichtDevice interface move getSupportedVideoDrivers to Device3D Add Device3D singleton & use it in various places Rename Device3D to Rendering engine & add helper functions to various device pointers More singleton work RenderingEngine owns draw_load_screen move draw functions to RenderingEngine Reduce IrrlichtDevice exposure and guienvironment RenderingEngine: Expose get_timer_time() to remove device from guiEngine Make irrlichtdevice & scene manager less exposed * Code style fixes * Move porting::getVideoDriverName, getVideoDriverFriendlyName, getDisplayDensity, getDisplaySize to RenderingEngine Fix XORG_USED macro -> RenderingEngine + create_engine_device from RenderingEngine constructor directly * enum paralax => enum parallax
* Cpp11 patchset 11: continue working on constructor style migration (#6004)Loïc Blot2017-06-18
|
* Fix the serialization error by ff73c7a (#5964)Rui2017-06-11
|
* Sound: Add pitch option (#5960)Rui2017-06-11
| | | | * Sound: Add pitch option
* C++11 patchset 2: remove util/cpp11.h and util/cpp11_container.h (#5821)Loïc Blot2017-06-04
|
* Various code cleanup & little performance improvement on HTTP download (#5772)Loïc Blot2017-05-20
| | | | | | * Disable or remove unused enum members/functions * Tiny code style fixes * Make some functions const * Replace ClientMediaDownloader std::unordered_map with std::map
* Fix alpha for liquid nodes (#5494)Dániel Juhász2017-05-19
|
* Shaders: Remove unused water surface shaderparamat2017-05-08
| | | | | | | | | | Also remove hardcoded MTGame node. The 'water surface shader' was duplicated shader code in preparation for intended new water surface shaders. For development purposes the MTGame node 'default:water_source' had it's top tile assigned to 'water surface shader'. Due to shader duplication this commit does not cause any change to shader behaviour.
* Allow mesh and nodeboxes to wave like plants or leaves. (#3497)Auke Kok2017-04-28
| | | | | | | | | | | | | | | | | | | | | | | | | | | | We introduce a new value for "waving" - 2: 0 - waving disabled 1 - wave like a plant 2 - wave like a leave Plantlike nodes will only allow waving = 1, but for leaves we will permit both 1 and 2 since current minetest_game sets it to 1 for all leaves. This makes it somewhat backwards compatible. For mesh and nodebox, values 1 and 2 are both valid, and the node can wave in both fashions as desired. I've tested this with the crops:corn plants, which are mesh nodes, and the results are really good. The code change is trivial as well, so I've opted to document the waving parameter in lua_api.txt because it was missing from there. Nodeboxes likely will not wave properly unless waving = 2. However it's possible that waving=1 may be desired by some mod developers for geometries I have not tried, so the code will not prohibit either value for mesh and nodebox drawtypes. Add lua_api.txt documentation for this feature and document both the existing functionality and the expansion to mesh and nodebox drawtypes.
* Fix various points reported by cppcheck (#5656)Loïc Blot2017-04-25
| | | | | | | | | | | | * Fix various performance issues reported by cppcheck + code style (CI) * Make CI happy with code style on master * guiFileSelectMenu: remove useless includes * some performance fixes pointed by cppcheck * remove some useless casts * TextDest: remove unused setFormSpec function * Fix various iterator post-increment reported by cppcheck
* Soft node overlay (#5186)Dániel Juhász2017-04-21
| | | | This commit adds node overlays, which are tiles that are drawn on top of other tiles.
* Only use palette if param_type2 is correctDániel Juhász2017-04-13
|
* Hardware coloring for itemstacksDániel Juhász2017-04-08
| | | | | | | | | | Adds the possibility to colorize item stacks based on their metadata. In the item/node definition you can specify palette (an image file) and color (fallback color if the item has no palette or metadata). Then you can add palette_index to the metadata. Dropped itemstacks with different colors do not merge.
* Some performance optimizations (#5424)Loïc Blot2017-03-22
| | | | | | | | | | | | | | | | | | | * Some performance optimizations This is globally removing some memory useless copy * use a const ref return on std::string Settings::get to prevent data copy on getters which doesn't need to copy it * pass some stack created strings to static const as they are not modified anywhere * Camera: return nametags per const ref instead of a list pointer, we only need to read it * INodeDefManager: getAll should be a result ref writer instead of a return copy * INodeDefManager: getAlias should return a const std::string ref * Minimap: unroll a Scolor creation in blitMinimapPixersToImageRadar to prvent many variable construct/destruct which are unneeded (we rewrite the content in the loop) * CNodeDefManager::updateAliases: prevent a idef getall copy * Profiler: constness * rollback_interface: create real_name later, and use const ref * MapBlockMesh updateFastFaceRow: unroll TileSpec next_tile, which has a cost of 1.8% CPU due to variable allocation/destruction, * MapBlockMesh updateFastFaceRow: copy next_tile to tile only if it's a different tilespec * MapBlockMesh updateFastFaceRow: use memcpy to copy next_lights to lights to do it in a single cpu operation
* Update server min protocol version to v24 (#5411)Loïc Blot2017-03-19
| | | | | | | | | | * Update server min protocol version to v24 It's based on @sfan5 stats. See https://kitsunemimi.pw/tmp/serverlist_stats_2017-03-17.txt v24 was bumped 25/08/14 and 0.4.11 was released 25/12/14 * Drop protocol v23 and lesser code
* Plantlike: Fix visual_scale being applied squaredparamat2017-02-10
| | | | | | | | | | | | | | | | | | This re-applies 2 commits that were reverted. Visual_scale was applied twice to plantlike by accident sometime between 2011 and 2013, squaring the requested scale value. Visual_scale is correctly applied once in it's other uses in signlike and torchlike. Two lines of code are removed, they also had no effect for the vast majority of nodes with the default visual_scale of 1.0. The texture continues to have it's base at ground level. Send sqrt(visual_scale) to old clients. Keep compatibility with protocol < 30 clients now that visual_scale is no longer applied twice to plantlike drawtype and mods are being updated to a new value.
* Revert "Plantlike visual scale: Send sqrt(visual_scale) to old clients"Craig Robbins2017-02-10
| | | | This reverts commit cdc538e0a242167cd7031d40670d2d4464b87f2c.
* Plantlike visual scale: Send sqrt(visual_scale) to old clientsparamat2017-01-30
| | | | | | Keep compatibility with protocol < 30 clients now that visual_scale is no longer applied twice to plantlike drawtype and mods are being updated to a new value.
* Add smooth lighting for all nodesnumber Zero2017-01-24
| | | | Note: Smooth lighting disables the mesh cache.
* Add hardware node coloring. Includes:Dániel Juhász2017-01-23
| | | | | | - Increase ContentFeatures serialization version - Color property and palettes for nodes - paramtype2 = "color", "colored facedir" or "colored wallmounted"
* Add particle animation, glowsfan52017-01-18
| | | | | This is implemented by reusing and extending the TileAnimation code for the methods used by particles.
* Environment & IGameDef code refactoring (#4985)Ner'zhul2017-01-09
| | | | | | | | | | | | | | | | | | | | | * Environment code refactoring * Cleanup includes & class declarations in client & server environment to improve build speed * ServerEnvironment::m_gamedef is now a pointer to Server instead of IGameDef, permitting to cleanup many casts. * Cleanup IGameDef * Move ITextureSource* IGameDef::getTextureSource() to Client only. * Also move ITextureSource *IGameDef::tsrc() helper * drop getShaderSource, getSceneManager, getSoundManager & getCamera abstract call * drop unused emerge() call * cleanup server unused functions (mentionned before) * Drop one unused parameter from ContentFeatures::updateTextures * move checkLocalPrivilege to Client * Remove some unnecessary casts * create_formspec_menu: remove IWritableTextureSource pointer, as client already knows it * Fix some comments * Change required IGameDef to Server/Client pointers * Previous change that game.cpp sometimes calls functions with Client + InventoryManager + IGameDef in same functions but it's the same objects * Remove duplicate Client pointer in GUIFormSpecMenu::GUIFormSpecMenu * drop ClientMap::sectorWasDrawn which is unused
* Improve getPointedThing() (#4346)Dániel Juhász2017-01-04
| | | | | | | | | | | | | | | | | | | * Improved getPointedThing() The new algorithm checks every node exactly once. Now the point and normal vector of the collision is also returned in the PointedThing (currently they are not used outside of the function). Now the CNodeDefManager keeps the union of all possible nodeboxes, so the raycast won't miss any nodes. Also if there are only small nodeboxes, getPointedThing() is exceptionally fast. Also adds unit test for VoxelLineIterator. * Cleanup, code move This commit moves getPointedThing() and Client::getSelectedActiveObject() to ClientEnvironment. The map nodes now can decide which neighbors they are connecting to (MapNode::getNeighbors()).
* Add 2D sheet animation for nodessfan52017-01-02
|
* Move TileAnimation code to seperate filesfan52017-01-02
|
* Limit light_source in the engine (#4814)juhdanad2016-11-28
| | | Since light_source>15 causes crash, it must be limited.
* Revert "Adding particle blend, glow and animation (#4705)"sfan52016-11-14
| | | | This reverts commit 93e3555eae2deaeca69ee252cfa9cc9c3e0e49ef.
* Adding particle blend, glow and animation (#4705)Foghrye42016-11-15
|
* Speed up emerge thread by using unordered map in a few places. Looking at ↵gregorycu2016-10-08
| | | | 25% speedup in Emerge thread on Just Test.
* Textures: Ignore unknown node in override.txtSmallJoker2016-10-08
|
* Add minetest.unregister_item and minetest.register_alias_forcepaly22016-09-08
|
* Make plantlike drawtype more funAuke Kok2016-08-26
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Adds several new ways that the plantlike drawtype mesh can be changed. This requires paramtype2 = "meshoptions" to be set in the node definition. The drawtype for these nodes should be "plantlike". These modifications are all done using param2. This field is now a complex bitfield that allows some or more of the combinations to be chosen, and the mesh draw code will choose the options based as neeeded for each plantlike node. bit layout: bits 0, 1 and 2 (values 0x1 through 0x7) are for choosing the plant mesh shape: 0 - ordinary plantlike plant ("x" shaped) 1 - ordinary plant, but rotated 45 degrees ("+" shaped) 2 - a plant with 3 faces ("*" shaped) 3 - a plant with 4 faces ("#" shaped) 4 - a plant with 4 faces ("#" shaped, leaning outwards) 5 through 7 are unused and reserved for future mesh shapes. bit 3 (0x8) causes the plant to be randomly offset in the x,z plane. The plant should fall within the 1x1x1 nodebox if regularly sized. bit 4 (0x10) causes the plant mesh to grow by sqrt(2), and will cause the plant mesh to fill out 1x1x1, and appear slightly larger. Texture makers will want to make their plant texture 23x16 pixels to have the best visual fit in 1x1x1 size. bit 5 (0x20) causes each face of the plant to have a slight negative Y offset in position, descending up to 0.125 downwards into the node below. Because this is per face, this causes the plant model to be less symmetric. bit 6 (0x40) through bit 7 (0x80) are unused and reserved for future use. !(https://youtu.be/qWuI664krsI)
lass="hl opt">(isValidPosition(p)) { return true; } else{ return m_parent->isValidPosition(getPosRelative() + p); } } MapNode MapBlock::getNodeParent(v3s16 p, bool *is_valid_position) { if (isValidPosition(p) == false) return m_parent->getNodeNoEx(getPosRelative() + p, is_valid_position); if (data == NULL) { if (is_valid_position) *is_valid_position = false; return MapNode(CONTENT_IGNORE); } if (is_valid_position) *is_valid_position = true; return data[p.Z * zstride + p.Y * ystride + p.X]; } std::string MapBlock::getModifiedReasonString() { std::string reason; const u32 ubound = MYMIN(sizeof(m_modified_reason) * CHAR_BIT, ARRLEN(modified_reason_strings)); for (u32 i = 0; i != ubound; i++) { if ((m_modified_reason & (1 << i)) == 0) continue; reason += modified_reason_strings[i]; reason += ", "; } if (reason.length() > 2) reason.resize(reason.length() - 2); return reason; } /* Propagates sunlight down through the block. Doesn't modify nodes that are not affected by sunlight. Returns false if sunlight at bottom block is invalid. Returns true if sunlight at bottom block is valid. Returns true if bottom block doesn't exist. If there is a block above, continues from it. If there is no block above, assumes there is sunlight, unless is_underground is set or highest node is water. All sunlighted nodes are added to light_sources. if remove_light==true, sets non-sunlighted nodes black. if black_air_left!=NULL, it is set to true if non-sunlighted air is left in block. */ bool MapBlock::propagateSunlight(std::set<v3s16> & light_sources, bool remove_light, bool *black_air_left) { INodeDefManager *nodemgr = m_gamedef->ndef(); // Whether the sunlight at the top of the bottom block is valid bool block_below_is_valid = true; v3s16 pos_relative = getPosRelative(); for(s16 x=0; x<MAP_BLOCKSIZE; x++) { for(s16 z=0; z<MAP_BLOCKSIZE; z++) { #if 1 bool no_sunlight = false; //bool no_top_block = false; // Check if node above block has sunlight bool is_valid_position; MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z), &is_valid_position); if (is_valid_position) { if(n.getContent() == CONTENT_IGNORE) { // Trust heuristics no_sunlight = is_underground; } else if(n.getLight(LIGHTBANK_DAY, m_gamedef->ndef()) != LIGHT_SUN) { no_sunlight = true; } } else { //no_top_block = true; // NOTE: This makes over-ground roofed places sunlighted // Assume sunlight, unless is_underground==true if(is_underground) { no_sunlight = true; } else { MapNode n = getNodeNoEx(v3s16(x, MAP_BLOCKSIZE-1, z)); if(m_gamedef->ndef()->get(n).sunlight_propagates == false) { no_sunlight = true; } } // NOTE: As of now, this just would make everything dark. // No sunlight here //no_sunlight = true; } #endif #if 0 // Doesn't work; nothing gets light. bool no_sunlight = true; bool no_top_block = false; // Check if node above block has sunlight try{ MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z)); if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN) { no_sunlight = false; } } catch(InvalidPositionException &e) { no_top_block = true; } #endif /*std::cout<<"("<<x<<","<<z<<"): " <<"no_top_block="<<no_top_block <<", is_underground="<<is_underground <<", no_sunlight="<<no_sunlight <<std::endl;*/ s16 y = MAP_BLOCKSIZE-1; // This makes difference to diminishing in water. bool stopped_to_solid_object = false; u8 current_light = no_sunlight ? 0 : LIGHT_SUN; for(; y >= 0; y--) { v3s16 pos(x, y, z); MapNode &n = getNodeRef(pos); if(current_light == 0) { // Do nothing } else if(current_light == LIGHT_SUN && nodemgr->get(n).sunlight_propagates) { // Do nothing: Sunlight is continued } else if(nodemgr->get(n).light_propagates == false) { // A solid object is on the way. stopped_to_solid_object = true; // Light stops. current_light = 0; } else { // Diminish light current_light = diminish_light(current_light); } u8 old_light = n.getLight(LIGHTBANK_DAY, nodemgr); if(current_light > old_light || remove_light) { n.setLight(LIGHTBANK_DAY, current_light, nodemgr); } if(diminish_light(current_light) != 0) { light_sources.insert(pos_relative + pos); } if(current_light == 0 && stopped_to_solid_object) { if(black_air_left) { *black_air_left = true; } } } // Whether or not the block below should see LIGHT_SUN bool sunlight_should_go_down = (current_light == LIGHT_SUN); /* If the block below hasn't already been marked invalid: Check if the node below the block has proper sunlight at top. If not, the block below is invalid. Ignore non-transparent nodes as they always have no light */ if(block_below_is_valid) { MapNode n = getNodeParent(v3s16(x, -1, z), &is_valid_position); if (is_valid_position) { if(nodemgr->get(n).light_propagates) { if(n.getLight(LIGHTBANK_DAY, nodemgr) == LIGHT_SUN && sunlight_should_go_down == false) block_below_is_valid = false; else if(n.getLight(LIGHTBANK_DAY, nodemgr) != LIGHT_SUN && sunlight_should_go_down == true) block_below_is_valid = false; } } else { /*std::cout<<"InvalidBlockException for bottom block node" <<std::endl;*/ // Just no block below, no need to panic. } } } } return block_below_is_valid; } void MapBlock::copyTo(VoxelManipulator &dst) { v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE); VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1)); // Copy from data to VoxelManipulator dst.copyFrom(data, data_area, v3s16(0,0,0), getPosRelative(), data_size); } void MapBlock::copyFrom(VoxelManipulator &dst) { v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE); VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1)); // Copy from VoxelManipulator to data dst.copyTo(data, data_area, v3s16(0,0,0), getPosRelative(), data_size); } void MapBlock::actuallyUpdateDayNightDiff() { INodeDefManager *nodemgr = m_gamedef->ndef(); // Running this function un-expires m_day_night_differs m_day_night_differs_expired = false; if (data == NULL) { m_day_night_differs = false; return; } bool differs; /* Check if any lighting value differs */ for (u32 i = 0; i < nodecount; i++) { MapNode &n = data[i]; differs = !n.isLightDayNightEq(nodemgr); if (differs) break; } /* If some lighting values differ, check if the whole thing is just air. If it is just air, differs = false */ if (differs) { bool only_air = true; for (u32 i = 0; i < nodecount; i++) { MapNode &n = data[i]; if (n.getContent() != CONTENT_AIR) { only_air = false; break; } } if (only_air) differs = false; } // Set member variable m_day_night_differs = differs; } void MapBlock::expireDayNightDiff() { //INodeDefManager *nodemgr = m_gamedef->ndef(); if(data == NULL){ m_day_night_differs = false; m_day_night_differs_expired = false; return; } m_day_night_differs_expired = true; } s16 MapBlock::getGroundLevel(v2s16 p2d) { if(isDummy()) return -3; try { s16 y = MAP_BLOCKSIZE-1; for(; y>=0; y--) { MapNode n = getNodeRef(p2d.X, y, p2d.Y); if(m_gamedef->ndef()->get(n).walkable) { if(y == MAP_BLOCKSIZE-1) return -2; else return y; } } return -1; } catch(InvalidPositionException &e) { return -3; } } /* Serialization */ // List relevant id-name pairs for ids in the block using nodedef // Renumbers the content IDs (starting at 0 and incrementing // use static memory requires about 65535 * sizeof(int) ram in order to be // sure we can handle all content ids. But it's absolutely worth it as it's // a speedup of 4 for one of the major time consuming functions on storing // mapblocks. static content_t getBlockNodeIdMapping_mapping[USHRT_MAX + 1]; static void getBlockNodeIdMapping(NameIdMapping *nimap, MapNode *nodes, INodeDefManager *nodedef) { memset(getBlockNodeIdMapping_mapping, 0xFF, (USHRT_MAX + 1) * sizeof(content_t)); std::set<content_t> unknown_contents; content_t id_counter = 0; for (u32 i = 0; i < MapBlock::nodecount; i++) { content_t global_id = nodes[i].getContent(); content_t id = CONTENT_IGNORE; // Try to find an existing mapping if (getBlockNodeIdMapping_mapping[global_id] != 0xFFFF) { id = getBlockNodeIdMapping_mapping[global_id]; } else { // We have to assign a new mapping id = id_counter++; getBlockNodeIdMapping_mapping[global_id] = id; const ContentFeatures &f = nodedef->get(global_id); const std::string &name = f.name; if(name == "") unknown_contents.insert(global_id); else nimap->set(id, name); } // Update the MapNode nodes[i].setContent(id); } for(std::set<content_t>::const_iterator i = unknown_contents.begin(); i != unknown_contents.end(); ++i){ errorstream<<"getBlockNodeIdMapping(): IGNORING ERROR: " <<"Name for node id "<<(*i)<<" not known"<<std::endl; } } // Correct ids in the block to match nodedef based on names. // Unknown ones are added to nodedef. // Will not update itself to match id-name pairs in nodedef. static void correctBlockNodeIds(const NameIdMapping *nimap, MapNode *nodes, IGameDef *gamedef) { INodeDefManager *nodedef = gamedef->ndef(); // This means the block contains incorrect ids, and we contain // the information to convert those to names. // nodedef contains information to convert our names to globally // correct ids. std::set<content_t> unnamed_contents; std::set<std::string> unallocatable_contents; for (u32 i = 0; i < MapBlock::nodecount; i++) { content_t local_id = nodes[i].getContent(); std::string name; bool found = nimap->getName(local_id, name); if(!found){ unnamed_contents.insert(local_id); continue; } content_t global_id; found = nodedef->getId(name, global_id); if(!found){ global_id = gamedef->allocateUnknownNodeId(name); if(global_id == CONTENT_IGNORE){ unallocatable_contents.insert(name); continue; } } nodes[i].setContent(global_id); } for(std::set<content_t>::const_iterator i = unnamed_contents.begin(); i != unnamed_contents.end(); ++i){ errorstream<<"correctBlockNodeIds(): IGNORING ERROR: " <<"Block contains id "<<(*i) <<" with no name mapping"<<std::endl; } for(std::set<std::string>::const_iterator i = unallocatable_contents.begin(); i != unallocatable_contents.end(); ++i){ errorstream<<"correctBlockNodeIds(): IGNORING ERROR: " <<"Could not allocate global id for node name \"" <<(*i)<<"\""<<std::endl; } } void MapBlock::serialize(std::ostream &os, u8 version, bool disk) { if(!ser_ver_supported(version)) throw VersionMismatchException("ERROR: MapBlock format not supported"); if(data == NULL) { throw SerializationError("ERROR: Not writing dummy block."); } FATAL_ERROR_IF(version < SER_FMT_VER_LOWEST_WRITE, "Serialisation version error"); // First byte u8 flags = 0; if(is_underground) flags |= 0x01; if(getDayNightDiff()) flags |= 0x02; if(m_generated == false) flags |= 0x08; writeU8(os, flags); if (version >= 27) { writeU16(os, m_lighting_complete); } /* Bulk node data */ NameIdMapping nimap; if(disk) { MapNode *tmp_nodes = new MapNode[nodecount]; for(u32 i=0; i<nodecount; i++) tmp_nodes[i] = data[i]; getBlockNodeIdMapping(&nimap, tmp_nodes, m_gamedef->ndef()); u8 content_width = 2; u8 params_width = 2; writeU8(os, content_width); writeU8(os, params_width); MapNode::serializeBulk(os, version, tmp_nodes, nodecount, content_width, params_width, true); delete[] tmp_nodes; } else { u8 content_width = 2; u8 params_width = 2; writeU8(os, content_width); writeU8(os, params_width); MapNode::serializeBulk(os, version, data, nodecount, content_width, params_width, true); } /* Node metadata */ std::ostringstream oss(std::ios_base::binary); m_node_metadata.serialize(oss); compressZlib(oss.str(), os); /* Data that goes to disk, but not the network */ if(disk) { if(version <= 24){ // Node timers m_node_timers.serialize(os, version); } // Static objects m_static_objects.serialize(os); // Timestamp writeU32(os, getTimestamp()); // Write block-specific node definition id mapping nimap.serialize(os); if(version >= 25){ // Node timers m_node_timers.serialize(os, version); } } } void MapBlock::serializeNetworkSpecific(std::ostream &os) { if (!data) { throw SerializationError("ERROR: Not writing dummy block."); } writeU8(os, 1); // version writeF1000(os, 0); // deprecated heat writeF1000(os, 0); // deprecated humidity } void MapBlock::deSerialize(std::istream &is, u8 version, bool disk) { if(!ser_ver_supported(version)) throw VersionMismatchException("ERROR: MapBlock format not supported"); TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())<<std::endl); m_day_night_differs_expired = false; if(version <= 21) { deSerialize_pre22(is, version, disk); return; } u8 flags = readU8(is); is_underground = (flags & 0x01) ? true : false; m_day_night_differs = (flags & 0x02) ? true : false; if (version < 27) { m_lighting_complete = 0xFFFF; } else { m_lighting_complete = readU16(is); } m_generated = (flags & 0x08) ? false : true; /* Bulk node data */ TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos()) <<": Bulk node data"<<std::endl); u8 content_width = readU8(is); u8 params_width = readU8(is); if(content_width != 1 && content_width != 2) throw SerializationError("MapBlock::deSerialize(): invalid content_width"); if(params_width != 2) throw SerializationError("MapBlock::deSerialize(): invalid params_width"); MapNode::deSerializeBulk(is, version, data, nodecount, content_width, params_width, true); /* NodeMetadata */ TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos()) <<": Node metadata"<<std::endl); // Ignore errors try { std::ostringstream oss(std::ios_base::binary); decompressZlib(is, oss); std::istringstream iss(oss.str(), std::ios_base::binary); if (version >= 23) m_node_metadata.deSerialize(iss, m_gamedef->idef()); else content_nodemeta_deserialize_legacy(iss, &m_node_metadata, &m_node_timers, m_gamedef->idef()); } catch(SerializationError &e) { warningstream<<"MapBlock::deSerialize(): Ignoring an error" <<" while deserializing node metadata at (" <<PP(getPos())<<": "<<e.what()<<std::endl; } /* Data that is only on disk */ if(disk) { // Node timers if(version == 23){ // Read unused zero readU8(is); } if(version == 24){ TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos()) <<": Node timers (ver==24)"<<std::endl); m_node_timers.deSerialize(is, version); } // Static objects TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos()) <<": Static objects"<<std::endl); m_static_objects.deSerialize(is); // Timestamp TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos()) <<": Timestamp"<<std::endl); setTimestamp(readU32(is)); m_disk_timestamp = m_timestamp; // Dynamically re-set ids based on node names TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos()) <<": NameIdMapping"<<std::endl); NameIdMapping nimap; nimap.deSerialize(is); correctBlockNodeIds(&nimap, data, m_gamedef); if(version >= 25){ TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos()) <<": Node timers (ver>=25)"<<std::endl); m_node_timers.deSerialize(is, version); } } TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos()) <<": Done."<<std::endl); } void MapBlock::deSerializeNetworkSpecific(std::istream &is) { try { int version = readU8(is); //if(version != 1) // throw SerializationError("unsupported MapBlock version"); if(version >= 1) { readF1000(is); // deprecated heat readF1000(is); // deprecated humidity } } catch(SerializationError &e) { warningstream<<"MapBlock::deSerializeNetworkSpecific(): Ignoring an error" <<": "<<e.what()<<std::endl; } } /* Legacy serialization */ void MapBlock::deSerialize_pre22(std::istream &is, u8 version, bool disk) { // Initialize default flags is_underground = false; m_day_night_differs = false; m_lighting_complete = 0xFFFF; m_generated = true; // Make a temporary buffer u32 ser_length = MapNode::serializedLength(version); SharedBuffer<u8> databuf_nodelist(nodecount * ser_length); // These have no compression if (version <= 3 || version == 5 || version == 6) { char tmp; is.read(&tmp, 1); if (is.gcount() != 1) throw SerializationError(std::string(FUNCTION_NAME) + ": not enough input data"); is_underground = tmp; is.read((char *)*databuf_nodelist, nodecount * ser_length); if ((u32)is.gcount() != nodecount * ser_length) throw SerializationError(std::string(FUNCTION_NAME) + ": not enough input data"); } else if (version <= 10) { u8 t8; is.read((char *)&t8, 1); is_underground = t8; { // Uncompress and set material data std::ostringstream os(std::ios_base::binary); decompress(is, os, version); std::string s = os.str(); if (s.size() != nodecount) throw SerializationError(std::string(FUNCTION_NAME) + ": not enough input data"); for (u32 i = 0; i < s.size(); i++) { databuf_nodelist[i*ser_length] = s[i]; } } { // Uncompress and set param data std::ostringstream os(std::ios_base::binary); decompress(is, os, version); std::string s = os.str(); if (s.size() != nodecount) throw SerializationError(std::string(FUNCTION_NAME) + ": not enough input data"); for (u32 i = 0; i < s.size(); i++) { databuf_nodelist[i*ser_length + 1] = s[i]; } } if (version >= 10) { // Uncompress and set param2 data std::ostringstream os(std::ios_base::binary); decompress(is, os, version); std::string s = os.str(); if (s.size() != nodecount) throw SerializationError(std::string(FUNCTION_NAME) + ": not enough input data"); for (u32 i = 0; i < s.size(); i++) { databuf_nodelist[i*ser_length + 2] = s[i]; } } } else { // All other versions (10 to 21) u8 flags; is.read((char*)&flags, 1); is_underground = (flags & 0x01) ? true : false; m_day_night_differs = (flags & 0x02) ? true : false; if(version >= 18) m_generated = (flags & 0x08) ? false : true; // Uncompress data std::ostringstream os(std::ios_base::binary); decompress(is, os, version); std::string s = os.str(); if (s.size() != nodecount * 3) throw SerializationError(std::string(FUNCTION_NAME) + ": decompress resulted in size other than nodecount*3"); // deserialize nodes from buffer for (u32 i = 0; i < nodecount; i++) { databuf_nodelist[i*ser_length] = s[i]; databuf_nodelist[i*ser_length + 1] = s[i+nodecount]; databuf_nodelist[i*ser_length + 2] = s[i+nodecount*2]; } /* NodeMetadata */ if (version >= 14) { // Ignore errors try { if (version <= 15) { std::string data = deSerializeString(is); std::istringstream iss(data, std::ios_base::binary); content_nodemeta_deserialize_legacy(iss, &m_node_metadata, &m_node_timers, m_gamedef->idef()); } else { //std::string data = deSerializeLongString(is); std::ostringstream oss(std::ios_base::binary); decompressZlib(is, oss); std::istringstream iss(oss.str(), std::ios_base::binary); content_nodemeta_deserialize_legacy(iss, &m_node_metadata, &m_node_timers, m_gamedef->idef()); } } catch(SerializationError &e) { warningstream<<"MapBlock::deSerialize(): Ignoring an error" <<" while deserializing node metadata"<<std::endl; } } } // Deserialize node data for (u32 i = 0; i < nodecount; i++) { data[i].deSerialize(&databuf_nodelist[i * ser_length], version); } if (disk) { /* Versions up from 9 have block objects. (DEPRECATED) */ if (version >= 9) { u16 count = readU16(is); // Not supported and length not known if count is not 0 if(count != 0){ warningstream<<"MapBlock::deSerialize_pre22(): " <<"Ignoring stuff coming at and after MBOs"<<std::endl; return; } } /* Versions up from 15 have static objects. */ if (version >= 15) m_static_objects.deSerialize(is); // Timestamp if (version >= 17) { setTimestamp(readU32(is)); m_disk_timestamp = m_timestamp; } else { setTimestamp(BLOCK_TIMESTAMP_UNDEFINED); } // Dynamically re-set ids based on node names NameIdMapping nimap; // If supported, read node definition id mapping if (version >= 21) { nimap.deSerialize(is); // Else set the legacy mapping } else { content_mapnode_get_name_id_mapping(&nimap); } correctBlockNodeIds(&nimap, data, m_gamedef); } // Legacy data changes // This code has to convert from pre-22 to post-22 format. INodeDefManager *nodedef = m_gamedef->ndef(); for(u32 i=0; i<nodecount; i++) { const ContentFeatures &f = nodedef->get(data[i].getContent()); // Mineral if(nodedef->getId("default:stone") == data[i].getContent() && data[i].getParam1() == 1) { data[i].setContent(nodedef->getId("default:stone_with_coal")); data[i].setParam1(0); } else if(nodedef->getId("default:stone") == data[i].getContent() && data[i].getParam1() == 2) { data[i].setContent(nodedef->getId("default:stone_with_iron")); data[i].setParam1(0); } // facedir_simple if(f.legacy_facedir_simple) { data[i].setParam2(data[i].getParam1()); data[i].setParam1(0); } // wall_mounted if(f.legacy_wallmounted) { u8 wallmounted_new_to_old[8] = {0x04, 0x08, 0x01, 0x02, 0x10, 0x20, 0, 0}; u8 dir_old_format = data[i].getParam2(); u8 dir_new_format = 0; for(u8 j=0; j<8; j++) { if((dir_old_format & wallmounted_new_to_old[j]) != 0) { dir_new_format = j; break; } } data[i].setParam2(dir_new_format); } } } /* Get a quick string to describe what a block actually contains */ std::string analyze_block(MapBlock *block) { if(block == NULL) return "NULL"; std::ostringstream desc; v3s16 p = block->getPos(); char spos[20]; snprintf(spos, 20, "(%2d,%2d,%2d), ", p.X, p.Y, p.Z); desc<<spos; switch(block->getModified()) { case MOD_STATE_CLEAN: desc<<"CLEAN, "; break; case MOD_STATE_WRITE_AT_UNLOAD: desc<<"WRITE_AT_UNLOAD, "; break; case MOD_STATE_WRITE_NEEDED: desc<<"WRITE_NEEDED, "; break; default: desc<<"unknown getModified()="+itos(block->getModified())+", "; } if(block->isGenerated()) desc<<"is_gen [X], "; else desc<<"is_gen [ ], "; if(block->getIsUnderground()) desc<<"is_ug [X], "; else desc<<"is_ug [ ], "; desc<<"lighting_complete: "<<block->getLightingComplete()<<", "; if(block->isDummy()) { desc<<"Dummy, "; } else { bool full_ignore = true; bool some_ignore = false; bool full_air = true; bool some_air = false; for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++) for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++) for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++) { v3s16 p(x0,y0,z0); MapNode n = block->getNodeNoEx(p); content_t c = n.getContent(); if(c == CONTENT_IGNORE) some_ignore = true; else full_ignore = false; if(c == CONTENT_AIR) some_air = true; else full_air = false; } desc<<"content {"; std::ostringstream ss; if(full_ignore) ss<<"IGNORE (full), "; else if(some_ignore) ss<<"IGNORE, "; if(full_air) ss<<"AIR (full), "; else if(some_air) ss<<"AIR, "; if(ss.str().size()>=2) desc<<ss.str().substr(0, ss.str().size()-2); desc<<"}, "; } return desc.str().substr(0, desc.str().size()-2); } //END