Weather support
[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                 m_parent(parent),
48                 m_pos(pos),
49                 m_gamedef(gamedef),
50                 m_modified(MOD_STATE_WRITE_NEEDED),
51                 m_modified_reason("initial"),
52                 m_modified_reason_too_long(false),
53                 is_underground(false),
54                 m_lighting_expired(true),
55                 m_day_night_differs(false),
56                 m_day_night_differs_expired(true),
57                 m_generated(false),
58                 m_timestamp(BLOCK_TIMESTAMP_UNDEFINED),
59                 m_disk_timestamp(BLOCK_TIMESTAMP_UNDEFINED),
60                 m_usage_timer(0),
61                 m_refcount(0),
62                 heat_time(0),
63                 heat(0),
64                 humidity_time(0),
65                 humidity(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         } else {
640                 if(version >= 26){
641                         writeF1000(os, heat);
642                         writeF1000(os, humidity);
643                 }
644         }
645 }
646
647 void MapBlock::deSerialize(std::istream &is, u8 version, bool disk)
648 {
649         if(!ser_ver_supported(version))
650                 throw VersionMismatchException("ERROR: MapBlock format not supported");
651         
652         TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())<<std::endl);
653
654         m_day_night_differs_expired = false;
655
656         if(version <= 21)
657         {
658                 deSerialize_pre22(is, version, disk);
659                 return;
660         }
661
662         u8 flags = readU8(is);
663         is_underground = (flags & 0x01) ? true : false;
664         m_day_night_differs = (flags & 0x02) ? true : false;
665         m_lighting_expired = (flags & 0x04) ? true : false;
666         m_generated = (flags & 0x08) ? false : true;
667
668         /*
669                 Bulk node data
670         */
671         TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
672                         <<": Bulk node data"<<std::endl);
673         u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
674         u8 content_width = readU8(is);
675         u8 params_width = readU8(is);
676         if(content_width != 1 && content_width != 2)
677                 throw SerializationError("MapBlock::deSerialize(): invalid content_width");
678         if(params_width != 2)
679                 throw SerializationError("MapBlock::deSerialize(): invalid params_width");
680         MapNode::deSerializeBulk(is, version, data, nodecount,
681                         content_width, params_width, true);
682
683         /*
684                 NodeMetadata
685         */
686         TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
687                         <<": Node metadata"<<std::endl);
688         // Ignore errors
689         try{
690                 std::ostringstream oss(std::ios_base::binary);
691                 decompressZlib(is, oss);
692                 std::istringstream iss(oss.str(), std::ios_base::binary);
693                 if(version >= 23)
694                         m_node_metadata.deSerialize(iss, m_gamedef);
695                 else
696                         content_nodemeta_deserialize_legacy(iss,
697                                         &m_node_metadata, &m_node_timers,
698                                         m_gamedef);
699         }
700         catch(SerializationError &e)
701         {
702                 errorstream<<"WARNING: MapBlock::deSerialize(): Ignoring an error"
703                                 <<" while deserializing node metadata at ("
704                                 <<PP(getPos())<<": "<<e.what()<<std::endl;
705         }
706
707         /*
708                 Data that is only on disk
709         */
710         if(disk)
711         {
712                 // Node timers
713                 if(version == 23){
714                         // Read unused zero
715                         readU8(is);
716                 }
717                 if(version == 24){
718                         TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
719                                         <<": Node timers (ver==24)"<<std::endl);
720                         m_node_timers.deSerialize(is, version);
721                 }
722
723                 // Static objects
724                 TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
725                                 <<": Static objects"<<std::endl);
726                 m_static_objects.deSerialize(is);
727                 
728                 // Timestamp
729                 TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
730                                 <<": Timestamp"<<std::endl);
731                 setTimestamp(readU32(is));
732                 m_disk_timestamp = m_timestamp;
733                 
734                 // Dynamically re-set ids based on node names
735                 TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
736                                 <<": NameIdMapping"<<std::endl);
737                 NameIdMapping nimap;
738                 nimap.deSerialize(is);
739                 correctBlockNodeIds(&nimap, data, m_gamedef);
740
741                 if(version >= 25){
742                         TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
743                                         <<": Node timers (ver>=25)"<<std::endl);
744                         m_node_timers.deSerialize(is, version);
745                 }
746         } else {
747                 if(version >= 26){
748                         heat = readF1000(is);
749                         humidity = readF1000(is);
750                 }
751         }
752                 
753         TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
754                         <<": Done."<<std::endl);
755 }
756
757 /*
758         Legacy serialization
759 */
760
761 void MapBlock::deSerialize_pre22(std::istream &is, u8 version, bool disk)
762 {
763         u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
764
765         // Initialize default flags
766         is_underground = false;
767         m_day_night_differs = false;
768         m_lighting_expired = false;
769         m_generated = true;
770
771         // Make a temporary buffer
772         u32 ser_length = MapNode::serializedLength(version);
773         SharedBuffer<u8> databuf_nodelist(nodecount * ser_length);
774
775         // These have no compression
776         if(version <= 3 || version == 5 || version == 6)
777         {
778                 char tmp;
779                 is.read(&tmp, 1);
780                 if(is.gcount() != 1)
781                         throw SerializationError
782                                         ("MapBlock::deSerialize: no enough input data");
783                 is_underground = tmp;
784                 is.read((char*)*databuf_nodelist, nodecount * ser_length);
785                 if((u32)is.gcount() != nodecount * ser_length)
786                         throw SerializationError
787                                         ("MapBlock::deSerialize: no enough input data");
788         }
789         else if(version <= 10)
790         {
791                 u8 t8;
792                 is.read((char*)&t8, 1);
793                 is_underground = t8;
794
795                 {
796                         // Uncompress and set material data
797                         std::ostringstream os(std::ios_base::binary);
798                         decompress(is, os, version);
799                         std::string s = os.str();
800                         if(s.size() != nodecount)
801                                 throw SerializationError
802                                                 ("MapBlock::deSerialize: invalid format");
803                         for(u32 i=0; i<s.size(); i++)
804                         {
805                                 databuf_nodelist[i*ser_length] = s[i];
806                         }
807                 }
808                 {
809                         // Uncompress and set param data
810                         std::ostringstream os(std::ios_base::binary);
811                         decompress(is, os, version);
812                         std::string s = os.str();
813                         if(s.size() != nodecount)
814                                 throw SerializationError
815                                                 ("MapBlock::deSerialize: invalid format");
816                         for(u32 i=0; i<s.size(); i++)
817                         {
818                                 databuf_nodelist[i*ser_length + 1] = s[i];
819                         }
820                 }
821         
822                 if(version >= 10)
823                 {
824                         // Uncompress and set param2 data
825                         std::ostringstream os(std::ios_base::binary);
826                         decompress(is, os, version);
827                         std::string s = os.str();
828                         if(s.size() != nodecount)
829                                 throw SerializationError
830                                                 ("MapBlock::deSerialize: invalid format");
831                         for(u32 i=0; i<s.size(); i++)
832                         {
833                                 databuf_nodelist[i*ser_length + 2] = s[i];
834                         }
835                 }
836         }
837         // All other versions (newest)
838         else
839         {
840                 u8 flags;
841                 is.read((char*)&flags, 1);
842                 is_underground = (flags & 0x01) ? true : false;
843                 m_day_night_differs = (flags & 0x02) ? true : false;
844                 m_lighting_expired = (flags & 0x04) ? true : false;
845                 if(version >= 18)
846                         m_generated = (flags & 0x08) ? false : true;
847
848                 // Uncompress data
849                 std::ostringstream os(std::ios_base::binary);
850                 decompress(is, os, version);
851                 std::string s = os.str();
852                 if(s.size() != nodecount*3)
853                         throw SerializationError
854                                         ("MapBlock::deSerialize: decompress resulted in size"
855                                         " other than nodecount*3");
856
857                 // deserialize nodes from buffer
858                 for(u32 i=0; i<nodecount; i++)
859                 {
860                         databuf_nodelist[i*ser_length] = s[i];
861                         databuf_nodelist[i*ser_length + 1] = s[i+nodecount];
862                         databuf_nodelist[i*ser_length + 2] = s[i+nodecount*2];
863                 }
864                 
865                 /*
866                         NodeMetadata
867                 */
868                 if(version >= 14)
869                 {
870                         // Ignore errors
871                         try{
872                                 if(version <= 15)
873                                 {
874                                         std::string data = deSerializeString(is);
875                                         std::istringstream iss(data, std::ios_base::binary);
876                                         content_nodemeta_deserialize_legacy(iss,
877                                                         &m_node_metadata, &m_node_timers,
878                                                         m_gamedef);
879                                 }
880                                 else
881                                 {
882                                         //std::string data = deSerializeLongString(is);
883                                         std::ostringstream oss(std::ios_base::binary);
884                                         decompressZlib(is, oss);
885                                         std::istringstream iss(oss.str(), std::ios_base::binary);
886                                         content_nodemeta_deserialize_legacy(iss,
887                                                         &m_node_metadata, &m_node_timers,
888                                                         m_gamedef);
889                                 }
890                         }
891                         catch(SerializationError &e)
892                         {
893                                 errorstream<<"WARNING: MapBlock::deSerialize(): Ignoring an error"
894                                                 <<" while deserializing node metadata"<<std::endl;
895                         }
896                 }
897         }
898
899         // Deserialize node data
900         for(u32 i=0; i<nodecount; i++)
901         {
902                 data[i].deSerialize(&databuf_nodelist[i*ser_length], version);
903         }
904
905         if(disk)
906         {
907                 /*
908                         Versions up from 9 have block objects. (DEPRECATED)
909                 */
910                 if(version >= 9){
911                         u16 count = readU16(is);
912                         // Not supported and length not known if count is not 0
913                         if(count != 0){
914                                 errorstream<<"WARNING: MapBlock::deSerialize_pre22(): "
915                                                 <<"Ignoring stuff coming at and after MBOs"<<std::endl;
916                                 return;
917                         }
918                 }
919
920                 /*
921                         Versions up from 15 have static objects.
922                 */
923                 if(version >= 15)
924                         m_static_objects.deSerialize(is);
925
926                 // Timestamp
927                 if(version >= 17){
928                         setTimestamp(readU32(is));
929                         m_disk_timestamp = m_timestamp;
930                 } else {
931                         setTimestamp(BLOCK_TIMESTAMP_UNDEFINED);
932                 }
933
934                 // Dynamically re-set ids based on node names
935                 NameIdMapping nimap;
936                 // If supported, read node definition id mapping
937                 if(version >= 21){
938                         nimap.deSerialize(is);
939                 // Else set the legacy mapping
940                 } else {
941                         content_mapnode_get_name_id_mapping(&nimap);
942                 }
943                 correctBlockNodeIds(&nimap, data, m_gamedef);
944         }
945
946
947         // Legacy data changes
948         // This code has to convert from pre-22 to post-22 format.
949         INodeDefManager *nodedef = m_gamedef->ndef();
950         for(u32 i=0; i<nodecount; i++)
951         {
952                 const ContentFeatures &f = nodedef->get(data[i].getContent());
953                 // Mineral
954                 if(nodedef->getId("default:stone") == data[i].getContent()
955                                 && data[i].getParam1() == 1)
956                 {
957                         data[i].setContent(nodedef->getId("default:stone_with_coal"));
958                         data[i].setParam1(0);
959                 }
960                 else if(nodedef->getId("default:stone") == data[i].getContent()
961                                 && data[i].getParam1() == 2)
962                 {
963                         data[i].setContent(nodedef->getId("default:stone_with_iron"));
964                         data[i].setParam1(0);
965                 }
966                 // facedir_simple
967                 if(f.legacy_facedir_simple)
968                 {
969                         data[i].setParam2(data[i].getParam1());
970                         data[i].setParam1(0);
971                 }
972                 // wall_mounted
973                 if(f.legacy_wallmounted)
974                 {
975                         u8 wallmounted_new_to_old[8] = {0x04, 0x08, 0x01, 0x02, 0x10, 0x20, 0, 0};
976                         u8 dir_old_format = data[i].getParam2();
977                         u8 dir_new_format = 0;
978                         for(u8 j=0; j<8; j++)
979                         {
980                                 if((dir_old_format & wallmounted_new_to_old[j]) != 0)
981                                 {
982                                         dir_new_format = j;
983                                         break;
984                                 }
985                         }
986                         data[i].setParam2(dir_new_format);
987                 }
988         }
989
990 }
991
992 /*
993         Get a quick string to describe what a block actually contains
994 */
995 std::string analyze_block(MapBlock *block)
996 {
997         if(block == NULL)
998                 return "NULL";
999
1000         std::ostringstream desc;
1001         
1002         v3s16 p = block->getPos();
1003         char spos[20];
1004         snprintf(spos, 20, "(%2d,%2d,%2d), ", p.X, p.Y, p.Z);
1005         desc<<spos;
1006         
1007         switch(block->getModified())
1008         {
1009         case MOD_STATE_CLEAN:
1010                 desc<<"CLEAN,           ";
1011                 break;
1012         case MOD_STATE_WRITE_AT_UNLOAD:
1013                 desc<<"WRITE_AT_UNLOAD, ";
1014                 break;
1015         case MOD_STATE_WRITE_NEEDED:
1016                 desc<<"WRITE_NEEDED,    ";
1017                 break;
1018         default:
1019                 desc<<"unknown getModified()="+itos(block->getModified())+", ";
1020         }
1021
1022         if(block->isGenerated())
1023                 desc<<"is_gen [X], ";
1024         else
1025                 desc<<"is_gen [ ], ";
1026
1027         if(block->getIsUnderground())
1028                 desc<<"is_ug [X], ";
1029         else
1030                 desc<<"is_ug [ ], ";
1031
1032         if(block->getLightingExpired())
1033                 desc<<"lighting_exp [X], ";
1034         else
1035                 desc<<"lighting_exp [ ], ";
1036
1037         if(block->isDummy())
1038         {
1039                 desc<<"Dummy, ";
1040         }
1041         else
1042         {
1043                 bool full_ignore = true;
1044                 bool some_ignore = false;
1045                 bool full_air = true;
1046                 bool some_air = false;
1047                 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
1048                 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
1049                 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
1050                 {
1051                         v3s16 p(x0,y0,z0);
1052                         MapNode n = block->getNode(p);
1053                         content_t c = n.getContent();
1054                         if(c == CONTENT_IGNORE)
1055                                 some_ignore = true;
1056                         else
1057                                 full_ignore = false;
1058                         if(c == CONTENT_AIR)
1059                                 some_air = true;
1060                         else
1061                                 full_air = false;
1062                 }
1063                 
1064                 desc<<"content {";
1065                 
1066                 std::ostringstream ss;
1067                 
1068                 if(full_ignore)
1069                         ss<<"IGNORE (full), ";
1070                 else if(some_ignore)
1071                         ss<<"IGNORE, ";
1072                 
1073                 if(full_air)
1074                         ss<<"AIR (full), ";
1075                 else if(some_air)
1076                         ss<<"AIR, ";
1077                 
1078                 if(ss.str().size()>=2)
1079                         desc<<ss.str().substr(0, ss.str().size()-2);
1080
1081                 desc<<"}, ";
1082         }
1083
1084         return desc.str().substr(0, desc.str().size()-2);
1085 }
1086
1087
1088 //END