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