LCOV - code coverage report
Current view: top level - src - mapblock.cpp (source / functions) Hit Total Coverage
Test: report Lines: 55 482 11.4 %
Date: 2015-07-11 18:23:49 Functions: 8 21 38.1 %

          Line data    Source code
       1             : /*
       2             : Minetest
       3             : Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
       4             : 
       5             : This program is free software; you can redistribute it and/or modify
       6             : it under the terms of the GNU Lesser General Public License as published by
       7             : the Free Software Foundation; either version 2.1 of the License, or
       8             : (at your option) any later version.
       9             : 
      10             : This program is distributed in the hope that it will be useful,
      11             : but WITHOUT ANY WARRANTY; without even the implied warranty of
      12             : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      13             : GNU Lesser General Public License for more details.
      14             : 
      15             : You should have received a copy of the GNU Lesser General Public License along
      16             : with this program; if not, write to the Free Software Foundation, Inc.,
      17             : 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
      18             : */
      19             : 
      20             : #include "mapblock.h"
      21             : 
      22             : #include <sstream>
      23             : #include "map.h"
      24             : #include "light.h"
      25             : #include "nodedef.h"
      26             : #include "nodemetadata.h"
      27             : #include "gamedef.h"
      28             : #include "log.h"
      29             : #include "nameidmapping.h"
      30             : #include "content_mapnode.h" // For legacy name-id mapping
      31             : #include "content_nodemeta.h" // For legacy deserialization
      32             : #include "serialization.h"
      33             : #ifndef SERVER
      34             : #include "mapblock_mesh.h"
      35             : #endif
      36             : #include "util/string.h"
      37             : #include "util/serialize.h"
      38             : 
      39             : #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
      40             : 
      41             : static const char *modified_reason_strings[] = {
      42             :         "initial",
      43             :         "reallocate",
      44             :         "setIsUnderground",
      45             :         "setLightingExpired",
      46             :         "setGenerated",
      47             :         "setNode",
      48             :         "setNodeNoCheck",
      49             :         "setTimestamp",
      50             :         "NodeMetaRef::reportMetadataChange",
      51             :         "clearAllObjects",
      52             :         "Timestamp expired (step)",
      53             :         "addActiveObjectRaw",
      54             :         "removeRemovedObjects/remove",
      55             :         "removeRemovedObjects/deactivate",
      56             :         "Stored list cleared in activateObjects due to overflow",
      57             :         "deactivateFarObjects: Static data moved in",
      58             :         "deactivateFarObjects: Static data moved out",
      59             :         "deactivateFarObjects: Static data changed considerably",
      60             :         "finishBlockMake: expireDayNightDiff"
      61             :         "unknown",
      62             : };
      63             : 
      64             : 
      65             : /*
      66             :         MapBlock
      67             : */
      68             : 
      69         751 : MapBlock::MapBlock(Map *parent, v3s16 pos, IGameDef *gamedef, bool dummy):
      70             :                 m_parent(parent),
      71             :                 m_pos(pos),
      72             :                 m_gamedef(gamedef),
      73             :                 m_modified(MOD_STATE_WRITE_NEEDED),
      74             :                 m_modified_reason(MOD_REASON_INITIAL),
      75             :                 is_underground(false),
      76             :                 m_lighting_expired(true),
      77             :                 m_day_night_differs(false),
      78             :                 m_day_night_differs_expired(true),
      79             :                 m_generated(false),
      80             :                 m_timestamp(BLOCK_TIMESTAMP_UNDEFINED),
      81             :                 m_disk_timestamp(BLOCK_TIMESTAMP_UNDEFINED),
      82             :                 m_usage_timer(0),
      83         751 :                 m_refcount(0)
      84             : {
      85         751 :         data = NULL;
      86         751 :         if(dummy == false)
      87         751 :                 reallocate();
      88             : 
      89             : #ifndef SERVER
      90         751 :         mesh = NULL;
      91             : #endif
      92         751 : }
      93             : 
      94        1502 : MapBlock::~MapBlock()
      95             : {
      96             : #ifndef SERVER
      97             :         {
      98             :                 //JMutexAutoLock lock(mesh_mutex);
      99             : 
     100         751 :                 if(mesh)
     101             :                 {
     102         654 :                         delete mesh;
     103         654 :                         mesh = NULL;
     104             :                 }
     105             :         }
     106             : #endif
     107             : 
     108         751 :         if(data)
     109         751 :                 delete[] data;
     110         751 : }
     111             : 
     112           0 : bool MapBlock::isValidPositionParent(v3s16 p)
     113             : {
     114           0 :         if(isValidPosition(p))
     115             :         {
     116           0 :                 return true;
     117             :         }
     118             :         else{
     119           0 :                 return m_parent->isValidPosition(getPosRelative() + p);
     120             :         }
     121             : }
     122             : 
     123           0 : MapNode MapBlock::getNodeParent(v3s16 p, bool *is_valid_position)
     124             : {
     125           0 :         if (isValidPosition(p) == false)
     126           0 :                 return m_parent->getNodeNoEx(getPosRelative() + p, is_valid_position);
     127             : 
     128           0 :         if (data == NULL) {
     129           0 :                 if (is_valid_position)
     130           0 :                         *is_valid_position = false;
     131           0 :                 return MapNode(CONTENT_IGNORE);
     132             :         }
     133           0 :         if (is_valid_position)
     134           0 :                 *is_valid_position = true;
     135           0 :         return data[p.Z * zstride + p.Y * ystride + p.X];
     136             : }
     137             : 
     138           0 : std::string MapBlock::getModifiedReasonString()
     139             : {
     140           0 :         std::string reason;
     141             : 
     142           0 :         const u32 ubound = MYMIN(sizeof(m_modified_reason) * CHAR_BIT,
     143             :                 ARRLEN(modified_reason_strings));
     144             : 
     145           0 :         for (u32 i = 0; i != ubound; i++) {
     146           0 :                 if ((m_modified_reason & (1 << i)) == 0)
     147           0 :                         continue;
     148             : 
     149           0 :                 reason += modified_reason_strings[i];
     150           0 :                 reason += ", ";
     151             :         }
     152             : 
     153           0 :         if (reason.length() > 2)
     154           0 :                 reason.resize(reason.length() - 2);
     155             : 
     156           0 :         return reason;
     157             : }
     158             : 
     159             : /*
     160             :         Propagates sunlight down through the block.
     161             :         Doesn't modify nodes that are not affected by sunlight.
     162             : 
     163             :         Returns false if sunlight at bottom block is invalid.
     164             :         Returns true if sunlight at bottom block is valid.
     165             :         Returns true if bottom block doesn't exist.
     166             : 
     167             :         If there is a block above, continues from it.
     168             :         If there is no block above, assumes there is sunlight, unless
     169             :         is_underground is set or highest node is water.
     170             : 
     171             :         All sunlighted nodes are added to light_sources.
     172             : 
     173             :         if remove_light==true, sets non-sunlighted nodes black.
     174             : 
     175             :         if black_air_left!=NULL, it is set to true if non-sunlighted
     176             :         air is left in block.
     177             : */
     178           0 : bool MapBlock::propagateSunlight(std::set<v3s16> & light_sources,
     179             :                 bool remove_light, bool *black_air_left)
     180             : {
     181           0 :         INodeDefManager *nodemgr = m_gamedef->ndef();
     182             : 
     183             :         // Whether the sunlight at the top of the bottom block is valid
     184           0 :         bool block_below_is_valid = true;
     185             : 
     186           0 :         v3s16 pos_relative = getPosRelative();
     187             : 
     188           0 :         for(s16 x=0; x<MAP_BLOCKSIZE; x++)
     189             :         {
     190           0 :                 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
     191             :                 {
     192             : #if 1
     193           0 :                         bool no_sunlight = false;
     194             :                         //bool no_top_block = false;
     195             : 
     196             :                         // Check if node above block has sunlight
     197             : 
     198             :                         bool is_valid_position;
     199             :                         MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z),
     200           0 :                                 &is_valid_position);
     201           0 :                         if (is_valid_position)
     202             :                         {
     203           0 :                                 if(n.getContent() == CONTENT_IGNORE)
     204             :                                 {
     205             :                                         // Trust heuristics
     206           0 :                                         no_sunlight = is_underground;
     207             :                                 }
     208           0 :                                 else if(n.getLight(LIGHTBANK_DAY, m_gamedef->ndef()) != LIGHT_SUN)
     209             :                                 {
     210           0 :                                         no_sunlight = true;
     211             :                                 }
     212             :                         }
     213             :                         else
     214             :                         {
     215             :                                 //no_top_block = true;
     216             : 
     217             :                                 // NOTE: This makes over-ground roofed places sunlighted
     218             :                                 // Assume sunlight, unless is_underground==true
     219           0 :                                 if(is_underground)
     220             :                                 {
     221           0 :                                         no_sunlight = true;
     222             :                                 }
     223             :                                 else
     224             :                                 {
     225           0 :                                         MapNode n = getNodeNoEx(v3s16(x, MAP_BLOCKSIZE-1, z));
     226           0 :                                         if(m_gamedef->ndef()->get(n).sunlight_propagates == false)
     227             :                                         {
     228           0 :                                                 no_sunlight = true;
     229             :                                         }
     230             :                                 }
     231             :                                 // NOTE: As of now, this just would make everything dark.
     232             :                                 // No sunlight here
     233             :                                 //no_sunlight = true;
     234             :                         }
     235             : #endif
     236             : #if 0 // Doesn't work; nothing gets light.
     237             :                         bool no_sunlight = true;
     238             :                         bool no_top_block = false;
     239             :                         // Check if node above block has sunlight
     240             :                         try{
     241             :                                 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
     242             :                                 if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
     243             :                                 {
     244             :                                         no_sunlight = false;
     245             :                                 }
     246             :                         }
     247             :                         catch(InvalidPositionException &e)
     248             :                         {
     249             :                                 no_top_block = true;
     250             :                         }
     251             : #endif
     252             : 
     253             :                         /*std::cout<<"("<<x<<","<<z<<"): "
     254             :                                         <<"no_top_block="<<no_top_block
     255             :                                         <<", is_underground="<<is_underground
     256             :                                         <<", no_sunlight="<<no_sunlight
     257             :                                         <<std::endl;*/
     258             : 
     259           0 :                         s16 y = MAP_BLOCKSIZE-1;
     260             : 
     261             :                         // This makes difference to diminishing in water.
     262           0 :                         bool stopped_to_solid_object = false;
     263             : 
     264           0 :                         u8 current_light = no_sunlight ? 0 : LIGHT_SUN;
     265             : 
     266           0 :                         for(; y >= 0; y--)
     267             :                         {
     268           0 :                                 v3s16 pos(x, y, z);
     269           0 :                                 MapNode &n = getNodeRef(pos);
     270             : 
     271           0 :                                 if(current_light == 0)
     272             :                                 {
     273             :                                         // Do nothing
     274             :                                 }
     275           0 :                                 else if(current_light == LIGHT_SUN && nodemgr->get(n).sunlight_propagates)
     276             :                                 {
     277             :                                         // Do nothing: Sunlight is continued
     278             :                                 }
     279           0 :                                 else if(nodemgr->get(n).light_propagates == false)
     280             :                                 {
     281             :                                         // A solid object is on the way.
     282           0 :                                         stopped_to_solid_object = true;
     283             : 
     284             :                                         // Light stops.
     285           0 :                                         current_light = 0;
     286             :                                 }
     287             :                                 else
     288             :                                 {
     289             :                                         // Diminish light
     290           0 :                                         current_light = diminish_light(current_light);
     291             :                                 }
     292             : 
     293           0 :                                 u8 old_light = n.getLight(LIGHTBANK_DAY, nodemgr);
     294             : 
     295           0 :                                 if(current_light > old_light || remove_light)
     296             :                                 {
     297           0 :                                         n.setLight(LIGHTBANK_DAY, current_light, nodemgr);
     298             :                                 }
     299             : 
     300           0 :                                 if(diminish_light(current_light) != 0)
     301             :                                 {
     302           0 :                                         light_sources.insert(pos_relative + pos);
     303             :                                 }
     304             : 
     305           0 :                                 if(current_light == 0 && stopped_to_solid_object)
     306             :                                 {
     307           0 :                                         if(black_air_left)
     308             :                                         {
     309           0 :                                                 *black_air_left = true;
     310             :                                         }
     311             :                                 }
     312             :                         }
     313             : 
     314             :                         // Whether or not the block below should see LIGHT_SUN
     315           0 :                         bool sunlight_should_go_down = (current_light == LIGHT_SUN);
     316             : 
     317             :                         /*
     318             :                                 If the block below hasn't already been marked invalid:
     319             : 
     320             :                                 Check if the node below the block has proper sunlight at top.
     321             :                                 If not, the block below is invalid.
     322             : 
     323             :                                 Ignore non-transparent nodes as they always have no light
     324             :                         */
     325             : 
     326           0 :                         if(block_below_is_valid)
     327             :                         {
     328           0 :                                 MapNode n = getNodeParent(v3s16(x, -1, z), &is_valid_position);
     329           0 :                                 if (is_valid_position) {
     330           0 :                                         if(nodemgr->get(n).light_propagates)
     331             :                                         {
     332           0 :                                                 if(n.getLight(LIGHTBANK_DAY, nodemgr) == LIGHT_SUN
     333           0 :                                                                 && sunlight_should_go_down == false)
     334           0 :                                                         block_below_is_valid = false;
     335           0 :                                                 else if(n.getLight(LIGHTBANK_DAY, nodemgr) != LIGHT_SUN
     336           0 :                                                                 && sunlight_should_go_down == true)
     337           0 :                                                         block_below_is_valid = false;
     338             :                                         }
     339             :                                 }
     340             :                                 else
     341             :                                 {
     342             :                                         /*std::cout<<"InvalidBlockException for bottom block node"
     343             :                                                         <<std::endl;*/
     344             :                                         // Just no block below, no need to panic.
     345             :                                 }
     346             :                         }
     347             :                 }
     348             :         }
     349             : 
     350           0 :         return block_below_is_valid;
     351             : }
     352             : 
     353             : 
     354       56320 : void MapBlock::copyTo(VoxelManipulator &dst)
     355             : {
     356       56320 :         v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
     357       56320 :         VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
     358             : 
     359             :         // Copy from data to VoxelManipulator
     360      225280 :         dst.copyFrom(data, data_area, v3s16(0,0,0),
     361      168960 :                         getPosRelative(), data_size);
     362       56320 : }
     363             : 
     364           0 : void MapBlock::copyFrom(VoxelManipulator &dst)
     365             : {
     366           0 :         v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
     367           0 :         VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
     368             : 
     369             :         // Copy from VoxelManipulator to data
     370           0 :         dst.copyTo(data, data_area, v3s16(0,0,0),
     371           0 :                         getPosRelative(), data_size);
     372           0 : }
     373             : 
     374           0 : void MapBlock::actuallyUpdateDayNightDiff()
     375             : {
     376           0 :         INodeDefManager *nodemgr = m_gamedef->ndef();
     377             : 
     378             :         // Running this function un-expires m_day_night_differs
     379           0 :         m_day_night_differs_expired = false;
     380             : 
     381           0 :         if (data == NULL) {
     382           0 :                 m_day_night_differs = false;
     383           0 :                 return;
     384             :         }
     385             : 
     386             :         bool differs;
     387             : 
     388             :         /*
     389             :                 Check if any lighting value differs
     390             :         */
     391           0 :         for (u32 i = 0; i < nodecount; i++) {
     392           0 :                 MapNode &n = data[i];
     393             : 
     394           0 :                 differs = !n.isLightDayNightEq(nodemgr);
     395           0 :                 if (differs)
     396           0 :                         break;
     397             :         }
     398             : 
     399             :         /*
     400             :                 If some lighting values differ, check if the whole thing is
     401             :                 just air. If it is just air, differs = false
     402             :         */
     403           0 :         if (differs) {
     404           0 :                 bool only_air = true;
     405           0 :                 for (u32 i = 0; i < nodecount; i++) {
     406           0 :                         MapNode &n = data[i];
     407           0 :                         if (n.getContent() != CONTENT_AIR) {
     408           0 :                                 only_air = false;
     409           0 :                                 break;
     410             :                         }
     411             :                 }
     412           0 :                 if (only_air)
     413           0 :                         differs = false;
     414             :         }
     415             : 
     416             :         // Set member variable
     417           0 :         m_day_night_differs = differs;
     418             : }
     419             : 
     420         108 : void MapBlock::expireDayNightDiff()
     421             : {
     422             :         //INodeDefManager *nodemgr = m_gamedef->ndef();
     423             : 
     424         108 :         if(data == NULL){
     425           0 :                 m_day_night_differs = false;
     426           0 :                 m_day_night_differs_expired = false;
     427           0 :                 return;
     428             :         }
     429             : 
     430         108 :         m_day_night_differs_expired = true;
     431             : }
     432             : 
     433           0 : s16 MapBlock::getGroundLevel(v2s16 p2d)
     434             : {
     435           0 :         if(isDummy())
     436           0 :                 return -3;
     437             :         try
     438             :         {
     439           0 :                 s16 y = MAP_BLOCKSIZE-1;
     440           0 :                 for(; y>=0; y--)
     441             :                 {
     442           0 :                         MapNode n = getNodeRef(p2d.X, y, p2d.Y);
     443           0 :                         if(m_gamedef->ndef()->get(n).walkable)
     444             :                         {
     445           0 :                                 if(y == MAP_BLOCKSIZE-1)
     446           0 :                                         return -2;
     447             :                                 else
     448           0 :                                         return y;
     449             :                         }
     450             :                 }
     451           0 :                 return -1;
     452             :         }
     453           0 :         catch(InvalidPositionException &e)
     454             :         {
     455           0 :                 return -3;
     456             :         }
     457             : }
     458             : 
     459             : /*
     460             :         Serialization
     461             : */
     462             : // List relevant id-name pairs for ids in the block using nodedef
     463             : // Renumbers the content IDs (starting at 0 and incrementing
     464             : // use static memory requires about 65535 * sizeof(int) ram in order to be
     465             : // sure we can handle all content ids. But it's absolutely worth it as it's
     466             : // a speedup of 4 for one of the major time consuming functions on storing
     467             : // mapblocks.
     468             : static content_t getBlockNodeIdMapping_mapping[USHRT_MAX + 1];
     469           0 : static void getBlockNodeIdMapping(NameIdMapping *nimap, MapNode *nodes,
     470             :                 INodeDefManager *nodedef)
     471             : {
     472           0 :         memset(getBlockNodeIdMapping_mapping, 0xFF, (USHRT_MAX + 1) * sizeof(content_t));
     473             : 
     474           0 :         std::set<content_t> unknown_contents;
     475           0 :         content_t id_counter = 0;
     476           0 :         for (u32 i = 0; i < MapBlock::nodecount; i++) {
     477           0 :                 content_t global_id = nodes[i].getContent();
     478           0 :                 content_t id = CONTENT_IGNORE;
     479             : 
     480             :                 // Try to find an existing mapping
     481           0 :                 if (getBlockNodeIdMapping_mapping[global_id] != 0xFFFF) {
     482           0 :                         id = getBlockNodeIdMapping_mapping[global_id];
     483             :                 }
     484             :                 else
     485             :                 {
     486             :                         // We have to assign a new mapping
     487           0 :                         id = id_counter++;
     488           0 :                         getBlockNodeIdMapping_mapping[global_id] = id;
     489             : 
     490           0 :                         const ContentFeatures &f = nodedef->get(global_id);
     491           0 :                         const std::string &name = f.name;
     492           0 :                         if(name == "")
     493           0 :                                 unknown_contents.insert(global_id);
     494             :                         else
     495           0 :                                 nimap->set(id, name);
     496             :                 }
     497             : 
     498             :                 // Update the MapNode
     499           0 :                 nodes[i].setContent(id);
     500             :         }
     501           0 :         for(std::set<content_t>::const_iterator
     502           0 :                         i = unknown_contents.begin();
     503           0 :                         i != unknown_contents.end(); i++){
     504           0 :                 errorstream<<"getBlockNodeIdMapping(): IGNORING ERROR: "
     505           0 :                                 <<"Name for node id "<<(*i)<<" not known"<<std::endl;
     506             :         }
     507           0 : }
     508             : // Correct ids in the block to match nodedef based on names.
     509             : // Unknown ones are added to nodedef.
     510             : // Will not update itself to match id-name pairs in nodedef.
     511           0 : static void correctBlockNodeIds(const NameIdMapping *nimap, MapNode *nodes,
     512             :                 IGameDef *gamedef)
     513             : {
     514           0 :         INodeDefManager *nodedef = gamedef->ndef();
     515             :         // This means the block contains incorrect ids, and we contain
     516             :         // the information to convert those to names.
     517             :         // nodedef contains information to convert our names to globally
     518             :         // correct ids.
     519           0 :         std::set<content_t> unnamed_contents;
     520           0 :         std::set<std::string> unallocatable_contents;
     521           0 :         for (u32 i = 0; i < MapBlock::nodecount; i++) {
     522           0 :                 content_t local_id = nodes[i].getContent();
     523           0 :                 std::string name;
     524           0 :                 bool found = nimap->getName(local_id, name);
     525           0 :                 if(!found){
     526           0 :                         unnamed_contents.insert(local_id);
     527           0 :                         continue;
     528             :                 }
     529             :                 content_t global_id;
     530           0 :                 found = nodedef->getId(name, global_id);
     531           0 :                 if(!found){
     532           0 :                         global_id = gamedef->allocateUnknownNodeId(name);
     533           0 :                         if(global_id == CONTENT_IGNORE){
     534           0 :                                 unallocatable_contents.insert(name);
     535           0 :                                 continue;
     536             :                         }
     537             :                 }
     538           0 :                 nodes[i].setContent(global_id);
     539             :         }
     540           0 :         for(std::set<content_t>::const_iterator
     541           0 :                         i = unnamed_contents.begin();
     542           0 :                         i != unnamed_contents.end(); i++){
     543           0 :                 errorstream<<"correctBlockNodeIds(): IGNORING ERROR: "
     544           0 :                                 <<"Block contains id "<<(*i)
     545           0 :                                 <<" with no name mapping"<<std::endl;
     546             :         }
     547           0 :         for(std::set<std::string>::const_iterator
     548           0 :                         i = unallocatable_contents.begin();
     549           0 :                         i != unallocatable_contents.end(); i++){
     550           0 :                 errorstream<<"correctBlockNodeIds(): IGNORING ERROR: "
     551           0 :                                 <<"Could not allocate global id for node name \""
     552           0 :                                 <<(*i)<<"\""<<std::endl;
     553             :         }
     554           0 : }
     555             : 
     556           0 : void MapBlock::serialize(std::ostream &os, u8 version, bool disk)
     557             : {
     558           0 :         if(!ser_ver_supported(version))
     559           0 :                 throw VersionMismatchException("ERROR: MapBlock format not supported");
     560             : 
     561           0 :         if(data == NULL)
     562             :         {
     563           0 :                 throw SerializationError("ERROR: Not writing dummy block.");
     564             :         }
     565             : 
     566           0 :         FATAL_ERROR_IF(version < SER_FMT_CLIENT_VER_LOWEST, "Serialize version error");
     567             : 
     568             :         // First byte
     569           0 :         u8 flags = 0;
     570           0 :         if(is_underground)
     571           0 :                 flags |= 0x01;
     572           0 :         if(getDayNightDiff())
     573           0 :                 flags |= 0x02;
     574           0 :         if(m_lighting_expired)
     575           0 :                 flags |= 0x04;
     576           0 :         if(m_generated == false)
     577           0 :                 flags |= 0x08;
     578           0 :         writeU8(os, flags);
     579             : 
     580             :         /*
     581             :                 Bulk node data
     582             :         */
     583           0 :         NameIdMapping nimap;
     584           0 :         if(disk)
     585             :         {
     586           0 :                 MapNode *tmp_nodes = new MapNode[nodecount];
     587           0 :                 for(u32 i=0; i<nodecount; i++)
     588           0 :                         tmp_nodes[i] = data[i];
     589           0 :                 getBlockNodeIdMapping(&nimap, tmp_nodes, m_gamedef->ndef());
     590             : 
     591           0 :                 u8 content_width = 2;
     592           0 :                 u8 params_width = 2;
     593           0 :                 writeU8(os, content_width);
     594           0 :                 writeU8(os, params_width);
     595           0 :                 MapNode::serializeBulk(os, version, tmp_nodes, nodecount,
     596           0 :                                 content_width, params_width, true);
     597           0 :                 delete[] tmp_nodes;
     598             :         }
     599             :         else
     600             :         {
     601           0 :                 u8 content_width = 2;
     602           0 :                 u8 params_width = 2;
     603           0 :                 writeU8(os, content_width);
     604           0 :                 writeU8(os, params_width);
     605           0 :                 MapNode::serializeBulk(os, version, data, nodecount,
     606           0 :                                 content_width, params_width, true);
     607             :         }
     608             : 
     609             :         /*
     610             :                 Node metadata
     611             :         */
     612           0 :         std::ostringstream oss(std::ios_base::binary);
     613           0 :         m_node_metadata.serialize(oss);
     614           0 :         compressZlib(oss.str(), os);
     615             : 
     616             :         /*
     617             :                 Data that goes to disk, but not the network
     618             :         */
     619           0 :         if(disk)
     620             :         {
     621           0 :                 if(version <= 24){
     622             :                         // Node timers
     623           0 :                         m_node_timers.serialize(os, version);
     624             :                 }
     625             : 
     626             :                 // Static objects
     627           0 :                 m_static_objects.serialize(os);
     628             : 
     629             :                 // Timestamp
     630           0 :                 writeU32(os, getTimestamp());
     631             : 
     632             :                 // Write block-specific node definition id mapping
     633           0 :                 nimap.serialize(os);
     634             : 
     635           0 :                 if(version >= 25){
     636             :                         // Node timers
     637           0 :                         m_node_timers.serialize(os, version);
     638             :                 }
     639             :         }
     640           0 : }
     641             : 
     642           0 : void MapBlock::serializeNetworkSpecific(std::ostream &os, u16 net_proto_version)
     643             : {
     644           0 :         if(data == NULL)
     645             :         {
     646           0 :                 throw SerializationError("ERROR: Not writing dummy block.");
     647             :         }
     648             : 
     649           0 :         if(net_proto_version >= 21){
     650           0 :                 int version = 1;
     651           0 :                 writeU8(os, version);
     652           0 :                 writeF1000(os, 0); // deprecated heat
     653           0 :                 writeF1000(os, 0); // deprecated humidity
     654             :         }
     655           0 : }
     656             : 
     657         786 : void MapBlock::deSerialize(std::istream &is, u8 version, bool disk)
     658             : {
     659         786 :         if(!ser_ver_supported(version))
     660           0 :                 throw VersionMismatchException("ERROR: MapBlock format not supported");
     661             : 
     662         786 :         TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())<<std::endl);
     663             : 
     664         786 :         m_day_night_differs_expired = false;
     665             : 
     666         786 :         if(version <= 21)
     667             :         {
     668           0 :                 deSerialize_pre22(is, version, disk);
     669           0 :                 return;
     670             :         }
     671             : 
     672         786 :         u8 flags = readU8(is);
     673         786 :         is_underground = (flags & 0x01) ? true : false;
     674         786 :         m_day_night_differs = (flags & 0x02) ? true : false;
     675         786 :         m_lighting_expired = (flags & 0x04) ? true : false;
     676         786 :         m_generated = (flags & 0x08) ? false : true;
     677             : 
     678             :         /*
     679             :                 Bulk node data
     680             :         */
     681         786 :         TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
     682             :                         <<": Bulk node data"<<std::endl);
     683         786 :         u8 content_width = readU8(is);
     684         786 :         u8 params_width = readU8(is);
     685         786 :         if(content_width != 1 && content_width != 2)
     686           0 :                 throw SerializationError("MapBlock::deSerialize(): invalid content_width");
     687         786 :         if(params_width != 2)
     688           0 :                 throw SerializationError("MapBlock::deSerialize(): invalid params_width");
     689         786 :         MapNode::deSerializeBulk(is, version, data, nodecount,
     690         786 :                         content_width, params_width, true);
     691             : 
     692             :         /*
     693             :                 NodeMetadata
     694             :         */
     695         786 :         TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
     696             :                         <<": Node metadata"<<std::endl);
     697             :         // Ignore errors
     698             :         try{
     699        1572 :                 std::ostringstream oss(std::ios_base::binary);
     700         786 :                 decompressZlib(is, oss);
     701        1572 :                 std::istringstream iss(oss.str(), std::ios_base::binary);
     702         786 :                 if(version >= 23)
     703         786 :                         m_node_metadata.deSerialize(iss, m_gamedef);
     704             :                 else
     705           0 :                         content_nodemeta_deserialize_legacy(iss,
     706             :                                         &m_node_metadata, &m_node_timers,
     707           0 :                                         m_gamedef);
     708             :         }
     709           0 :         catch(SerializationError &e)
     710             :         {
     711           0 :                 errorstream<<"WARNING: MapBlock::deSerialize(): Ignoring an error"
     712           0 :                                 <<" while deserializing node metadata at ("
     713           0 :                                 <<PP(getPos())<<": "<<e.what()<<std::endl;
     714             :         }
     715             : 
     716             :         /*
     717             :                 Data that is only on disk
     718             :         */
     719         786 :         if(disk)
     720             :         {
     721             :                 // Node timers
     722           0 :                 if(version == 23){
     723             :                         // Read unused zero
     724           0 :                         readU8(is);
     725             :                 }
     726           0 :                 if(version == 24){
     727           0 :                         TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
     728             :                                         <<": Node timers (ver==24)"<<std::endl);
     729           0 :                         m_node_timers.deSerialize(is, version);
     730             :                 }
     731             : 
     732             :                 // Static objects
     733           0 :                 TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
     734             :                                 <<": Static objects"<<std::endl);
     735           0 :                 m_static_objects.deSerialize(is);
     736             : 
     737             :                 // Timestamp
     738           0 :                 TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
     739             :                                 <<": Timestamp"<<std::endl);
     740           0 :                 setTimestamp(readU32(is));
     741           0 :                 m_disk_timestamp = m_timestamp;
     742             : 
     743             :                 // Dynamically re-set ids based on node names
     744           0 :                 TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
     745             :                                 <<": NameIdMapping"<<std::endl);
     746           0 :                 NameIdMapping nimap;
     747           0 :                 nimap.deSerialize(is);
     748           0 :                 correctBlockNodeIds(&nimap, data, m_gamedef);
     749             : 
     750           0 :                 if(version >= 25){
     751           0 :                         TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
     752             :                                         <<": Node timers (ver>=25)"<<std::endl);
     753           0 :                         m_node_timers.deSerialize(is, version);
     754             :                 }
     755             :         }
     756             : 
     757         786 :         TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
     758             :                         <<": Done."<<std::endl);
     759             : }
     760             : 
     761         786 : void MapBlock::deSerializeNetworkSpecific(std::istream &is)
     762             : {
     763             :         try {
     764         786 :                 int version = readU8(is);
     765             :                 //if(version != 1)
     766             :                 //      throw SerializationError("unsupported MapBlock version");
     767         786 :                 if(version >= 1) {
     768         786 :                         readF1000(is); // deprecated heat
     769         786 :                         readF1000(is); // deprecated humidity
     770             :                 }
     771             :         }
     772           0 :         catch(SerializationError &e)
     773             :         {
     774           0 :                 errorstream<<"WARNING: MapBlock::deSerializeNetworkSpecific(): Ignoring an error"
     775           0 :                                 <<": "<<e.what()<<std::endl;
     776             :         }
     777         786 : }
     778             : 
     779             : /*
     780             :         Legacy serialization
     781             : */
     782             : 
     783           0 : void MapBlock::deSerialize_pre22(std::istream &is, u8 version, bool disk)
     784             : {
     785             :         // Initialize default flags
     786           0 :         is_underground = false;
     787           0 :         m_day_night_differs = false;
     788           0 :         m_lighting_expired = false;
     789           0 :         m_generated = true;
     790             : 
     791             :         // Make a temporary buffer
     792           0 :         u32 ser_length = MapNode::serializedLength(version);
     793           0 :         SharedBuffer<u8> databuf_nodelist(nodecount * ser_length);
     794             : 
     795             :         // These have no compression
     796           0 :         if(version <= 3 || version == 5 || version == 6)
     797             :         {
     798             :                 char tmp;
     799           0 :                 is.read(&tmp, 1);
     800           0 :                 if(is.gcount() != 1)
     801             :                         throw SerializationError
     802           0 :                                         ("MapBlock::deSerialize: no enough input data");
     803           0 :                 is_underground = tmp;
     804           0 :                 is.read((char*)*databuf_nodelist, nodecount * ser_length);
     805           0 :                 if((u32)is.gcount() != nodecount * ser_length)
     806             :                         throw SerializationError
     807           0 :                                         ("MapBlock::deSerialize: no enough input data");
     808             :         }
     809           0 :         else if(version <= 10)
     810             :         {
     811             :                 u8 t8;
     812           0 :                 is.read((char*)&t8, 1);
     813           0 :                 is_underground = t8;
     814             : 
     815             :                 {
     816             :                         // Uncompress and set material data
     817           0 :                         std::ostringstream os(std::ios_base::binary);
     818           0 :                         decompress(is, os, version);
     819           0 :                         std::string s = os.str();
     820           0 :                         if(s.size() != nodecount)
     821             :                                 throw SerializationError
     822           0 :                                                 ("MapBlock::deSerialize: invalid format");
     823           0 :                         for(u32 i=0; i<s.size(); i++)
     824             :                         {
     825           0 :                                 databuf_nodelist[i*ser_length] = s[i];
     826             :                         }
     827             :                 }
     828             :                 {
     829             :                         // Uncompress and set param data
     830           0 :                         std::ostringstream os(std::ios_base::binary);
     831           0 :                         decompress(is, os, version);
     832           0 :                         std::string s = os.str();
     833           0 :                         if(s.size() != nodecount)
     834             :                                 throw SerializationError
     835           0 :                                                 ("MapBlock::deSerialize: invalid format");
     836           0 :                         for(u32 i=0; i<s.size(); i++)
     837             :                         {
     838           0 :                                 databuf_nodelist[i*ser_length + 1] = s[i];
     839             :                         }
     840             :                 }
     841             : 
     842           0 :                 if(version >= 10)
     843             :                 {
     844             :                         // Uncompress and set param2 data
     845           0 :                         std::ostringstream os(std::ios_base::binary);
     846           0 :                         decompress(is, os, version);
     847           0 :                         std::string s = os.str();
     848           0 :                         if(s.size() != nodecount)
     849             :                                 throw SerializationError
     850           0 :                                                 ("MapBlock::deSerialize: invalid format");
     851           0 :                         for(u32 i=0; i<s.size(); i++)
     852             :                         {
     853           0 :                                 databuf_nodelist[i*ser_length + 2] = s[i];
     854             :                         }
     855             :                 }
     856             :         }
     857             :         // All other versions (newest)
     858             :         else
     859             :         {
     860             :                 u8 flags;
     861           0 :                 is.read((char*)&flags, 1);
     862           0 :                 is_underground = (flags & 0x01) ? true : false;
     863           0 :                 m_day_night_differs = (flags & 0x02) ? true : false;
     864           0 :                 m_lighting_expired = (flags & 0x04) ? true : false;
     865           0 :                 if(version >= 18)
     866           0 :                         m_generated = (flags & 0x08) ? false : true;
     867             : 
     868             :                 // Uncompress data
     869           0 :                 std::ostringstream os(std::ios_base::binary);
     870           0 :                 decompress(is, os, version);
     871           0 :                 std::string s = os.str();
     872           0 :                 if(s.size() != nodecount*3)
     873             :                         throw SerializationError
     874             :                                         ("MapBlock::deSerialize: decompress resulted in size"
     875           0 :                                         " other than nodecount*3");
     876             : 
     877             :                 // deserialize nodes from buffer
     878           0 :                 for(u32 i=0; i<nodecount; i++)
     879             :                 {
     880           0 :                         databuf_nodelist[i*ser_length] = s[i];
     881           0 :                         databuf_nodelist[i*ser_length + 1] = s[i+nodecount];
     882           0 :                         databuf_nodelist[i*ser_length + 2] = s[i+nodecount*2];
     883             :                 }
     884             : 
     885             :                 /*
     886             :                         NodeMetadata
     887             :                 */
     888           0 :                 if(version >= 14)
     889             :                 {
     890             :                         // Ignore errors
     891             :                         try{
     892           0 :                                 if(version <= 15)
     893             :                                 {
     894           0 :                                         std::string data = deSerializeString(is);
     895           0 :                                         std::istringstream iss(data, std::ios_base::binary);
     896           0 :                                         content_nodemeta_deserialize_legacy(iss,
     897             :                                                         &m_node_metadata, &m_node_timers,
     898           0 :                                                         m_gamedef);
     899             :                                 }
     900             :                                 else
     901             :                                 {
     902             :                                         //std::string data = deSerializeLongString(is);
     903           0 :                                         std::ostringstream oss(std::ios_base::binary);
     904           0 :                                         decompressZlib(is, oss);
     905           0 :                                         std::istringstream iss(oss.str(), std::ios_base::binary);
     906           0 :                                         content_nodemeta_deserialize_legacy(iss,
     907             :                                                         &m_node_metadata, &m_node_timers,
     908           0 :                                                         m_gamedef);
     909             :                                 }
     910             :                         }
     911           0 :                         catch(SerializationError &e)
     912             :                         {
     913           0 :                                 errorstream<<"WARNING: MapBlock::deSerialize(): Ignoring an error"
     914           0 :                                                 <<" while deserializing node metadata"<<std::endl;
     915             :                         }
     916             :                 }
     917             :         }
     918             : 
     919             :         // Deserialize node data
     920           0 :         for(u32 i=0; i<nodecount; i++)
     921             :         {
     922           0 :                 data[i].deSerialize(&databuf_nodelist[i*ser_length], version);
     923             :         }
     924             : 
     925           0 :         if(disk)
     926             :         {
     927             :                 /*
     928             :                         Versions up from 9 have block objects. (DEPRECATED)
     929             :                 */
     930           0 :                 if(version >= 9){
     931           0 :                         u16 count = readU16(is);
     932             :                         // Not supported and length not known if count is not 0
     933           0 :                         if(count != 0){
     934           0 :                                 errorstream<<"WARNING: MapBlock::deSerialize_pre22(): "
     935           0 :                                                 <<"Ignoring stuff coming at and after MBOs"<<std::endl;
     936           0 :                                 return;
     937             :                         }
     938             :                 }
     939             : 
     940             :                 /*
     941             :                         Versions up from 15 have static objects.
     942             :                 */
     943           0 :                 if(version >= 15)
     944           0 :                         m_static_objects.deSerialize(is);
     945             : 
     946             :                 // Timestamp
     947           0 :                 if(version >= 17){
     948           0 :                         setTimestamp(readU32(is));
     949           0 :                         m_disk_timestamp = m_timestamp;
     950             :                 } else {
     951           0 :                         setTimestamp(BLOCK_TIMESTAMP_UNDEFINED);
     952             :                 }
     953             : 
     954             :                 // Dynamically re-set ids based on node names
     955           0 :                 NameIdMapping nimap;
     956             :                 // If supported, read node definition id mapping
     957           0 :                 if(version >= 21){
     958           0 :                         nimap.deSerialize(is);
     959             :                 // Else set the legacy mapping
     960             :                 } else {
     961           0 :                         content_mapnode_get_name_id_mapping(&nimap);
     962             :                 }
     963           0 :                 correctBlockNodeIds(&nimap, data, m_gamedef);
     964             :         }
     965             : 
     966             : 
     967             :         // Legacy data changes
     968             :         // This code has to convert from pre-22 to post-22 format.
     969           0 :         INodeDefManager *nodedef = m_gamedef->ndef();
     970           0 :         for(u32 i=0; i<nodecount; i++)
     971             :         {
     972           0 :                 const ContentFeatures &f = nodedef->get(data[i].getContent());
     973             :                 // Mineral
     974           0 :                 if(nodedef->getId("default:stone") == data[i].getContent()
     975           0 :                                 && data[i].getParam1() == 1)
     976             :                 {
     977           0 :                         data[i].setContent(nodedef->getId("default:stone_with_coal"));
     978           0 :                         data[i].setParam1(0);
     979             :                 }
     980           0 :                 else if(nodedef->getId("default:stone") == data[i].getContent()
     981           0 :                                 && data[i].getParam1() == 2)
     982             :                 {
     983           0 :                         data[i].setContent(nodedef->getId("default:stone_with_iron"));
     984           0 :                         data[i].setParam1(0);
     985             :                 }
     986             :                 // facedir_simple
     987           0 :                 if(f.legacy_facedir_simple)
     988             :                 {
     989           0 :                         data[i].setParam2(data[i].getParam1());
     990           0 :                         data[i].setParam1(0);
     991             :                 }
     992             :                 // wall_mounted
     993           0 :                 if(f.legacy_wallmounted)
     994             :                 {
     995           0 :                         u8 wallmounted_new_to_old[8] = {0x04, 0x08, 0x01, 0x02, 0x10, 0x20, 0, 0};
     996           0 :                         u8 dir_old_format = data[i].getParam2();
     997           0 :                         u8 dir_new_format = 0;
     998           0 :                         for(u8 j=0; j<8; j++)
     999             :                         {
    1000           0 :                                 if((dir_old_format & wallmounted_new_to_old[j]) != 0)
    1001             :                                 {
    1002           0 :                                         dir_new_format = j;
    1003           0 :                                         break;
    1004             :                                 }
    1005             :                         }
    1006           0 :                         data[i].setParam2(dir_new_format);
    1007             :                 }
    1008             :         }
    1009             : 
    1010             : }
    1011             : 
    1012             : /*
    1013             :         Get a quick string to describe what a block actually contains
    1014             : */
    1015           0 : std::string analyze_block(MapBlock *block)
    1016             : {
    1017           0 :         if(block == NULL)
    1018           0 :                 return "NULL";
    1019             : 
    1020           0 :         std::ostringstream desc;
    1021             : 
    1022           0 :         v3s16 p = block->getPos();
    1023             :         char spos[20];
    1024           0 :         snprintf(spos, 20, "(%2d,%2d,%2d), ", p.X, p.Y, p.Z);
    1025           0 :         desc<<spos;
    1026             : 
    1027           0 :         switch(block->getModified())
    1028             :         {
    1029             :         case MOD_STATE_CLEAN:
    1030           0 :                 desc<<"CLEAN,           ";
    1031           0 :                 break;
    1032             :         case MOD_STATE_WRITE_AT_UNLOAD:
    1033           0 :                 desc<<"WRITE_AT_UNLOAD, ";
    1034           0 :                 break;
    1035             :         case MOD_STATE_WRITE_NEEDED:
    1036           0 :                 desc<<"WRITE_NEEDED,    ";
    1037           0 :                 break;
    1038             :         default:
    1039           0 :                 desc<<"unknown getModified()="+itos(block->getModified())+", ";
    1040             :         }
    1041             : 
    1042           0 :         if(block->isGenerated())
    1043           0 :                 desc<<"is_gen [X], ";
    1044             :         else
    1045           0 :                 desc<<"is_gen [ ], ";
    1046             : 
    1047           0 :         if(block->getIsUnderground())
    1048           0 :                 desc<<"is_ug [X], ";
    1049             :         else
    1050           0 :                 desc<<"is_ug [ ], ";
    1051             : 
    1052           0 :         if(block->getLightingExpired())
    1053           0 :                 desc<<"lighting_exp [X], ";
    1054             :         else
    1055           0 :                 desc<<"lighting_exp [ ], ";
    1056             : 
    1057           0 :         if(block->isDummy())
    1058             :         {
    1059           0 :                 desc<<"Dummy, ";
    1060             :         }
    1061             :         else
    1062             :         {
    1063           0 :                 bool full_ignore = true;
    1064           0 :                 bool some_ignore = false;
    1065           0 :                 bool full_air = true;
    1066           0 :                 bool some_air = false;
    1067           0 :                 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
    1068           0 :                 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
    1069           0 :                 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
    1070             :                 {
    1071           0 :                         v3s16 p(x0,y0,z0);
    1072           0 :                         MapNode n = block->getNodeNoEx(p);
    1073           0 :                         content_t c = n.getContent();
    1074           0 :                         if(c == CONTENT_IGNORE)
    1075           0 :                                 some_ignore = true;
    1076             :                         else
    1077           0 :                                 full_ignore = false;
    1078           0 :                         if(c == CONTENT_AIR)
    1079           0 :                                 some_air = true;
    1080             :                         else
    1081           0 :                                 full_air = false;
    1082             :                 }
    1083             : 
    1084           0 :                 desc<<"content {";
    1085             : 
    1086           0 :                 std::ostringstream ss;
    1087             : 
    1088           0 :                 if(full_ignore)
    1089           0 :                         ss<<"IGNORE (full), ";
    1090           0 :                 else if(some_ignore)
    1091           0 :                         ss<<"IGNORE, ";
    1092             : 
    1093           0 :                 if(full_air)
    1094           0 :                         ss<<"AIR (full), ";
    1095           0 :                 else if(some_air)
    1096           0 :                         ss<<"AIR, ";
    1097             : 
    1098           0 :                 if(ss.str().size()>=2)
    1099           0 :                         desc<<ss.str().substr(0, ss.str().size()-2);
    1100             : 
    1101           0 :                 desc<<"}, ";
    1102             :         }
    1103             : 
    1104           0 :         return desc.str().substr(0, desc.str().size()-2);
    1105           3 : }
    1106             : 
    1107             : 
    1108             : //END

Generated by: LCOV version 1.11