]> git.lizzy.rs Git - dragonfireclient.git/blob - src/mapblock.cpp
Don't include client/game.h on server build
[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 "client/mapblock_mesh.h"
35 #endif
36 #include "porting.h"
37 #include "util/string.h"
38 #include "util/serialize.h"
39 #include "util/basic_macros.h"
40
41 static const char *modified_reason_strings[] = {
42         "initial",
43         "reallocate",
44         "setIsUnderground",
45         "setLightingExpired",
46         "setGenerated",
47         "setNode",
48         "setNodeNoCheck",
49         "setTimestamp",
50         "NodeMetaRef::reportMetadataChange",
51         "clearAllObjects",
52         "Timestamp expired (step)",
53         "addActiveObjectRaw",
54         "removeRemovedObjects/remove",
55         "removeRemovedObjects/deactivate",
56         "Stored list cleared in activateObjects due to overflow",
57         "deactivateFarObjects: Static data moved in",
58         "deactivateFarObjects: Static data moved out",
59         "deactivateFarObjects: Static data changed considerably",
60         "finishBlockMake: expireDayNightDiff",
61         "unknown",
62 };
63
64
65 /*
66         MapBlock
67 */
68
69 MapBlock::MapBlock(Map *parent, v3s16 pos, IGameDef *gamedef, bool dummy):
70                 m_parent(parent),
71                 m_pos(pos),
72                 m_pos_relative(pos * MAP_BLOCKSIZE),
73                 m_gamedef(gamedef)
74 {
75         if (!dummy)
76                 reallocate();
77 }
78
79 MapBlock::~MapBlock()
80 {
81 #ifndef SERVER
82         {
83                 delete mesh;
84                 mesh = nullptr;
85         }
86 #endif
87
88         delete[] data;
89 }
90
91 bool MapBlock::isValidPositionParent(v3s16 p)
92 {
93         if (isValidPosition(p)) {
94                 return true;
95         }
96
97         return m_parent->isValidPosition(getPosRelative() + p);
98 }
99
100 MapNode MapBlock::getNodeParent(v3s16 p, bool *is_valid_position)
101 {
102         if (!isValidPosition(p))
103                 return m_parent->getNode(getPosRelative() + p, is_valid_position);
104
105         if (!data) {
106                 if (is_valid_position)
107                         *is_valid_position = false;
108                 return {CONTENT_IGNORE};
109         }
110         if (is_valid_position)
111                 *is_valid_position = true;
112         return data[p.Z * zstride + p.Y * ystride + p.X];
113 }
114
115 std::string MapBlock::getModifiedReasonString()
116 {
117         std::string reason;
118
119         const u32 ubound = MYMIN(sizeof(m_modified_reason) * CHAR_BIT,
120                 ARRLEN(modified_reason_strings));
121
122         for (u32 i = 0; i != ubound; i++) {
123                 if ((m_modified_reason & (1 << i)) == 0)
124                         continue;
125
126                 reason += modified_reason_strings[i];
127                 reason += ", ";
128         }
129
130         if (reason.length() > 2)
131                 reason.resize(reason.length() - 2);
132
133         return reason;
134 }
135
136
137 void MapBlock::copyTo(VoxelManipulator &dst)
138 {
139         v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
140         VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
141
142         // Copy from data to VoxelManipulator
143         dst.copyFrom(data, data_area, v3s16(0,0,0),
144                         getPosRelative(), data_size);
145 }
146
147 void MapBlock::copyFrom(VoxelManipulator &dst)
148 {
149         v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
150         VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
151
152         // Copy from VoxelManipulator to data
153         dst.copyTo(data, data_area, v3s16(0,0,0),
154                         getPosRelative(), data_size);
155 }
156
157 void MapBlock::actuallyUpdateDayNightDiff()
158 {
159         const NodeDefManager *nodemgr = m_gamedef->ndef();
160
161         // Running this function un-expires m_day_night_differs
162         m_day_night_differs_expired = false;
163
164         if (!data) {
165                 m_day_night_differs = false;
166                 return;
167         }
168
169         bool differs = false;
170
171         /*
172                 Check if any lighting value differs
173         */
174
175         MapNode previous_n(CONTENT_IGNORE);
176         for (u32 i = 0; i < nodecount; i++) {
177                 MapNode n = data[i];
178
179                 // If node is identical to previous node, don't verify if it differs
180                 if (n == previous_n)
181                         continue;
182
183                 differs = !n.isLightDayNightEq(nodemgr);
184                 if (differs)
185                         break;
186                 previous_n = n;
187         }
188
189         /*
190                 If some lighting values differ, check if the whole thing is
191                 just air. If it is just air, differs = false
192         */
193         if (differs) {
194                 bool only_air = true;
195                 for (u32 i = 0; i < nodecount; i++) {
196                         MapNode &n = data[i];
197                         if (n.getContent() != CONTENT_AIR) {
198                                 only_air = false;
199                                 break;
200                         }
201                 }
202                 if (only_air)
203                         differs = false;
204         }
205
206         // Set member variable
207         m_day_night_differs = differs;
208 }
209
210 void MapBlock::expireDayNightDiff()
211 {
212         if (!data) {
213                 m_day_night_differs = false;
214                 m_day_night_differs_expired = false;
215                 return;
216         }
217
218         m_day_night_differs_expired = true;
219 }
220
221 /*
222         Serialization
223 */
224 // List relevant id-name pairs for ids in the block using nodedef
225 // Renumbers the content IDs (starting at 0 and incrementing
226 // use static memory requires about 65535 * sizeof(int) ram in order to be
227 // sure we can handle all content ids. But it's absolutely worth it as it's
228 // a speedup of 4 for one of the major time consuming functions on storing
229 // mapblocks.
230 static content_t getBlockNodeIdMapping_mapping[USHRT_MAX + 1];
231 static void getBlockNodeIdMapping(NameIdMapping *nimap, MapNode *nodes,
232         const NodeDefManager *nodedef)
233 {
234         memset(getBlockNodeIdMapping_mapping, 0xFF, (USHRT_MAX + 1) * sizeof(content_t));
235
236         std::set<content_t> unknown_contents;
237         content_t id_counter = 0;
238         for (u32 i = 0; i < MapBlock::nodecount; i++) {
239                 content_t global_id = nodes[i].getContent();
240                 content_t id = CONTENT_IGNORE;
241
242                 // Try to find an existing mapping
243                 if (getBlockNodeIdMapping_mapping[global_id] != 0xFFFF) {
244                         id = getBlockNodeIdMapping_mapping[global_id];
245                 }
246                 else
247                 {
248                         // We have to assign a new mapping
249                         id = id_counter++;
250                         getBlockNodeIdMapping_mapping[global_id] = id;
251
252                         const ContentFeatures &f = nodedef->get(global_id);
253                         const std::string &name = f.name;
254                         if (name.empty())
255                                 unknown_contents.insert(global_id);
256                         else
257                                 nimap->set(id, name);
258                 }
259
260                 // Update the MapNode
261                 nodes[i].setContent(id);
262         }
263         for (u16 unknown_content : unknown_contents) {
264                 errorstream << "getBlockNodeIdMapping(): IGNORING ERROR: "
265                                 << "Name for node id " << unknown_content << " not known" << std::endl;
266         }
267 }
268 // Correct ids in the block to match nodedef based on names.
269 // Unknown ones are added to nodedef.
270 // Will not update itself to match id-name pairs in nodedef.
271 static void correctBlockNodeIds(const NameIdMapping *nimap, MapNode *nodes,
272                 IGameDef *gamedef)
273 {
274         const NodeDefManager *nodedef = gamedef->ndef();
275         // This means the block contains incorrect ids, and we contain
276         // the information to convert those to names.
277         // nodedef contains information to convert our names to globally
278         // correct ids.
279         std::unordered_set<content_t> unnamed_contents;
280         std::unordered_set<std::string> unallocatable_contents;
281
282         bool previous_exists = false;
283         content_t previous_local_id = CONTENT_IGNORE;
284         content_t previous_global_id = CONTENT_IGNORE;
285
286         for (u32 i = 0; i < MapBlock::nodecount; i++) {
287                 content_t local_id = nodes[i].getContent();
288                 // If previous node local_id was found and same than before, don't lookup maps
289                 // apply directly previous resolved id
290                 // This permits to massively improve loading performance when nodes are similar
291                 // example: default:air, default:stone are massively present
292                 if (previous_exists && local_id == previous_local_id) {
293                         nodes[i].setContent(previous_global_id);
294                         continue;
295                 }
296
297                 std::string name;
298                 if (!nimap->getName(local_id, name)) {
299                         unnamed_contents.insert(local_id);
300                         previous_exists = false;
301                         continue;
302                 }
303
304                 content_t global_id;
305                 if (!nodedef->getId(name, global_id)) {
306                         global_id = gamedef->allocateUnknownNodeId(name);
307                         if (global_id == CONTENT_IGNORE) {
308                                 unallocatable_contents.insert(name);
309                                 previous_exists = false;
310                                 continue;
311                         }
312                 }
313                 nodes[i].setContent(global_id);
314
315                 // Save previous node local_id & global_id result
316                 previous_local_id = local_id;
317                 previous_global_id = global_id;
318                 previous_exists = true;
319         }
320
321         for (const content_t c: unnamed_contents) {
322                 errorstream << "correctBlockNodeIds(): IGNORING ERROR: "
323                                 << "Block contains id " << c
324                                 << " with no name mapping" << std::endl;
325         }
326         for (const std::string &node_name: unallocatable_contents) {
327                 errorstream << "correctBlockNodeIds(): IGNORING ERROR: "
328                                 << "Could not allocate global id for node name \""
329                                 << node_name << "\"" << std::endl;
330         }
331 }
332
333 void MapBlock::serialize(std::ostream &os_compressed, u8 version, bool disk, int compression_level)
334 {
335         if(!ser_ver_supported(version))
336                 throw VersionMismatchException("ERROR: MapBlock format not supported");
337
338         if (!data)
339                 throw SerializationError("ERROR: Not writing dummy block.");
340
341         FATAL_ERROR_IF(version < SER_FMT_VER_LOWEST_WRITE, "Serialisation version error");
342
343         std::ostringstream os_raw(std::ios_base::binary);
344         std::ostream &os = version >= 29 ? os_raw : os_compressed;
345
346         // First byte
347         u8 flags = 0;
348         if(is_underground)
349                 flags |= 0x01;
350         if(getDayNightDiff())
351                 flags |= 0x02;
352         if (!m_generated)
353                 flags |= 0x08;
354         writeU8(os, flags);
355         if (version >= 27) {
356                 writeU16(os, m_lighting_complete);
357         }
358
359         /*
360                 Bulk node data
361         */
362         NameIdMapping nimap;
363         SharedBuffer<u8> buf;
364         const u8 content_width = 2;
365         const u8 params_width = 2;
366         if(disk)
367         {
368                 MapNode *tmp_nodes = new MapNode[nodecount];
369                 memcpy(tmp_nodes, data, nodecount * sizeof(MapNode));
370                 getBlockNodeIdMapping(&nimap, tmp_nodes, m_gamedef->ndef());
371
372                 buf = MapNode::serializeBulk(version, tmp_nodes, nodecount,
373                                 content_width, params_width);
374                 delete[] tmp_nodes;
375
376                 // write timestamp and node/id mapping first
377                 if (version >= 29) {
378                         writeU32(os, getTimestamp());
379
380                         nimap.serialize(os);
381                 }
382         }
383         else
384         {
385                 buf = MapNode::serializeBulk(version, data, nodecount,
386                                 content_width, params_width);
387         }
388
389         writeU8(os, content_width);
390         writeU8(os, params_width);
391         if (version >= 29) {
392                 os.write(reinterpret_cast<char*>(*buf), buf.getSize());
393         } else {
394                 // prior to 29 node data was compressed individually
395                 compress(buf, os, version, compression_level);
396         }
397
398         /*
399                 Node metadata
400         */
401         if (version >= 29) {
402                 m_node_metadata.serialize(os, version, disk);
403         } else {
404                 // use os_raw from above to avoid allocating another stream object
405                 m_node_metadata.serialize(os_raw, version, disk);
406                 // prior to 29 node data was compressed individually
407                 compress(os_raw.str(), os, version, compression_level);
408         }
409
410         /*
411                 Data that goes to disk, but not the network
412         */
413         if(disk)
414         {
415                 if(version <= 24){
416                         // Node timers
417                         m_node_timers.serialize(os, version);
418                 }
419
420                 // Static objects
421                 m_static_objects.serialize(os);
422
423                 if(version < 29){
424                         // Timestamp
425                         writeU32(os, getTimestamp());
426
427                         // Write block-specific node definition id mapping
428                         nimap.serialize(os);
429                 }
430
431                 if(version >= 25){
432                         // Node timers
433                         m_node_timers.serialize(os, version);
434                 }
435         }
436
437         if (version >= 29) {
438                 // now compress the whole thing
439                 compress(os_raw.str(), os_compressed, version, compression_level);
440         }
441 }
442
443 void MapBlock::serializeNetworkSpecific(std::ostream &os)
444 {
445         if (!data) {
446                 throw SerializationError("ERROR: Not writing dummy block.");
447         }
448
449         writeU8(os, 2); // version
450 }
451
452 void MapBlock::deSerialize(std::istream &in_compressed, u8 version, bool disk)
453 {
454         if(!ser_ver_supported(version))
455                 throw VersionMismatchException("ERROR: MapBlock format not supported");
456
457         TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())<<std::endl);
458
459         m_day_night_differs_expired = false;
460
461         if(version <= 21)
462         {
463                 deSerialize_pre22(in_compressed, version, disk);
464                 return;
465         }
466
467         // Decompress the whole block (version >= 29)
468         std::stringstream in_raw(std::ios_base::binary | std::ios_base::in | std::ios_base::out);
469         if (version >= 29)
470                 decompress(in_compressed, in_raw, version);
471         std::istream &is = version >= 29 ? in_raw : in_compressed;
472
473         u8 flags = readU8(is);
474         is_underground = (flags & 0x01) != 0;
475         m_day_night_differs = (flags & 0x02) != 0;
476         if (version < 27)
477                 m_lighting_complete = 0xFFFF;
478         else
479                 m_lighting_complete = readU16(is);
480         m_generated = (flags & 0x08) == 0;
481
482         NameIdMapping nimap;
483         if (disk && version >= 29) {
484                 // Timestamp
485                 TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
486                                 <<": Timestamp"<<std::endl);
487                 setTimestampNoChangedFlag(readU32(is));
488                 m_disk_timestamp = m_timestamp;
489
490                 // Node/id mapping
491                 TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
492                                 <<": NameIdMapping"<<std::endl);
493                 nimap.deSerialize(is);
494         }
495
496         TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
497                         <<": Bulk node data"<<std::endl);
498         u8 content_width = readU8(is);
499         u8 params_width = readU8(is);
500         if(content_width != 1 && content_width != 2)
501                 throw SerializationError("MapBlock::deSerialize(): invalid content_width");
502         if(params_width != 2)
503                 throw SerializationError("MapBlock::deSerialize(): invalid params_width");
504
505         /*
506                 Bulk node data
507         */
508         if (version >= 29) {
509                 MapNode::deSerializeBulk(is, version, data, nodecount,
510                         content_width, params_width);
511         } else {
512                 // use in_raw from above to avoid allocating another stream object
513                 decompress(is, in_raw, version);
514                 MapNode::deSerializeBulk(in_raw, version, data, nodecount,
515                         content_width, params_width);
516         }
517
518         /*
519                 NodeMetadata
520         */
521         TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
522                         <<": Node metadata"<<std::endl);
523         if (version >= 29) {
524                 m_node_metadata.deSerialize(is, m_gamedef->idef());
525         } else {
526                 try {
527                         // reuse in_raw
528                         in_raw.str("");
529                         in_raw.clear();
530                         decompress(is, in_raw, version);
531                         if (version >= 23)
532                                 m_node_metadata.deSerialize(in_raw, m_gamedef->idef());
533                         else
534                                 content_nodemeta_deserialize_legacy(in_raw,
535                                         &m_node_metadata, &m_node_timers,
536                                         m_gamedef->idef());
537                 } catch(SerializationError &e) {
538                         warningstream<<"MapBlock::deSerialize(): Ignoring an error"
539                                         <<" while deserializing node metadata at ("
540                                         <<PP(getPos())<<": "<<e.what()<<std::endl;
541                 }
542         }
543
544         /*
545                 Data that is only on disk
546         */
547         if(disk)
548         {
549                 // Node timers
550                 if(version == 23){
551                         // Read unused zero
552                         readU8(is);
553                 }
554                 if(version == 24){
555                         TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
556                                         <<": Node timers (ver==24)"<<std::endl);
557                         m_node_timers.deSerialize(is, version);
558                 }
559
560                 // Static objects
561                 TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
562                                 <<": Static objects"<<std::endl);
563                 m_static_objects.deSerialize(is);
564
565                 if(version < 29) {
566                         // Timestamp
567                         TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
568                                     <<": Timestamp"<<std::endl);
569                         setTimestampNoChangedFlag(readU32(is));
570                         m_disk_timestamp = m_timestamp;
571
572                         // Node/id mapping
573                         TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
574                                     <<": NameIdMapping"<<std::endl);
575                         nimap.deSerialize(is);
576                 }
577
578                 // Dynamically re-set ids based on node names
579                 correctBlockNodeIds(&nimap, data, m_gamedef);
580
581                 if(version >= 25){
582                         TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
583                                         <<": Node timers (ver>=25)"<<std::endl);
584                         m_node_timers.deSerialize(is, version);
585                 }
586         }
587
588         TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
589                         <<": Done."<<std::endl);
590 }
591
592 void MapBlock::deSerializeNetworkSpecific(std::istream &is)
593 {
594         try {
595                 readU8(is);
596                 //const u8 version = readU8(is);
597                 //if (version != 1)
598                         //throw SerializationError("unsupported MapBlock version");
599
600         } catch(SerializationError &e) {
601                 warningstream<<"MapBlock::deSerializeNetworkSpecific(): Ignoring an error"
602                                 <<": "<<e.what()<<std::endl;
603         }
604 }
605
606 /*
607         Legacy serialization
608 */
609
610 void MapBlock::deSerialize_pre22(std::istream &is, u8 version, bool disk)
611 {
612         // Initialize default flags
613         is_underground = false;
614         m_day_night_differs = false;
615         m_lighting_complete = 0xFFFF;
616         m_generated = true;
617
618         // Make a temporary buffer
619         u32 ser_length = MapNode::serializedLength(version);
620         SharedBuffer<u8> databuf_nodelist(nodecount * ser_length);
621
622         // These have no compression
623         if (version <= 3 || version == 5 || version == 6) {
624                 char tmp;
625                 is.read(&tmp, 1);
626                 if (is.gcount() != 1)
627                         throw SerializationError(std::string(FUNCTION_NAME)
628                                 + ": not enough input data");
629                 is_underground = tmp;
630                 is.read((char *)*databuf_nodelist, nodecount * ser_length);
631                 if ((u32)is.gcount() != nodecount * ser_length)
632                         throw SerializationError(std::string(FUNCTION_NAME)
633                                 + ": not enough input data");
634         } else if (version <= 10) {
635                 u8 t8;
636                 is.read((char *)&t8, 1);
637                 is_underground = t8;
638
639                 {
640                         // Uncompress and set material data
641                         std::ostringstream os(std::ios_base::binary);
642                         decompress(is, os, version);
643                         std::string s = os.str();
644                         if (s.size() != nodecount)
645                                 throw SerializationError(std::string(FUNCTION_NAME)
646                                         + ": not enough input data");
647                         for (u32 i = 0; i < s.size(); i++) {
648                                 databuf_nodelist[i*ser_length] = s[i];
649                         }
650                 }
651                 {
652                         // Uncompress and set param data
653                         std::ostringstream os(std::ios_base::binary);
654                         decompress(is, os, version);
655                         std::string s = os.str();
656                         if (s.size() != nodecount)
657                                 throw SerializationError(std::string(FUNCTION_NAME)
658                                         + ": not enough input data");
659                         for (u32 i = 0; i < s.size(); i++) {
660                                 databuf_nodelist[i*ser_length + 1] = s[i];
661                         }
662                 }
663
664                 if (version >= 10) {
665                         // Uncompress and set param2 data
666                         std::ostringstream os(std::ios_base::binary);
667                         decompress(is, os, version);
668                         std::string s = os.str();
669                         if (s.size() != nodecount)
670                                 throw SerializationError(std::string(FUNCTION_NAME)
671                                         + ": not enough input data");
672                         for (u32 i = 0; i < s.size(); i++) {
673                                 databuf_nodelist[i*ser_length + 2] = s[i];
674                         }
675                 }
676         } else { // All other versions (10 to 21)
677                 u8 flags;
678                 is.read((char*)&flags, 1);
679                 is_underground = (flags & 0x01) != 0;
680                 m_day_night_differs = (flags & 0x02) != 0;
681                 if(version >= 18)
682                         m_generated = (flags & 0x08) == 0;
683
684                 // Uncompress data
685                 std::ostringstream os(std::ios_base::binary);
686                 decompress(is, os, version);
687                 std::string s = os.str();
688                 if (s.size() != nodecount * 3)
689                         throw SerializationError(std::string(FUNCTION_NAME)
690                                 + ": decompress resulted in size other than nodecount*3");
691
692                 // deserialize nodes from buffer
693                 for (u32 i = 0; i < nodecount; i++) {
694                         databuf_nodelist[i*ser_length] = s[i];
695                         databuf_nodelist[i*ser_length + 1] = s[i+nodecount];
696                         databuf_nodelist[i*ser_length + 2] = s[i+nodecount*2];
697                 }
698
699                 /*
700                         NodeMetadata
701                 */
702                 if (version >= 14) {
703                         // Ignore errors
704                         try {
705                                 if (version <= 15) {
706                                         std::string data = deSerializeString16(is);
707                                         std::istringstream iss(data, std::ios_base::binary);
708                                         content_nodemeta_deserialize_legacy(iss,
709                                                 &m_node_metadata, &m_node_timers,
710                                                 m_gamedef->idef());
711                                 } else {
712                                         //std::string data = deSerializeString32(is);
713                                         std::ostringstream oss(std::ios_base::binary);
714                                         decompressZlib(is, oss);
715                                         std::istringstream iss(oss.str(), std::ios_base::binary);
716                                         content_nodemeta_deserialize_legacy(iss,
717                                                 &m_node_metadata, &m_node_timers,
718                                                 m_gamedef->idef());
719                                 }
720                         } catch(SerializationError &e) {
721                                 warningstream<<"MapBlock::deSerialize(): Ignoring an error"
722                                                 <<" while deserializing node metadata"<<std::endl;
723                         }
724                 }
725         }
726
727         // Deserialize node data
728         for (u32 i = 0; i < nodecount; i++) {
729                 data[i].deSerialize(&databuf_nodelist[i * ser_length], version);
730         }
731
732         if (disk) {
733                 /*
734                         Versions up from 9 have block objects. (DEPRECATED)
735                 */
736                 if (version >= 9) {
737                         u16 count = readU16(is);
738                         // Not supported and length not known if count is not 0
739                         if(count != 0){
740                                 warningstream<<"MapBlock::deSerialize_pre22(): "
741                                                 <<"Ignoring stuff coming at and after MBOs"<<std::endl;
742                                 return;
743                         }
744                 }
745
746                 /*
747                         Versions up from 15 have static objects.
748                 */
749                 if (version >= 15)
750                         m_static_objects.deSerialize(is);
751
752                 // Timestamp
753                 if (version >= 17) {
754                         setTimestampNoChangedFlag(readU32(is));
755                         m_disk_timestamp = m_timestamp;
756                 } else {
757                         setTimestampNoChangedFlag(BLOCK_TIMESTAMP_UNDEFINED);
758                 }
759
760                 // Dynamically re-set ids based on node names
761                 NameIdMapping nimap;
762                 // If supported, read node definition id mapping
763                 if (version >= 21) {
764                         nimap.deSerialize(is);
765                 // Else set the legacy mapping
766                 } else {
767                         content_mapnode_get_name_id_mapping(&nimap);
768                 }
769                 correctBlockNodeIds(&nimap, data, m_gamedef);
770         }
771
772
773         // Legacy data changes
774         // This code has to convert from pre-22 to post-22 format.
775         const NodeDefManager *nodedef = m_gamedef->ndef();
776         for(u32 i=0; i<nodecount; i++)
777         {
778                 const ContentFeatures &f = nodedef->get(data[i].getContent());
779                 // Mineral
780                 if(nodedef->getId("default:stone") == data[i].getContent()
781                                 && data[i].getParam1() == 1)
782                 {
783                         data[i].setContent(nodedef->getId("default:stone_with_coal"));
784                         data[i].setParam1(0);
785                 }
786                 else if(nodedef->getId("default:stone") == data[i].getContent()
787                                 && data[i].getParam1() == 2)
788                 {
789                         data[i].setContent(nodedef->getId("default:stone_with_iron"));
790                         data[i].setParam1(0);
791                 }
792                 // facedir_simple
793                 if(f.legacy_facedir_simple)
794                 {
795                         data[i].setParam2(data[i].getParam1());
796                         data[i].setParam1(0);
797                 }
798                 // wall_mounted
799                 if(f.legacy_wallmounted)
800                 {
801                         u8 wallmounted_new_to_old[8] = {0x04, 0x08, 0x01, 0x02, 0x10, 0x20, 0, 0};
802                         u8 dir_old_format = data[i].getParam2();
803                         u8 dir_new_format = 0;
804                         for(u8 j=0; j<8; j++)
805                         {
806                                 if((dir_old_format & wallmounted_new_to_old[j]) != 0)
807                                 {
808                                         dir_new_format = j;
809                                         break;
810                                 }
811                         }
812                         data[i].setParam2(dir_new_format);
813                 }
814         }
815
816 }
817
818 /*
819         Get a quick string to describe what a block actually contains
820 */
821 std::string analyze_block(MapBlock *block)
822 {
823         if(block == NULL)
824                 return "NULL";
825
826         std::ostringstream desc;
827
828         v3s16 p = block->getPos();
829         char spos[25];
830         porting::mt_snprintf(spos, sizeof(spos), "(%2d,%2d,%2d), ", p.X, p.Y, p.Z);
831         desc<<spos;
832
833         switch(block->getModified())
834         {
835         case MOD_STATE_CLEAN:
836                 desc<<"CLEAN,           ";
837                 break;
838         case MOD_STATE_WRITE_AT_UNLOAD:
839                 desc<<"WRITE_AT_UNLOAD, ";
840                 break;
841         case MOD_STATE_WRITE_NEEDED:
842                 desc<<"WRITE_NEEDED,    ";
843                 break;
844         default:
845                 desc<<"unknown getModified()="+itos(block->getModified())+", ";
846         }
847
848         if(block->isGenerated())
849                 desc<<"is_gen [X], ";
850         else
851                 desc<<"is_gen [ ], ";
852
853         if(block->getIsUnderground())
854                 desc<<"is_ug [X], ";
855         else
856                 desc<<"is_ug [ ], ";
857
858         desc<<"lighting_complete: "<<block->getLightingComplete()<<", ";
859
860         if(block->isDummy())
861         {
862                 desc<<"Dummy, ";
863         }
864         else
865         {
866                 bool full_ignore = true;
867                 bool some_ignore = false;
868                 bool full_air = true;
869                 bool some_air = false;
870                 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
871                 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
872                 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
873                 {
874                         v3s16 p(x0,y0,z0);
875                         MapNode n = block->getNodeNoEx(p);
876                         content_t c = n.getContent();
877                         if(c == CONTENT_IGNORE)
878                                 some_ignore = true;
879                         else
880                                 full_ignore = false;
881                         if(c == CONTENT_AIR)
882                                 some_air = true;
883                         else
884                                 full_air = false;
885                 }
886
887                 desc<<"content {";
888
889                 std::ostringstream ss;
890
891                 if(full_ignore)
892                         ss<<"IGNORE (full), ";
893                 else if(some_ignore)
894                         ss<<"IGNORE, ";
895
896                 if(full_air)
897                         ss<<"AIR (full), ";
898                 else if(some_air)
899                         ss<<"AIR, ";
900
901                 if(ss.str().size()>=2)
902                         desc<<ss.str().substr(0, ss.str().size()-2);
903
904                 desc<<"}, ";
905         }
906
907         return desc.str().substr(0, desc.str().size()-2);
908 }
909
910
911 //END