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