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