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