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