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