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