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